blob: 5fbb3049ea75f06dd04b045becc4f4182e1d3797 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes5c9b1fc2021-01-07 14:20:49 +00002 * Copyright (C) 2015 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 Hughesbd4d2852017-09-13 14:05:14 +010016#include <string.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000017#ifdef HAVE_UTSNAME_H
Richard Hughes473c5202018-01-11 21:06:16 +000018#include <sys/utsname.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000019#endif
Richard Hughes019a1bc2019-11-26 10:19:33 +000020#include <errno.h>
Richard Hughes9945edb2017-06-19 10:03:55 +010021
22#include "fwupd-common-private.h"
23#include "fwupd-enums-private.h"
24#include "fwupd-error.h"
25#include "fwupd-release-private.h"
26#include "fwupd-remote-private.h"
27#include "fwupd-resources.h"
Mario Limonciello75835b42021-01-21 12:47:32 -060028#include "fwupd-security-attr-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010029
Richard Hughes38911712021-03-12 14:19:14 +000030#include "fu-backend.h"
Richard Hughesc6eb4162020-02-27 10:02:36 +000031#include "fu-cabinet.h"
Richard Hughesdeea2da2017-12-15 15:40:44 +000032#include "fu-common-cab.h"
Richard Hughes943d2c92017-06-21 09:04:39 +010033#include "fu-common.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010034#include "fu-config.h"
Richard Hughesb333e002021-04-01 10:40:02 +010035#include "fu-context-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010036#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 Hughesdf89cd52020-06-26 20:25:18 +010041#include "fu-engine-request.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"
Richard Hughes405baeb2018-09-06 14:23:08 +010054#include "fu-udev-device-private.h"
Richard Hughes117f8572021-02-09 20:02:48 +000055
56#ifdef HAVE_GUDEV
57#include "fu-udev-backend.h"
58#endif
59#ifdef HAVE_GUSB
60#include "fu-usb-backend.h"
61#endif
Ricardo Cañuelo536fb852021-02-12 12:48:58 +010062#ifdef HAVE_BLUEZ
63#include "fu-bluez-backend.h"
64#endif
Richard Hughes9945edb2017-06-19 10:03:55 +010065
Richard Hughes95c98a92019-10-22 16:03:15 +010066#include "fu-dfu-firmware.h"
Richard Hughes98972f42021-01-29 11:35:02 +000067#include "fu-dfuse-firmware.h"
Benson Leungacba98b2020-05-29 14:39:41 -070068#include "fu-fmap-firmware.h"
Richard Hughes95c98a92019-10-22 16:03:15 +010069#include "fu-ihex-firmware.h"
70#include "fu-srec-firmware.h"
71
Richard Hughesafda9622020-06-10 12:48:31 +010072/* only needed until we hard depend on jcat 0.1.3 */
73#include <libjcat/jcat-version.h>
74
Richard Hughes3d005222019-05-17 14:02:41 +010075#ifdef HAVE_SYSTEMD
76#include "fu-systemd.h"
77#endif
78
Richard Hughes9945edb2017-06-19 10:03:55 +010079static void fu_engine_finalize (GObject *obj);
Richard Hughesfb0a9382020-06-23 16:56:28 +010080static void fu_engine_ensure_security_attrs (FuEngine *self);
Richard Hughes9945edb2017-06-19 10:03:55 +010081
82struct _FuEngine
83{
84 GObject parent_instance;
Richard Hughes5b5f6552018-05-18 10:22:39 +010085 FuAppFlags app_flags;
Richard Hughes117f8572021-02-09 20:02:48 +000086 GPtrArray *backends;
Richard Hughes9945edb2017-06-19 10:03:55 +010087 FuConfig *config;
Richard Hughesd1808aa2019-12-10 15:20:30 +000088 FuRemoteList *remote_list;
Richard Hughes0a7e7832017-11-22 11:01:13 +000089 FuDeviceList *device_list;
Richard Hughes9945edb2017-06-19 10:03:55 +010090 FwupdStatus status;
Richard Hughesf425d292019-01-18 17:57:39 +000091 gboolean tainted;
Richard Hughes9945edb2017-06-19 10:03:55 +010092 guint percentage;
Richard Hughesbc3a4e12018-01-06 22:41:47 +000093 FuHistory *history;
Richard Hughes75b965d2018-11-15 13:51:21 +000094 FuIdle *idle;
Richard Hughes481aa2a2018-09-18 20:51:46 +010095 XbSilo *silo;
Richard Hughes9945edb2017-06-19 10:03:55 +010096 guint coldplug_id;
Richard Hughese7e95452017-11-22 09:05:53 +000097 FuPluginList *plugin_list;
Richard Hughesc02ee4d2018-05-22 15:46:03 +010098 GPtrArray *plugin_filter;
Richard Hughesb333e002021-04-01 10:40:02 +010099 FuContext *ctx;
Richard Hughes0eb123b2018-04-19 12:00:04 +0100100 GHashTable *runtime_versions;
Richard Hughes34e0dab2018-04-20 16:43:00 +0100101 GHashTable *compile_versions;
Richard Hughes3444cf62020-06-22 15:10:28 +0100102 GHashTable *approved_firmware; /* (nullable) */
Richard Hughes31206832020-07-27 15:31:11 +0100103 GHashTable *blocked_firmware; /* (nullable) */
Richard Hughes0917fb62019-09-21 12:55:37 +0100104 gchar *host_machine_id;
Richard Hughesd5aab652020-02-25 12:47:50 +0000105 JcatContext *jcat_context;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600106 gboolean loaded;
Richard Hughes196c6c62020-05-11 19:42:47 +0100107 gchar *host_security_id;
Richard Hughesfb0a9382020-06-23 16:56:28 +0100108 FuSecurityAttrs *host_security_attrs;
Richard Hughes9945edb2017-06-19 10:03:55 +0100109};
110
111enum {
112 SIGNAL_CHANGED,
113 SIGNAL_DEVICE_ADDED,
114 SIGNAL_DEVICE_REMOVED,
115 SIGNAL_DEVICE_CHANGED,
116 SIGNAL_STATUS_CHANGED,
117 SIGNAL_PERCENTAGE_CHANGED,
118 SIGNAL_LAST
119};
120
121static guint signals[SIGNAL_LAST] = { 0 };
122
123G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT)
124
Richard Hughes9945edb2017-06-19 10:03:55 +0100125static void
126fu_engine_emit_changed (FuEngine *self)
127{
128 g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
Richard Hughes75b965d2018-11-15 13:51:21 +0000129 fu_engine_idle_reset (self);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600130
131 /* update the motd */
132 if (self->loaded &&
133 fu_config_get_update_motd (self->config)) {
134 g_autoptr(GError) error_local = NULL;
135 if (!fu_engine_update_motd (self, &error_local))
Richard Hughes521c2e22020-10-01 14:12:41 +0100136 g_debug ("failed to update MOTD: %s",
137 error_local->message);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600138 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100139}
140
141static void
Richard Hughes170c0c12017-11-22 11:26:24 +0000142fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
143{
Richard Hughesfb0a9382020-06-23 16:56:28 +0100144 /* invalidate host security attributes */
145 g_clear_pointer (&self->host_security_id, g_free);
Richard Hughes170c0c12017-11-22 11:26:24 +0000146 g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
147}
148
Richard Hughesb333e002021-04-01 10:40:02 +0100149FuContext *
150fu_engine_get_context (FuEngine *self)
Mario Limoncielloda0d1882020-05-05 14:16:39 -0500151{
Richard Hughesb333e002021-04-01 10:40:02 +0100152 return self->ctx;
Richard Hughes95c98a92019-10-22 16:03:15 +0100153}
154
Richard Hughes9945edb2017-06-19 10:03:55 +0100155/**
156 * fu_engine_get_status:
157 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +0100158 *
159 * Gets the current engine status.
160 *
161 * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING
162 **/
163FwupdStatus
164fu_engine_get_status (FuEngine *self)
165{
166 g_return_val_if_fail (FU_IS_ENGINE (self), 0);
167 return self->status;
168}
169
Richard Hughes9945edb2017-06-19 10:03:55 +0100170static void
171fu_engine_set_status (FuEngine *self, FwupdStatus status)
172{
173 if (self->status == status)
174 return;
175 self->status = status;
176
177 /* emit changed */
178 g_debug ("Emitting PropertyChanged('Status'='%s')",
179 fwupd_status_to_string (status));
180 g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status);
181}
182
183static void
184fu_engine_set_percentage (FuEngine *self, guint percentage)
185{
186 if (self->percentage == percentage)
187 return;
188 self->percentage = percentage;
189
190 /* emit changed */
191 g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
192}
193
194static void
Richard Hughes4a036012017-11-30 16:33:24 +0000195fu_engine_progress_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
196{
Richard Hughes2a77afc2019-01-30 11:19:48 +0000197 if (fu_device_get_status (device) == FWUPD_STATUS_UNKNOWN)
198 return;
Richard Hughes4a036012017-11-30 16:33:24 +0000199 fu_engine_set_percentage (self, fu_device_get_progress (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100200 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000201}
202
203static void
204fu_engine_status_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
205{
206 fu_engine_set_status (self, fu_device_get_status (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100207 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000208}
209
210static void
Richard Hughesfbcebe02017-12-11 16:37:19 +0000211fu_engine_watch_device (FuEngine *self, FuDevice *device)
Richard Hughes4a036012017-11-30 16:33:24 +0000212{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100213 g_autoptr(FuDevice) device_old = fu_device_list_get_old (self->device_list, device);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000214 if (device_old != NULL) {
215 g_signal_handlers_disconnect_by_func (device_old,
216 fu_engine_progress_notify_cb,
217 self);
218 g_signal_handlers_disconnect_by_func (device_old,
219 fu_engine_status_notify_cb,
220 self);
221 }
Richard Hughes4a036012017-11-30 16:33:24 +0000222 g_signal_connect (device, "notify::progress",
223 G_CALLBACK (fu_engine_progress_notify_cb), self);
224 g_signal_connect (device, "notify::status",
225 G_CALLBACK (fu_engine_status_notify_cb), self);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000226}
227
228static void
Richard Hughes4d76d182021-04-06 13:35:15 +0100229fu_engine_ensure_device_battery_inhibit (FuEngine *self, FuDevice *device)
230{
231 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC) &&
232 (fu_context_get_battery_state (self->ctx) == FU_BATTERY_STATE_DISCHARGING ||
233 fu_context_get_battery_state (self->ctx) == FU_BATTERY_STATE_EMPTY)) {
234 fu_device_inhibit (device, "battery-system",
235 "Cannot install update when not on AC power");
236 return;
237 }
238 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REQUIRE_AC) &&
239 fu_context_get_battery_level (self->ctx) != FU_BATTERY_VALUE_INVALID &&
240 fu_context_get_battery_threshold (self->ctx) != FU_BATTERY_VALUE_INVALID &&
241 fu_context_get_battery_level (self->ctx) < fu_context_get_battery_threshold (self->ctx)) {
242 g_autofree gchar *reason = NULL;
243 reason = g_strdup_printf ("Cannot install update when system battery is not at least %u%%",
244 fu_context_get_battery_threshold (self->ctx));
245 fu_device_inhibit (device, "battery-system", reason);
246 return;
247 }
248 fu_device_uninhibit (device, "battery-system");
249}
250
251static void
Richard Hughesfbcebe02017-12-11 16:37:19 +0000252fu_engine_device_added_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
253{
254 fu_engine_watch_device (self, device);
Richard Hughes4d76d182021-04-06 13:35:15 +0100255 fu_engine_ensure_device_battery_inhibit (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000256 g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device);
257}
258
259static void
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500260fu_engine_device_runner_device_removed (FuEngine *self, FuDevice *device)
261{
262 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
263 for (guint j = 0; j < plugins->len; j++) {
264 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
265 fu_plugin_runner_device_removed (plugin_tmp, device);
266 }
267}
268
269static void
Richard Hughes4a036012017-11-30 16:33:24 +0000270fu_engine_device_removed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
271{
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500272 fu_engine_device_runner_device_removed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000273 g_signal_handlers_disconnect_by_data (device, self);
274 g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device);
275}
276
277static void
278fu_engine_device_changed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
279{
Richard Hughesfbcebe02017-12-11 16:37:19 +0000280 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000281 fu_engine_emit_device_changed (self, device);
282}
283
Richard Hughes481aa2a2018-09-18 20:51:46 +0100284/* convert hex and decimal versions to dotted style */
285static gchar *
Richard Hughes2c40b372019-04-17 13:41:47 +0100286fu_engine_get_release_version (FuEngine *self, FuDevice *dev, XbNode *rel, GError **error)
Richard Hughes611f1a92018-01-11 11:54:28 +0000287{
Mario Limonciellof675c212020-02-25 11:12:35 -0600288 FwupdVersionFormat fmt = fu_device_get_version_format (dev);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100289 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100290 guint64 ver_uint32;
291
292 /* get version */
293 version = xb_node_get_attr (rel, "version");
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100294 if (version == NULL) {
295 g_set_error_literal (error,
296 FWUPD_ERROR,
297 FWUPD_ERROR_NOT_SUPPORTED,
298 "version unset");
Richard Hughes611f1a92018-01-11 11:54:28 +0000299 return NULL;
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100300 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100301
302 /* already dotted notation */
303 if (g_strstr_len (version, -1, ".") != NULL)
304 return g_strdup (version);
305
Richard Hughes481aa2a2018-09-18 20:51:46 +0100306 /* don't touch my version! */
Richard Hughesc84b36c2019-04-27 09:24:42 +0100307 if (fmt == FWUPD_VERSION_FORMAT_PLAIN)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100308 return g_strdup (version);
309
310 /* parse as integer */
311 ver_uint32 = fu_common_strtoull (version);
Mario Limonciellof675c212020-02-25 11:12:35 -0600312 if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN ||
313 ver_uint32 == 0 || ver_uint32 > G_MAXUINT32)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100314 return g_strdup (version);
315
316 /* convert to dotted decimal */
317 return fu_common_version_from_uint32 ((guint32) ver_uint32, fmt);
Richard Hughes611f1a92018-01-11 11:54:28 +0000318}
319
Richard Hughesb856f0b2021-01-28 10:59:49 +0000320static gint
321fu_engine_scheme_compare_cb (gconstpointer a, gconstpointer b, gpointer user_data)
322{
323 FuEngine *self = FU_ENGINE (user_data);
324 const gchar *location1 = *((const gchar ** )a);
325 const gchar *location2 = *((const gchar **) b);
326 g_autofree gchar *scheme1 = fu_common_uri_get_scheme (location1);
327 g_autofree gchar *scheme2 = fu_common_uri_get_scheme (location2);
328 guint prio1 = fu_config_get_uri_scheme_prio (self->config, scheme1);
329 guint prio2 = fu_config_get_uri_scheme_prio (self->config, scheme2);
330 if (prio1 < prio2)
331 return -1;
332 if (prio1 > prio2)
333 return 1;
334 return 0;
335}
336
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100337static gboolean
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000338fu_engine_set_release_from_artifact (FuEngine *self,
339 FwupdRelease *rel,
340 FwupdRemote *remote,
341 XbNode *artifact,
342 GError **error)
343{
Richard Hughesc2f7ed32021-01-28 20:11:01 +0000344 const gchar *filename;
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000345 guint64 size;
346 g_autoptr(GPtrArray) locations = NULL;
347 g_autoptr(GPtrArray) checksums = NULL;
348
Richard Hughesc2f7ed32021-01-28 20:11:01 +0000349 /* filename */
350 filename = xb_node_query_text (artifact, "filename", NULL);
351 if (filename != NULL)
352 fwupd_release_set_filename (rel, filename);
353
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000354 /* location */
355 locations = xb_node_query (artifact, "location", 0, NULL);
356 if (locations != NULL) {
357 for (guint i = 0; i < locations->len; i++) {
358 XbNode *n = g_ptr_array_index (locations, i);
Richard Hughesb856f0b2021-01-28 10:59:49 +0000359 g_autofree gchar *scheme = NULL;
360
361 /* check the scheme is allowed */
362 scheme = fu_common_uri_get_scheme (xb_node_get_text (n));
363 if (scheme != NULL) {
364 guint prio = fu_config_get_uri_scheme_prio (self->config, scheme);
365 if (prio == G_MAXUINT)
366 continue;
367 }
368
369 /* build the complete URI */
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000370 if (remote != NULL) {
371 g_autofree gchar *uri = NULL;
372 uri = fwupd_remote_build_firmware_uri (remote,
373 xb_node_get_text (n),
374 NULL);
375 if (uri != NULL) {
376 fwupd_release_add_location (rel, uri);
377 continue;
378 }
379 }
380 fwupd_release_add_location (rel, xb_node_get_text (n));
381 }
382 }
383
384 /* checksum */
385 checksums = xb_node_query (artifact, "checksum", 0, NULL);
386 if (checksums != NULL) {
387 for (guint i = 0; i < checksums->len; i++) {
388 XbNode *n = g_ptr_array_index (checksums, i);
389 fwupd_release_add_checksum (rel, xb_node_get_text (n));
390 }
391 }
392
393 /* size */
394 size = xb_node_query_text_as_uint (artifact, "size[@type='installed']", NULL);
395 if (size != G_MAXUINT64)
396 fwupd_release_set_size (rel, size);
397
398 /* success */
399 return TRUE;
400}
401
402static gboolean
Richard Hughesbd4d2852017-09-13 14:05:14 +0100403fu_engine_set_release_from_appstream (FuEngine *self,
Richard Hughes2c40b372019-04-17 13:41:47 +0100404 FuDevice *dev,
Richard Hughesbd4d2852017-09-13 14:05:14 +0100405 FwupdRelease *rel,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100406 XbNode *component,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100407 XbNode *release,
408 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +0100409{
Richard Hughesc6afb512017-08-22 10:22:20 +0100410 FwupdRemote *remote = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100411 const gchar *tmp;
Richard Hughes611f1a92018-01-11 11:54:28 +0000412 const gchar *remote_id;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100413 guint64 tmp64;
414 g_autofree gchar *version_rel = NULL;
Richard Hughes02ac92c2019-03-29 17:56:46 +0000415 g_autoptr(GPtrArray) cats = NULL;
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100416 g_autoptr(GPtrArray) issues = NULL;
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000417 g_autoptr(XbNode) artifact = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100418 g_autoptr(XbNode) description = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100419
Richard Hughes481aa2a2018-09-18 20:51:46 +0100420 /* set from the component */
421 tmp = xb_node_query_text (component, "id", NULL);
422 if (tmp != NULL)
423 fwupd_release_set_appstream_id (rel, tmp);
424 tmp = xb_node_query_text (component, "url[@type='homepage']", NULL);
425 if (tmp != NULL)
426 fwupd_release_set_homepage (rel, tmp);
427 tmp = xb_node_query_text (component, "project_license", NULL);
428 if (tmp != NULL)
429 fwupd_release_set_license (rel, tmp);
430 tmp = xb_node_query_text (component, "name", NULL);
431 if (tmp != NULL)
432 fwupd_release_set_name (rel, tmp);
433 tmp = xb_node_query_text (component, "summary", NULL);
434 if (tmp != NULL)
435 fwupd_release_set_summary (rel, tmp);
Richard Hughes460c4b72020-09-25 20:59:28 +0100436 tmp = xb_node_query_text (component, "branch", NULL);
437 if (tmp != NULL)
438 fwupd_release_set_branch (rel, tmp);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100439 tmp = xb_node_query_text (component, "developer_name", NULL);
440 if (tmp != NULL)
441 fwupd_release_set_vendor (rel, tmp);
442
Mario Limonciello92ff9c72020-03-09 16:54:56 -0500443 /* refresh the device and release to the new version format too */
444 fu_engine_md_refresh_device_from_component (self, dev, component);
445
Richard Hughes481aa2a2018-09-18 20:51:46 +0100446 /* the version is fixed up at runtime */
Richard Hughes2c40b372019-04-17 13:41:47 +0100447 version_rel = fu_engine_get_release_version (self, dev, release, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100448 if (version_rel == NULL)
449 return FALSE;
450 fwupd_release_set_version (rel, version_rel);
Richard Hughes74fa2ca2018-01-10 21:33:39 +0000451
Richard Hughesc6afb512017-08-22 10:22:20 +0100452 /* find the remote */
Richard Hughes33171fd2018-11-09 13:29:11 +0000453 remote_id = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL);
Richard Hughes611f1a92018-01-11 11:54:28 +0000454 if (remote_id != NULL) {
455 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughesd1808aa2019-12-10 15:20:30 +0000456 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100457 if (remote == NULL)
458 g_warning ("no remote found for release %s", version_rel);
Richard Hughesbd4d2852017-09-13 14:05:14 +0100459 }
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000460 artifact = xb_node_query_first (release, "artifacts/artifact", NULL);
461 if (artifact != NULL) {
462 if (!fu_engine_set_release_from_artifact (self,
463 rel,
464 remote,
465 artifact,
466 error))
467 return FALSE;
468 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100469 description = xb_node_query_first (release, "description", NULL);
470 if (description != NULL) {
471 g_autofree gchar *xml = NULL;
472 xml = xb_node_export (description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL);
473 if (xml != NULL)
474 fwupd_release_set_description (rel, xml);
475 }
Richard Hughes5213fa52021-01-28 10:56:22 +0000476 if (artifact == NULL) {
477 tmp = xb_node_query_text (release, "location", NULL);
478 if (tmp != NULL) {
479 g_autofree gchar *uri = NULL;
480 if (remote != NULL)
481 uri = fwupd_remote_build_firmware_uri (remote, tmp, NULL);
482 if (uri == NULL)
483 uri = g_strdup (tmp);
Richard Hughesb8dfacc2021-01-25 20:20:59 +0000484 fwupd_release_add_location (rel, uri);
Richard Hughes5213fa52021-01-28 10:56:22 +0000485 } else if (remote != NULL &&
486 fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
487 g_autofree gchar *uri = NULL;
488 tmp = xb_node_query_text (component, "../custom/value[@key='fwupd::FilenameCache']", NULL);
489 if (tmp != NULL) {
490 uri = g_strdup_printf ("file://%s", tmp);
491 fwupd_release_add_location (rel, uri);
492 }
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600493 }
Richard Hughesc6afb512017-08-22 10:22:20 +0100494 }
Richard Hughesc2f7ed32021-01-28 20:11:01 +0000495 if (artifact == NULL) {
496 tmp = xb_node_query_text (release, "checksum[@target='content']", NULL);
497 if (tmp != NULL)
498 fwupd_release_set_filename (rel, tmp);
499 }
Richard Hughes71858282019-01-26 12:15:55 +0000500 tmp = xb_node_query_text (release, "url[@type='details']", NULL);
501 if (tmp != NULL)
502 fwupd_release_set_details_url (rel, tmp);
503 tmp = xb_node_query_text (release, "url[@type='source']", NULL);
504 if (tmp != NULL)
505 fwupd_release_set_source_url (rel, tmp);
Richard Hughes5213fa52021-01-28 10:56:22 +0000506 if (artifact == NULL) {
507 tmp = xb_node_query_text (release, "checksum[@target='container']", NULL);
508 if (tmp != NULL)
509 fwupd_release_add_checksum (rel, tmp);
510 }
511 if (artifact == NULL) {
512 tmp64 = xb_node_query_text_as_uint (release, "size[@type='installed']", NULL);
513 if (tmp64 != G_MAXUINT64)
514 fwupd_release_set_size (rel, tmp64);
515 }
516 if (fwupd_release_get_size (rel) == 0) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100517 GBytes *sz = xb_node_get_data (release, "fwupd::ReleaseSize");
518 if (sz != NULL) {
519 const guint64 *sizeptr = g_bytes_get_data (sz, NULL);
520 fwupd_release_set_size (rel, *sizeptr);
521 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100522 }
Richard Hughes52c1a4d2020-04-02 11:21:06 +0100523 tmp = xb_node_get_attr (release, "urgency");
524 if (tmp != NULL)
525 fwupd_release_set_urgency (rel, fwupd_release_urgency_from_string (tmp));
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000526 tmp64 = xb_node_get_attr_as_uint (release, "install_duration");
Richard Hughes319dbcb2018-12-18 17:59:05 +0000527 if (tmp64 != G_MAXUINT64)
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000528 fwupd_release_set_install_duration (rel, tmp64);
Richard Hughes14797f82020-04-02 10:52:04 +0100529 tmp64 = xb_node_get_attr_as_uint (release, "timestamp");
530 if (tmp64 != G_MAXUINT64)
531 fwupd_release_set_created (rel, tmp64);
Richard Hughes02ac92c2019-03-29 17:56:46 +0000532 cats = xb_node_query (component, "categories/category", 0, NULL);
533 if (cats != NULL) {
534 for (guint i = 0; i < cats->len; i++) {
535 XbNode *n = g_ptr_array_index (cats, i);
536 fwupd_release_add_category (rel, xb_node_get_text (n));
537 }
538 }
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100539 issues = xb_node_query (component, "issues/issue", 0, NULL);
540 if (issues != NULL) {
541 for (guint i = 0; i < issues->len; i++) {
542 XbNode *n = g_ptr_array_index (issues, i);
543 fwupd_release_add_issue (rel, xb_node_get_text (n));
544 }
545 }
Richard Hughes868db4e2019-09-26 14:56:57 +0100546 tmp = xb_node_query_text (component, "screenshots/screenshot/caption", NULL);
547 if (tmp != NULL)
548 fwupd_release_set_detach_caption (rel, tmp);
549 tmp = xb_node_query_text (component, "screenshots/screenshot/image", NULL);
550 if (tmp != NULL)
551 fwupd_release_set_detach_image (rel, tmp);
Richard Hughesadeb2b12018-12-14 11:56:41 +0000552 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateProtocol']", NULL);
553 if (tmp != NULL)
554 fwupd_release_set_protocol (rel, tmp);
Mario Limonciello32241f42019-01-24 10:12:41 -0600555 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL);
556 if (tmp != NULL)
557 fwupd_release_set_update_message (rel, tmp);
Richard Hughes4fd4b982020-06-25 18:58:57 +0100558 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateImage']", NULL);
559 if (tmp != NULL)
560 fwupd_release_set_update_image (rel, tmp);
Richard Hughesb856f0b2021-01-28 10:59:49 +0000561
562 /* sort the locations by scheme */
563 g_ptr_array_sort_with_data (fwupd_release_get_locations (rel),
564 fu_engine_scheme_compare_cb, self);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100565 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100566}
567
Richard Hughes481aa2a2018-09-18 20:51:46 +0100568/* finds the remote-id for the first firmware in the silo that matches this
Richard Hughes534255c2018-01-28 19:51:56 +0000569 * container checksum */
570static const gchar *
571fu_engine_get_remote_id_for_checksum (FuEngine *self, const gchar *csum)
572{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100573 g_autofree gchar *xpath = NULL;
574 g_autoptr(XbNode) key = NULL;
Richard Hughes4cbe99c2020-11-22 13:14:33 +0000575 xpath = g_strdup_printf ("components/component[@type='firmware']/releases/release/"
Richard Hughes33171fd2018-11-09 13:29:11 +0000576 "checksum[@target='container'][text()='%s']/../../"
577 "../../custom/value[@key='fwupd::RemoteId']", csum);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100578 key = xb_silo_query_first (self->silo, xpath, NULL);
579 if (key == NULL)
580 return NULL;
581 return xb_node_get_text (key);
Richard Hughes534255c2018-01-28 19:51:56 +0000582}
583
Richard Hughes9945edb2017-06-19 10:03:55 +0100584/**
585 * fu_engine_unlock:
586 * @self: A #FuEngine
587 * @device_id: A device ID
588 * @error: A #GError, or %NULL
589 *
590 * Unlocks a device.
591 *
592 * Returns: %TRUE for success
593 **/
594gboolean
595fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error)
596{
Richard Hughes34834102017-11-21 21:55:00 +0000597 FuPlugin *plugin;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100598 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100599
600 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
601 g_return_val_if_fail (device_id != NULL, FALSE);
602 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
603
604 /* check the device exists */
Richard Hughes40127542018-01-12 20:25:55 +0000605 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000606 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100607 return FALSE;
608
Richard Hughes34834102017-11-21 21:55:00 +0000609 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000610 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000611 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000612 error);
Richard Hughes34834102017-11-21 21:55:00 +0000613 if (plugin == NULL)
614 return FALSE;
615
Richard Hughes9945edb2017-06-19 10:03:55 +0100616 /* run the correct plugin that added this */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000617 if (!fu_plugin_runner_unlock (plugin, device, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100618 return FALSE;
619
620 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000621 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100622 fu_engine_emit_changed (self);
623 return TRUE;
624}
625
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100626gboolean
627fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, GError **error)
628{
629 const gchar *keys[] = {
630 "ArchiveSizeMax",
Richard Hughes85226fd2020-06-30 14:43:48 +0100631 "DisabledDevices",
Richard Hughes31206832020-07-27 15:31:11 +0100632 "BlockedFirmware",
Richard Hughes85226fd2020-06-30 14:43:48 +0100633 "DisabledPlugins",
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100634 "IdleTimeout",
Mario Limonciello38027e62019-04-17 15:16:07 +0100635 "VerboseDomains",
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600636 "UpdateMotd",
Mario Limonciello4fa95a72020-03-28 10:50:57 -0500637 "EnumerateAllDevices",
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100638 NULL };
639
640 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
641 g_return_val_if_fail (key != NULL, FALSE);
642 g_return_val_if_fail (value != NULL, FALSE);
643 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
644
645 /* check keys are valid */
646 if (!g_strv_contains (keys, key)) {
647 g_set_error (error,
648 FWUPD_ERROR,
649 FWUPD_ERROR_NOT_FOUND,
650 "key %s not supported", key);
651 return FALSE;
652 }
653
654 /* modify, effective next reboot */
Richard Hughesd1808aa2019-12-10 15:20:30 +0000655 return fu_config_set_key_value (self->config, key, value, error);
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100656}
657
Richard Hughes9945edb2017-06-19 10:03:55 +0100658/**
Richard Hughesa6bd5582017-09-07 14:32:22 +0100659 * fu_engine_modify_remote:
660 * @self: A #FuEngine
661 * @remote_id: A remote ID
Richard Hughes4eada342017-10-03 21:20:32 +0100662 * @key: the key, e.g. `Enabled`
663 * @value: the key, e.g. `true`
Richard Hughesa6bd5582017-09-07 14:32:22 +0100664 * @error: A #GError, or %NULL
665 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100666 * Updates the verification silo entry for a specific device.
Richard Hughesa6bd5582017-09-07 14:32:22 +0100667 *
668 * Returns: %TRUE for success
669 **/
670gboolean
671fu_engine_modify_remote (FuEngine *self,
672 const gchar *remote_id,
673 const gchar *key,
674 const gchar *value,
675 GError **error)
676{
Richard Hughes9bc9deb2020-05-19 19:49:51 +0100677 const gchar *keys[] = {
Richard Hughescc93f7c2020-06-01 12:04:03 +0100678 "ApprovalRequired",
Richard Hughes9bc9deb2020-05-19 19:49:51 +0100679 "AutomaticReports",
680 "AutomaticSecurityReports",
681 "Enabled",
682 "FirmwareBaseURI",
683 "MetadataURI",
684 "ReportURI",
685 "SecurityReportURI",
686 NULL,
687 };
Richard Hughesa6bd5582017-09-07 14:32:22 +0100688
689 /* check keys are valid */
690 if (!g_strv_contains (keys, key)) {
691 g_set_error (error,
692 FWUPD_ERROR,
693 FWUPD_ERROR_NOT_FOUND,
694 "key %s not supported", key);
695 return FALSE;
696 }
Richard Hughesd1808aa2019-12-10 15:20:30 +0000697 return fu_remote_list_set_key_value (self->remote_list, remote_id, key, value, error);
Richard Hughesa6bd5582017-09-07 14:32:22 +0100698}
699
700/**
Richard Hughes6b222952018-01-11 10:20:48 +0000701 * fu_engine_modify_device:
702 * @self: A #FuEngine
703 * @device_id: A device ID
704 * @key: the key, e.g. `Flags`
705 * @value: the key, e.g. `reported`
706 * @error: A #GError, or %NULL
707 *
708 * Sets the reported flag for a specific device. This ensures that other
709 * front-end clients for fwupd do not report the same event.
710 *
711 * Returns: %TRUE for success
712 **/
713gboolean
714fu_engine_modify_device (FuEngine *self,
715 const gchar *device_id,
716 const gchar *key,
717 const gchar *value,
718 GError **error)
719{
720 g_autoptr(FuDevice) device = NULL;
721
722 /* find the correct device */
Richard Hughes0b9d9962018-01-12 16:31:28 +0000723 device = fu_history_get_device_by_id (self->history, device_id, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000724 if (device == NULL)
725 return FALSE;
726
727 /* support adding a subset of the device flags */
728 if (g_strcmp0 (key, "Flags") == 0) {
729 FwupdDeviceFlags flag = fwupd_device_flag_from_string (value);
730 if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) {
731 g_set_error (error,
732 FWUPD_ERROR,
733 FWUPD_ERROR_NOT_SUPPORTED,
734 "key %s not a valid flag", key);
735 return FALSE;
736 }
Richard Hughesad54f652018-01-30 17:22:37 +0000737 if (flag != FWUPD_DEVICE_FLAG_REPORTED &&
738 flag != FWUPD_DEVICE_FLAG_NOTIFIED) {
Richard Hughes6b222952018-01-11 10:20:48 +0000739 g_set_error (error,
740 FWUPD_ERROR,
741 FWUPD_ERROR_NOT_SUPPORTED,
742 "flag %s cannot be set from client", key);
743 return FALSE;
744 }
Richard Hughesc0cd0232018-01-31 15:02:00 +0000745 fu_device_add_flag (device, flag);
Richard Hughes0bbef292019-11-01 12:15:15 +0000746 return fu_history_modify_device (self->history, device, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000747 }
748
749 /* others invalid */
750 g_set_error (error,
751 FWUPD_ERROR,
752 FWUPD_ERROR_NOT_SUPPORTED,
753 "key %s not supported", key);
754 return FALSE;
755}
756
Richard Hughes481aa2a2018-09-18 20:51:46 +0100757static const gchar *
758fu_engine_checksum_type_to_string (GChecksumType checksum_type)
759{
760 if (checksum_type == G_CHECKSUM_SHA1)
761 return "sha1";
762 if (checksum_type == G_CHECKSUM_SHA256)
763 return "sha256";
764 if (checksum_type == G_CHECKSUM_SHA512)
765 return "sha512";
766 return "sha1";
767}
768
Richard Hughes6b222952018-01-11 10:20:48 +0000769/**
Richard Hughes9945edb2017-06-19 10:03:55 +0100770 * fu_engine_verify_update:
771 * @self: A #FuEngine
772 * @device_id: A device ID
773 * @error: A #GError, or %NULL
774 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100775 * Updates the verification silo entry for a specific device.
Richard Hughes9945edb2017-06-19 10:03:55 +0100776 *
777 * Returns: %TRUE for success
778 **/
779gboolean
780fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error)
781{
Richard Hughes34834102017-11-21 21:55:00 +0000782 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100783 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100784 GPtrArray *guids;
785 g_autofree gchar *fn = NULL;
786 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100787 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100788 g_autoptr(GFile) file = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100789 g_autoptr(XbBuilder) builder = xb_builder_new ();
790 g_autoptr(XbBuilderNode) component = NULL;
791 g_autoptr(XbBuilderNode) provides = NULL;
792 g_autoptr(XbBuilderNode) release = NULL;
793 g_autoptr(XbBuilderNode) releases = NULL;
794 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100795
796 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
797 g_return_val_if_fail (device_id != NULL, FALSE);
798 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
799
800 /* check the devices still exists */
Richard Hughes40127542018-01-12 20:25:55 +0000801 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000802 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100803 return FALSE;
804
Richard Hughes34834102017-11-21 21:55:00 +0000805 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000806 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000807 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000808 error);
Richard Hughes34834102017-11-21 21:55:00 +0000809 if (plugin == NULL)
810 return FALSE;
811
Richard Hughes9945edb2017-06-19 10:03:55 +0100812 /* get the checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000813 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100814 if (checksums->len == 0) {
Richard Hughes0a7e7832017-11-22 11:01:13 +0000815 if (!fu_plugin_runner_verify (plugin, device,
Richard Hughes9945edb2017-06-19 10:03:55 +0100816 FU_PLUGIN_VERIFY_FLAG_NONE,
817 error))
818 return FALSE;
Richard Hughes0a7e7832017-11-22 11:01:13 +0000819 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100820 }
821
822 /* we got nothing */
823 if (checksums->len == 0) {
824 g_set_error_literal (error,
825 FWUPD_ERROR,
826 FWUPD_ERROR_NOT_SUPPORTED,
827 "device verification not supported");
828 return FALSE;
829 }
830
Richard Hughes481aa2a2018-09-18 20:51:46 +0100831 /* build XML */
832 component = xb_builder_node_insert (NULL, "component",
833 "type", "firmware",
834 NULL);
835 provides = xb_builder_node_insert (component, "provides", NULL);
836 guids = fu_device_get_guids (device);
837 for (guint i = 0; i < guids->len; i++) {
838 const gchar *guid = g_ptr_array_index (guids, i);
839 g_autoptr(XbBuilderNode) provide = NULL;
840 provide = xb_builder_node_insert (provides, "firmware",
841 "type", "flashed",
842 NULL);
843 xb_builder_node_set_text (provide, guid, -1);
844 }
845 releases = xb_builder_node_insert (component, "releases", NULL);
846 release = xb_builder_node_insert (releases, "release",
847 "version", fu_device_get_version (device),
848 NULL);
849 for (guint i = 0; i < checksums->len; i++) {
850 const gchar *checksum = g_ptr_array_index (checksums, i);
851 GChecksumType kind = fwupd_checksum_guess_kind (checksum);
852 g_autoptr(XbBuilderNode) csum = NULL;
Richard Hughesaaa60c62018-11-07 11:05:50 +0000853 csum = xb_builder_node_insert (release, "checksum",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100854 "type", fu_engine_checksum_type_to_string (kind),
855 "target", "content",
856 NULL);
857 xb_builder_node_set_text (csum, checksum, -1);
858 }
859 xb_builder_import_node (builder, component);
860
861 /* save silo */
862 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
863 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
Richard Hughes11f612c2019-03-15 16:27:18 +0000864 if (!fu_common_mkdir_parent (fn, error))
865 return FALSE;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100866 file = g_file_new_for_path (fn);
867 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
868 if (silo == NULL)
869 return FALSE;
870 if (!xb_silo_export_file (silo, file,
871 XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE,
872 NULL, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100873 return FALSE;
874
Richard Hughes481aa2a2018-09-18 20:51:46 +0100875 /* success */
876 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100877}
878
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600879XbNode *
880fu_engine_get_component_by_guids (FuEngine *self, FuDevice *device)
Richard Hughes9945edb2017-06-19 10:03:55 +0100881{
882 GPtrArray *guids = fu_device_get_guids (device);
Richard Hughesab1bc892018-11-15 15:04:35 +0000883 g_autoptr(GString) xpath = g_string_new (NULL);
884 g_autoptr(XbNode) component = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100885 for (guint i = 0; i < guids->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100886 const gchar *guid = g_ptr_array_index (guids, i);
Richard Hughesab1bc892018-11-15 15:04:35 +0000887 xb_string_append_union (xpath,
Richard Hughes4cbe99c2020-11-22 13:14:33 +0000888 "components/component[@type='firmware']/"
Richard Hughesab1bc892018-11-15 15:04:35 +0000889 "provides/firmware[@type='flashed'][text()='%s']/"
890 "../..", guid);
Richard Hughes9945edb2017-06-19 10:03:55 +0100891 }
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600892 component = xb_silo_query_first (self->silo, xpath->str, NULL);
Richard Hughesab1bc892018-11-15 15:04:35 +0000893 if (component != NULL)
894 return g_steal_pointer (&component);
Richard Hughes9945edb2017-06-19 10:03:55 +0100895 return NULL;
896}
897
Richard Hughes35ac0722020-06-23 15:05:14 +0100898static XbNode *
899fu_engine_verify_from_local_metadata (FuEngine *self,
900 FuDevice *device,
901 GError **error)
902{
903 g_autofree gchar *fn = NULL;
904 g_autofree gchar *localstatedir = NULL;
905 g_autofree gchar *xpath = NULL;
906 g_autoptr(GFile) file = NULL;
907 g_autoptr(XbBuilder) builder = xb_builder_new ();
908 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
909 g_autoptr(XbNode) release = NULL;
910 g_autoptr(XbSilo) silo = NULL;
911
912 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
913 fn = g_strdup_printf ("%s/verify/%s.xml",
914 localstatedir,
915 fu_device_get_id (device));
916 file = g_file_new_for_path (fn);
917 if (!g_file_query_exists (file, NULL)) {
918 g_set_error (error,
919 G_IO_ERROR,
920 G_IO_ERROR_NOT_FOUND,
921 "failed to find %s", fn);
922 return NULL;
923 }
924
925 if (!xb_builder_source_load_file (source, file,
926 XB_BUILDER_SOURCE_FLAG_NONE,
927 NULL, error))
928 return NULL;
929 xb_builder_import_source (builder, source);
930 silo = xb_builder_compile (builder,
931 XB_BUILDER_COMPILE_FLAG_NONE,
932 NULL, error);
933 if (silo == NULL)
934 return NULL;
935 xpath = g_strdup_printf ("component/releases/release[@version='%s']",
936 fu_device_get_version (device));
937 release = xb_silo_query_first (silo, xpath, error);
938 if (release == NULL)
939 return NULL;
940
Richard Hughes6f4f1ca2020-09-24 10:43:26 +0100941 /* silo has to have same lifecycle as node */
Richard Hughes35ac0722020-06-23 15:05:14 +0100942 g_object_set_data_full (G_OBJECT (release), "XbSilo",
943 g_steal_pointer (&silo),
944 (GDestroyNotify) g_object_unref);
945 return g_steal_pointer (&release);
946}
947
948static XbNode *
949fu_engine_verify_from_system_metadata (FuEngine *self,
950 FuDevice *device,
951 GError **error)
952{
953 FwupdVersionFormat fmt = fu_device_get_version_format (device);
954 GPtrArray *guids = fu_device_get_guids (device);
955 g_autoptr(XbQuery) query = NULL;
956
957 /* prepare query with bound GUID parameter */
958 query = xb_query_new_full (self->silo,
Richard Hughes4cbe99c2020-11-22 13:14:33 +0000959 "components/component[@type='firmware']/"
Richard Hughes35ac0722020-06-23 15:05:14 +0100960 "provides/firmware[@type='flashed'][text()=?]/"
961 "../../releases/release",
962 XB_QUERY_FLAG_OPTIMIZE |
963 XB_QUERY_FLAG_USE_INDEXES,
964 error);
965 if (query == NULL)
966 return NULL;
967
968 /* use prepared query for each GUID */
969 for (guint i = 0; i < guids->len; i++) {
970 const gchar *guid = g_ptr_array_index (guids, i);
971 g_autoptr(GError) error_local = NULL;
972 g_autoptr(GPtrArray) releases = NULL;
Philip Withnallaa48b6b2020-08-19 14:17:58 +0100973#if LIBXMLB_CHECK_VERSION(0,3,0)
974 g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT ();
975#endif
Richard Hughes35ac0722020-06-23 15:05:14 +0100976
977 /* bind GUID and then query */
Philip Withnallaa48b6b2020-08-19 14:17:58 +0100978#if LIBXMLB_CHECK_VERSION(0,3,0)
979 xb_value_bindings_bind_str (xb_query_context_get_bindings (&context), 0, guid, NULL);
980 releases = xb_silo_query_with_context (self->silo, query, &context, &error_local);
981#else
Richard Hughes35ac0722020-06-23 15:05:14 +0100982 if (!xb_query_bind_str (query, 0, guid, error)) {
983 g_prefix_error (error, "failed to bind string: ");
984 return NULL;
985 }
986 releases = xb_silo_query_full (self->silo, query, &error_local);
Philip Withnallaa48b6b2020-08-19 14:17:58 +0100987#endif
Richard Hughes35ac0722020-06-23 15:05:14 +0100988 if (releases == NULL) {
989 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
990 g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
991 g_debug ("could not find %s: %s",
992 guid, error_local->message);
993 continue;
994 }
995 g_propagate_error (error, g_steal_pointer (&error_local));
996 return NULL;
997 }
998 for (guint j = 0; j < releases->len; j++) {
999 XbNode *rel = g_ptr_array_index (releases, j);
1000 const gchar *rel_ver = xb_node_get_attr (rel, "version");
1001 g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt);
1002 if (fu_common_vercmp_full (tmp_ver, fu_device_get_version (device), fmt) == 0)
1003 return g_object_ref (rel);
1004 }
1005 }
1006
1007 /* not found */
1008 g_set_error_literal (error,
1009 G_IO_ERROR,
1010 G_IO_ERROR_NOT_FOUND,
1011 "failed to find release");
1012 return NULL;
1013}
1014
Richard Hughes9945edb2017-06-19 10:03:55 +01001015/**
1016 * fu_engine_verify:
1017 * @self: A #FuEngine
1018 * @device_id: A device ID
1019 * @error: A #GError, or %NULL
1020 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01001021 * Verifies a device firmware checksum using the verification silo entry.
Richard Hughes9945edb2017-06-19 10:03:55 +01001022 *
1023 * Returns: %TRUE for success
1024 **/
1025gboolean
1026fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error)
1027{
Richard Hughes34834102017-11-21 21:55:00 +00001028 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +01001029 GPtrArray *checksums;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001030 g_autoptr(FuDevice) device = NULL;
Richard Hughes35ac0722020-06-23 15:05:14 +01001031 g_autoptr(GError) error_local = NULL;
Richard Hughes45bbfc92018-12-12 10:57:08 +00001032 g_autoptr(GString) xpath_csum = g_string_new (NULL);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001033 g_autoptr(XbNode) csum = NULL;
1034 g_autoptr(XbNode) release = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001035
1036 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
1037 g_return_val_if_fail (device_id != NULL, FALSE);
1038 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1039
1040 /* check the id exists */
Richard Hughes40127542018-01-12 20:25:55 +00001041 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00001042 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01001043 return FALSE;
1044
Richard Hughes34834102017-11-21 21:55:00 +00001045 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00001046 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00001047 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00001048 error);
Richard Hughes34834102017-11-21 21:55:00 +00001049 if (plugin == NULL)
1050 return FALSE;
1051
Mario Limonciello8fa0b382019-10-14 07:36:04 -05001052 /* update the device firmware hashes if possible */
1053 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) {
1054 if (!fu_plugin_runner_verify (plugin, device,
1055 FU_PLUGIN_VERIFY_FLAG_NONE, error))
1056 return FALSE;
1057 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001058
Richard Hughes35ac0722020-06-23 15:05:14 +01001059 /* find component in local metadata */
1060 release = fu_engine_verify_from_local_metadata (self, device, &error_local);
1061 if (release == NULL) {
1062 if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
1063 !g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
1064 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes481aa2a2018-09-18 20:51:46 +01001065 return FALSE;
Richard Hughes35ac0722020-06-23 15:05:14 +01001066 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001067 }
1068
Richard Hughes481aa2a2018-09-18 20:51:46 +01001069 /* try again with the system metadata */
Richard Hughes9945edb2017-06-19 10:03:55 +01001070 if (release == NULL) {
Richard Hughes35ac0722020-06-23 15:05:14 +01001071 g_autoptr(GError) error_system = NULL;
1072 release = fu_engine_verify_from_system_metadata (self, device, &error_system);
1073 if (release == NULL) {
1074 if (!g_error_matches (error_system, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
1075 !g_error_matches (error_system, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
1076 g_propagate_error (error, g_steal_pointer (&error_system));
Mario Limonciello91d36092019-10-14 08:44:39 -05001077 return FALSE;
Richard Hughesd63cedc2020-06-21 13:56:50 +01001078 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001079 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001080 }
1081 if (release == NULL) {
1082 g_set_error (error,
1083 FWUPD_ERROR,
1084 FWUPD_ERROR_NOT_FOUND,
Richard Hughes35ac0722020-06-23 15:05:14 +01001085 "No release found for version %s",
1086 fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01001087 return FALSE;
1088 }
1089
Richard Hughes9945edb2017-06-19 10:03:55 +01001090 /* get the matching checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +00001091 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +01001092 if (checksums->len == 0) {
1093 g_set_error (error,
1094 FWUPD_ERROR,
1095 FWUPD_ERROR_NOT_FOUND,
Richard Hughes35ac0722020-06-23 15:05:14 +01001096 "No device checksums for %s",
1097 fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01001098 return FALSE;
1099 }
Richard Hughes45bbfc92018-12-12 10:57:08 +00001100
1101 /* do any of the checksums in the release match any in the device */
Richard Hughes9945edb2017-06-19 10:03:55 +01001102 for (guint j = 0; j < checksums->len; j++) {
1103 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
Richard Hughes45bbfc92018-12-12 10:57:08 +00001104 xb_string_append_union (xpath_csum,
1105 "checksum[@target='device'][text()='%s']",
1106 hash_tmp);
1107 xb_string_append_union (xpath_csum,
1108 "checksum[@target='content'][text()='%s']",
1109 hash_tmp);
1110 }
1111 csum = xb_node_query_first (release, xpath_csum->str, NULL);
1112 if (csum == NULL) {
1113 g_autoptr(GString) checksums_device = g_string_new (NULL);
1114 g_autoptr(GString) checksums_metadata = g_string_new (NULL);
1115 g_autoptr(GPtrArray) csums = NULL;
1116 g_autoptr(GString) xpath = g_string_new (NULL);
1117
1118 /* get all checksums to display a useful error */
1119 xb_string_append_union (xpath, "checksum[@target='device']");
Mario Limonciello2a0e20b2019-10-14 12:09:35 -05001120 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE))
1121 xb_string_append_union (xpath, "checksum[@target='content']");
Richard Hughes45bbfc92018-12-12 10:57:08 +00001122 csums = xb_node_query (release, xpath->str, 0, NULL);
1123 if (csums == NULL) {
1124 g_set_error (error,
1125 FWUPD_ERROR,
1126 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -05001127 "No stored checksums for %s",
Richard Hughes35ac0722020-06-23 15:05:14 +01001128 fu_device_get_version (device));
Richard Hughes45bbfc92018-12-12 10:57:08 +00001129 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001130 }
Richard Hughes45bbfc92018-12-12 10:57:08 +00001131 for (guint i = 0; i < csums->len; i++) {
1132 XbNode *csum_tmp = g_ptr_array_index (csums, i);
1133 xb_string_append_union (checksums_metadata,
1134 "%s", xb_node_get_text (csum_tmp));
1135 }
1136 for (guint i = 0; i < checksums->len; i++) {
1137 const gchar *hash_tmp = g_ptr_array_index (checksums, i);
1138 xb_string_append_union (checksums_device, "%s", hash_tmp);
1139 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001140 g_set_error (error,
1141 FWUPD_ERROR,
1142 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -05001143 "For %s %s expected %s, got %s",
1144 fu_device_get_name (device),
Richard Hughes35ac0722020-06-23 15:05:14 +01001145 fu_device_get_version (device),
Richard Hughes45bbfc92018-12-12 10:57:08 +00001146 checksums_metadata->str,
1147 checksums_device->str);
Richard Hughes9945edb2017-06-19 10:03:55 +01001148 return FALSE;
1149 }
1150
1151 /* success */
1152 return TRUE;
1153}
1154
Richard Hughes481aa2a2018-09-18 20:51:46 +01001155static gboolean
Richard Hughes9a680842020-02-20 11:11:13 +00001156fu_engine_require_vercmp (XbNode *req,
1157 const gchar *version,
1158 FwupdVersionFormat fmt,
1159 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +00001160{
Richard Hughes481aa2a2018-09-18 20:51:46 +01001161 gboolean ret = FALSE;
1162 gint rc = 0;
1163 const gchar *tmp = xb_node_get_attr (req, "compare");
1164 const gchar *version_req = xb_node_get_attr (req, "version");
1165
1166 if (g_strcmp0 (tmp, "eq") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001167 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001168 ret = rc == 0;
1169 } else if (g_strcmp0 (tmp, "ne") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001170 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001171 ret = rc != 0;
1172 } else if (g_strcmp0 (tmp, "lt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001173 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001174 ret = rc < 0;
1175 } else if (g_strcmp0 (tmp, "gt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001176 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001177 ret = rc > 0;
1178 } else if (g_strcmp0 (tmp, "le") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001179 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001180 ret = rc <= 0;
1181 } else if (g_strcmp0 (tmp, "ge") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +00001182 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001183 ret = rc >= 0;
1184 } else if (g_strcmp0 (tmp, "glob") == 0) {
Richard Hughes5c508de2019-11-22 09:57:34 +00001185 ret = fu_common_fnmatch (version_req, version);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001186 } else if (g_strcmp0 (tmp, "regex") == 0) {
1187 ret = g_regex_match_simple (version_req, version, 0, 0);
1188 } else {
1189 g_set_error (error,
1190 FWUPD_ERROR,
1191 FWUPD_ERROR_NOT_SUPPORTED,
1192 "failed to compare [%s] and [%s]",
1193 version_req,
1194 version);
1195 return FALSE;
Richard Hughes650dade2017-12-14 14:43:11 +00001196 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001197
1198 /* set error */
1199 if (!ret) {
1200 g_set_error (error,
1201 FWUPD_ERROR,
1202 FWUPD_ERROR_INTERNAL,
1203 "failed predicate [%s %s %s]",
1204 version_req, tmp, version);
1205 }
1206 return ret;
Richard Hughes650dade2017-12-14 14:43:11 +00001207}
1208
Richard Hughes9945edb2017-06-19 10:03:55 +01001209static gboolean
Richard Hughesb62c3a42019-04-08 12:13:47 +01001210fu_engine_check_requirement_not_child (FuEngine *self, XbNode *req,
1211 FuDevice *device, GError **error)
1212{
1213 GPtrArray *children = fu_device_get_children (device);
1214
1215 /* only <firmware> supported */
1216 if (g_strcmp0 (xb_node_get_element (req), "firmware") != 0) {
1217 g_set_error (error,
1218 FWUPD_ERROR,
1219 FWUPD_ERROR_NOT_SUPPORTED,
1220 "cannot handle not-child %s requirement",
1221 xb_node_get_element (req));
1222 return FALSE;
1223 }
1224
1225 /* check each child */
1226 for (guint i = 0; i < children->len; i++) {
1227 FuDevice *child = g_ptr_array_index (children, i);
1228 const gchar *version = fu_device_get_version (child);
Richard Hughesec14e4b2019-11-01 12:07:10 +00001229 if (version == NULL) {
1230 g_set_error (error,
1231 FWUPD_ERROR,
1232 FWUPD_ERROR_NOT_SUPPORTED,
1233 "no version provided by %s, child of %s",
1234 fu_device_get_name (child),
1235 fu_device_get_name (device));
1236 return FALSE;
1237 }
Richard Hughes9a680842020-02-20 11:11:13 +00001238 if (fu_engine_require_vercmp (req, version,
1239 fu_device_get_version_format (child),
1240 NULL)) {
Richard Hughesb62c3a42019-04-08 12:13:47 +01001241 g_set_error (error,
1242 FWUPD_ERROR,
1243 FWUPD_ERROR_NOT_SUPPORTED,
1244 "Not compatible with child device version %s",
1245 version);
1246 return FALSE;
1247 }
1248 }
1249 return TRUE;
1250}
1251
1252static gboolean
Richard Hugheseddaed02021-01-03 12:08:38 +00001253fu_engine_check_requirement_vendor_id (FuEngine *self, XbNode *req,
1254 FuDevice *device, GError **error)
1255{
1256 GPtrArray *vendor_ids;
1257 const gchar *vendor_ids_metadata;
1258 g_autofree gchar *vendor_ids_device = NULL;
Richard Hugheseddaed02021-01-03 12:08:38 +00001259
1260 /* devices without vendor IDs should not exist! */
1261 vendor_ids = fu_device_get_vendor_ids (device);
1262 if (vendor_ids->len == 0) {
1263 g_set_error (error,
1264 FWUPD_ERROR,
1265 FWUPD_ERROR_NOT_SUPPORTED,
1266 "device [%s] has no vendor ID",
1267 fu_device_get_id (device));
1268 return FALSE;
1269 }
1270
1271 /* metadata with empty vendor IDs should not exist! */
1272 vendor_ids_metadata = xb_node_get_attr (req, "version");
1273 if (vendor_ids_metadata == NULL) {
1274 g_set_error_literal (error,
1275 FWUPD_ERROR,
1276 FWUPD_ERROR_NOT_SUPPORTED,
1277 "metadata has no vendor ID");
1278 return FALSE;
1279 }
1280
Richard Hugheseddaed02021-01-03 12:08:38 +00001281 /* it is always safe to use a regex, even for simple strings */
Richard Hughese52e1b42021-01-26 09:59:15 +00001282 vendor_ids_device = fu_common_strjoin_array ("|", vendor_ids);
Richard Hugheseddaed02021-01-03 12:08:38 +00001283 if (!g_regex_match_simple (vendor_ids_metadata, vendor_ids_device, 0, 0)) {
1284 g_set_error (error,
1285 FWUPD_ERROR,
1286 FWUPD_ERROR_INVALID_FILE,
1287 "Not compatible with vendor %s: got %s",
1288 vendor_ids_device,
1289 vendor_ids_metadata);
1290 return FALSE;
1291 }
1292
1293 /* success */
1294 return TRUE;
1295}
1296
1297static gboolean
Richard Hughesbf439782020-11-03 11:32:29 +00001298fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req, FuDevice *device,
1299 FwupdInstallFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001300{
Richard Hughesf7006d22019-11-14 13:42:52 +00001301 guint64 depth;
1302 g_autoptr(FuDevice) device_actual = g_object_ref (device);
Richard Hughes88adcbe2017-11-21 14:33:56 +00001303 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001304
Richard Hughesf7006d22019-11-14 13:42:52 +00001305 /* look at the parent device */
1306 depth = xb_node_get_attr_as_uint (req, "depth");
1307 if (depth != G_MAXUINT64) {
1308 for (guint64 i = 0; i < depth; i++) {
1309 FuDevice *device_tmp = fu_device_get_parent (device_actual);
1310 if (device_actual == NULL) {
1311 g_set_error (error,
1312 FWUPD_ERROR,
1313 FWUPD_ERROR_NOT_SUPPORTED,
1314 "No parent device for %s "
1315 "(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")",
1316 fu_device_get_name (device_actual), i, depth);
1317 return FALSE;
1318 }
1319 g_set_object (&device_actual, device_tmp);
1320 }
1321 }
1322
Richard Hughes0eb123b2018-04-19 12:00:04 +01001323 /* old firmware version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001324 if (xb_node_get_text (req) == NULL) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001325 const gchar *version = fu_device_get_version (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001326 if (!fu_engine_require_vercmp (req, version,
1327 fu_device_get_version_format (device_actual),
1328 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001329 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001330 g_set_error (error,
1331 FWUPD_ERROR,
1332 FWUPD_ERROR_INVALID_FILE,
1333 "Not compatible with firmware version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001334 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +00001335 } else {
1336 g_set_error (error,
1337 FWUPD_ERROR,
1338 FWUPD_ERROR_INVALID_FILE,
1339 "Not compatible with firmware version: %s",
1340 error_local->message);
1341 }
1342 return FALSE;
1343 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001344 return TRUE;
1345 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001346
Richard Hughes0eb123b2018-04-19 12:00:04 +01001347 /* bootloader version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001348 if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001349 const gchar *version = fu_device_get_version_bootloader (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001350 if (!fu_engine_require_vercmp (req, version,
1351 fu_device_get_version_format (device_actual),
1352 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001353 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001354 g_set_error (error,
1355 FWUPD_ERROR,
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001356 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes88adcbe2017-11-21 14:33:56 +00001357 "Not compatible with bootloader version %s, requires >= %s",
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001358 version, xb_node_get_attr (req, "version"));
1359
Richard Hughes88adcbe2017-11-21 14:33:56 +00001360 } else {
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001361 g_debug ("Bootloader is not compatible: %s", error_local->message);
1362 g_set_error_literal (error,
1363 FWUPD_ERROR,
1364 FWUPD_ERROR_NOT_SUPPORTED,
1365 "Bootloader is not compatible");
Richard Hughes88adcbe2017-11-21 14:33:56 +00001366 }
1367 return FALSE;
1368 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001369 return TRUE;
1370 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001371
Richard Hughes0eb123b2018-04-19 12:00:04 +01001372 /* vendor ID */
Richard Hugheseddaed02021-01-03 12:08:38 +00001373 if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) {
1374 if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID)
1375 return TRUE;
1376 return fu_engine_check_requirement_vendor_id (self, req, device_actual, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01001377 }
1378
Richard Hughesb62c3a42019-04-08 12:13:47 +01001379 /* child version */
1380 if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0)
Richard Hughesf7006d22019-11-14 13:42:52 +00001381 return fu_engine_check_requirement_not_child (self, req, device_actual, error);
Richard Hughesb62c3a42019-04-08 12:13:47 +01001382
Richard Hughes12c84992018-10-02 11:07:28 +01001383 /* another device */
Richard Hughes592baed2019-02-03 18:30:24 +00001384 if (fwupd_guid_is_valid (xb_node_get_text (req))) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001385 const gchar *guid = xb_node_get_text (req);
Richard Hughes12c84992018-10-02 11:07:28 +01001386 const gchar *version;
Richard Hughes12c84992018-10-02 11:07:28 +01001387
1388 /* find if the other device exists */
Richard Hughesf7006d22019-11-14 13:42:52 +00001389 if (depth == G_MAXUINT64) {
1390 g_autoptr(FuDevice) device_tmp = NULL;
1391 device_tmp = fu_device_list_get_by_guid (self->device_list, guid, error);
1392 if (device_tmp == NULL)
1393 return FALSE;
1394 g_set_object (&device_actual, device_tmp);
1395
1396 /* verify the parent device has the GUID */
1397 } else {
1398 if (!fu_device_has_guid (device_actual, guid)) {
1399 g_set_error (error,
1400 FWUPD_ERROR,
1401 FWUPD_ERROR_NOT_SUPPORTED,
1402 "No GUID of %s on parent device %s",
1403 guid, fu_device_get_name (device_actual));
1404 return FALSE;
1405 }
1406 }
Richard Hughes12c84992018-10-02 11:07:28 +01001407
1408 /* get the version of the other device */
Richard Hughesf7006d22019-11-14 13:42:52 +00001409 version = fu_device_get_version (device_actual);
Richard Hughesaaf0ce72019-07-16 08:43:57 +01001410 if (version != NULL &&
Richard Hughesf7006d22019-11-14 13:42:52 +00001411 xb_node_get_attr (req, "compare") != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00001412 !fu_engine_require_vercmp (req, version,
1413 fu_device_get_version_format (device_actual),
1414 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001415 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes12c84992018-10-02 11:07:28 +01001416 g_set_error (error,
1417 FWUPD_ERROR,
1418 FWUPD_ERROR_INVALID_FILE,
1419 "Not compatible with %s version %s, requires >= %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001420 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001421 version,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001422 xb_node_get_attr (req, "version"));
Richard Hughes12c84992018-10-02 11:07:28 +01001423 } else {
1424 g_set_error (error,
1425 FWUPD_ERROR,
1426 FWUPD_ERROR_INVALID_FILE,
1427 "Not compatible with %s: %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001428 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001429 error_local->message);
1430 }
1431 return FALSE;
1432 }
1433 return TRUE;
1434
1435 }
1436
Richard Hughes0eb123b2018-04-19 12:00:04 +01001437 /* not supported */
1438 g_set_error (error,
1439 FWUPD_ERROR,
1440 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001441 "cannot handle firmware requirement '%s'",
1442 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001443 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001444}
Richard Hughes9945edb2017-06-19 10:03:55 +01001445
1446static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001447fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error)
Richard Hughes2ec78d62017-11-03 21:48:54 +00001448{
Richard Hughes0eb123b2018-04-19 12:00:04 +01001449 g_autoptr(GError) error_local = NULL;
1450 const gchar *version = g_hash_table_lookup (self->runtime_versions,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001451 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001452 if (version == NULL) {
1453 g_set_error (error,
1454 FWUPD_ERROR,
1455 FWUPD_ERROR_NOT_FOUND,
1456 "no version available for %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001457 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001458 return FALSE;
1459 }
Richard Hughes9a680842020-02-20 11:11:13 +00001460 if (!fu_engine_require_vercmp (req, version, FWUPD_VERSION_FORMAT_UNKNOWN, &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001461 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes2ec78d62017-11-03 21:48:54 +00001462 g_set_error (error,
1463 FWUPD_ERROR,
1464 FWUPD_ERROR_INVALID_FILE,
Richard Hughes0eb123b2018-04-19 12:00:04 +01001465 "Not compatible with %s version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001466 xb_node_get_text (req), version,
1467 xb_node_get_attr (req, "version"));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001468 } else {
1469 g_set_error (error,
1470 FWUPD_ERROR,
1471 FWUPD_ERROR_INVALID_FILE,
1472 "Not compatible with %s version: %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001473 xb_node_get_text (req), error_local->message);
Richard Hughes2ec78d62017-11-03 21:48:54 +00001474 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001475 return FALSE;
Richard Hughes2ec78d62017-11-03 21:48:54 +00001476 }
1477
Mario Limonciello67a8b892020-09-28 13:44:39 -05001478 g_debug ("requirement %s %s %s -> %s passed",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001479 xb_node_get_attr (req, "version"),
1480 xb_node_get_attr (req, "compare"),
1481 version, xb_node_get_text (req));
Richard Hughes2ec78d62017-11-03 21:48:54 +00001482 return TRUE;
1483}
Richard Hughes2ec78d62017-11-03 21:48:54 +00001484
1485static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001486fu_engine_check_requirement_hardware (FuEngine *self, XbNode *req, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001487{
Richard Hughes3d71c162018-04-30 16:40:44 +01001488 g_auto(GStrv) hwid_split = NULL;
1489
1490 /* split and treat as OR */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001491 hwid_split = g_strsplit (xb_node_get_text (req), "|", -1);
Richard Hughes3d71c162018-04-30 16:40:44 +01001492 for (guint i = 0; hwid_split[i] != NULL; i++) {
Richard Hughesb333e002021-04-01 10:40:02 +01001493 if (fu_context_has_hwid_guid (self->ctx, hwid_split[i])) {
Richard Hughes3d71c162018-04-30 16:40:44 +01001494 g_debug ("HWID provided %s", hwid_split[i]);
1495 return TRUE;
1496 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001497 }
Richard Hughes3d71c162018-04-30 16:40:44 +01001498
1499 /* nothing matched */
1500 g_set_error (error,
1501 FWUPD_ERROR,
1502 FWUPD_ERROR_INVALID_FILE,
1503 "no HWIDs matched %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001504 xb_node_get_text (req));
Richard Hughes3d71c162018-04-30 16:40:44 +01001505 return FALSE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001506}
1507
1508static gboolean
Richard Hughesdf89cd52020-06-26 20:25:18 +01001509fu_engine_check_requirement_client (FuEngine *self,
1510 FuEngineRequest *request,
1511 XbNode *req,
1512 GError **error)
1513{
1514 FwupdFeatureFlags flags;
1515 g_auto(GStrv) feature_split = NULL;
1516
1517 /* split and treat as AND */
1518 feature_split = g_strsplit (xb_node_get_text (req), "|", -1);
1519 flags = fu_engine_request_get_feature_flags (request);
1520 for (guint i = 0; feature_split[i] != NULL; i++) {
1521 FwupdFeatureFlags flag = fwupd_feature_flag_from_string (feature_split[i]);
1522
Richard Hughes0ec634e2020-12-07 20:55:18 +00001523 /* not recognized */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001524 if (flag == FWUPD_FEATURE_FLAG_LAST) {
1525 g_set_error (error,
1526 FWUPD_ERROR,
1527 FWUPD_ERROR_NOT_FOUND,
1528 "client requirement %s unknown",
1529 feature_split[i]);
1530 return FALSE;
1531 }
1532
1533 /* not supported */
1534 if ((flags & flag) == 0) {
1535 g_set_error (error,
1536 FWUPD_ERROR,
1537 FWUPD_ERROR_NOT_SUPPORTED,
1538 "client requirement %s not supported",
1539 feature_split[i]);
1540 return FALSE;
1541 }
1542 }
1543
1544 /* success */
1545 return TRUE;
1546}
1547
1548static gboolean
1549fu_engine_check_requirement (FuEngine *self,
1550 FuEngineRequest *request,
1551 XbNode *req,
1552 FuDevice *device,
Richard Hughesbf439782020-11-03 11:32:29 +00001553 FwupdInstallFlags flags,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001554 GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001555{
1556 /* ensure component requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001557 if (g_strcmp0 (xb_node_get_element (req), "id") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001558 return fu_engine_check_requirement_id (self, req, error);
1559
1560 /* ensure firmware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001561 if (g_strcmp0 (xb_node_get_element (req), "firmware") == 0) {
Richard Hughes881f6242018-08-06 11:03:06 +01001562 if (device == NULL)
1563 return TRUE;
Richard Hughesbf439782020-11-03 11:32:29 +00001564 return fu_engine_check_requirement_firmware (self, req, device,
1565 flags, error);
Richard Hughes881f6242018-08-06 11:03:06 +01001566 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001567
1568 /* ensure hardware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001569 if (g_strcmp0 (xb_node_get_element (req), "hardware") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001570 return fu_engine_check_requirement_hardware (self, req, error);
1571
Richard Hughesdf89cd52020-06-26 20:25:18 +01001572 /* ensure client requirement */
1573 if (g_strcmp0 (xb_node_get_element (req), "client") == 0)
1574 return fu_engine_check_requirement_client (self, request, req, error);
1575
Richard Hughes0eb123b2018-04-19 12:00:04 +01001576 /* not supported */
1577 g_set_error (error,
1578 FWUPD_ERROR,
1579 FWUPD_ERROR_NOT_SUPPORTED,
1580 "cannot handle requirement type %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001581 xb_node_get_element (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001582 return FALSE;
1583}
1584
1585gboolean
Mario Limonciello381c27c2020-10-23 16:32:26 -05001586fu_engine_check_trust (FuInstallTask *task, GError **error)
1587{
1588#ifndef HAVE_POLKIT
1589 if ((fu_install_task_get_trust_flags (task) & FWUPD_TRUST_FLAG_PAYLOAD) == 0) {
1590 g_set_error_literal (error,
1591 FWUPD_ERROR,
1592 FWUPD_ERROR_INVALID_FILE,
1593 "archive signature missing or not trusted");
1594 return FALSE;
1595 }
1596#endif
1597 return TRUE;
1598}
1599
1600gboolean
Richard Hughesdf89cd52020-06-26 20:25:18 +01001601fu_engine_check_requirements (FuEngine *self,
1602 FuEngineRequest *request,
1603 FuInstallTask *task,
1604 FwupdInstallFlags flags,
1605 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001606{
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001607 FuDevice *device = fu_install_task_get_device (task);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001608 g_autoptr(GError) error_local = NULL;
1609 g_autoptr(GPtrArray) reqs = NULL;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001610
1611 /* all install task checks require a device */
1612 if (device != NULL) {
1613 if (!fu_install_task_check_requirements (task, flags, error))
1614 return FALSE;
1615 }
1616
1617 /* do engine checks */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001618 reqs = xb_node_query (fu_install_task_get_component (task),
1619 "requires/*", 0, &error_local);
Mario Limonciello7db8e882020-10-20 08:52:51 -05001620 if (reqs != NULL) {
1621 for (guint i = 0; i < reqs->len; i++) {
1622 XbNode *req = g_ptr_array_index (reqs, i);
Richard Hughesbf439782020-11-03 11:32:29 +00001623 if (!fu_engine_check_requirement (self, request,
1624 req, device,
1625 flags, error))
Mario Limonciello7db8e882020-10-20 08:52:51 -05001626 return FALSE;
1627 }
1628 } else if (!g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
1629 !g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
1630 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001631 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001632 }
Mario Limonciello7db8e882020-10-20 08:52:51 -05001633
Richard Hughes9945edb2017-06-19 10:03:55 +01001634 return TRUE;
1635}
1636
Richard Hughes75b965d2018-11-15 13:51:21 +00001637void
1638fu_engine_idle_reset (FuEngine *self)
1639{
1640 fu_idle_reset (self->idle);
1641}
1642
Richard Hughes9945edb2017-06-19 10:03:55 +01001643static gchar *
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001644fu_engine_get_boot_time (void)
1645{
1646 g_autofree gchar *buf = NULL;
1647 g_auto(GStrv) lines = NULL;
1648 if (!g_file_get_contents ("/proc/stat", &buf, NULL, NULL))
1649 return NULL;
1650 lines = g_strsplit (buf, "\n", -1);
1651 for (guint i = 0; lines[i] != NULL; i++) {
1652 if (g_str_has_prefix (lines[i], "btime "))
1653 return g_strdup (lines[i] + 6);
1654 }
1655 return NULL;
1656}
1657
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001658static gboolean
1659fu_engine_get_report_metadata_os_release (GHashTable *hash, GError **error)
Richard Hughes473c5202018-01-11 21:06:16 +00001660{
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001661 g_autoptr(GHashTable) os_release = NULL;
1662 struct {
1663 const gchar *key;
1664 const gchar *val;
1665 } distro_kv[] = {
1666 { "ID", "DistroId" },
1667 { "VERSION_ID", "DistroVersion" },
1668 { "VARIANT_ID", "DistroVariant" },
1669 { NULL, NULL }
1670 };
1671
1672 /* get all required os-release keys */
1673 os_release = fwupd_get_os_release (error);
1674 if (os_release == NULL)
1675 return FALSE;
1676 for (guint i = 0; distro_kv[i].key != NULL; i++) {
1677 const gchar *tmp = g_hash_table_lookup (os_release, distro_kv[i].key);
1678 if (tmp != NULL) {
1679 g_hash_table_insert (hash,
1680 g_strdup (distro_kv[i].val),
1681 g_strdup (tmp));
1682 }
1683 }
1684 return TRUE;
1685}
1686
Richard Hughesdc867dd2020-05-20 18:47:20 +01001687static gboolean
1688fu_engine_get_report_metadata_kernel_cmdline (GHashTable *hash, GError **error)
1689{
1690 gsize bufsz = 0;
1691 g_autofree gchar *buf = NULL;
1692 const gchar *ignore[] = {
1693 "",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001694 "auto",
Richard Hughesd94ce342020-06-12 17:13:14 +01001695 "boot",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001696 "BOOT_IMAGE",
1697 "console",
1698 "cryptdevice",
Richard Hughes7f7f0ae2020-06-15 12:10:58 +01001699 "cryptkey",
1700 "earlycon",
1701 "earlyprintk",
1702 "ether",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001703 "initrd",
Richard Hughesd94ce342020-06-12 17:13:14 +01001704 "ip",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001705 "LANG",
1706 "loglevel",
Richard Hughesd94ce342020-06-12 17:13:14 +01001707 "luks.key",
1708 "luks.name",
1709 "luks.options",
1710 "luks.uuid",
1711 "mount.usr",
1712 "mount.usrflags",
1713 "mount.usrfstype",
Richard Hughes7f7f0ae2020-06-15 12:10:58 +01001714 "netdev",
Richard Hughesd94ce342020-06-12 17:13:14 +01001715 "netroot",
1716 "nfsaddrs",
1717 "nfs.nfs4_unique_id",
1718 "nfsroot",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001719 "noplymouth",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001720 "ostree",
1721 "quiet",
Richard Hughesd94ce342020-06-12 17:13:14 +01001722 "rd.dm.uuid",
1723 "rd.luks.allow-discards",
1724 "rd.luks.key",
1725 "rd.luks.name",
1726 "rd.luks.options",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001727 "rd.luks.uuid",
1728 "rd.lvm.lv",
Richard Hughesd94ce342020-06-12 17:13:14 +01001729 "rd.lvm.vg",
Richard Hughes4a623292020-05-21 20:58:28 +01001730 "rd.md.uuid",
Richard Hughesd94ce342020-06-12 17:13:14 +01001731 "rd.systemd.mask",
1732 "rd.systemd.wants",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001733 "resume",
Richard Hughesd94ce342020-06-12 17:13:14 +01001734 "resumeflags",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001735 "rhgb",
1736 "ro",
1737 "root",
1738 "rootflags",
Richard Hughesd94ce342020-06-12 17:13:14 +01001739 "roothash",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001740 "rw",
1741 "showopts",
1742 "splash",
1743 "swap",
Richard Hughesd94ce342020-06-12 17:13:14 +01001744 "systemd.mask",
Richard Hughes7f7f0ae2020-06-15 12:10:58 +01001745 "systemd.unit",
Richard Hughesd94ce342020-06-12 17:13:14 +01001746 "systemd.verity_root_data",
1747 "systemd.verity_root_hash",
1748 "systemd.wants",
Richard Hughesc05ac2d2020-05-20 22:35:09 +01001749 "verbose",
Richard Hughesdc867dd2020-05-20 18:47:20 +01001750 "vt.handoff",
1751 "zfs",
1752 NULL, /* last entry */
1753 };
1754
1755 /* get a PII-safe kernel command line */
1756 if (!g_file_get_contents ("/proc/cmdline", &buf, &bufsz, error))
1757 return FALSE;
1758 if (bufsz > 0) {
1759 g_auto(GStrv) tokens = fu_common_strnsplit (buf, bufsz - 1, " ", -1);
1760 g_autoptr(GString) cmdline_safe = g_string_new (NULL);
1761 for (guint i = 0; tokens[i] != NULL; i++) {
Jan Tojnara8b3f9e2020-11-07 16:57:12 +01001762 g_auto(GStrv) kv = NULL;
1763 if (strlen (tokens[i]) == 0)
1764 continue;
1765 kv = g_strsplit (tokens[i], "=", 2);
Richard Hughesdc867dd2020-05-20 18:47:20 +01001766 if (g_strv_contains (ignore, kv[0]))
1767 continue;
1768 if (cmdline_safe->len > 0)
1769 g_string_append (cmdline_safe, " ");
1770 g_string_append (cmdline_safe, tokens[i]);
1771 }
1772 if (cmdline_safe->len > 0) {
1773 g_hash_table_insert (hash,
1774 g_strdup ("KernelCmdline"),
1775 g_strdup (cmdline_safe->str));
1776 }
1777 }
1778 return TRUE;
1779}
1780
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001781GHashTable *
1782fu_engine_get_report_metadata (FuEngine *self, GError **error)
1783{
Richard Hughesa778ac92020-05-20 18:44:24 +01001784 const gchar *tmp;
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001785 gchar *btime;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001786#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001787 struct utsname name_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001788#endif
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001789 g_autoptr(GHashTable) hash = NULL;
Richard Hughes34e0dab2018-04-20 16:43:00 +01001790 g_autoptr(GList) compile_keys = g_hash_table_get_keys (self->compile_versions);
1791 g_autoptr(GList) runtime_keys = g_hash_table_get_keys (self->runtime_versions);
Richard Hughes473c5202018-01-11 21:06:16 +00001792
Richard Hughes34e0dab2018-04-20 16:43:00 +01001793 /* convert all the runtime and compile-time versions */
Richard Hughes473c5202018-01-11 21:06:16 +00001794 hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes34e0dab2018-04-20 16:43:00 +01001795 for (GList *l = compile_keys; l != NULL; l = l->next) {
1796 const gchar *id = l->data;
1797 const gchar *version = g_hash_table_lookup (self->compile_versions, id);
1798 g_hash_table_insert (hash,
1799 g_strdup_printf ("CompileVersion(%s)", id),
1800 g_strdup (version));
1801 }
1802 for (GList *l = runtime_keys; l != NULL; l = l->next) {
1803 const gchar *id = l->data;
1804 const gchar *version = g_hash_table_lookup (self->runtime_versions, id);
1805 g_hash_table_insert (hash,
1806 g_strdup_printf ("RuntimeVersion(%s)", id),
1807 g_strdup (version));
1808 }
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001809 if (!fu_engine_get_report_metadata_os_release (hash, error))
1810 return NULL;
Richard Hughesdc867dd2020-05-20 18:47:20 +01001811 if (!fu_engine_get_report_metadata_kernel_cmdline (hash, error))
1812 return NULL;
Richard Hughes473c5202018-01-11 21:06:16 +00001813
Richard Hughesa778ac92020-05-20 18:44:24 +01001814 /* DMI data */
Richard Hughesb333e002021-04-01 10:40:02 +01001815 tmp = fu_context_get_hwid_value (self->ctx, FU_HWIDS_KEY_PRODUCT_NAME);
Richard Hughesa778ac92020-05-20 18:44:24 +01001816 if (tmp != NULL)
1817 g_hash_table_insert (hash, g_strdup ("HostProduct"), g_strdup (tmp));
Richard Hughesb333e002021-04-01 10:40:02 +01001818 tmp = fu_context_get_hwid_value (self->ctx, FU_HWIDS_KEY_FAMILY);
Richard Hughesa778ac92020-05-20 18:44:24 +01001819 if (tmp != NULL)
1820 g_hash_table_insert (hash, g_strdup ("HostFamily"), g_strdup (tmp));
Richard Hughesb333e002021-04-01 10:40:02 +01001821 tmp = fu_context_get_hwid_value (self->ctx, FU_HWIDS_KEY_PRODUCT_SKU);
Richard Hughesa778ac92020-05-20 18:44:24 +01001822 if (tmp != NULL)
1823 g_hash_table_insert (hash, g_strdup ("HostSku"), g_strdup (tmp));
Richard Hughesb333e002021-04-01 10:40:02 +01001824 tmp = fu_context_get_hwid_value (self->ctx, FU_HWIDS_KEY_MANUFACTURER);
Richard Hughesa778ac92020-05-20 18:44:24 +01001825 if (tmp != NULL)
1826 g_hash_table_insert (hash, g_strdup ("HostVendor"), g_strdup (tmp));
1827
Richard Hughes473c5202018-01-11 21:06:16 +00001828 /* kernel version is often important for debugging failures */
Richard Hughesfc1e2672019-11-22 08:53:33 +00001829#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001830 memset (&name_tmp, 0, sizeof (struct utsname));
Richard Hughes473c5202018-01-11 21:06:16 +00001831 if (uname (&name_tmp) >= 0) {
1832 g_hash_table_insert (hash,
1833 g_strdup ("CpuArchitecture"),
1834 g_strdup (name_tmp.machine));
Richard Hughes08bb9222020-05-20 18:43:59 +01001835 g_hash_table_insert (hash,
1836 g_strdup ("KernelVersion"),
1837 g_strdup (name_tmp.release));
Richard Hughes473c5202018-01-11 21:06:16 +00001838 }
Richard Hughesfc1e2672019-11-22 08:53:33 +00001839#endif
Richard Hughes473c5202018-01-11 21:06:16 +00001840
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001841 /* add the kernel boot time so we can detect a reboot */
1842 btime = fu_engine_get_boot_time ();
1843 if (btime != NULL)
1844 g_hash_table_insert (hash, g_strdup ("BootTime"), btime);
1845
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01001846 return g_steal_pointer (&hash);
Richard Hughes473c5202018-01-11 21:06:16 +00001847}
1848
Richard Hughes9945edb2017-06-19 10:03:55 +01001849/**
Richard Hughesdbd8c762018-06-15 20:31:40 +01001850 * fu_engine_composite_prepare:
1851 * @self: A #FuEngine
1852 * @devices: (element-type #FuDevice): devices that will be updated
1853 * @error: A #GError, or %NULL
1854 *
1855 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1856 *
1857 * Any failure in any plugin will abort all of the actions before they are started.
1858 *
1859 * Returns: %TRUE for success
1860 **/
1861gboolean
1862fu_engine_composite_prepare (FuEngine *self, GPtrArray *devices, GError **error)
1863{
1864 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1865 for (guint j = 0; j < plugins->len; j++) {
1866 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1867 if (!fu_plugin_runner_composite_prepare (plugin_tmp, devices, error))
1868 return FALSE;
1869 }
1870 return TRUE;
1871}
1872
1873/**
1874 * fu_engine_composite_cleanup:
1875 * @self: A #FuEngine
1876 * @devices: (element-type #FuDevice): devices that will be updated
1877 * @error: A #GError, or %NULL
1878 *
1879 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1880 *
1881 * Returns: %TRUE for success
1882 **/
1883gboolean
1884fu_engine_composite_cleanup (FuEngine *self, GPtrArray *devices, GError **error)
1885{
1886 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1887 for (guint j = 0; j < plugins->len; j++) {
1888 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1889 if (!fu_plugin_runner_composite_cleanup (plugin_tmp, devices, error))
1890 return FALSE;
1891 }
1892 return TRUE;
1893}
1894
1895/**
1896 * fu_engine_install_tasks:
1897 * @self: A #FuEngine
Richard Hughesdf89cd52020-06-26 20:25:18 +01001898 * @request: A #FuEngineRequest
Richard Hughesdbd8c762018-06-15 20:31:40 +01001899 * @install_tasks: (element-type FuInstallTask): A #FuDevice
1900 * @blob_cab: The #GBytes of the .cab file
1901 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
1902 * @error: A #GError, or %NULL
1903 *
1904 * Installs a specific firmware file on one or more install tasks.
1905 *
1906 * By this point all the requirements and tests should have been done in
1907 * fu_engine_check_requirements() so this should not fail before running
1908 * the plugin loader.
1909 *
1910 * Returns: %TRUE for success
1911 **/
1912gboolean
1913fu_engine_install_tasks (FuEngine *self,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001914 FuEngineRequest *request,
Richard Hughesdbd8c762018-06-15 20:31:40 +01001915 GPtrArray *install_tasks,
1916 GBytes *blob_cab,
1917 FwupdInstallFlags flags,
1918 GError **error)
1919{
Richard Hughes75b965d2018-11-15 13:51:21 +00001920 g_autoptr(FuIdleLocker) locker = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001921 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001922 g_autoptr(GPtrArray) devices_new = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001923
Richard Hughes75b965d2018-11-15 13:51:21 +00001924 /* do not allow auto-shutdown during this time */
Mario Limonciello67a8b892020-09-28 13:44:39 -05001925 locker = fu_idle_locker_new (self->idle, "update");
Richard Hughes75b965d2018-11-15 13:51:21 +00001926 g_assert (locker != NULL);
1927
Richard Hughesdbd8c762018-06-15 20:31:40 +01001928 /* notify the plugins about the composite action */
1929 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1930 for (guint i = 0; i < install_tasks->len; i++) {
1931 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
Richard Hughesca6af0b2020-04-16 17:08:33 +01001932 g_debug ("composite update %u: %s", i + 1,
1933 fu_device_get_id (fu_install_task_get_device (task)));
Richard Hughesdbd8c762018-06-15 20:31:40 +01001934 g_ptr_array_add (devices, g_object_ref (fu_install_task_get_device (task)));
1935 }
1936 if (!fu_engine_composite_prepare (self, devices, error)) {
1937 g_prefix_error (error, "failed to prepare composite action: ");
1938 return FALSE;
1939 }
1940
1941 /* all authenticated, so install all the things */
1942 for (guint i = 0; i < install_tasks->len; i++) {
1943 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1944 if (!fu_engine_install (self, task, blob_cab, flags, error)) {
1945 g_autoptr(GError) error_local = NULL;
1946 if (!fu_engine_composite_cleanup (self, devices, &error_local)) {
1947 g_warning ("failed to cleanup failed composite action: %s",
1948 error_local->message);
1949 }
1950 return FALSE;
1951 }
1952 }
1953
Richard Hughes96019e82019-01-30 11:12:57 +00001954 /* set all the device statuses back to unknown */
1955 for (guint i = 0; i < install_tasks->len; i++) {
1956 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1957 FuDevice *device = fu_install_task_get_device (task);
1958 fu_device_set_status (device, FWUPD_STATUS_UNKNOWN);
1959 }
1960
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001961 /* get a new list of devices in case they replugged */
1962 devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1963 for (guint i = 0; i < devices->len; i++) {
1964 FuDevice *device;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001965 g_autoptr(FuDevice) device_new = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001966 g_autoptr(GError) error_local = NULL;
1967 device = g_ptr_array_index (devices, i);
1968 device_new = fu_device_list_get_by_id (self->device_list,
1969 fu_device_get_id (device),
1970 &error_local);
1971 if (device_new == NULL) {
Mario Limonciello769d7682018-09-28 08:43:37 -05001972 g_debug ("failed to find new device: %s",
1973 error_local->message);
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001974 continue;
1975 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001976 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001977 }
1978
Richard Hughesdbd8c762018-06-15 20:31:40 +01001979 /* notify the plugins about the composite action */
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001980 if (!fu_engine_composite_cleanup (self, devices_new, error)) {
Richard Hughesdbd8c762018-06-15 20:31:40 +01001981 g_prefix_error (error, "failed to cleanup composite action: ");
1982 return FALSE;
1983 }
1984
1985 /* success */
1986 return TRUE;
1987}
1988
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001989static FwupdRelease *
Richard Hughesb1146612020-06-16 14:56:16 +01001990fu_engine_create_release_metadata (FuEngine *self,
1991 FuDevice *device,
1992 FuPlugin *plugin,
1993 GError **error)
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001994{
Richard Hughesdad35972019-12-06 11:00:25 +00001995 GPtrArray *metadata_sources;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001996 g_autoptr(FwupdRelease) release = fwupd_release_new ();
Richard Hughesb1146612020-06-16 14:56:16 +01001997 g_autoptr(GHashTable) metadata_device = NULL;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001998 g_autoptr(GHashTable) metadata_hash = NULL;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001999
2000 /* build the version metadata */
Richard Hughes6ecc4ca2020-05-20 18:42:46 +01002001 metadata_hash = fu_engine_get_report_metadata (self, error);
2002 if (metadata_hash == NULL)
2003 return NULL;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002004 fwupd_release_add_metadata (release, metadata_hash);
Richard Hughes1d900f72020-06-22 15:17:39 +01002005 if (fu_plugin_get_report_metadata (plugin) != NULL)
2006 fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin));
Richard Hughesb1146612020-06-16 14:56:16 +01002007 metadata_device = fu_device_report_metadata_pre (device);
2008 if (metadata_device != NULL)
2009 fwupd_release_add_metadata (release, metadata_device);
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002010
Richard Hughesdad35972019-12-06 11:00:25 +00002011 /* allow other plugins to contribute metadata too */
2012 metadata_sources = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_METADATA_SOURCE);
Richard Hughes11c59412020-06-22 15:29:48 +01002013 if (metadata_sources != NULL) {
2014 for (guint i = 0; i < metadata_sources->len; i++) {
2015 FuPlugin *plugin_tmp;
2016 const gchar *plugin_name = g_ptr_array_index (metadata_sources, i);
2017 g_autoptr(GError) error_local = NULL;
Richard Hughesdad35972019-12-06 11:00:25 +00002018
Richard Hughes11c59412020-06-22 15:29:48 +01002019 plugin_tmp = fu_plugin_list_find_by_name (self->plugin_list,
2020 plugin_name,
2021 &error_local);
2022 if (plugin_tmp == NULL) {
2023 g_warning ("could not add metadata for %s: %s",
2024 plugin_name,
2025 error_local->message);
2026 continue;
2027 }
2028 if (fu_plugin_get_report_metadata (plugin_tmp) != NULL) {
2029 fwupd_release_add_metadata (release,
2030 fu_plugin_get_report_metadata (plugin_tmp));
2031 }
Richard Hughes1d900f72020-06-22 15:17:39 +01002032 }
Richard Hughesdad35972019-12-06 11:00:25 +00002033 }
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002034 return g_steal_pointer (&release);
2035}
2036
Richard Hughes3d005222019-05-17 14:02:41 +01002037static gboolean
2038fu_engine_is_running_offline (FuEngine *self)
2039{
2040#ifdef HAVE_SYSTEMD
2041 g_autofree gchar *default_target = NULL;
2042 g_autoptr(GError) error = NULL;
2043 default_target = fu_systemd_get_default_target (&error);
2044 if (default_target == NULL) {
2045 g_warning ("failed to get default.target: %s", error->message);
2046 return FALSE;
2047 }
2048 return g_strcmp0 (default_target, "system-update.target") == 0;
2049#else
2050 return FALSE;
2051#endif
2052}
2053
Richard Hughes019a1bc2019-11-26 10:19:33 +00002054static gboolean
2055fu_engine_offline_setup (GError **error)
2056{
Richard Hughes9e5675e2019-11-22 09:35:03 +00002057#ifdef HAVE_GIO_UNIX
Richard Hughes019a1bc2019-11-26 10:19:33 +00002058 gint rc;
2059 g_autofree gchar *filename = NULL;
2060 g_autofree gchar *symlink_target = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
2061 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
2062
2063 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2064
2065 /* does already exist */
2066 filename = fu_common_realpath (trigger, NULL);
2067 if (g_strcmp0 (filename, symlink_target) == 0) {
2068 g_debug ("%s already points to %s, skipping creation",
2069 trigger, symlink_target);
2070 return TRUE;
2071 }
2072
2073 /* create symlink for the systemd-system-update-generator */
2074 rc = symlink (symlink_target, trigger);
2075 if (rc < 0) {
2076 g_set_error (error,
2077 FWUPD_ERROR,
2078 FWUPD_ERROR_INTERNAL,
2079 "Failed to create symlink %s to %s: %s",
2080 trigger, symlink_target, strerror (errno));
2081 return FALSE;
2082 }
2083 return TRUE;
Richard Hughes9e5675e2019-11-22 09:35:03 +00002084#else
2085 g_set_error (error,
2086 FWUPD_ERROR,
2087 FWUPD_ERROR_NOT_SUPPORTED,
2088 "Not supported as <gio-unix.h> not available");
2089 return FALSE;
2090#endif
2091
Richard Hughes019a1bc2019-11-26 10:19:33 +00002092}
2093
2094static gboolean
2095fu_engine_offline_invalidate (GError **error)
2096{
2097 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
2098 g_autoptr(GError) error_local = NULL;
2099 g_autoptr(GFile) file1 = NULL;
2100
2101 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2102
2103 file1 = g_file_new_for_path (trigger);
2104 if (!g_file_query_exists (file1, NULL))
2105 return TRUE;
2106 if (!g_file_delete (file1, NULL, &error_local)) {
2107 g_set_error (error,
2108 FWUPD_ERROR,
2109 FWUPD_ERROR_INTERNAL,
2110 "Cannot delete %s: %s",
2111 trigger,
2112 error_local->message);
2113 return FALSE;
2114 }
2115 return TRUE;
2116}
2117
2118/**
2119 * fu_engine_schedule_update:
2120 * @self: a #FuEngine
2121 * @device: a #FuDevice
2122 * @release: A #FwupdRelease
2123 * @blob_cab: A #GBytes
2124 * @flags: #FwupdInstallFlags
2125 * @error: A #GError or NULL
2126 *
2127 * Schedule an offline update for the device
2128 *
2129 * Returns: #TRUE for success, #FALSE for failure
2130 *
2131 * Since: 1.3.5
2132 **/
2133gboolean
2134fu_engine_schedule_update (FuEngine *self,
2135 FuDevice *device,
2136 FwupdRelease *release,
2137 GBytes *blob_cab,
2138 FwupdInstallFlags flags,
2139 GError **error)
2140{
2141 gchar tmpname[] = {"XXXXXX.cab"};
2142 g_autofree gchar *dirname = NULL;
2143 g_autofree gchar *filename = NULL;
2144 g_autoptr(FuHistory) history = NULL;
2145 g_autoptr(GFile) file = NULL;
2146
2147 /* id already exists */
2148 history = fu_history_new ();
2149 if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2150 g_autoptr(FuDevice) res_tmp = NULL;
2151 res_tmp = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL);
2152 if (res_tmp != NULL &&
2153 fu_device_get_update_state (res_tmp) == FWUPD_UPDATE_STATE_PENDING) {
2154 g_set_error (error,
2155 FWUPD_ERROR,
2156 FWUPD_ERROR_ALREADY_PENDING,
2157 "%s is already scheduled to be updated",
2158 fu_device_get_id (device));
2159 return FALSE;
2160 }
2161 }
2162
2163 /* create directory */
2164 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
2165 file = g_file_new_for_path (dirname);
2166 if (!g_file_query_exists (file, NULL)) {
2167 if (!g_file_make_directory_with_parents (file, NULL, error))
2168 return FALSE;
2169 }
2170
2171 /* get a random filename */
2172 for (guint i = 0; i < 6; i++)
2173 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
2174 filename = g_build_filename (dirname, tmpname, NULL);
2175
2176 /* just copy to the temp file */
2177 fu_device_set_status (device, FWUPD_STATUS_SCHEDULING);
2178 if (!g_file_set_contents (filename,
2179 g_bytes_get_data (blob_cab, NULL),
2180 (gssize) g_bytes_get_size (blob_cab),
2181 error))
2182 return FALSE;
2183
2184 /* schedule for next boot */
2185 g_debug ("schedule %s to be installed to %s on next boot",
2186 filename, fu_device_get_id (device));
2187 fwupd_release_set_filename (release, filename);
2188
2189 /* add to database */
2190 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
2191 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_PENDING);
2192 if (!fu_history_add_device (history, device, release, error))
2193 return FALSE;
2194
2195 /* next boot we run offline */
2196 fu_device_set_progress (device, 100);
2197 return fu_engine_offline_setup (error);
2198}
2199
Richard Hughes4bd2e042019-12-22 12:19:52 +00002200static gboolean
2201fu_engine_install_release (FuEngine *self,
Richard Hughes293a64d2020-02-14 12:08:08 +00002202 FuDevice *device_orig,
Richard Hughes4bd2e042019-12-22 12:19:52 +00002203 XbNode *component,
2204 XbNode *rel,
2205 FwupdInstallFlags flags,
2206 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002207{
Richard Hughesafca2032019-02-01 18:05:30 +00002208 FuPlugin *plugin;
Richard Hughes9a680842020-02-20 11:11:13 +00002209 FwupdVersionFormat fmt;
Richard Hughes9945edb2017-06-19 10:03:55 +01002210 GBytes *blob_fw;
Richard Hughesd5aab652020-02-25 12:47:50 +00002211 const gchar *tmp;
Richard Hughes84af6e72019-02-01 18:19:41 +00002212 g_autofree gchar *version_orig = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002213 g_autofree gchar *version_rel = NULL;
Richard Hughes68db74b2019-03-14 09:52:05 +00002214 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes293a64d2020-02-14 12:08:08 +00002215 g_autoptr(FuDevice) device = g_object_ref (device_orig);
Richard Hughes41cbe2a2017-08-08 14:13:35 +01002216 g_autoptr(GBytes) blob_fw2 = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002217 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002218
Richard Hughesd5aab652020-02-25 12:47:50 +00002219 /* get per-release firmware blob */
2220 blob_fw = xb_node_get_data (rel, "fwupd::FirmwareBlob");
Richard Hughes9945edb2017-06-19 10:03:55 +01002221 if (blob_fw == NULL) {
Richard Hughesd5aab652020-02-25 12:47:50 +00002222 g_set_error_literal (error,
2223 FWUPD_ERROR,
2224 FWUPD_ERROR_INTERNAL,
2225 "Failed to get firmware blob from release");
Richard Hughes9945edb2017-06-19 10:03:55 +01002226 return FALSE;
2227 }
2228
Richard Hughes41cbe2a2017-08-08 14:13:35 +01002229 /* use a bubblewrap helper script to build the firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002230 tmp = g_object_get_data (G_OBJECT (component), "fwupd::BuilderScript");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01002231 if (tmp != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01002232 const gchar *tmp2 = g_object_get_data (G_OBJECT (component), "fwupd::BuilderOutput");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01002233 if (tmp2 == NULL)
2234 tmp2 = "firmware.bin";
2235 blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error);
2236 if (blob_fw2 == NULL)
2237 return FALSE;
2238 } else {
2239 blob_fw2 = g_bytes_ref (blob_fw);
2240 }
2241
Richard Hughesafca2032019-02-01 18:05:30 +00002242 /* get the plugin */
2243 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2244 fu_device_get_plugin (device),
2245 error);
2246 if (plugin == NULL)
2247 return FALSE;
2248
Richard Hughesf8e353e2019-03-25 14:26:57 +00002249 /* schedule this for the next reboot if not in system-update.target,
2250 * but first check if allowed on battery power */
Richard Hughes2c40b372019-04-17 13:41:47 +01002251 version_rel = fu_engine_get_release_version (self, device, rel, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01002252 if (version_rel == NULL) {
2253 g_prefix_error (error, "failed to get release version: ");
2254 return FALSE;
2255 }
Richard Hughesf8e353e2019-03-25 14:26:57 +00002256
Richard Hughesafca2032019-02-01 18:05:30 +00002257 /* add device to database */
Richard Hughesafca2032019-02-01 18:05:30 +00002258 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) {
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002259 g_autoptr(FwupdRelease) release_tmp = NULL;
Richard Hughesb1146612020-06-16 14:56:16 +01002260 release_tmp = fu_engine_create_release_metadata (self, device, plugin, error);
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002261 if (release_tmp == NULL)
Richard Hughesafca2032019-02-01 18:05:30 +00002262 return FALSE;
Richard Hughesafca2032019-02-01 18:05:30 +00002263 tmp = xb_node_query_text (component,
2264 "releases/release/checksum[@target='container']",
2265 NULL);
Richard Hughesb9bbe4c2019-03-25 14:03:27 +00002266 if (tmp != NULL)
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002267 fwupd_release_add_checksum (release_tmp, tmp);
2268 fwupd_release_set_version (release_tmp, version_rel);
Richard Hughesafca2032019-02-01 18:05:30 +00002269 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes81d7a5c2019-03-25 14:26:04 +00002270 if (!fu_history_add_device (self->history, device, release_tmp, error))
Richard Hughesafca2032019-02-01 18:05:30 +00002271 return FALSE;
2272 }
2273
2274 /* install firmware blob */
Richard Hughes84af6e72019-02-01 18:19:41 +00002275 version_orig = g_strdup (fu_device_get_version (device));
Richard Hughes03df0d52019-02-01 18:31:39 +00002276 if (!fu_engine_install_blob (self, device, blob_fw2, flags, &error_local)) {
2277 fu_device_set_status (device, FWUPD_STATUS_IDLE);
Richard Hughescce6a1c2019-04-16 17:25:48 +01002278 if (g_error_matches (error_local,
2279 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01002280 FWUPD_ERROR_AC_POWER_REQUIRED) ||
2281 g_error_matches (error_local,
2282 FWUPD_ERROR,
Richard Hughes4266ac42019-07-11 16:49:50 +01002283 FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW) ||
2284 g_error_matches (error_local,
2285 FWUPD_ERROR,
Mario Limonciello2e06dcd2019-10-30 19:28:52 -05002286 FWUPD_ERROR_NEEDS_USER_ACTION) ||
2287 g_error_matches (error_local,
2288 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01002289 FWUPD_ERROR_BROKEN_SYSTEM)) {
Richard Hughescce6a1c2019-04-16 17:25:48 +01002290 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
2291 } else {
2292 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
2293 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002294 fu_device_set_update_error (device, error_local->message);
2295 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00002296 !fu_history_modify_device (self->history, device, error)) {
Richard Hughes03df0d52019-02-01 18:31:39 +00002297 return FALSE;
2298 }
2299 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes84af6e72019-02-01 18:19:41 +00002300 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002301 }
Richard Hughes84af6e72019-02-01 18:19:41 +00002302
Richard Hughes68db74b2019-03-14 09:52:05 +00002303 /* the device may have changed */
2304 device_tmp = fu_device_list_get_by_id (self->device_list,
2305 fu_device_get_id (device),
2306 error);
2307 if (device_tmp == NULL) {
2308 g_prefix_error (error, "failed to get device after install: ");
2309 return FALSE;
2310 }
2311 g_set_object (&device, device_tmp);
2312
Richard Hughes84af6e72019-02-01 18:19:41 +00002313 /* update database */
2314 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) ||
2315 fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) {
2316 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
2317 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00002318 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00002319 return FALSE;
2320 /* success */
2321 return TRUE;
2322 }
2323
2324 /* for online updates, verify the version changed if not a re-install */
Richard Hughes9a680842020-02-20 11:11:13 +00002325 fmt = fu_device_get_version_format (device);
Richard Hughes84af6e72019-02-01 18:19:41 +00002326 if (version_rel != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00002327 fu_common_vercmp_full (version_orig, version_rel, fmt) != 0 &&
Mario Limonciello9e96d412020-12-01 07:35:30 -06002328 fu_common_vercmp_full (version_orig, fu_device_get_version (device), fmt) == 0 &&
Mario Limonciello3165e942021-01-05 13:15:42 -06002329 !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
Richard Hughes68db74b2019-03-14 09:52:05 +00002330 g_autofree gchar *str = NULL;
Richard Hughes84af6e72019-02-01 18:19:41 +00002331 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes68db74b2019-03-14 09:52:05 +00002332 str = g_strdup_printf ("device version not updated on success, %s != %s",
2333 version_rel, fu_device_get_version (device));
2334 fu_device_set_update_error (device, str);
Richard Hughes84af6e72019-02-01 18:19:41 +00002335 }
2336
2337 /* success */
Richard Hughes84af6e72019-02-01 18:19:41 +00002338 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00002339 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00002340 return FALSE;
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06002341
2342 /* make the UI update */
2343 fu_engine_emit_changed (self);
2344
Richard Hughes84af6e72019-02-01 18:19:41 +00002345 return TRUE;
Richard Hughes6e7419d2018-05-18 10:11:36 +01002346}
2347
Richard Hughesaf140732019-12-22 18:42:12 +00002348typedef struct {
2349 gboolean ret;
2350 GError **error;
2351 FuEngine *self;
2352 FuDevice *device;
2353} FuEngineSortHelper;
2354
2355static gint
2356fu_engine_sort_release_versions_cb (gconstpointer a, gconstpointer b, gpointer user_data)
2357{
2358 FuEngineSortHelper *helper = (FuEngineSortHelper *) user_data;
2359 XbNode *na = *((XbNode **) a);
2360 XbNode *nb = *((XbNode **) b);
2361 g_autofree gchar *va = NULL;
2362 g_autofree gchar *vb = NULL;
2363
2364 /* already failed */
2365 if (!helper->ret)
2366 return 0;
2367
2368 /* get the semver from the release */
2369 va = fu_engine_get_release_version (helper->self, helper->device, na, helper->error);
2370 if (va == NULL) {
2371 g_prefix_error (helper->error, "failed to get release version: ");
2372 return 0;
2373 }
2374 vb = fu_engine_get_release_version (helper->self, helper->device, nb, helper->error);
2375 if (vb == NULL) {
2376 g_prefix_error (helper->error, "failed to get release version: ");
2377 return 0;
2378 }
Richard Hughes9a680842020-02-20 11:11:13 +00002379 return fu_common_vercmp_full (va, vb, fu_device_get_version_format (helper->device));
Richard Hughesaf140732019-12-22 18:42:12 +00002380}
2381
2382static gboolean
2383fu_engine_sort_releases (FuEngine *self, FuDevice *device, GPtrArray *rels, GError **error)
2384{
2385 FuEngineSortHelper helper = {
2386 .ret = TRUE,
2387 .self = self,
2388 .device = device,
2389 .error = error,
2390 };
2391 g_ptr_array_sort_with_data (rels, fu_engine_sort_release_versions_cb, &helper);
2392 return helper.ret;
2393}
2394
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002395/**
Richard Hughes4bd2e042019-12-22 12:19:52 +00002396 * fu_engine_install:
2397 * @self: A #FuEngine
2398 * @task: A #FuInstallTask
2399 * @blob_cab: The #GBytes of the .cab file
2400 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
2401 * @error: A #GError, or %NULL
2402 *
2403 * Installs a specific firmware file on a device.
2404 *
2405 * By this point all the requirements and tests should have been done in
2406 * fu_engine_check_requirements() so this should not fail before running
2407 * the plugin loader.
2408 *
2409 * Returns: %TRUE for success
2410 **/
2411gboolean
2412fu_engine_install (FuEngine *self,
2413 FuInstallTask *task,
2414 GBytes *blob_cab,
2415 FwupdInstallFlags flags,
2416 GError **error)
2417{
2418 XbNode *component = fu_install_task_get_component (task);
2419 g_autoptr(FuDevice) device = NULL;
2420 g_autoptr(GError) error_local = NULL;
2421 g_autoptr(XbNode) rel_newest = NULL;
Richard Hughes664b8aa2020-06-22 21:22:42 +01002422#if LIBXMLB_CHECK_VERSION(0,2,0)
2423 g_autoptr(XbQuery) query = NULL;
2424#endif
Richard Hughes4bd2e042019-12-22 12:19:52 +00002425
2426 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2427 g_return_val_if_fail (XB_IS_NODE (component), FALSE);
2428 g_return_val_if_fail (blob_cab != NULL, FALSE);
2429 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2430
2431 /* not in bootloader mode */
2432 device = g_object_ref (fu_install_task_get_device (task));
2433 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
2434 const gchar *caption = NULL;
2435 caption = xb_node_query_text (component,
2436 "screenshots/screenshot/caption",
2437 NULL);
2438 if (caption != NULL) {
2439 g_set_error (error,
2440 FWUPD_ERROR,
2441 FWUPD_ERROR_NEEDS_USER_ACTION,
2442 "Device %s needs to manually be put in update mode: %s",
2443 fu_device_get_name (device), caption);
2444 } else {
2445 g_set_error (error,
2446 FWUPD_ERROR,
2447 FWUPD_ERROR_NEEDS_USER_ACTION,
2448 "Device %s needs to manually be put in update mode",
2449 fu_device_get_name (device));
2450 }
2451 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
2452 if (error != NULL)
2453 fu_device_set_update_error (device, (*error)->message);
2454 return FALSE;
2455 }
2456
2457 /* get the newest version */
Richard Hughes664b8aa2020-06-22 21:22:42 +01002458#if LIBXMLB_CHECK_VERSION(0,2,0)
2459 query = xb_query_new_full (xb_node_get_silo (component),
2460 "releases/release",
2461 XB_QUERY_FLAG_FORCE_NODE_CACHE,
2462 error);
2463 if (query == NULL)
2464 return FALSE;
2465 rel_newest = xb_node_query_first_full (component, query, &error_local);
2466#else
Richard Hughes4bd2e042019-12-22 12:19:52 +00002467 rel_newest = xb_node_query_first (component, "releases/release", &error_local);
Richard Hughes664b8aa2020-06-22 21:22:42 +01002468#endif
Richard Hughes4bd2e042019-12-22 12:19:52 +00002469 if (rel_newest == NULL) {
2470 g_set_error (error,
2471 FWUPD_ERROR,
2472 FWUPD_ERROR_INVALID_FILE,
2473 "No releases in the firmware component: %s",
2474 error_local->message);
2475 return FALSE;
2476 }
2477
2478 /* schedule this for the next reboot if not in system-update.target,
2479 * but first check if allowed on battery power */
2480 if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 &&
2481 !fu_engine_is_running_offline (self)) {
2482 FuPlugin *plugin;
2483 g_autoptr(FwupdRelease) release_tmp = NULL;
2484 g_autofree gchar *version_rel = NULL;
2485 version_rel = fu_engine_get_release_version (self, device, rel_newest, error);
2486 if (version_rel == NULL) {
2487 g_prefix_error (error, "failed to get release version: ");
2488 return FALSE;
2489 }
2490 plugin = fu_plugin_list_find_by_name (self->plugin_list, "upower", NULL);
2491 if (plugin != NULL) {
2492 if (!fu_plugin_runner_update_prepare (plugin, flags, device, error))
2493 return FALSE;
2494 }
Richard Hughesb1146612020-06-16 14:56:16 +01002495 release_tmp = fu_engine_create_release_metadata (self, device, plugin, error);
Richard Hughes4bd2e042019-12-22 12:19:52 +00002496 if (release_tmp == NULL)
2497 return FALSE;
2498 fwupd_release_set_version (release_tmp, version_rel);
2499 return fu_engine_schedule_update (self, device, release_tmp,
2500 blob_cab, flags, error);
2501 }
2502
Richard Hughesaf140732019-12-22 18:42:12 +00002503 /* install each intermediate release, or install only the newest version */
2504 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) {
2505 g_autoptr(GPtrArray) rels = NULL;
Richard Hughes664b8aa2020-06-22 21:22:42 +01002506#if LIBXMLB_CHECK_VERSION(0,2,0)
2507 rels = xb_node_query_full (component, query, &error_local);
2508#else
Richard Hughesaf140732019-12-22 18:42:12 +00002509 rels = xb_node_query (component, "releases/release", 0, &error_local);
Richard Hughes664b8aa2020-06-22 21:22:42 +01002510#endif
Richard Hughesaf140732019-12-22 18:42:12 +00002511 if (rels == NULL) {
2512 g_set_error (error,
2513 FWUPD_ERROR,
2514 FWUPD_ERROR_INVALID_FILE,
2515 "No releases in the firmware component: %s",
2516 error_local->message);
2517 return FALSE;
2518 }
2519 if (!fu_engine_sort_releases (self, device, rels, error))
2520 return FALSE;
2521 for (guint i = 0; i < rels->len; i++) {
2522 XbNode *rel = g_ptr_array_index (rels, i);
2523 if (!fu_engine_install_release (self, device, component, rel, flags, error))
2524 return FALSE;
2525 }
2526 } else {
2527 if (!fu_engine_install_release (self, device, component, rel_newest, flags, error))
2528 return FALSE;
2529 }
Richard Hughes4bd2e042019-12-22 12:19:52 +00002530
2531 /* success */
2532 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2533 return TRUE;
2534}
2535
2536/**
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002537 * fu_engine_get_plugins:
2538 * @self: A #FuPluginList
2539 *
2540 * Gets all the plugins that have been added.
2541 *
2542 * Returns: (transfer none) (element-type FuPlugin): the plugins
2543 *
2544 * Since: 1.0.8
2545 **/
2546GPtrArray *
2547fu_engine_get_plugins (FuEngine *self)
2548{
2549 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2550 return fu_plugin_list_get_all (self->plugin_list);
2551}
2552
Mario Limonciello02085a02020-09-11 14:59:35 -05002553/**
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002554 * fu_engine_get_device:
Mario Limonciello02085a02020-09-11 14:59:35 -05002555 * @self: A #FuEngine
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002556 * @device_id: A device ID
2557 * @error: A #GError, or %NULL
Mario Limonciello02085a02020-09-11 14:59:35 -05002558 *
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002559 * Gets a specific device.
Mario Limonciello02085a02020-09-11 14:59:35 -05002560 *
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002561 * Returns: (transfer full): a device, or %NULL if not found
Mario Limonciello02085a02020-09-11 14:59:35 -05002562 **/
2563FuDevice *
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002564fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error)
Richard Hughesd583baf2018-11-23 13:57:22 +00002565{
2566 g_autoptr(FuDevice) device1 = NULL;
2567 g_autoptr(FuDevice) device2 = NULL;
Richard Hughesc3afed32020-03-10 16:05:29 +00002568 g_autoptr(FuDevice) root = NULL;
Richard Hughesd583baf2018-11-23 13:57:22 +00002569
2570 /* find device */
2571 device1 = fu_device_list_get_by_id (self->device_list, device_id, error);
2572 if (device1 == NULL)
2573 return NULL;
2574
Richard Hughesd583baf2018-11-23 13:57:22 +00002575 /* wait for device to disconnect and reconnect */
Richard Hughesc3afed32020-03-10 16:05:29 +00002576 root = fu_device_get_root (device1);
2577 if (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2578 if (!fu_device_list_wait_for_replug (self->device_list, device1, error)) {
2579 g_prefix_error (error, "failed to wait for detach replug: ");
2580 return NULL;
2581 }
2582 } else if (fu_device_has_flag (root, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2583 if (!fu_device_list_wait_for_replug (self->device_list, root, error)) {
2584 g_prefix_error (error, "failed to wait for detach replug: ");
2585 return NULL;
2586 }
2587 } else {
2588 /* no replug required */
2589 return g_steal_pointer (&device1);
Richard Hughesd583baf2018-11-23 13:57:22 +00002590 }
2591
2592 /* get the new device */
2593 device2 = fu_device_list_get_by_id (self->device_list, device_id, error);
2594 if (device2 == NULL) {
2595 g_prefix_error (error, "failed to get device after replug: ");
2596 return NULL;
2597 }
2598
2599 /* success */
2600 return g_steal_pointer (&device2);
2601}
2602
Richard Hughes2031ce32019-10-30 14:16:24 +00002603/* same as FuDevice->prepare, but with the device open */
2604static gboolean
2605fu_engine_device_prepare (FuEngine *self,
2606 FuDevice *device,
2607 FwupdInstallFlags flags,
2608 GError **error)
2609{
2610 g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002611 if (locker == NULL) {
2612 g_prefix_error (error, "failed to open device for prepare: ");
Richard Hughes2031ce32019-10-30 14:16:24 +00002613 return FALSE;
Richard Hughes8bb64982020-04-16 17:03:18 +01002614 }
Richard Hughesa76a0da2021-03-05 11:39:34 +00002615
Richard Hughes62a8b0c2021-03-31 16:43:36 +01002616 /* check battery level is sane */
Richard Hughesa76a0da2021-03-05 11:39:34 +00002617 if (fu_device_get_battery_level (device) > 0 &&
Richard Hughes62a8b0c2021-03-31 16:43:36 +01002618 fu_device_get_battery_level (device) < fu_device_get_battery_threshold (device)) {
Richard Hughesa76a0da2021-03-05 11:39:34 +00002619 g_set_error (error,
2620 FWUPD_ERROR,
2621 FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW,
2622 "battery level is too low: %u%%",
2623 fu_device_get_battery_level (device));
2624 return FALSE;
2625 }
2626
Richard Hughes2031ce32019-10-30 14:16:24 +00002627 return fu_device_prepare (device, flags, error);
2628}
2629
2630/* same as FuDevice->cleanup, but with the device open */
2631static gboolean
2632fu_engine_device_cleanup (FuEngine *self,
2633 FuDevice *device,
2634 FwupdInstallFlags flags,
2635 GError **error)
2636{
Mario Limoncielloe2b8a272019-11-09 22:10:35 -06002637 g_autoptr(FuDeviceLocker) locker = NULL;
2638
2639 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2640 g_debug ("skipping device cleanup due to will-disappear flag");
2641 return TRUE;
2642 }
2643
2644 locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002645 if (locker == NULL) {
2646 g_prefix_error (error, "failed to open device for cleanup: ");
Richard Hughes2031ce32019-10-30 14:16:24 +00002647 return FALSE;
Richard Hughes8bb64982020-04-16 17:03:18 +01002648 }
Richard Hughes2031ce32019-10-30 14:16:24 +00002649 return fu_device_cleanup (device, flags, error);
2650}
2651
Richard Hughes03df0d52019-02-01 18:31:39 +00002652static gboolean
2653fu_engine_update_prepare (FuEngine *self,
2654 FwupdInstallFlags flags,
2655 const gchar *device_id,
2656 GError **error)
2657{
2658 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002659 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002660 g_autoptr(FuDevice) device = NULL;
2661
2662 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002663 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002664 if (device == NULL)
2665 return FALSE;
Richard Hughes1a580622020-08-03 10:02:25 +01002666
2667 /* don't rely on a plugin clearing this */
2668 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED);
2669
Richard Hughes791c91b2019-10-17 13:33:30 +01002670 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002671 g_debug ("prepare -> %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002672 if (!fu_engine_device_prepare (self, device, flags, error))
Mario Limonciello44b9e462019-10-22 14:32:10 -05002673 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002674 for (guint j = 0; j < plugins->len; j++) {
2675 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2676 if (!fu_plugin_runner_update_prepare (plugin_tmp, flags, device, error))
2677 return FALSE;
2678 }
Richard Hughes802bb312019-10-17 13:32:23 +01002679
2680 /* wait for device to disconnect and reconnect */
2681 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2682 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2683 g_prefix_error (error, "failed to wait for prepare replug: ");
2684 return FALSE;
2685 }
2686 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002687 return TRUE;
2688}
2689
2690static gboolean
2691fu_engine_update_cleanup (FuEngine *self,
2692 FwupdInstallFlags flags,
2693 const gchar *device_id,
2694 GError **error)
2695{
2696 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002697 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002698 g_autoptr(FuDevice) device = NULL;
2699
2700 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002701 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002702 if (device == NULL)
2703 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002704 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002705 g_debug ("cleanup -> %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002706 if (!fu_engine_device_cleanup (self, device, flags, error))
Richard Hughes791c91b2019-10-17 13:33:30 +01002707 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002708 for (guint j = 0; j < plugins->len; j++) {
2709 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2710 if (!fu_plugin_runner_update_cleanup (plugin_tmp, flags, device, error))
2711 return FALSE;
2712 }
Richard Hughes802bb312019-10-17 13:32:23 +01002713
2714 /* wait for device to disconnect and reconnect */
2715 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2716 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2717 g_prefix_error (error, "failed to wait for cleanup replug: ");
2718 return FALSE;
2719 }
2720 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002721 return TRUE;
2722}
2723
2724static gboolean
2725fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error)
2726{
2727 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002728 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002729 g_autoptr(FuDevice) device = NULL;
2730
2731 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002732 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002733 if (device == NULL)
2734 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002735 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002736 g_debug ("detach -> %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002737 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2738 fu_device_get_plugin (device),
2739 error);
2740 if (plugin == NULL)
2741 return FALSE;
2742 if (!fu_plugin_runner_update_detach (plugin, device, error))
2743 return FALSE;
2744 return TRUE;
2745}
2746
2747static gboolean
2748fu_engine_update_attach (FuEngine *self, const gchar *device_id, GError **error)
2749{
2750 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002751 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002752 g_autoptr(FuDevice) device = NULL;
2753
2754 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002755 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002756 if (device == NULL) {
2757 g_prefix_error (error, "failed to get device after update: ");
2758 return FALSE;
2759 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002760 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002761 g_debug ("attach -> %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002762 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2763 fu_device_get_plugin (device),
2764 error);
2765 if (plugin == NULL)
2766 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002767
Richard Hughes03df0d52019-02-01 18:31:39 +00002768 if (!fu_plugin_runner_update_attach (plugin, device, error))
2769 return FALSE;
2770 return TRUE;
2771}
2772
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002773gboolean
2774fu_engine_activate (FuEngine *self, const gchar *device_id, GError **error)
2775{
2776 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002777 g_autofree gchar *str = NULL;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002778 g_autoptr(FuDevice) device = NULL;
2779
2780 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2781 g_return_val_if_fail (device_id != NULL, FALSE);
2782 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2783
2784 /* check the device exists */
2785 device = fu_device_list_get_by_id (self->device_list, device_id, error);
2786 if (device == NULL)
2787 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002788 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002789 g_debug ("activate -> %s", str);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002790 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2791 fu_device_get_plugin (device),
2792 error);
2793 if (plugin == NULL)
2794 return FALSE;
2795 g_debug ("Activating %s", fu_device_get_name (device));
2796
2797 if (!fu_plugin_runner_activate (plugin, device, error))
2798 return FALSE;
2799
2800 fu_engine_emit_device_changed (self, device);
2801 fu_engine_emit_changed (self);
2802
2803 return TRUE;
2804}
2805
Richard Hughes03df0d52019-02-01 18:31:39 +00002806static gboolean
2807fu_engine_update_reload (FuEngine *self, const gchar *device_id, GError **error)
2808{
2809 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002810 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002811 g_autoptr(FuDevice) device = NULL;
2812
2813 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002814 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002815 if (device == NULL) {
2816 g_prefix_error (error, "failed to get device after update: ");
2817 return FALSE;
2818 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002819 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002820 g_debug ("reload -> %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002821 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2822 fu_device_get_plugin (device),
2823 error);
2824 if (plugin == NULL)
2825 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002826
2827 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2828 g_debug ("skipping reload due to will-disappear flag");
2829 return TRUE;
2830 }
2831
Richard Hughes03df0d52019-02-01 18:31:39 +00002832 if (!fu_plugin_runner_update_reload (plugin, device, error)) {
2833 g_prefix_error (error, "failed to reload device: ");
2834 return FALSE;
2835 }
2836 return TRUE;
2837}
2838
2839static gboolean
2840fu_engine_update (FuEngine *self,
2841 const gchar *device_id,
2842 GBytes *blob_fw2,
2843 FwupdInstallFlags flags,
2844 GError **error)
2845{
2846 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002847 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002848 g_autoptr(FuDevice) device = NULL;
Richard Hughes019a1bc2019-11-26 10:19:33 +00002849 g_autoptr(FuDevice) device_pending = NULL;
2850
2851 /* cancel the pending action */
2852 if (!fu_engine_offline_invalidate (error))
2853 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002854
2855 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002856 device = fu_engine_get_device (self, device_id, error);
Richard Hughes03df0d52019-02-01 18:31:39 +00002857 if (device == NULL) {
2858 g_prefix_error (error, "failed to get device after detach: ");
2859 return FALSE;
2860 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002861 device_pending = fu_history_get_device_by_id (self->history, device_id, NULL);
Richard Hughes791c91b2019-10-17 13:33:30 +01002862 str = fu_device_to_string (device);
Mario Limonciello67a8b892020-09-28 13:44:39 -05002863 g_debug ("update -> %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002864 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2865 fu_device_get_plugin (device),
2866 error);
2867 if (plugin == NULL)
2868 return FALSE;
2869 if (!fu_plugin_runner_update (plugin, device, blob_fw2, flags, error)) {
2870 g_autoptr(GError) error_attach = NULL;
2871 g_autoptr(GError) error_cleanup = NULL;
2872
2873 /* attack back into runtime then cleanup */
2874 if (!fu_plugin_runner_update_attach (plugin,
2875 device,
2876 &error_attach)) {
2877 g_warning ("failed to attach device after failed update: %s",
2878 error_attach->message);
2879 }
2880 if (!fu_engine_update_cleanup (self, flags, device_id, &error_cleanup)) {
2881 g_warning ("failed to update-cleanup after failed update: %s",
2882 error_cleanup->message);
2883 }
2884 return FALSE;
2885 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002886
2887 /* cleanup */
2888 if (device_pending != NULL) {
2889 const gchar *tmp;
2890 FwupdRelease *release;
2891
2892 /* update history database */
2893 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2894 if (!fu_history_modify_device (self->history, device, error))
2895 return FALSE;
2896
2897 /* delete cab file */
2898 release = fu_device_get_release_default (device_pending);
2899 tmp = fwupd_release_get_filename (release);
2900 if (tmp != NULL && g_str_has_prefix (tmp, FWUPD_LIBEXECDIR)) {
2901 g_autoptr(GError) error_delete = NULL;
2902 g_autoptr(GFile) file = NULL;
2903 file = g_file_new_for_path (tmp);
2904 if (!g_file_delete (file, NULL, &error_delete)) {
2905 g_set_error (error,
2906 FWUPD_ERROR,
2907 FWUPD_ERROR_INVALID_FILE,
2908 "Failed to delete %s: %s",
2909 tmp, error_delete->message);
2910 return FALSE;
2911 }
2912 }
2913 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002914 return TRUE;
2915}
2916
Richard Hughesa58510b2019-10-30 10:03:12 +00002917GBytes *
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002918fu_engine_firmware_dump (FuEngine *self,
Richard Hughesa58510b2019-10-30 10:03:12 +00002919 FuDevice *device,
2920 FwupdInstallFlags flags,
2921 GError **error)
2922{
2923 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughesa58510b2019-10-30 10:03:12 +00002924
Richard Hughes26e57dd2020-10-05 13:49:23 +01002925 /* open, read, close */
Richard Hughesa58510b2019-10-30 10:03:12 +00002926 locker = fu_device_locker_new (device, error);
Richard Hughes8bb64982020-04-16 17:03:18 +01002927 if (locker == NULL) {
2928 g_prefix_error (error, "failed to open device for firmware read: ");
Richard Hughesa58510b2019-10-30 10:03:12 +00002929 return NULL;
Richard Hughes8bb64982020-04-16 17:03:18 +01002930 }
Richard Hughes26e57dd2020-10-05 13:49:23 +01002931 return fu_device_dump_firmware (device, error);
Richard Hughesa58510b2019-10-30 10:03:12 +00002932}
2933
Richard Hughes6e7419d2018-05-18 10:11:36 +01002934gboolean
2935fu_engine_install_blob (FuEngine *self,
Richard Hughes03df0d52019-02-01 18:31:39 +00002936 FuDevice *device,
2937 GBytes *blob_fw,
Richard Hughes6e7419d2018-05-18 10:11:36 +01002938 FwupdInstallFlags flags,
2939 GError **error)
2940{
Richard Hughes52976782019-02-01 21:23:01 +00002941 guint retries = 0;
Richard Hughes03df0d52019-02-01 18:31:39 +00002942 g_autofree gchar *device_id = NULL;
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05002943 g_autoptr(GTimer) timer = g_timer_new ();
Richard Hughes6e7419d2018-05-18 10:11:36 +01002944
Richard Hughesadcc16a2017-08-21 12:26:46 +01002945 /* test the firmware is not an empty blob */
Richard Hughes03df0d52019-02-01 18:31:39 +00002946 if (g_bytes_get_size (blob_fw) == 0) {
Richard Hughesadcc16a2017-08-21 12:26:46 +01002947 g_set_error (error,
2948 FWUPD_ERROR,
2949 FWUPD_ERROR_INVALID_FILE,
2950 "Firmware is invalid as has zero size");
2951 return FALSE;
2952 }
2953
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002954 /* mark this as modified even if we actually fail to do the update */
2955 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
2956
Richard Hughes52976782019-02-01 21:23:01 +00002957 /* plugins can set FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED to run again, but they
2958 * must return TRUE rather than an error */
Richard Hughes03df0d52019-02-01 18:31:39 +00002959 device_id = g_strdup (fu_device_get_id (device));
Richard Hughes52976782019-02-01 21:23:01 +00002960 do {
Richard Hughes1a580622020-08-03 10:02:25 +01002961 g_autoptr(FuDevice) device_tmp = NULL;
2962
Richard Hughes52976782019-02-01 21:23:01 +00002963 /* check for a loop */
2964 if (++retries > 5) {
2965 g_set_error_literal (error,
2966 FWUPD_ERROR,
2967 FWUPD_ERROR_INTERNAL,
2968 "aborting device write loop, limit 5");
2969 return FALSE;
2970 }
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002971
Richard Hughes52976782019-02-01 21:23:01 +00002972 /* signal to all the plugins the update is about to happen */
2973 if (!fu_engine_update_prepare (self, flags, device_id, error))
2974 return FALSE;
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002975
Richard Hughes52976782019-02-01 21:23:01 +00002976 /* detach to bootloader mode */
2977 if (!fu_engine_update_detach (self, device_id, error))
2978 return FALSE;
2979
2980 /* install */
2981 if (!fu_engine_update (self, device_id, blob_fw, flags, error))
2982 return FALSE;
2983
2984 /* attach into runtime mode */
2985 if (!fu_engine_update_attach (self, device_id, error))
2986 return FALSE;
2987
Richard Hughes1a580622020-08-03 10:02:25 +01002988 /* the device and plugin both may have changed */
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002989 device_tmp = fu_engine_get_device (self, device_id, error);
Richard Hughes1a580622020-08-03 10:02:25 +01002990 if (device_tmp == NULL)
2991 return FALSE;
2992 if (!fu_device_has_flag (device_tmp, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED))
2993 break;
2994
2995 } while (TRUE);
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002996
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002997 /* get the new version number */
Richard Hughes03df0d52019-02-01 18:31:39 +00002998 if (!fu_engine_update_reload (self, device_id, error))
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002999 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01003000
3001 /* signal to all the plugins the update has happened */
Richard Hughes03df0d52019-02-01 18:31:39 +00003002 if (!fu_engine_update_cleanup (self, flags, device_id, error))
3003 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01003004
3005 /* make the UI update */
Richard Hughes7772dcf2018-09-10 15:49:59 +01003006 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05003007 g_debug ("Updating %s took %f seconds", fu_device_get_name (device),
3008 g_timer_elapsed (timer, NULL));
Richard Hughes76e0f942018-05-14 16:24:00 +01003009 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01003010}
3011
Richard Hughes0a7e7832017-11-22 11:01:13 +00003012static FuDevice *
Richard Hughesbc3a4e12018-01-06 22:41:47 +00003013fu_engine_get_item_by_id_fallback_history (FuEngine *self, const gchar *id, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003014{
Richard Hughes9945edb2017-06-19 10:03:55 +01003015 g_autoptr(GPtrArray) devices = NULL;
3016
3017 /* not a wildcard */
Richard Hughes65e44ca2018-01-30 17:26:30 +00003018 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
3019 g_autoptr(FuDevice) dev = NULL;
3020 g_autoptr(GError) error_local = NULL;
3021
3022 /* get this one device */
3023 dev = fu_history_get_device_by_id (self->history, id, &error_local);
3024 if (dev == NULL) {
3025 g_set_error (error,
3026 FWUPD_ERROR,
3027 FWUPD_ERROR_NOTHING_TO_DO,
3028 "Failed to find %s in history database: %s",
3029 id, error_local->message);
3030 return NULL;
3031 }
3032
3033 /* only useful */
3034 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01003035 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00003036 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
3037 return g_steal_pointer (&dev);
3038 }
3039
3040 /* nothing in database */
3041 g_set_error (error,
3042 FWUPD_ERROR,
3043 FWUPD_ERROR_NOTHING_TO_DO,
3044 "Device %s has no results to report",
3045 fu_device_get_id (dev));
3046 return NULL;
3047 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003048
3049 /* allow '*' for any */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00003050 devices = fu_history_get_devices (self->history, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01003051 if (devices == NULL)
3052 return NULL;
3053 for (guint i = 0; i < devices->len; i++) {
Richard Hughes65e44ca2018-01-30 17:26:30 +00003054 FuDevice *dev = g_ptr_array_index (devices, i);
3055 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01003056 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00003057 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED)
3058 return g_object_ref (dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003059 }
Richard Hughes65e44ca2018-01-30 17:26:30 +00003060 g_set_error_literal (error,
3061 FWUPD_ERROR,
3062 FWUPD_ERROR_NOTHING_TO_DO,
3063 "Failed to find any useful results to report");
3064 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003065}
3066
Richard Hughes481aa2a2018-09-18 20:51:46 +01003067/* for the self tests */
3068void
3069fu_engine_set_silo (FuEngine *self, XbSilo *silo)
Richard Hughesbd4d2852017-09-13 14:05:14 +01003070{
Richard Hughes481aa2a2018-09-18 20:51:46 +01003071 g_return_if_fail (FU_IS_ENGINE (self));
3072 g_return_if_fail (XB_IS_SILO (silo));
3073 g_set_object (&self->silo, silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01003074}
3075
3076static gboolean
Richard Hughes1af48b12018-12-03 11:51:17 +00003077fu_engine_appstream_upgrade_cb (XbBuilderFixup *self,
3078 XbBuilderNode *bn,
3079 gpointer user_data,
3080 GError **error)
3081{
3082 if (g_strcmp0 (xb_builder_node_get_element (bn), "metadata") == 0)
3083 xb_builder_node_set_element (bn, "custom");
3084 return TRUE;
3085}
3086
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003087static XbBuilderSource *
3088fu_engine_create_metadata_builder_source (FuEngine *self,
3089 const gchar *fn,
3090 GError **error)
3091{
3092 g_autoptr(GBytes) blob = NULL;
3093 g_autoptr(XbSilo) silo = NULL;
3094 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
3095 g_autofree gchar *xml = NULL;
3096
3097 g_debug ("building metadata for %s", fn);
3098 blob = fu_common_get_contents_bytes (fn, error);
3099 if (blob == NULL)
3100 return NULL;
3101
3102 /* convert the silo for the CAB into a XbBuilderSource */
3103 silo = fu_engine_get_silo_from_blob (self, blob, error);
3104 if (silo == NULL)
3105 return NULL;
3106 xml = xb_silo_export (silo, XB_NODE_EXPORT_FLAG_NONE, error);
3107 if (xml == NULL)
3108 return NULL;
3109 if (!xb_builder_source_load_xml (source, xml,
3110 XB_BUILDER_SOURCE_FLAG_NONE,
3111 error))
3112 return NULL;
3113 return g_steal_pointer (&source);
3114}
3115
3116static gboolean
3117fu_engine_create_metadata (FuEngine *self, XbBuilder *builder,
3118 FwupdRemote *remote, GError **error)
3119{
3120 g_autoptr(GPtrArray) files = NULL;
3121 const gchar *path;
3122
3123 /* find all files in directory */
3124 path = fwupd_remote_get_filename_cache (remote);
3125 files = fu_common_get_files_recursive (path, error);
3126 if (files == NULL)
3127 return FALSE;
3128
3129 /* add each source */
3130 for (guint i = 0; i < files->len; i++) {
3131 g_autoptr(XbBuilderNode) custom = NULL;
3132 g_autoptr(XbBuilderSource) source = NULL;
3133 g_autoptr(GError) error_local = NULL;
3134 const gchar *fn = g_ptr_array_index (files, i);
Crag Wangae422982020-03-05 23:42:53 +08003135 g_autofree gchar *fn_lowercase = g_ascii_strdown (fn, -1);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003136
3137 /* check is cab file */
Crag Wangae422982020-03-05 23:42:53 +08003138 if (!g_str_has_suffix (fn_lowercase, ".cab")) {
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003139 g_debug ("ignoring: %s", fn);
3140 continue;
3141 }
3142
3143 /* build source for file */
3144 source = fu_engine_create_metadata_builder_source (self, fn, &error_local);
3145 if (source == NULL) {
Richard Hughes521c2e22020-10-01 14:12:41 +01003146 g_warning ("failed to create builder source: %s",
3147 error_local->message);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003148 continue;
3149 }
3150
3151 /* add metadata */
3152 custom = xb_builder_node_new ("custom");
3153 xb_builder_node_insert_text (custom,
3154 "value", fn,
3155 "key", "fwupd::FilenameCache",
3156 NULL);
3157 xb_builder_node_insert_text (custom,
3158 "value", fwupd_remote_get_id (remote),
3159 "key", "fwupd::RemoteId",
3160 NULL);
3161 xb_builder_source_set_info (source, custom);
3162 xb_builder_import_source (builder, source);
3163 }
3164 return TRUE;
3165}
3166
Richard Hughesf43381f2020-02-24 10:11:31 +00003167static void
Richard Hughes1ee18002020-03-31 21:46:49 +01003168fu_engine_ensure_device_supported (FuEngine *self, FuDevice *device)
Richard Hughesf43381f2020-02-24 10:11:31 +00003169{
Richard Hughes1ee18002020-03-31 21:46:49 +01003170 gboolean is_supported = FALSE;
3171 g_autoptr(GError) error = NULL;
3172 g_autoptr(GPtrArray) releases = NULL;
Richard Hughesdf89cd52020-06-26 20:25:18 +01003173 g_autoptr(FuEngineRequest) request = fu_engine_request_new ();
3174
3175 /* all flags set */
3176 fu_engine_request_set_feature_flags (request, ~0);
Richard Hughes1ee18002020-03-31 21:46:49 +01003177
3178 /* get all releases that pass the requirements */
3179 releases = fu_engine_get_releases_for_device (self,
Richard Hughesdf89cd52020-06-26 20:25:18 +01003180 request,
Richard Hughes1ee18002020-03-31 21:46:49 +01003181 device,
3182 &error);
3183 if (releases == NULL) {
3184 if (!g_error_matches (error,
3185 FWUPD_ERROR,
3186 FWUPD_ERROR_NOTHING_TO_DO) &&
3187 !g_error_matches (error,
3188 FWUPD_ERROR,
3189 FWUPD_ERROR_NOT_SUPPORTED)) {
3190 g_warning ("failed to get releases for %s: %s",
3191 fu_device_get_name (device),
3192 error->message);
3193 }
3194 } else {
3195 if (releases->len > 0)
3196 is_supported = TRUE;
3197 }
3198
Richard Hughesf43381f2020-02-24 10:11:31 +00003199 /* was supported, now unsupported */
Richard Hughes1ee18002020-03-31 21:46:49 +01003200 if (!is_supported) {
Richard Hughesf43381f2020-02-24 10:11:31 +00003201 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
3202 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
3203 fu_engine_emit_device_changed (self, device);
3204 }
3205 return;
3206 }
3207
3208 /* was unsupported, now supported */
3209 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
3210 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
3211 fu_engine_emit_device_changed (self, device);
3212 }
3213}
3214
3215static void
3216fu_engine_md_refresh_device_name (FuEngine *self, FuDevice *device, XbNode *component)
3217{
3218 const gchar *name = NULL;
3219
3220 /* require data */
3221 if (component == NULL)
3222 return;
3223
3224 /* copy 1:1 */
3225 name = xb_node_query_text (component, "name", NULL);
Richard Hughes2d22ee22020-10-01 13:25:15 +01003226 if (name != NULL) {
Richard Hughesf43381f2020-02-24 10:11:31 +00003227 fu_device_set_name (device, name);
Richard Hughescf100292021-01-04 10:36:37 +00003228 fu_device_remove_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME);
Richard Hughes2d22ee22020-10-01 13:25:15 +01003229 }
Richard Hughesf43381f2020-02-24 10:11:31 +00003230}
3231
Richard Hughes3e445ec2020-11-06 11:30:22 +00003232static void
3233fu_engine_md_refresh_device_icon (FuEngine *self, FuDevice *device, XbNode *component)
3234{
3235 const gchar *icon = NULL;
3236
3237 /* require data */
3238 if (component == NULL)
3239 return;
3240
3241 /* copy 1:1 */
3242 icon = xb_node_query_text (component, "icon", NULL);
3243 if (icon != NULL) {
3244 fu_device_add_icon (device, icon);
Richard Hughescf100292021-01-04 10:36:37 +00003245 fu_device_remove_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON);
Richard Hughes3e445ec2020-11-06 11:30:22 +00003246 }
3247}
3248
Richard Hughesf43381f2020-02-24 10:11:31 +00003249static const gchar *
3250fu_common_device_category_to_name (const gchar *cat)
3251{
3252 if (g_strcmp0 (cat, "X-EmbeddedController") == 0)
3253 return "Embedded Controller";
3254 if (g_strcmp0 (cat, "X-ManagementEngine") == 0)
3255 return "Intel Management Engine";
3256 if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0)
3257 return "Intel Management Engine";
3258 if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0)
3259 return "Intel Management Engine";
3260 if (g_strcmp0 (cat, "X-ThunderboltController") == 0)
3261 return "Thunderbolt Controller";
3262 if (g_strcmp0 (cat, "X-PlatformSecurityProcessor") == 0)
3263 return "Platform Security Processor";
Mario Limonciello7a9bb7e2020-04-02 10:28:41 -05003264 if (g_strcmp0 (cat, "X-CpuMicrocode") == 0)
3265 return "CPU Microcode";
Richard Hughes2d3b8c82021-03-22 17:40:06 +00003266 if (g_strcmp0 (cat, "X-Battery") == 0)
3267 return "Battery";
3268 if (g_strcmp0 (cat, "X-Camera") == 0)
3269 return "Camera";
3270 if (g_strcmp0 (cat, "X-TPM") == 0)
3271 return "TPM";
3272 if (g_strcmp0 (cat, "X-Touchpad") == 0)
3273 return "Touchpad";
3274 if (g_strcmp0 (cat, "X-Mouse") == 0)
3275 return "Mouse";
3276 if (g_strcmp0 (cat, "X-Keyboard") == 0)
3277 return "Keyboard";
Richard Hughesf43381f2020-02-24 10:11:31 +00003278 return NULL;
3279}
3280
3281static void
3282fu_engine_md_refresh_device_name_category (FuEngine *self, FuDevice *device, XbNode *component)
3283{
3284 const gchar *name = NULL;
3285 g_autoptr(GPtrArray) cats = NULL;
3286
3287 /* require data */
3288 if (component == NULL)
3289 return;
3290
3291 /* get AppStream and safe-compat categories */
3292 cats = xb_node_query (component, "categories/category|X-categories/category", 0, NULL);
3293 if (cats == NULL)
3294 return;
3295 for (guint i = 0; i < cats->len; i++) {
3296 XbNode *n = g_ptr_array_index (cats, i);
3297 name = fu_common_device_category_to_name (xb_node_get_text (n));
3298 if (name != NULL)
3299 break;
3300 }
Richard Hughes2d22ee22020-10-01 13:25:15 +01003301 if (name != NULL) {
Richard Hughesf43381f2020-02-24 10:11:31 +00003302 fu_device_set_name (device, name);
Richard Hughescf100292021-01-04 10:36:37 +00003303 fu_device_remove_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY);
Richard Hughes2d22ee22020-10-01 13:25:15 +01003304 }
Richard Hughesf43381f2020-02-24 10:11:31 +00003305}
3306
3307static void
Richard Hughesb0976032020-02-24 14:17:04 +00003308_g_ptr_array_reverse (GPtrArray *array)
3309{
3310 guint last_idx = array->len - 1;
3311 for (guint i = 0; i < array->len / 2; i++) {
3312 gpointer tmp = array->pdata[i];
3313 array->pdata[i] = array->pdata[last_idx - i];
3314 array->pdata[last_idx - i] = tmp;
3315 }
3316}
3317
3318static void
3319fu_engine_md_refresh_device_verfmt (FuEngine *self, FuDevice *device, XbNode *component)
3320{
3321 FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN;
3322 g_autoptr(GPtrArray) verfmts = NULL;
3323
3324 /* require data */
3325 if (component == NULL)
3326 return;
3327
3328 /* get metadata */
3329 verfmts = xb_node_query (component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL);
3330 if (verfmts == NULL)
3331 return;
3332 _g_ptr_array_reverse (verfmts);
3333 for (guint i = 0; i < verfmts->len; i++) {
3334 XbNode *value = g_ptr_array_index (verfmts, i);
3335 verfmt = fwupd_version_format_from_string (xb_node_get_text (value));
3336 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN)
3337 break;
3338 }
3339
3340 /* found and different to existing */
3341 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN &&
3342 fu_device_get_version_format (device) != verfmt) {
3343 fu_device_set_version_format (device, verfmt);
3344 if (fu_device_get_version_raw (device) != 0x0) {
3345 g_autofree gchar *version = NULL;
3346 version = fu_common_version_from_uint32 (fu_device_get_version_raw (device), verfmt);
3347 fu_device_set_version (device, version);
3348 }
3349 if (fu_device_get_version_lowest_raw (device) != 0x0) {
3350 g_autofree gchar *version = NULL;
3351 version = fu_common_version_from_uint32 (fu_device_get_version_lowest_raw (device), verfmt);
3352 fu_device_set_version_lowest (device, version);
3353 }
3354 if (fu_device_get_version_bootloader_raw (device) != 0x0) {
3355 g_autofree gchar *version = NULL;
3356 version = fu_common_version_from_uint32 (fu_device_get_version_bootloader_raw (device), verfmt);
3357 fu_device_set_version_bootloader (device, version);
3358 }
3359 }
Richard Hughes2d22ee22020-10-01 13:25:15 +01003360
3361 /* do not try to do this again */
Richard Hughescf100292021-01-04 10:36:37 +00003362 fu_device_remove_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT);
Richard Hughesb0976032020-02-24 14:17:04 +00003363}
3364
Mario Limonciello537da0e2020-03-09 15:38:17 -05003365void
Mario Limonciellof430da02020-03-09 14:03:03 -05003366fu_engine_md_refresh_device_from_component (FuEngine *self, FuDevice *device, XbNode *component)
Richard Hughesf43381f2020-02-24 10:11:31 +00003367{
Richard Hughesf43381f2020-02-24 10:11:31 +00003368 /* set the name */
Richard Hughescf100292021-01-04 10:36:37 +00003369 if (fu_device_has_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME))
Richard Hughesf43381f2020-02-24 10:11:31 +00003370 fu_engine_md_refresh_device_name (self, device, component);
Richard Hughescf100292021-01-04 10:36:37 +00003371 if (fu_device_has_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_NAME_CATEGORY))
Richard Hughesf43381f2020-02-24 10:11:31 +00003372 fu_engine_md_refresh_device_name_category (self, device, component);
Richard Hughescf100292021-01-04 10:36:37 +00003373 if (fu_device_has_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_ICON))
Richard Hughes3e445ec2020-11-06 11:30:22 +00003374 fu_engine_md_refresh_device_icon (self, device, component);
Richard Hughesb0976032020-02-24 14:17:04 +00003375
3376 /* fix the version */
Richard Hughescf100292021-01-04 10:36:37 +00003377 if (fu_device_has_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT))
Richard Hughesb0976032020-02-24 14:17:04 +00003378 fu_engine_md_refresh_device_verfmt (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00003379}
3380
3381static void
3382fu_engine_md_refresh_devices (FuEngine *self)
3383{
3384 g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list);
3385 for (guint i = 0; i < devices->len; i++) {
3386 FuDevice *device = g_ptr_array_index (devices, i);
Mario Limonciellof430da02020-03-09 14:03:03 -05003387 g_autoptr(XbNode) component = fu_engine_get_component_by_guids (self, device);
Mario Limonciello537da0e2020-03-09 15:38:17 -05003388
3389 /* set or clear the SUPPORTED flag */
Richard Hughes1ee18002020-03-31 21:46:49 +01003390 fu_engine_ensure_device_supported (self, device);
Mario Limonciello537da0e2020-03-09 15:38:17 -05003391
3392 /* fixup the name and format as needed */
Mario Limonciellof430da02020-03-09 14:03:03 -05003393 fu_engine_md_refresh_device_from_component (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00003394 }
3395}
3396
Richard Hughes1af48b12018-12-03 11:51:17 +00003397static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00003398fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003399{
Richard Hughes9945edb2017-06-19 10:03:55 +01003400 GPtrArray *remotes;
Richard Hughesc8cc77c2019-03-07 11:57:24 +00003401 XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003402 g_autofree gchar *cachedirpkg = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003403 g_autofree gchar *xmlbfn = NULL;
3404 g_autoptr(GFile) xmlb = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003405 g_autoptr(GPtrArray) components = NULL;
3406 g_autoptr(XbBuilder) builder = xb_builder_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01003407
Richard Hughes481aa2a2018-09-18 20:51:46 +01003408 /* clear existing silo */
3409 g_clear_object (&self->silo);
3410
3411 /* verbose profiling */
Richard Hughesc46aa812020-09-28 14:20:26 +01003412 if (g_getenv ("FWUPD_XMLB_VERBOSE") != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01003413 xb_builder_set_profile_flags (builder,
3414 XB_SILO_PROFILE_FLAG_XPATH |
3415 XB_SILO_PROFILE_FLAG_DEBUG);
3416 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003417
3418 /* load each enabled metadata file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00003419 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003420 for (guint i = 0; i < remotes->len; i++) {
3421 const gchar *path = NULL;
Richard Hughes5f733f22017-11-26 16:14:20 +00003422 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003423 g_autoptr(GFile) file = NULL;
Richard Hughes1af48b12018-12-03 11:51:17 +00003424 g_autoptr(XbBuilderFixup) fixup = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003425 g_autoptr(XbBuilderNode) custom = NULL;
3426 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
3427
Richard Hughes9945edb2017-06-19 10:03:55 +01003428 FwupdRemote *remote = g_ptr_array_index (remotes, i);
Mario Limonciello933e4302020-10-02 16:33:12 -05003429 if (!fwupd_remote_get_enabled (remote))
Richard Hughes9945edb2017-06-19 10:03:55 +01003430 continue;
Richard Hughes9945edb2017-06-19 10:03:55 +01003431 path = fwupd_remote_get_filename_cache (remote);
Mario Limonciello933e4302020-10-02 16:33:12 -05003432 if (!g_file_test (path, G_FILE_TEST_EXISTS))
Richard Hughes9945edb2017-06-19 10:03:55 +01003433 continue;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003434
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06003435 /* generate all metadata on demand */
3436 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
3437 g_debug ("building metadata for remote '%s'",
3438 fwupd_remote_get_id (remote));
3439 if (!fu_engine_create_metadata (self, builder, remote, &error_local)) {
3440 g_warning ("failed to generate remote %s: %s",
3441 fwupd_remote_get_id (remote),
3442 error_local->message);
3443 }
3444 continue;
3445 }
3446
Richard Hughes481aa2a2018-09-18 20:51:46 +01003447 /* save the remote-id in the custom metadata space */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003448 file = g_file_new_for_path (path);
3449 if (!xb_builder_source_load_file (source, file,
3450 XB_BUILDER_SOURCE_FLAG_NONE,
3451 NULL, &error_local)) {
Richard Hughes068d3432017-09-16 08:26:46 +01003452 g_warning ("failed to load remote %s: %s",
3453 fwupd_remote_get_id (remote),
3454 error_local->message);
3455 continue;
3456 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003457
Richard Hughes1af48b12018-12-03 11:51:17 +00003458 /* fix up any legacy installed files */
3459 fixup = xb_builder_fixup_new ("AppStreamUpgrade",
3460 fu_engine_appstream_upgrade_cb,
3461 self, NULL);
3462 xb_builder_fixup_set_max_depth (fixup, 3);
3463 xb_builder_source_add_fixup (source, fixup);
3464
Richard Hughes33171fd2018-11-09 13:29:11 +00003465 /* add metadata */
3466 custom = xb_builder_node_new ("custom");
3467 xb_builder_node_insert_text (custom,
3468 "value", path,
3469 "key", "fwupd::FilenameCache",
3470 NULL);
3471 xb_builder_node_insert_text (custom,
3472 "value", fwupd_remote_get_id (remote),
3473 "key", "fwupd::RemoteId",
3474 NULL);
3475 xb_builder_source_set_info (source, custom);
3476
Richard Hughes481aa2a2018-09-18 20:51:46 +01003477 /* we need to watch for changes? */
3478 xb_builder_import_source (builder, source);
Richard Hughes9945edb2017-06-19 10:03:55 +01003479 }
3480
Richard Hughes88dc0f42019-03-07 11:58:11 +00003481 /* on a read-only filesystem don't care about the cache GUID */
Richard Hughesc7d870a2020-12-10 10:05:35 +00003482 if (flags & FU_ENGINE_LOAD_FLAG_READONLY)
Richard Hughes88dc0f42019-03-07 11:58:11 +00003483 compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
Richard Hughes88dc0f42019-03-07 11:58:11 +00003484
Richard Hughes481aa2a2018-09-18 20:51:46 +01003485 /* ensure silo is up to date */
3486 cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
3487 xmlbfn = g_build_filename (cachedirpkg, "metadata.xmlb", NULL);
3488 xmlb = g_file_new_for_path (xmlbfn);
Richard Hughesc8cc77c2019-03-07 11:57:24 +00003489 self->silo = xb_builder_ensure (builder, xmlb, compile_flags, NULL, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003490 if (self->silo == NULL)
3491 return FALSE;
3492
Richard Hughes9945edb2017-06-19 10:03:55 +01003493 /* print what we've got */
Richard Hughes4cbe99c2020-11-22 13:14:33 +00003494 components = xb_silo_query (self->silo,
3495 "components/component[@type='firmware']",
3496 0, NULL);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003497 if (components != NULL)
3498 g_debug ("%u components now in silo", components->len);
Richard Hughes9945edb2017-06-19 10:03:55 +01003499
Richard Hughes634da032018-11-05 11:42:20 +00003500 /* build the index */
3501 if (!xb_silo_query_build_index (self->silo,
Richard Hughes4cbe99c2020-11-22 13:14:33 +00003502 "components/component",
Richard Hughes634da032018-11-05 11:42:20 +00003503 "type", error))
3504 return FALSE;
3505 if (!xb_silo_query_build_index (self->silo,
Richard Hughes4cbe99c2020-11-22 13:14:33 +00003506 "components/component[@type='firmware']/provides/firmware",
3507 "type", error))
3508 return FALSE;
3509 if (!xb_silo_query_build_index (self->silo,
3510 "components/component[@type='firmware']/provides/firmware",
Richard Hughes634da032018-11-05 11:42:20 +00003511 NULL, error))
3512 return FALSE;
3513
Richard Hughesf43381f2020-02-24 10:11:31 +00003514 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01003515 return TRUE;
3516}
3517
Mario Limonciello263cab92019-08-20 17:16:00 -05003518static void
3519fu_engine_config_changed_cb (FuConfig *config, FuEngine *self)
3520{
Richard Hughesd1808aa2019-12-10 15:20:30 +00003521 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (config));
3522}
3523
3524static void
3525fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self)
3526{
Mario Limonciello263cab92019-08-20 17:16:00 -05003527 g_autoptr(GError) error_local = NULL;
3528 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE,
3529 &error_local))
3530 g_warning ("Failed to reload metadata store: %s",
3531 error_local->message);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003532
Richard Hughesf43381f2020-02-24 10:11:31 +00003533 /* set device properties from the metadata */
3534 fu_engine_md_refresh_devices (self);
3535
Richard Hughesfb0a9382020-06-23 16:56:28 +01003536 /* invalidate host security attributes */
3537 g_clear_pointer (&self->host_security_id, g_free);
3538
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003539 /* make the UI update */
3540 fu_engine_emit_changed (self);
Mario Limonciello263cab92019-08-20 17:16:00 -05003541}
3542
Richard Hughesd5aab652020-02-25 12:47:50 +00003543static gint
3544fu_engine_sort_jcat_results_timestamp_cb (gconstpointer a, gconstpointer b)
3545{
3546 JcatResult *ra = *((JcatResult **) a);
3547 JcatResult *rb = *((JcatResult **) b);
3548 if (jcat_result_get_timestamp (ra) < jcat_result_get_timestamp (rb))
Richard Hughesd5aab652020-02-25 12:47:50 +00003549 return 1;
Richard Hughesafda9622020-06-10 12:48:31 +01003550 if (jcat_result_get_timestamp (ra) > jcat_result_get_timestamp (rb))
3551 return -1;
Richard Hughesd5aab652020-02-25 12:47:50 +00003552 return 0;
3553}
3554
3555static JcatResult *
Richard Hughesafda9622020-06-10 12:48:31 +01003556fu_engine_get_newest_signature_jcat_result (GPtrArray *results, GError **error)
3557{
3558 /* sort by timestamp, newest first */
3559 g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
3560
3561 /* get the first signature, ignoring the checksums */
3562 for (guint i = 0; i < results->len; i++) {
3563 JcatResult *result = g_ptr_array_index (results, i);
3564#if LIBJCAT_CHECK_VERSION(0, 1, 3)
3565 if (jcat_result_get_method (result) == JCAT_BLOB_METHOD_SIGNATURE)
3566 return g_object_ref (result);
3567#else
3568 guint verify_kind = 0;
3569 g_autoptr(JcatEngine) engine = NULL;
3570 g_object_get (result, "engine", &engine, NULL);
3571 g_object_get (engine, "verify-kind", &verify_kind, NULL);
3572 if (verify_kind == 2) /* SIGNATURE */
3573 return g_object_ref (result);
3574#endif
3575 }
3576
3577 /* should never happen due to %JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE */
3578 g_set_error_literal (error,
3579 FWUPD_ERROR,
3580 FWUPD_ERROR_INVALID_FILE,
3581 "no signature method in results");
3582 return NULL;
3583}
3584
3585static JcatResult *
Richard Hughesd5aab652020-02-25 12:47:50 +00003586fu_engine_get_system_jcat_result (FuEngine *self, FwupdRemote *remote, GError **error)
Richard Hughesf69a4812017-08-16 12:27:51 +01003587{
3588 g_autoptr(GBytes) blob = NULL;
3589 g_autoptr(GBytes) blob_sig = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003590 g_autoptr(GInputStream) istream = NULL;
3591 g_autoptr(GPtrArray) results = NULL;
3592 g_autoptr(JcatItem) jcat_item = NULL;
3593 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
3594
Richard Hughesf69a4812017-08-16 12:27:51 +01003595 blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
3596 if (blob == NULL)
3597 return NULL;
3598 blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
3599 if (blob_sig == NULL)
3600 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003601 istream = g_memory_input_stream_new_from_bytes (blob_sig);
3602 if (!jcat_file_import_stream (jcat_file, istream,
3603 JCAT_IMPORT_FLAG_NONE,
3604 NULL, error))
3605 return NULL;
3606 jcat_item = jcat_file_get_item_default (jcat_file, error);
3607 if (jcat_item == NULL)
3608 return NULL;
3609 results = jcat_context_verify_item (self->jcat_context,
3610 blob, jcat_item,
3611 JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
3612 JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
3613 error);
3614 if (results == NULL)
3615 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003616
Richard Hughesafda9622020-06-10 12:48:31 +01003617 /* return the newest signature */
3618 return fu_engine_get_newest_signature_jcat_result (results, error);
Richard Hughesd5aab652020-02-25 12:47:50 +00003619}
3620
3621static gboolean
3622fu_engine_validate_result_timestamp (JcatResult *jcat_result,
3623 JcatResult *jcat_result_old,
3624 GError **error)
3625{
3626 gint64 delta = 0;
3627
3628 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result), FALSE);
3629 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result_old), FALSE);
3630
Richard Hughes64ebf912020-05-29 07:51:38 +01003631 if (jcat_result_get_timestamp (jcat_result) == 0) {
3632 g_set_error (error,
3633 FWUPD_ERROR,
3634 FWUPD_ERROR_INVALID_FILE,
3635 "no signing timestamp");
3636 return FALSE;
3637 }
3638 if (jcat_result_get_timestamp (jcat_result_old) > 0) {
Richard Hughesd5aab652020-02-25 12:47:50 +00003639 delta = jcat_result_get_timestamp (jcat_result) -
3640 jcat_result_get_timestamp (jcat_result_old);
3641 }
3642 if (delta < 0) {
3643 g_set_error (error,
3644 FWUPD_ERROR,
3645 FWUPD_ERROR_INVALID_FILE,
3646 "new signing timestamp was %"
3647 G_GINT64_FORMAT " seconds older",
3648 -delta);
3649 return FALSE;
3650 }
3651 if (delta > 0)
3652 g_debug ("timestamp increased, so no rollback");
3653 return TRUE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003654}
3655
Richard Hughes9945edb2017-06-19 10:03:55 +01003656/**
Richard Hughesbe5b0192020-01-17 14:32:23 +00003657 * fu_engine_update_metadata_bytes:
Richard Hughes9945edb2017-06-19 10:03:55 +01003658 * @self: A #FuEngine
Richard Hughes4eada342017-10-03 21:20:32 +01003659 * @remote_id: A remote ID, e.g. `lvfs`
Richard Hughesbe5b0192020-01-17 14:32:23 +00003660 * @bytes_raw: Blob of metadata
Richard Hughesd5aab652020-02-25 12:47:50 +00003661 * @bytes_sig: Blob of metadata signature, typically Jcat binary format
Richard Hughes9945edb2017-06-19 10:03:55 +01003662 * @error: A #GError, or %NULL
3663 *
3664 * Updates the metadata for a specific remote.
3665 *
Richard Hughes9945edb2017-06-19 10:03:55 +01003666 * Returns: %TRUE for success
3667 **/
3668gboolean
Richard Hughesbe5b0192020-01-17 14:32:23 +00003669fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id,
3670 GBytes *bytes_raw, GBytes *bytes_sig, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003671{
Richard Hughes7403dc52017-08-10 15:34:10 +01003672 FwupdKeyringKind keyring_kind;
Richard Hughes9945edb2017-06-19 10:03:55 +01003673 FwupdRemote *remote;
Richard Hughes8bcc5f32020-11-30 09:41:10 +00003674 JcatVerifyFlags jcat_flags = JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE;
3675 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01003676
3677 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3678 g_return_val_if_fail (remote_id != NULL, FALSE);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003679 g_return_val_if_fail (bytes_raw != NULL, FALSE);
3680 g_return_val_if_fail (bytes_sig != NULL, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003681 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3682
Richard Hughes9945edb2017-06-19 10:03:55 +01003683 /* check remote is valid */
Richard Hughesd1808aa2019-12-10 15:20:30 +00003684 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes9945edb2017-06-19 10:03:55 +01003685 if (remote == NULL) {
3686 g_set_error (error,
3687 FWUPD_ERROR,
3688 FWUPD_ERROR_NOT_FOUND,
3689 "remote %s not found", remote_id);
3690 return FALSE;
3691 }
3692 if (!fwupd_remote_get_enabled (remote)) {
3693 g_set_error (error,
3694 FWUPD_ERROR,
3695 FWUPD_ERROR_NOT_SUPPORTED,
3696 "remote %s not enabled", remote_id);
3697 return FALSE;
3698 }
3699
Richard Hughes8bcc5f32020-11-30 09:41:10 +00003700 /* verify JCatFile, or create a dummy one from legacy data */
Richard Hughes7403dc52017-08-10 15:34:10 +01003701 keyring_kind = fwupd_remote_get_keyring_kind (remote);
Richard Hughes8bcc5f32020-11-30 09:41:10 +00003702 if (keyring_kind == FWUPD_KEYRING_KIND_JCAT) {
Richard Hughesd5aab652020-02-25 12:47:50 +00003703 g_autoptr(GInputStream) istream = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003704 istream = g_memory_input_stream_new_from_bytes (bytes_sig);
3705 if (!jcat_file_import_stream (jcat_file, istream,
3706 JCAT_IMPORT_FLAG_NONE,
3707 NULL, error))
Richard Hughes7403dc52017-08-10 15:34:10 +01003708 return FALSE;
Richard Hughes8bcc5f32020-11-30 09:41:10 +00003709 jcat_flags |= JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM;
3710 } else if (keyring_kind == FWUPD_KEYRING_KIND_GPG) {
3711 g_autoptr(JcatBlob) jcab_blob = NULL;
3712 g_autoptr(JcatItem) jcat_item = jcat_item_new ("");
3713 jcab_blob = jcat_blob_new (JCAT_BLOB_KIND_GPG, bytes_sig);
3714 jcat_item_add_blob (jcat_item, jcab_blob);
3715 jcat_file_add_item (jcat_file, jcat_item);
3716 } else if (keyring_kind == FWUPD_KEYRING_KIND_PKCS7) {
3717 g_autoptr(JcatBlob) jcab_blob = NULL;
3718 g_autoptr(JcatItem) jcat_item = jcat_item_new ("");
3719 jcab_blob = jcat_blob_new (JCAT_BLOB_KIND_PKCS7, bytes_sig);
3720 jcat_item_add_blob (jcat_item, jcab_blob);
3721 jcat_file_add_item (jcat_file, jcat_item);
3722 }
3723
3724 /* verify file */
3725 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
3726 g_autoptr(GError) error_local = NULL;
3727 g_autoptr(GPtrArray) results = NULL;
3728 g_autoptr(JcatItem) jcat_item = NULL;
3729 g_autoptr(JcatResult) jcat_result = NULL;
3730 g_autoptr(JcatResult) jcat_result_old = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003731
3732 /* this should only be signing one thing */
3733 jcat_item = jcat_file_get_item_default (jcat_file, error);
3734 if (jcat_item == NULL)
Richard Hughes14047d72017-08-18 10:58:47 +01003735 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003736 results = jcat_context_verify_item (self->jcat_context,
3737 bytes_raw, jcat_item,
Richard Hughes8bcc5f32020-11-30 09:41:10 +00003738 jcat_flags, error);
Richard Hughesd5aab652020-02-25 12:47:50 +00003739 if (results == NULL)
Richard Hughes7403dc52017-08-10 15:34:10 +01003740 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003741
Richard Hughesafda9622020-06-10 12:48:31 +01003742 /* return the newest signature */
3743 jcat_result = fu_engine_get_newest_signature_jcat_result (results, error);
3744 if (jcat_result == NULL)
3745 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003746
Richard Hughesf69a4812017-08-16 12:27:51 +01003747 /* verify the metadata was signed later than the existing
3748 * metadata for this remote to mitigate a rollback attack */
Richard Hughesd5aab652020-02-25 12:47:50 +00003749 jcat_result_old = fu_engine_get_system_jcat_result (self, remote, &error_local);
3750 if (jcat_result_old == NULL) {
Richard Hughesf69a4812017-08-16 12:27:51 +01003751 if (g_error_matches (error_local,
3752 G_FILE_ERROR,
3753 G_FILE_ERROR_NOENT)) {
3754 g_debug ("no existing valid keyrings: %s",
3755 error_local->message);
3756 } else {
3757 g_warning ("could not get existing keyring result: %s",
3758 error_local->message);
3759 }
3760 } else {
Richard Hughesd5aab652020-02-25 12:47:50 +00003761 if (!fu_engine_validate_result_timestamp (jcat_result,
3762 jcat_result_old,
3763 error))
Richard Hughesf69a4812017-08-16 12:27:51 +01003764 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003765 }
Richard Hughes7403dc52017-08-10 15:34:10 +01003766 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003767
Richard Hughes99e621d2017-08-16 12:24:18 +01003768 /* save XML and signature to remotes.d */
Richard Hughes943d2c92017-06-21 09:04:39 +01003769 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache (remote),
3770 bytes_raw, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003771 return FALSE;
Richard Hughes99e621d2017-08-16 12:24:18 +01003772 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
3773 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache_sig (remote),
3774 bytes_sig, error))
3775 return FALSE;
3776 }
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003777 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, error))
3778 return FALSE;
Richard Hughesfb0a9382020-06-23 16:56:28 +01003779
3780 /* refresh SUPPORTED flag on devices */
Richard Hughesf43381f2020-02-24 10:11:31 +00003781 fu_engine_md_refresh_devices (self);
Richard Hughesfb0a9382020-06-23 16:56:28 +01003782
3783 /* invalidate host security attributes */
3784 g_clear_pointer (&self->host_security_id, g_free);
3785
3786 /* make the UI update */
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003787 fu_engine_emit_changed (self);
3788 return TRUE;
Richard Hughesbe5b0192020-01-17 14:32:23 +00003789}
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003790
Richard Hughesbe5b0192020-01-17 14:32:23 +00003791/**
3792 * fu_engine_update_metadata:
3793 * @self: A #FuEngine
3794 * @remote_id: A remote ID, e.g. `lvfs`
3795 * @fd: file descriptor of the metadata
3796 * @fd_sig: file descriptor of the metadata signature
3797 * @error: A #GError, or %NULL
3798 *
3799 * Updates the metadata for a specific remote.
3800 *
3801 * Note: this will close the fds when done
3802 *
3803 * Returns: %TRUE for success
3804 **/
3805gboolean
3806fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
3807 gint fd, gint fd_sig, GError **error)
3808{
3809#ifdef HAVE_GIO_UNIX
3810 g_autoptr(GBytes) bytes_raw = NULL;
3811 g_autoptr(GBytes) bytes_sig = NULL;
3812 g_autoptr(GInputStream) stream_fd = NULL;
3813 g_autoptr(GInputStream) stream_sig = NULL;
3814
3815 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3816 g_return_val_if_fail (remote_id != NULL, FALSE);
3817 g_return_val_if_fail (fd > 0, FALSE);
3818 g_return_val_if_fail (fd_sig > 0, FALSE);
3819 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3820
3821 /* ensures the fd's are closed on error */
3822 stream_fd = g_unix_input_stream_new (fd, TRUE);
3823 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
3824
3825 /* read the entire file into memory */
3826 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
3827 if (bytes_raw == NULL)
3828 return FALSE;
3829
3830 /* read signature */
Richard Hughes64d868a2020-01-17 17:40:00 +00003831 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x100000, NULL, error);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003832 if (bytes_sig == NULL)
3833 return FALSE;
3834
3835 /* update with blobs */
3836 return fu_engine_update_metadata_bytes (self, remote_id,
3837 bytes_raw, bytes_sig,
3838 error);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003839#else
3840 g_set_error (error,
3841 FWUPD_ERROR,
3842 FWUPD_ERROR_NOT_SUPPORTED,
3843 "Not supported as <glib-unix.h> is unavailable");
3844 return FALSE;
3845#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01003846}
3847
Richard Hughes9945edb2017-06-19 10:03:55 +01003848/**
Richard Hughes481aa2a2018-09-18 20:51:46 +01003849 * fu_engine_get_silo_from_blob:
Richard Hughes9945edb2017-06-19 10:03:55 +01003850 * @self: A #FuEngine
3851 * @blob_cab: A #GBytes
3852 * @error: A #GError, or %NULL
3853 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003854 * Creates a silo from a .cab file blob.
Richard Hughes9945edb2017-06-19 10:03:55 +01003855 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003856 * Returns: (transfer container): a #XbSilo, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01003857 **/
Richard Hughes481aa2a2018-09-18 20:51:46 +01003858XbSilo *
3859fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003860{
Richard Hughesc6eb4162020-02-27 10:02:36 +00003861 g_autoptr(FuCabinet) cabinet = fu_cabinet_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003862 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003863
3864 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3865 g_return_val_if_fail (blob_cab != NULL, NULL);
3866 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3867
3868 /* load file */
Richard Hughes9945edb2017-06-19 10:03:55 +01003869 fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003870 fu_cabinet_set_size_max (cabinet, fu_engine_get_archive_size_max (self));
Richard Hughesd5aab652020-02-25 12:47:50 +00003871 fu_cabinet_set_jcat_context (cabinet, self->jcat_context);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003872 if (!fu_cabinet_parse (cabinet, blob_cab, FU_CABINET_PARSE_FLAG_NONE, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003873 return NULL;
Richard Hughesc6eb4162020-02-27 10:02:36 +00003874 silo = fu_cabinet_get_silo (cabinet);
Mario Limonciello5735fd62017-07-13 15:47:52 -05003875 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003876 return g_steal_pointer (&silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01003877}
3878
Richard Hughes75449d92019-04-17 13:36:31 +01003879static FuDevice *
Richard Hughesdf89cd52020-06-26 20:25:18 +01003880fu_engine_get_result_from_component (FuEngine *self,
3881 FuEngineRequest *request,
3882 XbNode *component,
3883 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003884{
Richard Hughes245885c2019-03-04 08:46:02 +00003885 FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003886 g_autoptr(FuInstallTask) task = NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003887 g_autoptr(FuDevice) dev = NULL;
Richard Hughes93b15762017-09-15 11:05:23 +01003888 g_autoptr(FwupdRelease) rel = NULL;
Salud Lemus980f2d92018-08-28 09:25:40 -07003889 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003890 g_autoptr(GPtrArray) provides = NULL;
3891 g_autoptr(XbNode) description = NULL;
3892 g_autoptr(XbNode) release = NULL;
Richard Hughes890dd122020-06-25 22:49:04 +01003893#if LIBXMLB_CHECK_VERSION(0,2,0)
3894 g_autoptr(XbQuery) query = NULL;
3895#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01003896
Richard Hughes75449d92019-04-17 13:36:31 +01003897 dev = fu_device_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003898 provides = xb_node_query (component,
Richard Hughes634da032018-11-05 11:42:20 +00003899 "provides/firmware[@type=$'flashed']",
Richard Hughes481aa2a2018-09-18 20:51:46 +01003900 0, &error_local);
3901 if (provides == NULL) {
3902 g_set_error (error,
3903 FWUPD_ERROR,
3904 FWUPD_ERROR_INTERNAL,
3905 "failed to get release: %s",
3906 error_local->message);
3907 return NULL;
3908 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003909 for (guint i = 0; i < provides->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01003910 XbNode *prov = XB_NODE (g_ptr_array_index (provides, i));
Richard Hughes9945edb2017-06-19 10:03:55 +01003911 const gchar *guid;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003912 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003913
Richard Hughes9945edb2017-06-19 10:03:55 +01003914 /* is a online or offline update appropriate */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003915 guid = xb_node_get_text (prov);
Richard Hughes9945edb2017-06-19 10:03:55 +01003916 if (guid == NULL)
3917 continue;
Richard Hughes40127542018-01-12 20:25:55 +00003918 device = fu_device_list_get_by_guid (self->device_list, guid, NULL);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003919 if (device != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01003920 fu_device_set_name (dev, fu_device_get_name (device));
3921 fu_device_set_flags (dev, fu_device_get_flags (device));
3922 fu_device_set_id (dev, fu_device_get_id (device));
Mario Limonciellof430da02020-03-09 14:03:03 -05003923 fu_device_set_version_raw (dev, fu_device_get_version_raw (device));
Mario Limonciello8bcdfaa2020-02-25 11:18:15 -06003924 fu_device_set_version_format (dev, fu_device_get_version_format (device));
3925 fu_device_set_version (dev, fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01003926 }
3927
3928 /* add GUID */
Richard Hughes75449d92019-04-17 13:36:31 +01003929 fu_device_add_guid (dev, guid);
Richard Hughes9945edb2017-06-19 10:03:55 +01003930 }
Richard Hughes75449d92019-04-17 13:36:31 +01003931 if (fu_device_get_guids(dev)->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003932 g_set_error_literal (error,
3933 FWUPD_ERROR,
3934 FWUPD_ERROR_INTERNAL,
3935 "component has no GUIDs");
3936 return NULL;
3937 }
3938
3939 /* check we can install it */
Mario Limonciello13d07732020-10-23 17:18:43 -05003940 task = fu_install_task_new (NULL, component);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003941 if (!fu_engine_check_requirements (self, request, task,
Richard Hughesbf439782020-11-03 11:32:29 +00003942 FWUPD_INSTALL_FLAG_IGNORE_VID_PID,
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003943 error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003944 return NULL;
3945
3946 /* verify trust */
Richard Hughes890dd122020-06-25 22:49:04 +01003947#if LIBXMLB_CHECK_VERSION(0,2,0)
3948 query = xb_query_new_full (xb_node_get_silo (component),
3949 "releases/release",
3950 XB_QUERY_FLAG_FORCE_NODE_CACHE,
3951 error);
3952 if (query == NULL)
3953 return NULL;
3954 release = xb_node_query_first_full (component, query, &error_local);
3955#else
Richard Hughes481aa2a2018-09-18 20:51:46 +01003956 release = xb_node_query_first (component,
3957 "releases/release",
3958 &error_local);
Richard Hughes890dd122020-06-25 22:49:04 +01003959#endif
Richard Hughes481aa2a2018-09-18 20:51:46 +01003960 if (release == NULL) {
3961 g_set_error (error,
3962 FWUPD_ERROR,
3963 FWUPD_ERROR_INTERNAL,
3964 "failed to get release: %s",
3965 error_local->message);
3966 return NULL;
3967 }
Richard Hughes245885c2019-03-04 08:46:02 +00003968 if (!fu_keyring_get_release_flags (release,
3969 &release_flags,
3970 &error_local)) {
Salud Lemus980f2d92018-08-28 09:25:40 -07003971 if (g_error_matches (error_local,
3972 FWUPD_ERROR,
3973 FWUPD_ERROR_NOT_SUPPORTED)) {
3974 g_warning ("Ignoring verification: %s",
3975 error_local->message);
3976 } else {
3977 g_propagate_error (error, g_steal_pointer (&error_local));
3978 return NULL;
3979 }
3980 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003981
Richard Hughes9945edb2017-06-19 10:03:55 +01003982 /* create a result with all the metadata in */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003983 description = xb_node_query_first (component, "description", NULL);
3984 if (description != NULL) {
3985 g_autofree gchar *xml = NULL;
3986 xml = xb_node_export (description,
3987 XB_NODE_EXPORT_FLAG_ONLY_CHILDREN,
3988 NULL);
3989 if (xml != NULL)
Richard Hughes75449d92019-04-17 13:36:31 +01003990 fu_device_set_description (dev, xml);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003991 }
Richard Hughes93b15762017-09-15 11:05:23 +01003992 rel = fwupd_release_new ();
Richard Hughes245885c2019-03-04 08:46:02 +00003993 fwupd_release_set_flags (rel, release_flags);
Richard Hughes75449d92019-04-17 13:36:31 +01003994 if (!fu_engine_set_release_from_appstream (self, dev, rel, component, release, error))
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003995 return NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003996 fu_device_add_release (dev, rel);
Richard Hughes93b15762017-09-15 11:05:23 +01003997 return g_steal_pointer (&dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003998}
3999
Richard Hughesb3d3f212020-05-21 21:14:49 +01004000static gint
4001fu_engine_get_details_sort_cb (gconstpointer a, gconstpointer b)
4002{
4003 FuDevice *device1 = *((FuDevice **) a);
4004 FuDevice *device2 = *((FuDevice **) b);
4005 if (!fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4006 fu_device_has_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE))
4007 return 1;
4008 if (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4009 !fu_device_has_flag (device2, FWUPD_DEVICE_FLAG_UPDATABLE))
4010 return -1;
4011 return 0;
4012}
4013
Richard Hughes9945edb2017-06-19 10:03:55 +01004014/**
Richard Hughes07f963a2017-09-15 14:28:47 +01004015 * fu_engine_get_details:
Richard Hughes9945edb2017-06-19 10:03:55 +01004016 * @self: A #FuEngine
Richard Hughesdf89cd52020-06-26 20:25:18 +01004017 * @request: A #FuEngineRequest
Richard Hughes9945edb2017-06-19 10:03:55 +01004018 * @fd: A file descriptor
4019 * @error: A #GError, or %NULL
4020 *
4021 * Gets the details about a local file.
4022 *
4023 * Note: this will close the fd when done
4024 *
Richard Hughes75449d92019-04-17 13:36:31 +01004025 * Returns: (transfer container) (element-type FuDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01004026 **/
4027GPtrArray *
Richard Hughesdf89cd52020-06-26 20:25:18 +01004028fu_engine_get_details (FuEngine *self, FuEngineRequest *request, gint fd, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01004029{
Richard Hughes534255c2018-01-28 19:51:56 +00004030 const gchar *remote_id;
4031 g_autofree gchar *csum = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004032 g_autoptr(GBytes) blob = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004033 g_autoptr(GError) error_local = NULL;
4034 g_autoptr(GPtrArray) components = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004035 g_autoptr(GPtrArray) details = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004036 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004037
4038 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4039 g_return_val_if_fail (fd > 0, NULL);
4040 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4041
Richard Hughes481aa2a2018-09-18 20:51:46 +01004042 /* get all components */
Richard Hughesc7bbbc22018-01-02 22:22:25 +00004043 blob = fu_common_get_contents_fd (fd,
4044 fu_engine_get_archive_size_max (self),
4045 error);
Richard Hughes9945edb2017-06-19 10:03:55 +01004046 if (blob == NULL)
4047 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004048 silo = fu_engine_get_silo_from_blob (self, blob, error);
4049 if (silo == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004050 return NULL;
Richard Hughes4cbe99c2020-11-22 13:14:33 +00004051 components = xb_silo_query (silo,
4052 "components/component[@type='firmware']",
4053 0, &error_local);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004054 if (components == NULL) {
4055 g_set_error (error,
4056 FWUPD_ERROR,
4057 FWUPD_ERROR_INVALID_FILE,
4058 "no components: %s",
4059 error_local->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01004060 return NULL;
4061 }
4062
Richard Hughesec6190c2018-11-25 12:19:33 +00004063 /* build the index */
Richard Hughes4cbe99c2020-11-22 13:14:33 +00004064 if (!xb_silo_query_build_index (silo, "components/component[@type='firmware']/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00004065 "type", error))
Richard Hughes2eee2582019-03-11 13:17:39 +00004066 return NULL;
Richard Hughes4cbe99c2020-11-22 13:14:33 +00004067 if (!xb_silo_query_build_index (silo, "components/component[@type='firmware']/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00004068 NULL, error))
Richard Hughes2eee2582019-03-11 13:17:39 +00004069 return NULL;
Richard Hughesec6190c2018-11-25 12:19:33 +00004070
Richard Hughes534255c2018-01-28 19:51:56 +00004071 /* does this exist in any enabled remote */
4072 csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
4073 remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
4074
Richard Hughes9945edb2017-06-19 10:03:55 +01004075 /* create results with all the metadata in */
4076 details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004077 for (guint i = 0; i < components->len; i++) {
4078 XbNode *component = g_ptr_array_index (components, i);
Richard Hughes75449d92019-04-17 13:36:31 +01004079 FuDevice *dev;
Richard Hughesdf89cd52020-06-26 20:25:18 +01004080 dev = fu_engine_get_result_from_component (self, request, component, error);
Richard Hughes534255c2018-01-28 19:51:56 +00004081 if (dev == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004082 return NULL;
Richard Hughes534255c2018-01-28 19:51:56 +00004083 if (remote_id != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01004084 FwupdRelease *rel = fu_device_get_release_default (dev);
Richard Hughes534255c2018-01-28 19:51:56 +00004085 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughes75449d92019-04-17 13:36:31 +01004086 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes534255c2018-01-28 19:51:56 +00004087 }
Richard Hughescf100292021-01-04 10:36:37 +00004088 if (fu_device_has_internal_flag (dev, FU_DEVICE_INTERNAL_FLAG_MD_SET_VERFMT))
Richard Hughes137649d2020-05-28 13:36:24 +01004089 fu_engine_md_refresh_device_verfmt (self, dev, component);
Richard Hughesb3d3f212020-05-21 21:14:49 +01004090
4091 /* if this matched a device on the system, ensure all the
4092 * requirements passed before setting UPDATABLE */
4093 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) {
4094 g_autoptr(FuInstallTask) task = fu_install_task_new (dev, component);
4095 g_autoptr(GError) error_req = NULL;
Richard Hughesdf89cd52020-06-26 20:25:18 +01004096 if (!fu_engine_check_requirements (self, request, task,
Richard Hughesb3d3f212020-05-21 21:14:49 +01004097 FWUPD_INSTALL_FLAG_OFFLINE |
Richard Hughesbf439782020-11-03 11:32:29 +00004098 FWUPD_INSTALL_FLAG_IGNORE_VID_PID |
Richard Hughesb3d3f212020-05-21 21:14:49 +01004099 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
Richard Hughes5bbf0132020-10-05 13:44:39 +01004100 FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH |
Richard Hughesb3d3f212020-05-21 21:14:49 +01004101 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
4102 &error_req)) {
4103 g_debug ("%s failed requirement checks: %s",
4104 fu_device_get_id (dev),
4105 error_req->message);
4106 fu_device_remove_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE);
4107 } else {
4108 g_debug ("%s passed requirement checks",
4109 fu_device_get_id (dev));
4110 }
4111 }
4112
Richard Hughes534255c2018-01-28 19:51:56 +00004113 g_ptr_array_add (details, dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01004114 }
Richard Hughesb3d3f212020-05-21 21:14:49 +01004115
4116 /* order multiple devices so that the one that passes the requirement
4117 * is listed first */
4118 g_ptr_array_sort (details, fu_engine_get_details_sort_cb);
4119
Richard Hughes9945edb2017-06-19 10:03:55 +01004120 return g_steal_pointer (&details);
4121}
4122
Mario Limonciello343095d2018-10-23 17:21:13 -05004123static gint
Mario Limonciello6ff8a162019-08-23 12:55:10 -05004124fu_engine_sort_devices_by_priority_name (gconstpointer a, gconstpointer b)
Mario Limonciello343095d2018-10-23 17:21:13 -05004125{
4126 FuDevice *dev_a = *((FuDevice **) a);
4127 FuDevice *dev_b = *((FuDevice **) b);
4128 gint prio_a = fu_device_get_priority (dev_a);
4129 gint prio_b = fu_device_get_priority (dev_b);
Mario Limonciello6ff8a162019-08-23 12:55:10 -05004130 const gchar *name_a = fu_device_get_name (dev_a);
4131 const gchar *name_b = fu_device_get_name (dev_b);
Mario Limonciello343095d2018-10-23 17:21:13 -05004132
4133 if (prio_a > prio_b)
4134 return -1;
4135 if (prio_a < prio_b)
4136 return 1;
Mario Limonciello6ff8a162019-08-23 12:55:10 -05004137 if (g_strcmp0 (name_a, name_b) > 0)
4138 return 1;
4139 if (g_strcmp0 (name_a, name_b) < 0)
4140 return -1;
Mario Limonciello343095d2018-10-23 17:21:13 -05004141 return 0;
4142}
4143
Richard Hughes9945edb2017-06-19 10:03:55 +01004144/**
4145 * fu_engine_get_devices:
4146 * @self: A #FuEngine
4147 * @error: A #GError, or %NULL
4148 *
4149 * Gets the list of devices.
4150 *
4151 * Returns: (transfer container) (element-type FwupdDevice): results
4152 **/
4153GPtrArray *
4154fu_engine_get_devices (FuEngine *self, GError **error)
4155{
Richard Hughesf2eccde2017-09-20 11:18:03 +01004156 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004157
4158 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4159 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4160
Richard Hughes70425fe2017-11-22 12:33:27 +00004161 devices = fu_device_list_get_active (self->device_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01004162 if (devices->len == 0) {
4163 g_set_error_literal (error,
4164 FWUPD_ERROR,
4165 FWUPD_ERROR_NOTHING_TO_DO,
4166 "No detected devices");
4167 return NULL;
4168 }
Mario Limonciello6ff8a162019-08-23 12:55:10 -05004169 g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority_name);
Richard Hughesf2eccde2017-09-20 11:18:03 +01004170 return g_steal_pointer (&devices);
Richard Hughes9945edb2017-06-19 10:03:55 +01004171}
4172
Richard Hughes12040b52018-05-14 13:32:10 +01004173/**
Richard Hughes3aaf53c2020-04-20 09:39:46 +01004174 * fu_engine_get_devices_by_guid:
4175 * @self: A #FuEngine
4176 * @guid: A GUID
4177 * @error: A #GError, or %NULL
4178 *
4179 * Gets a specific device.
4180 *
4181 * Returns: (transfer full): a device, or %NULL if not found
4182 **/
4183GPtrArray *
4184fu_engine_get_devices_by_guid (FuEngine *self, const gchar *guid, GError **error)
4185{
4186 g_autoptr(GPtrArray) devices = NULL;
4187 g_autoptr(GPtrArray) devices_tmp = NULL;
4188
4189 /* find the devices by GUID */
4190 devices_tmp = fu_device_list_get_all (self->device_list);
4191 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4192 for (guint i = 0; i < devices_tmp->len; i++) {
4193 FuDevice *dev_tmp = g_ptr_array_index (devices_tmp, i);
4194 if (fu_device_has_guid (dev_tmp, guid))
4195 g_ptr_array_add (devices, g_object_ref (dev_tmp));
4196 }
4197
4198 /* nothing */
4199 if (devices->len == 0) {
4200 g_set_error (error,
4201 FWUPD_ERROR,
4202 FWUPD_ERROR_NOT_FOUND,
4203 "failed to find any device providing %s", guid);
4204 return NULL;
4205 }
4206
4207 /* success */
4208 return g_steal_pointer (&devices);
4209}
4210
Richard Hughes63bc42e2021-04-22 09:32:07 +01004211/**
4212 * fu_engine_get_devices_by_composite_id:
4213 * @self: A #FuEngine
4214 * @composite_id: A device ID
4215 * @error: A #GError, or %NULL
4216 *
4217 * Gets all devices that match a specific composite ID.
4218 *
4219 * Returns: (transfer full) (element-type FuDevice): devices
4220 **/
4221GPtrArray *
4222fu_engine_get_devices_by_composite_id (FuEngine *self,
4223 const gchar *composite_id,
4224 GError **error)
4225{
4226 g_autoptr(GPtrArray) devices = NULL;
4227 g_autoptr(GPtrArray) devices_tmp = NULL;
4228
4229 /* find the devices by composite ID */
4230 devices_tmp = fu_device_list_get_all (self->device_list);
4231 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4232 for (guint i = 0; i < devices_tmp->len; i++) {
4233 FuDevice *dev_tmp = g_ptr_array_index (devices_tmp, i);
4234 if (g_strcmp0 (fu_device_get_composite_id (dev_tmp), composite_id) == 0)
4235 g_ptr_array_add (devices, g_object_ref (dev_tmp));
4236 }
4237
4238 /* nothing */
4239 if (devices->len == 0) {
4240 g_set_error (error,
4241 FWUPD_ERROR,
4242 FWUPD_ERROR_NOT_FOUND,
4243 "failed to find any device with composite ID %s",
4244 composite_id);
4245 return NULL;
4246 }
4247
4248 /* success */
4249 return g_steal_pointer (&devices);
4250}
4251
Richard Hughescef874f2020-05-17 21:03:13 +01004252static void
4253fu_engine_get_history_set_hsi_attrs (FuEngine *self, FuDevice *device)
4254{
Richard Hughescef874f2020-05-17 21:03:13 +01004255 g_autoptr(GPtrArray) vals = NULL;
4256
Richard Hughesfb0a9382020-06-23 16:56:28 +01004257 /* ensure up to date */
4258 fu_engine_ensure_security_attrs (self);
4259
Richard Hughescef874f2020-05-17 21:03:13 +01004260 /* add attributes */
Richard Hughesfb0a9382020-06-23 16:56:28 +01004261 vals = fu_security_attrs_get_all (self->host_security_attrs);
Richard Hughescef874f2020-05-17 21:03:13 +01004262 for (guint i = 0; i < vals->len; i++) {
4263 FwupdSecurityAttr *attr = g_ptr_array_index (vals, i);
Richard Hughesb246bca2020-05-18 14:31:35 +01004264 const gchar *tmp;
4265 tmp = fwupd_security_attr_result_to_string (fwupd_security_attr_get_result (attr));
4266 fu_device_set_metadata (device, fwupd_security_attr_get_appstream_id (attr), tmp);
Richard Hughescef874f2020-05-17 21:03:13 +01004267 }
4268
4269 /* computed value */
Richard Hughesfb0a9382020-06-23 16:56:28 +01004270 fu_device_set_metadata (device, "HSI", self->host_security_id);
Richard Hughescef874f2020-05-17 21:03:13 +01004271}
4272
Richard Hughes3aaf53c2020-04-20 09:39:46 +01004273/**
Richard Hughes476363a2018-01-11 10:08:58 +00004274 * fu_engine_get_history:
4275 * @self: A #FuEngine
4276 * @error: A #GError, or %NULL
4277 *
4278 * Gets the list of history.
4279 *
4280 * Returns: (transfer container) (element-type FwupdDevice): results
4281 **/
4282GPtrArray *
4283fu_engine_get_history (FuEngine *self, GError **error)
4284{
4285 g_autoptr(GPtrArray) devices = NULL;
4286
4287 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4288 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4289
4290 devices = fu_history_get_devices (self->history, error);
4291 if (devices == NULL)
4292 return NULL;
4293 if (devices->len == 0) {
4294 g_set_error_literal (error,
4295 FWUPD_ERROR,
4296 FWUPD_ERROR_NOTHING_TO_DO,
4297 "No history");
4298 return NULL;
4299 }
Richard Hughes611f1a92018-01-11 11:54:28 +00004300
Richard Hughescef874f2020-05-17 21:03:13 +01004301 /* if this is the system firmware device, add the HSI attrs */
4302 for (guint i = 0; i < devices->len; i++) {
4303 FuDevice *dev = g_ptr_array_index (devices, i);
4304 if (fu_device_has_instance_id (dev, "main-system-firmware"))
4305 fu_engine_get_history_set_hsi_attrs (self, dev);
4306 }
4307
Richard Hughes611f1a92018-01-11 11:54:28 +00004308 /* try to set the remote ID for each device */
4309 for (guint i = 0; i < devices->len; i++) {
4310 FuDevice *dev = g_ptr_array_index (devices, i);
4311 FwupdRelease *rel;
4312 GPtrArray *csums;
4313
4314 /* get the checksums */
4315 rel = fu_device_get_release_default (dev);
4316 if (rel == NULL)
4317 continue;
4318
4319 /* find the checksum that matches */
4320 csums = fwupd_release_get_checksums (rel);
4321 for (guint j = 0; j < csums->len; j++) {
4322 const gchar *csum = g_ptr_array_index (csums, j);
4323 const gchar *remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
4324 if (remote_id != NULL) {
4325 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
4326 fwupd_release_set_remote_id (rel, remote_id);
4327 break;
4328 }
4329 }
4330 }
4331
Richard Hughes476363a2018-01-11 10:08:58 +00004332 return g_steal_pointer (&devices);
4333}
4334
Richard Hughes50ff31d2020-09-09 17:07:38 +01004335#if !GLIB_CHECK_VERSION(2,62,0)
4336static GPtrArray *
4337g_ptr_array_copy (GPtrArray *array, GCopyFunc func, gpointer user_data)
4338{
4339 GPtrArray *new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4340 for (guint i = 0; i < array->len; i++) {
4341 GObject *obj = g_ptr_array_index (array, i);
4342 g_ptr_array_add (new, g_object_ref (obj));
4343 }
4344 return new;
4345}
4346#endif
4347
Richard Hughes476363a2018-01-11 10:08:58 +00004348/**
Richard Hughes9945edb2017-06-19 10:03:55 +01004349 * fu_engine_get_remotes:
4350 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +01004351 * @error: A #GError, or %NULL
4352 *
4353 * Gets the list of remotes in use by the engine.
4354 *
4355 * Returns: (transfer container) (element-type FwupdRemote): results
4356 **/
4357GPtrArray *
4358fu_engine_get_remotes (FuEngine *self, GError **error)
4359{
4360 GPtrArray *remotes;
4361
4362 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4363 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4364
Richard Hughesd1808aa2019-12-10 15:20:30 +00004365 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01004366 if (remotes->len == 0) {
4367 g_set_error (error,
4368 FWUPD_ERROR,
4369 FWUPD_ERROR_INTERNAL,
4370 "No remotes configured");
4371 return NULL;
4372 }
Richard Hughes50ff31d2020-09-09 17:07:38 +01004373
4374 /* deep copy so the remote list can be kept up to date */
4375 return g_ptr_array_copy (remotes, (GCopyFunc) g_object_ref, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01004376}
4377
Mario Limonciello46aaee82019-01-10 12:58:00 -06004378/**
4379 * fu_engine_get_remote_by_id:
4380 * @self: A #FuEngine
4381 * @remote_id: A string representation of a remote
4382 * @error: A #GError, or %NULL
4383 *
4384 * Gets the FwupdRemote object.
4385 *
4386 * Returns: FwupdRemote
4387 **/
4388FwupdRemote *
4389fu_engine_get_remote_by_id (FuEngine *self, const gchar *remote_id, GError **error)
4390{
4391 g_autoptr(GPtrArray) remotes = NULL;
4392
4393 remotes = fu_engine_get_remotes (self, error);
4394 if (remotes == NULL)
4395 return NULL;
4396
4397 for (guint i = 0; i < remotes->len; i++) {
4398 FwupdRemote *remote = g_ptr_array_index (remotes, i);
4399 if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0)
4400 return remote;
4401 }
4402
4403 g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
4404 "Couldn't find remote %s", remote_id);
4405
4406 return NULL;
4407}
4408
4409
Richard Hughes225f3a92017-09-13 19:38:51 +01004410static gint
Richard Hughes9a680842020-02-20 11:11:13 +00004411fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b, gpointer user_data)
Richard Hughes225f3a92017-09-13 19:38:51 +01004412{
Richard Hughes9a680842020-02-20 11:11:13 +00004413 FuDevice *device = FU_DEVICE (user_data);
Richard Hughes225f3a92017-09-13 19:38:51 +01004414 FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a));
4415 FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b));
Richard Hughes460c4b72020-09-25 20:59:28 +01004416 gint rc;
4417
4418 /* first by branch */
4419 rc = g_strcmp0 (fwupd_release_get_branch (rel_b), fwupd_release_get_branch (rel_a));
4420 if (rc != 0)
4421 return rc;
4422
4423 /* then by version */
Richard Hughes9a680842020-02-20 11:11:13 +00004424 return fu_common_vercmp_full (fwupd_release_get_version (rel_b),
4425 fwupd_release_get_version (rel_a),
4426 fu_device_get_version_format (device));
Richard Hughes225f3a92017-09-13 19:38:51 +01004427}
4428
Richard Hughes481aa2a2018-09-18 20:51:46 +01004429static gboolean
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004430fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel)
4431{
4432 GPtrArray *csums = fwupd_release_get_checksums (rel);
Richard Hughes3444cf62020-06-22 15:10:28 +01004433 if (self->approved_firmware == NULL)
4434 return FALSE;
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004435 for (guint i = 0; i < csums->len; i++) {
4436 const gchar *csum = g_ptr_array_index (csums, i);
4437 g_debug ("checking %s against approved list", csum);
4438 if (g_hash_table_lookup (self->approved_firmware, csum) != NULL)
4439 return TRUE;
4440 }
4441 return FALSE;
4442}
4443
4444static gboolean
Richard Hughes31206832020-07-27 15:31:11 +01004445fu_engine_check_release_is_blocked (FuEngine *self, FwupdRelease *rel)
4446{
4447 GPtrArray *csums = fwupd_release_get_checksums (rel);
4448 if (self->blocked_firmware == NULL)
4449 return FALSE;
4450 for (guint i = 0; i < csums->len; i++) {
4451 const gchar *csum = g_ptr_array_index (csums, i);
Richard Hughes31206832020-07-27 15:31:11 +01004452 if (g_hash_table_lookup (self->blocked_firmware, csum) != NULL)
4453 return TRUE;
4454 }
4455 return FALSE;
4456}
4457
4458static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01004459fu_engine_add_releases_for_device_component (FuEngine *self,
Richard Hughesdf89cd52020-06-26 20:25:18 +01004460 FuEngineRequest *request,
Richard Hughes481aa2a2018-09-18 20:51:46 +01004461 FuDevice *device,
4462 XbNode *component,
4463 GPtrArray *releases,
4464 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +00004465{
Richard Hughes460c4b72020-09-25 20:59:28 +01004466 FwupdFeatureFlags feature_flags;
Richard Hughes9a680842020-02-20 11:11:13 +00004467 FwupdVersionFormat fmt = fu_device_get_version_format (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004468 g_autoptr(GError) error_local = NULL;
4469 g_autoptr(FuInstallTask) task = fu_install_task_new (device, component);
4470 g_autoptr(GPtrArray) releases_tmp = NULL;
Richard Hughes650dade2017-12-14 14:43:11 +00004471
Richard Hughesdf89cd52020-06-26 20:25:18 +01004472 if (!fu_engine_check_requirements (self, request, task,
Richard Hughesce756272019-03-24 12:21:34 +00004473 FWUPD_INSTALL_FLAG_OFFLINE |
Richard Hughesbf439782020-11-03 11:32:29 +00004474 FWUPD_INSTALL_FLAG_IGNORE_VID_PID |
Richard Hughes5bbf0132020-10-05 13:44:39 +01004475 FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH |
Richard Hughes481aa2a2018-09-18 20:51:46 +01004476 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
4477 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
4478 error))
4479 return FALSE;
4480
4481 /* get all releases */
4482 releases_tmp = xb_node_query (component, "releases/release", 0, &error_local);
4483 if (releases_tmp == NULL) {
4484 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
4485 return TRUE;
4486 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
4487 return TRUE;
4488 g_propagate_error (error, g_steal_pointer (&error_local));
4489 return FALSE;
4490 }
Richard Hughes460c4b72020-09-25 20:59:28 +01004491 feature_flags = fu_engine_request_get_feature_flags (request);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004492 for (guint i = 0; i < releases_tmp->len; i++) {
4493 XbNode *release = g_ptr_array_index (releases_tmp, i);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004494 const gchar *remote_id;
Mario Limonciello32241f42019-01-24 10:12:41 -06004495 const gchar *update_message;
Richard Hughes4fd4b982020-06-25 18:58:57 +01004496 const gchar *update_image;
Richard Hughes8e0cc802019-03-04 14:10:17 +00004497 gint vercmp;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004498 GPtrArray *checksums;
Richard Hughesb8dfacc2021-01-25 20:20:59 +00004499 GPtrArray *locations;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004500 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004501 g_autoptr(GError) error_loop = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004502
4503 /* create new FwupdRelease for the XbNode */
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004504 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01004505 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004506 rel,
4507 component,
4508 release,
4509 &error_loop)) {
4510 g_warning ("failed to set release for component: %s",
4511 error_loop->message);
4512 continue;
4513 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01004514
Richard Hughes6e0c8f82018-11-11 21:46:41 +00004515 /* fall back to quirk-provided value */
4516 if (fwupd_release_get_install_duration (rel) == 0)
4517 fwupd_release_set_install_duration (rel, fu_device_get_install_duration (device));
4518
Richard Hughes481aa2a2018-09-18 20:51:46 +01004519 /* invalid */
Richard Hughesb8dfacc2021-01-25 20:20:59 +00004520 locations = fwupd_release_get_locations (rel);
4521 if (locations->len == 0)
Richard Hughes650dade2017-12-14 14:43:11 +00004522 continue;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004523 checksums = fwupd_release_get_checksums (rel);
4524 if (checksums->len == 0)
4525 continue;
4526
Richard Hughes460c4b72020-09-25 20:59:28 +01004527 /* different branch */
4528 if (g_strcmp0 (fwupd_release_get_branch (rel),
4529 fu_device_get_branch (device)) != 0) {
4530 if ((feature_flags & FWUPD_FEATURE_FLAG_SWITCH_BRANCH) == 0) {
4531 g_debug ("client does not understand branches, skipping %s:%s",
4532 fwupd_release_get_branch (rel),
4533 fwupd_release_get_version (rel));
4534 continue;
4535 }
4536 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH);
4537 }
4538
Richard Hughes8e0cc802019-03-04 14:10:17 +00004539 /* test for upgrade or downgrade */
Richard Hughes9a680842020-02-20 11:11:13 +00004540 vercmp = fu_common_vercmp_full (fwupd_release_get_version (rel),
4541 fu_device_get_version (device),
4542 fmt);
Richard Hughes8e0cc802019-03-04 14:10:17 +00004543 if (vercmp > 0)
4544 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_UPGRADE);
4545 else if (vercmp < 0)
4546 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_DOWNGRADE);
4547
4548 /* lower than allowed to downgrade to */
4549 if (fu_device_get_version_lowest (device) != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00004550 fu_common_vercmp_full (fwupd_release_get_version (rel),
4551 fu_device_get_version_lowest (device),
4552 fmt) < 0) {
Richard Hughes8e0cc802019-03-04 14:10:17 +00004553 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION);
4554 }
4555
Richard Hughes31206832020-07-27 15:31:11 +01004556 /* manually blocked */
4557 if (fu_engine_check_release_is_blocked (self, rel))
4558 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL);
4559
Richard Hughes85226fd2020-06-30 14:43:48 +01004560 /* check if remote is filtering firmware */
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004561 remote_id = fwupd_release_get_remote_id (rel);
4562 if (remote_id != NULL) {
4563 FwupdRemote *remote = fu_engine_get_remote_by_id (self, remote_id, NULL);
4564 if (remote != NULL &&
4565 fwupd_remote_get_approval_required (remote) &&
4566 !fu_engine_check_release_is_approved (self, rel)) {
4567 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL);
4568 }
4569 }
4570
Mario Limonciello32241f42019-01-24 10:12:41 -06004571 /* add update message if exists but device doesn't already have one */
4572 update_message = fwupd_release_get_update_message (rel);
4573 if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL &&
4574 update_message != NULL) {
Richard Hughes4fd4b982020-06-25 18:58:57 +01004575 fwupd_device_set_update_message (FWUPD_DEVICE (device), update_message);
4576 }
4577 update_image = fwupd_release_get_update_image (rel);
4578 if (fwupd_device_get_update_image (FWUPD_DEVICE (device)) == NULL &&
4579 update_image != NULL) {
4580 fwupd_device_set_update_image (FWUPD_DEVICE (device), update_image);
Mario Limonciello32241f42019-01-24 10:12:41 -06004581 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01004582 /* success */
4583 g_ptr_array_add (releases, g_steal_pointer (&rel));
Richard Hughes650dade2017-12-14 14:43:11 +00004584 }
4585
Richard Hughes481aa2a2018-09-18 20:51:46 +01004586 /* success */
4587 return TRUE;
Richard Hughes650dade2017-12-14 14:43:11 +00004588}
4589
Richard Hughes88fdc592020-10-15 10:00:41 +01004590static const gchar *
4591fu_engine_get_branch_fallback (const gchar *nullable_branch)
4592{
4593 if (nullable_branch == NULL)
4594 return "default";
4595 return nullable_branch;
4596}
4597
Filipe Laínsd3a4fd32020-03-30 21:05:14 +01004598GPtrArray *
Richard Hughesdf89cd52020-06-26 20:25:18 +01004599fu_engine_get_releases_for_device (FuEngine *self,
4600 FuEngineRequest *request,
4601 FuDevice *device,
4602 GError **error)
Richard Hughes97284b12017-09-13 17:07:58 +01004603{
4604 GPtrArray *device_guids;
4605 GPtrArray *releases;
4606 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004607 g_autoptr(GError) error_all = NULL;
4608 g_autoptr(GError) error_local = NULL;
Richard Hughes460c4b72020-09-25 20:59:28 +01004609 g_autoptr(GPtrArray) branches = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004610 g_autoptr(GPtrArray) components = NULL;
4611 g_autoptr(GString) xpath = g_string_new (NULL);
Richard Hughes97284b12017-09-13 17:07:58 +01004612
4613 /* get device version */
4614 version = fu_device_get_version (device);
4615 if (version == NULL) {
4616 g_set_error (error,
4617 FWUPD_ERROR,
4618 FWUPD_ERROR_NOT_SUPPORTED,
4619 "no version set");
4620 return NULL;
4621 }
4622
4623 /* only show devices that can be updated */
Richard Hughes6b64bc72021-04-22 12:06:04 +01004624 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4625 !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004626 g_set_error_literal (error,
4627 FWUPD_ERROR,
4628 FWUPD_ERROR_NOT_SUPPORTED,
4629 "is not updatable");
Richard Hughes97284b12017-09-13 17:07:58 +01004630 return NULL;
4631 }
4632
Richard Hughes481aa2a2018-09-18 20:51:46 +01004633 /* get all the components that provide any of these GUIDs */
Richard Hughes97284b12017-09-13 17:07:58 +01004634 device_guids = fu_device_get_guids (device);
4635 for (guint i = 0; i < device_guids->len; i++) {
Richard Hughes97284b12017-09-13 17:07:58 +01004636 const gchar *guid = g_ptr_array_index (device_guids, i);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004637 xb_string_append_union (xpath,
Richard Hughes4cbe99c2020-11-22 13:14:33 +00004638 "components/component[@type='firmware']/"
Richard Hughes634da032018-11-05 11:42:20 +00004639 "provides/firmware[@type=$'flashed'][text()=$'%s']/"
Richard Hughes481aa2a2018-09-18 20:51:46 +01004640 "../..", guid);
4641 }
4642 components = xb_silo_query (self->silo, xpath->str, 0, &error_local);
4643 if (components == NULL) {
Richard Hughesa07a80f2018-12-03 14:57:33 +00004644 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
4645 g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004646 g_set_error_literal (error,
4647 FWUPD_ERROR,
4648 FWUPD_ERROR_NOTHING_TO_DO,
4649 "No releases found");
Richard Hughesb095df62018-11-05 11:41:36 +00004650 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004651 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01004652 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughesb095df62018-11-05 11:41:36 +00004653 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01004654 }
4655
4656 /* find all the releases that pass all the requirements */
4657 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4658 for (guint i = 0; i < components->len; i++) {
4659 XbNode *component = XB_NODE (g_ptr_array_index (components, i));
4660 g_autoptr(GError) error_tmp = NULL;
4661 if (!fu_engine_add_releases_for_device_component (self,
Richard Hughesdf89cd52020-06-26 20:25:18 +01004662 request,
Richard Hughes481aa2a2018-09-18 20:51:46 +01004663 device,
4664 component,
4665 releases,
4666 &error_tmp)) {
4667 if (error_all == NULL) {
4668 error_all = g_steal_pointer (&error_tmp);
4669 continue;
4670 }
4671
4672 /* assume the domain and code is the same */
4673 g_prefix_error (&error_all, "%s, ", error_tmp->message);
4674 }
4675 }
4676
Richard Hughes460c4b72020-09-25 20:59:28 +01004677 /* are there multiple branches available */
4678 branches = g_ptr_array_new_with_free_func (g_free);
Richard Hughes88fdc592020-10-15 10:00:41 +01004679 g_ptr_array_add (branches,
4680 g_strdup (fu_engine_get_branch_fallback (fu_device_get_branch (device))));
Richard Hughes460c4b72020-09-25 20:59:28 +01004681 for (guint i = 0; i < releases->len; i++) {
4682 FwupdRelease *rel_tmp = FWUPD_RELEASE (g_ptr_array_index (releases, i));
Richard Hughes88fdc592020-10-15 10:00:41 +01004683 const gchar *branch_tmp = fu_engine_get_branch_fallback (fwupd_release_get_branch (rel_tmp));
Richard Hughesdb8533f2020-12-14 12:04:30 +00004684#if GLIB_CHECK_VERSION(2,54,3)
Richard Hughes460c4b72020-09-25 20:59:28 +01004685 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
4686 g_str_equal, NULL))
4687 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00004688#endif
Richard Hughes460c4b72020-09-25 20:59:28 +01004689 g_ptr_array_add (branches, g_strdup (branch_tmp));
4690 }
Richard Hughesf64e7a92020-10-01 18:05:36 +01004691 if (branches->len > 1)
Richard Hughes460c4b72020-09-25 20:59:28 +01004692 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES);
4693
Richard Hughes481aa2a2018-09-18 20:51:46 +01004694 /* return the compound error */
4695 if (releases->len == 0) {
Richard Hughesd56ad5b2018-11-12 10:48:06 +00004696 if (error_all != NULL) {
Richard Hughes5b715e12019-04-08 13:33:59 +01004697 g_propagate_prefixed_error (error, g_steal_pointer (&error_all),
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004698 "No releases found: ");
Richard Hughesd56ad5b2018-11-12 10:48:06 +00004699 return NULL;
4700 }
4701 g_set_error (error,
4702 FWUPD_ERROR,
4703 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004704 "No releases found");
Richard Hughes481aa2a2018-09-18 20:51:46 +01004705 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004706 }
4707 return releases;
4708}
4709
Richard Hughes9945edb2017-06-19 10:03:55 +01004710/**
4711 * fu_engine_get_releases:
4712 * @self: A #FuEngine
Richard Hughesdf89cd52020-06-26 20:25:18 +01004713 * @request: A #FuEngineRequest
Richard Hughes9945edb2017-06-19 10:03:55 +01004714 * @device_id: A device ID
4715 * @error: A #GError, or %NULL
4716 *
4717 * Gets the releases available for a specific device.
4718 *
Richard Hughes93b15762017-09-15 11:05:23 +01004719 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01004720 **/
4721GPtrArray *
Richard Hughesdf89cd52020-06-26 20:25:18 +01004722fu_engine_get_releases (FuEngine *self,
4723 FuEngineRequest *request,
4724 const gchar *device_id,
4725 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01004726{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004727 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004728 g_autoptr(GPtrArray) releases = NULL;
4729
4730 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4731 g_return_val_if_fail (device_id != NULL, NULL);
4732 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4733
4734 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004735 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004736 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004737 return NULL;
4738
4739 /* get all the releases for the device */
Richard Hughesdf89cd52020-06-26 20:25:18 +01004740 releases = fu_engine_get_releases_for_device (self, request, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01004741 if (releases == NULL)
4742 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004743 if (releases->len == 0) {
4744 g_set_error_literal (error,
4745 FWUPD_ERROR,
4746 FWUPD_ERROR_NOTHING_TO_DO,
4747 "No releases for device");
4748 return NULL;
4749 }
Richard Hughes9a680842020-02-20 11:11:13 +00004750 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes97284b12017-09-13 17:07:58 +01004751 return g_steal_pointer (&releases);
4752}
4753
4754/**
4755 * fu_engine_get_downgrades:
4756 * @self: A #FuEngine
Richard Hughesdf89cd52020-06-26 20:25:18 +01004757 * @request: A #FuEngineRequest
Richard Hughes97284b12017-09-13 17:07:58 +01004758 * @device_id: A device ID
4759 * @error: A #GError, or %NULL
4760 *
4761 * Gets the downgrades available for a specific device.
4762 *
Richard Hughes93b15762017-09-15 11:05:23 +01004763 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes97284b12017-09-13 17:07:58 +01004764 **/
4765GPtrArray *
Richard Hughesdf89cd52020-06-26 20:25:18 +01004766fu_engine_get_downgrades (FuEngine *self,
4767 FuEngineRequest *request,
4768 const gchar *device_id,
4769 GError **error)
Richard Hughes97284b12017-09-13 17:07:58 +01004770{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004771 g_autoptr(FuDevice) device = NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01004772 g_autoptr(GPtrArray) releases = NULL;
4773 g_autoptr(GPtrArray) releases_tmp = NULL;
4774 g_autoptr(GString) error_str = g_string_new (NULL);
4775
4776 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4777 g_return_val_if_fail (device_id != NULL, NULL);
4778 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4779
4780 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004781 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004782 if (device == NULL)
Richard Hughes97284b12017-09-13 17:07:58 +01004783 return NULL;
4784
4785 /* get all the releases for the device */
Richard Hughesdf89cd52020-06-26 20:25:18 +01004786 releases_tmp = fu_engine_get_releases_for_device (self, request, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01004787 if (releases_tmp == NULL)
4788 return NULL;
4789 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4790 for (guint i = 0; i < releases_tmp->len; i++) {
4791 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughes97284b12017-09-13 17:07:58 +01004792
Richard Hughes8e0cc802019-03-04 14:10:17 +00004793 /* same as installed */
4794 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
4795 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01004796 g_string_append_printf (error_str, "%s=same, ",
4797 fwupd_release_get_version (rel_tmp));
4798 g_debug ("ignoring %s as the same as %s",
4799 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004800 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004801 continue;
4802 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00004803
4804 /* newer than current */
4805 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01004806 g_string_append_printf (error_str, "%s=newer, ",
4807 fwupd_release_get_version (rel_tmp));
4808 g_debug ("ignoring %s as newer than %s",
4809 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004810 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004811 continue;
4812 }
4813
Richard Hughesdce91202019-04-08 12:47:45 +01004814 /* don't show releases we are not allowed to downgrade to */
Richard Hughes8e0cc802019-03-04 14:10:17 +00004815 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_VERSION)) {
4816 g_string_append_printf (error_str, "%s=lowest, ",
4817 fwupd_release_get_version (rel_tmp));
4818 g_debug ("ignoring %s as older than lowest %s",
4819 fwupd_release_get_version (rel_tmp),
4820 fu_device_get_version_lowest (device));
4821 continue;
Richard Hughes97284b12017-09-13 17:07:58 +01004822 }
Richard Hughes460c4b72020-09-25 20:59:28 +01004823
4824 /* different branch */
4825 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) {
4826 g_debug ("ignoring release %s as branch %s, and device is %s",
4827 fwupd_release_get_version (rel_tmp),
4828 fwupd_release_get_branch (rel_tmp),
4829 fu_device_get_branch (device));
4830 continue;
4831 }
4832
Richard Hughes97284b12017-09-13 17:07:58 +01004833 g_ptr_array_add (releases, g_object_ref (rel_tmp));
4834 }
4835 if (error_str->len > 2)
4836 g_string_truncate (error_str, error_str->len - 2);
4837 if (releases->len == 0) {
4838 if (error_str->len > 0) {
4839 g_set_error (error,
4840 FWUPD_ERROR,
4841 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004842 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004843 fu_device_get_version (device),
Richard Hughes97284b12017-09-13 17:07:58 +01004844 error_str->str);
4845 } else {
4846 g_set_error (error,
4847 FWUPD_ERROR,
4848 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004849 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004850 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01004851 }
4852 return NULL;
4853 }
Richard Hughes9a680842020-02-20 11:11:13 +00004854 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01004855 return g_steal_pointer (&releases);
4856}
4857
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004858GPtrArray *
4859fu_engine_get_approved_firmware (FuEngine *self)
4860{
4861 GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free);
Richard Hughes3444cf62020-06-22 15:10:28 +01004862 if (self->approved_firmware != NULL) {
4863 g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware);
4864 for (GList *l = keys; l != NULL; l = l->next) {
4865 const gchar *csum = l->data;
4866 g_ptr_array_add (checksums, g_strdup (csum));
4867 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004868 }
4869 return checksums;
4870}
4871
4872void
4873fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum)
4874{
Richard Hughes3444cf62020-06-22 15:10:28 +01004875 if (self->approved_firmware == NULL) {
4876 self->approved_firmware = g_hash_table_new_full (g_str_hash,
4877 g_str_equal,
4878 g_free,
4879 NULL);
4880 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004881 g_hash_table_add (self->approved_firmware, g_strdup (checksum));
4882}
4883
Richard Hughes31206832020-07-27 15:31:11 +01004884GPtrArray *
4885fu_engine_get_blocked_firmware (FuEngine *self)
4886{
4887 GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free);
4888 if (self->blocked_firmware != NULL) {
4889 g_autoptr(GList) keys = g_hash_table_get_keys (self->blocked_firmware);
4890 for (GList *l = keys; l != NULL; l = l->next) {
4891 const gchar *csum = l->data;
4892 g_ptr_array_add (checksums, g_strdup (csum));
4893 }
4894 }
4895 return checksums;
4896}
4897
4898void
4899fu_engine_add_blocked_firmware (FuEngine *self, const gchar *checksum)
4900{
4901 if (self->blocked_firmware == NULL) {
4902 self->blocked_firmware = g_hash_table_new_full (g_str_hash,
4903 g_str_equal,
4904 g_free,
4905 NULL);
4906 }
4907 g_hash_table_add (self->blocked_firmware, g_strdup (checksum));
4908}
4909
4910gboolean
4911fu_engine_set_blocked_firmware (FuEngine *self, GPtrArray *checksums, GError **error)
4912{
4913 /* update in-memory hash */
4914 if (self->blocked_firmware != NULL) {
4915 g_hash_table_unref (self->blocked_firmware);
4916 self->blocked_firmware = NULL;
4917 }
4918 for (guint i = 0; i < checksums->len; i++) {
4919 const gchar *csum = g_ptr_array_index (checksums, i);
4920 fu_engine_add_blocked_firmware (self, csum);
4921 }
4922
4923 /* save database */
4924 if (!fu_history_clear_blocked_firmware (self->history, error))
4925 return FALSE;
4926 for (guint i = 0; i < checksums->len; i++) {
4927 const gchar *csum = g_ptr_array_index (checksums, i);
4928 if (!fu_history_add_blocked_firmware (self->history, csum, error))
4929 return FALSE;
4930 }
4931 return TRUE;
4932}
4933
Richard Hughes3d607622019-03-07 16:59:27 +00004934gchar *
4935fu_engine_self_sign (FuEngine *self,
4936 const gchar *value,
Richard Hughesd5aab652020-02-25 12:47:50 +00004937 JcatSignFlags flags,
Richard Hughes3d607622019-03-07 16:59:27 +00004938 GError **error)
4939{
Richard Hughesd5aab652020-02-25 12:47:50 +00004940 g_autoptr(JcatBlob) jcat_signature = NULL;
4941 g_autoptr(JcatEngine) jcat_engine = NULL;
4942 g_autoptr(JcatResult) jcat_result = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004943 g_autoptr(GBytes) payload = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004944
4945 /* create detached signature and verify */
Richard Hughesd5aab652020-02-25 12:47:50 +00004946 jcat_engine = jcat_context_get_engine (self->jcat_context,
4947 JCAT_BLOB_KIND_PKCS7,
4948 error);
4949 if (jcat_engine == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004950 return NULL;
4951 payload = g_bytes_new (value, strlen (value));
Richard Hughesd5aab652020-02-25 12:47:50 +00004952 jcat_signature = jcat_engine_self_sign (jcat_engine, payload, flags, error);
4953 if (jcat_signature == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004954 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004955 jcat_result = jcat_engine_self_verify (jcat_engine, payload,
4956 jcat_blob_get_data (jcat_signature),
4957 JCAT_VERIFY_FLAG_NONE, error);
4958 if (jcat_result == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004959 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004960 return jcat_blob_get_data_as_string (jcat_signature);
Richard Hughes3d607622019-03-07 16:59:27 +00004961}
4962
Richard Hughes9945edb2017-06-19 10:03:55 +01004963/**
Richard Hughesa96413a2017-09-13 17:19:59 +01004964 * fu_engine_get_upgrades:
4965 * @self: A #FuEngine
Richard Hughesdf89cd52020-06-26 20:25:18 +01004966 * @request: A #FuEngineRequest
Richard Hughesa96413a2017-09-13 17:19:59 +01004967 * @device_id: A device ID
4968 * @error: A #GError, or %NULL
4969 *
4970 * Gets the upgrades available for a specific device.
4971 *
Richard Hughes93b15762017-09-15 11:05:23 +01004972 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughesa96413a2017-09-13 17:19:59 +01004973 **/
4974GPtrArray *
Richard Hughesdf89cd52020-06-26 20:25:18 +01004975fu_engine_get_upgrades (FuEngine *self,
4976 FuEngineRequest *request,
4977 const gchar *device_id,
4978 GError **error)
Richard Hughesa96413a2017-09-13 17:19:59 +01004979{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004980 g_autoptr(FuDevice) device = NULL;
Richard Hughesa96413a2017-09-13 17:19:59 +01004981 g_autoptr(GPtrArray) releases = NULL;
4982 g_autoptr(GPtrArray) releases_tmp = NULL;
4983 g_autoptr(GString) error_str = g_string_new (NULL);
4984
4985 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4986 g_return_val_if_fail (device_id != NULL, NULL);
4987 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4988
4989 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004990 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004991 if (device == NULL)
Richard Hughesa96413a2017-09-13 17:19:59 +01004992 return NULL;
4993
Mario Limonciellofc139352018-09-13 10:06:39 -05004994 /* don't show upgrades again until we reboot */
4995 if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004996 g_set_error_literal (error,
4997 FWUPD_ERROR,
4998 FWUPD_ERROR_NOTHING_TO_DO,
4999 "A reboot is pending");
Mario Limonciellofc139352018-09-13 10:06:39 -05005000 return NULL;
5001 }
5002
Richard Hughesa96413a2017-09-13 17:19:59 +01005003 /* get all the releases for the device */
Richard Hughesdf89cd52020-06-26 20:25:18 +01005004 releases_tmp = fu_engine_get_releases_for_device (self, request, device, error);
Richard Hughesa96413a2017-09-13 17:19:59 +01005005 if (releases_tmp == NULL)
5006 return NULL;
5007 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
5008 for (guint i = 0; i < releases_tmp->len; i++) {
5009 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughesa96413a2017-09-13 17:19:59 +01005010
Richard Hughes8e0cc802019-03-04 14:10:17 +00005011 /* same as installed */
5012 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
5013 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01005014 g_string_append_printf (error_str, "%s=same, ",
5015 fwupd_release_get_version (rel_tmp));
Mario Limonciello7a3664f2020-09-28 15:04:44 -05005016 g_debug ("ignoring %s == %s",
Richard Hughesa96413a2017-09-13 17:19:59 +01005017 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00005018 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01005019 continue;
5020 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00005021
5022 /* older than current */
5023 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01005024 g_string_append_printf (error_str, "%s=older, ",
5025 fwupd_release_get_version (rel_tmp));
Mario Limonciello7a3664f2020-09-28 15:04:44 -05005026 g_debug ("ignoring %s < %s",
Richard Hughesa96413a2017-09-13 17:19:59 +01005027 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00005028 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01005029 continue;
5030 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005031
5032 /* not approved */
5033 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL)) {
5034 g_string_append_printf (error_str, "%s=not-approved, ",
5035 fwupd_release_get_version (rel_tmp));
5036 g_debug ("ignoring %s as not approved as required by %s",
5037 fwupd_release_get_version (rel_tmp),
5038 fwupd_release_get_remote_id (rel_tmp));
5039 continue;
5040 }
5041
Richard Hughes460c4b72020-09-25 20:59:28 +01005042 /* different branch */
5043 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_ALTERNATE_BRANCH)) {
5044 g_debug ("ignoring release %s as branch %s, and device is %s",
5045 fwupd_release_get_version (rel_tmp),
5046 fwupd_release_get_branch (rel_tmp),
5047 fu_device_get_branch (device));
5048 continue;
5049 }
5050
Richard Hughesa96413a2017-09-13 17:19:59 +01005051 g_ptr_array_add (releases, g_object_ref (rel_tmp));
5052 }
5053 if (error_str->len > 2)
5054 g_string_truncate (error_str, error_str->len - 2);
5055 if (releases->len == 0) {
5056 if (error_str->len > 0) {
5057 g_set_error (error,
5058 FWUPD_ERROR,
5059 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06005060 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00005061 fu_device_get_version (device),
Richard Hughesa96413a2017-09-13 17:19:59 +01005062 error_str->str);
5063 } else {
5064 g_set_error (error,
5065 FWUPD_ERROR,
5066 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06005067 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00005068 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01005069 }
5070 return NULL;
5071 }
Richard Hughes9a680842020-02-20 11:11:13 +00005072 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughesa96413a2017-09-13 17:19:59 +01005073 return g_steal_pointer (&releases);
5074}
5075
5076/**
Richard Hughes9945edb2017-06-19 10:03:55 +01005077 * fu_engine_clear_results:
5078 * @self: A #FuEngine
5079 * @device_id: A device ID
5080 * @error: A #GError, or %NULL
5081 *
5082 * Clear the historical state of a specific device operation.
5083 *
5084 * Returns: %TRUE for success
5085 **/
5086gboolean
5087fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error)
5088{
Richard Hughes65e44ca2018-01-30 17:26:30 +00005089 g_autoptr(FuDevice) device = NULL;
Richard Hughes34834102017-11-21 21:55:00 +00005090 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +01005091
5092 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
5093 g_return_val_if_fail (device_id != NULL, FALSE);
5094 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
5095
5096 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00005097 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00005098 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01005099 return FALSE;
5100
Richard Hughes65e44ca2018-01-30 17:26:30 +00005101 /* already set on the database */
5102 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
5103 g_set_error_literal (error,
5104 FWUPD_ERROR,
5105 FWUPD_ERROR_NOT_SUPPORTED,
5106 "device already has notified flag");
5107 return FALSE;
5108 }
5109
5110 /* call into the plugin if it still exists */
Richard Hughese7e95452017-11-22 09:05:53 +00005111 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00005112 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00005113 error);
Richard Hughes65e44ca2018-01-30 17:26:30 +00005114 if (plugin != NULL) {
5115 if (!fu_plugin_runner_clear_results (plugin, device, error))
5116 return FALSE;
5117 }
Richard Hughes34834102017-11-21 21:55:00 +00005118
Richard Hughes65e44ca2018-01-30 17:26:30 +00005119 /* override */
Richard Hughesc0cd0232018-01-31 15:02:00 +00005120 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED);
Richard Hughes0bbef292019-11-01 12:15:15 +00005121 return fu_history_modify_device (self->history, device, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01005122}
5123
5124/**
5125 * fu_engine_get_results:
5126 * @self: A #FuEngine
5127 * @device_id: A device ID
5128 * @error: A #GError, or %NULL
5129 *
5130 * Gets the historical state of a specific device operation.
5131 *
Richard Hughes93b15762017-09-15 11:05:23 +01005132 * Returns: (transfer container): a #FwupdDevice, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01005133 **/
Richard Hughes93b15762017-09-15 11:05:23 +01005134FwupdDevice *
Richard Hughes9945edb2017-06-19 10:03:55 +01005135fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error)
5136{
Richard Hughes65e44ca2018-01-30 17:26:30 +00005137 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005138
5139 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
5140 g_return_val_if_fail (device_id != NULL, NULL);
5141 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
5142
5143 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00005144 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00005145 if (device == NULL)
Richard Hughesfe221dc2018-05-29 09:33:44 +01005146 return NULL;
Richard Hughes34834102017-11-21 21:55:00 +00005147
Richard Hughes65e44ca2018-01-30 17:26:30 +00005148 /* the notification has already been shown to the user */
5149 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
5150 g_set_error (error,
5151 FWUPD_ERROR,
5152 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes7e77bf32018-05-08 14:56:46 +01005153 "User has already been notified about %s [%s]",
5154 fu_device_get_name (device),
Richard Hughes65e44ca2018-01-30 17:26:30 +00005155 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01005156 return NULL;
Richard Hughes65e44ca2018-01-30 17:26:30 +00005157 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005158
Richard Hughes65e44ca2018-01-30 17:26:30 +00005159 /* success */
Richard Hugheseec8a3c2018-01-02 20:37:31 +00005160 return g_object_ref (FWUPD_DEVICE (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01005161}
5162
5163static void
5164fu_engine_plugins_setup (FuEngine *self)
5165{
Richard Hughese671c052018-09-18 10:17:15 +01005166 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughese7e95452017-11-22 09:05:53 +00005167 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005168 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00005169 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01005170 if (!fu_plugin_runner_startup (plugin, &error)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005171 fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED);
5172 if (g_error_matches (error,
5173 FWUPD_ERROR,
5174 FWUPD_ERROR_NOT_SUPPORTED)) {
5175 fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_NO_HARDWARE);
5176 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005177 g_message ("disabling plugin because: %s", error->message);
5178 }
5179 }
5180}
5181
5182static void
Richard Hughes4ae7b5e2021-04-01 14:43:43 +01005183fu_engine_plugins_coldplug (FuEngine *self)
Richard Hughes9945edb2017-06-19 10:03:55 +01005184{
Richard Hughese7e95452017-11-22 09:05:53 +00005185 GPtrArray *plugins;
Richard Hughes535664c2017-07-24 10:30:09 +01005186 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01005187
Richard Hughes9945edb2017-06-19 10:03:55 +01005188 /* prepare */
Richard Hughese7e95452017-11-22 09:05:53 +00005189 plugins = fu_plugin_list_get_all (self->plugin_list);
5190 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005191 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00005192 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01005193 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
5194 g_warning ("failed to prepare coldplug: %s", error->message);
5195 }
5196
Richard Hughes9945edb2017-06-19 10:03:55 +01005197 /* exec */
Richard Hughese7e95452017-11-22 09:05:53 +00005198 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005199 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00005200 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes4ae7b5e2021-04-01 14:43:43 +01005201 if (!fu_plugin_runner_coldplug (plugin, &error)) {
5202 fu_plugin_add_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED);
5203 g_message ("disabling plugin because: %s",
5204 error->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01005205 }
5206 }
5207
5208 /* cleanup */
Richard Hughese7e95452017-11-22 09:05:53 +00005209 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005210 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00005211 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01005212 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
5213 g_warning ("failed to cleanup coldplug: %s", error->message);
5214 }
5215
Richard Hughes535664c2017-07-24 10:30:09 +01005216 /* print what we do have */
Richard Hughese7e95452017-11-22 09:05:53 +00005217 for (guint i = 0; i < plugins->len; i++) {
5218 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005219 if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED))
Richard Hughes535664c2017-07-24 10:30:09 +01005220 continue;
5221 g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin));
5222 }
5223 if (str->len > 2) {
5224 g_string_truncate (str, str->len - 2);
Mario Limonciello59cd4702018-08-09 12:43:47 -05005225 g_debug ("using plugins: %s", str->str);
Richard Hughes535664c2017-07-24 10:30:09 +01005226 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005227}
5228
5229static void
Richard Hughese1fd34d2017-08-24 14:19:51 +01005230fu_engine_plugin_device_register (FuEngine *self, FuDevice *device)
5231{
Richard Hughese7e95452017-11-22 09:05:53 +00005232 GPtrArray *plugins;
Richard Hughese1fd34d2017-08-24 14:19:51 +01005233 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) {
5234 g_warning ("already registered %s, ignoring",
5235 fu_device_get_id (device));
5236 return;
5237 }
Richard Hughese7e95452017-11-22 09:05:53 +00005238 plugins = fu_plugin_list_get_all (self->plugin_list);
5239 for (guint i = 0; i < plugins->len; i++) {
5240 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughese1fd34d2017-08-24 14:19:51 +01005241 fu_plugin_runner_device_register (plugin, device);
5242 }
5243 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REGISTERED);
5244}
5245
5246static void
5247fu_engine_plugin_device_register_cb (FuPlugin *plugin,
5248 FuDevice *device,
5249 gpointer user_data)
5250{
5251 FuEngine *self = FU_ENGINE (user_data);
5252 fu_engine_plugin_device_register (self, device);
5253}
5254
5255static void
Richard Hughes9945edb2017-06-19 10:03:55 +01005256fu_engine_plugin_device_added_cb (FuPlugin *plugin,
5257 FuDevice *device,
5258 gpointer user_data)
5259{
Richard Hughes4299ba02020-04-14 15:40:56 +01005260 FuEngine *self = FU_ENGINE (user_data);
Richard Hughesfecc4382020-04-15 11:24:34 +01005261
5262 /* plugin has prio and device not already set from quirk */
5263 if (fu_plugin_get_priority (plugin) > 0 &&
5264 fu_device_get_priority (device) == 0) {
5265 g_debug ("auto-setting %s priority to %u",
5266 fu_device_get_id (device),
5267 fu_plugin_get_priority (plugin));
5268 fu_device_set_priority (device, fu_plugin_get_priority (plugin));
5269 }
5270
Richard Hughes0a7e7832017-11-22 11:01:13 +00005271 fu_engine_add_device (self, device);
5272}
5273
Richard Hughes5e447292018-04-27 14:25:54 +01005274static void
5275fu_engine_adopt_children (FuEngine *self, FuDevice *device)
5276{
5277 GPtrArray *guids;
5278 g_autoptr(GPtrArray) devices = fu_device_list_get_active (self->device_list);
5279
5280 /* find the parent GUID in any existing device */
5281 guids = fu_device_get_parent_guids (device);
5282 for (guint j = 0; j < guids->len; j++) {
5283 const gchar *guid = g_ptr_array_index (guids, j);
5284 for (guint i = 0; i < devices->len; i++) {
5285 FuDevice *device_tmp = g_ptr_array_index (devices, i);
5286 if (fu_device_get_parent (device) != NULL)
5287 continue;
5288 if (fu_device_has_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01005289 g_debug ("setting parent of %s [%s] to be %s [%s]",
5290 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01005291 fu_device_get_id (device),
Richard Hughes7e77bf32018-05-08 14:56:46 +01005292 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01005293 fu_device_get_id (device_tmp));
Richard Hughes2ab379e2020-04-09 16:03:36 +01005294 fu_device_set_parent (device, device_tmp);
Richard Hughes5e447292018-04-27 14:25:54 +01005295 break;
5296 }
5297 }
5298 }
5299
5300 /* the new device is the parent to an existing child */
5301 guids = fu_device_get_guids (device);
5302 for (guint j = 0; j < guids->len; j++) {
5303 const gchar *guid = g_ptr_array_index (guids, j);
5304 for (guint i = 0; i < devices->len; i++) {
5305 FuDevice *device_tmp = g_ptr_array_index (devices, i);
5306 if (fu_device_get_parent (device_tmp) != NULL)
5307 continue;
5308 if (fu_device_has_parent_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01005309 g_debug ("setting parent of %s [%s] to be %s [%s]",
5310 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01005311 fu_device_get_id (device_tmp),
Richard Hughes7e77bf32018-05-08 14:56:46 +01005312 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01005313 fu_device_get_id (device));
Richard Hughes2ab379e2020-04-09 16:03:36 +01005314 fu_device_set_parent (device_tmp, device);
Richard Hughes5e447292018-04-27 14:25:54 +01005315 }
5316 }
5317 }
5318}
5319
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005320static void
Richard Hughes35ca4cb2020-04-14 17:09:01 +01005321fu_engine_set_proxy_device (FuEngine *self, FuDevice *device)
5322{
5323 GPtrArray *guids;
5324 g_autoptr(FuDevice) proxy = NULL;
5325 g_autoptr(GPtrArray) devices = NULL;
5326
5327 if (fu_device_get_proxy (device) != NULL)
5328 return;
5329 if (fu_device_get_proxy_guid (device) == NULL)
5330 return;
5331
5332 /* find the proxy GUID in any existing device */
5333 proxy = fu_device_list_get_by_guid (self->device_list,
5334 fu_device_get_proxy_guid (device),
5335 NULL);
5336 if (proxy != NULL) {
5337 g_debug ("setting proxy of %s to %s for %s",
5338 fu_device_get_id (proxy),
5339 fu_device_get_id (device),
5340 fu_device_get_proxy_guid (device));
5341 fu_device_set_proxy (device, proxy);
5342 return;
5343 }
5344
5345 /* are we the parent of an existing device */
5346 guids = fu_device_get_guids (device);
5347 for (guint j = 0; j < guids->len; j++) {
5348 const gchar *guid = g_ptr_array_index (guids, j);
5349 devices = fu_device_list_get_active (self->device_list);
5350 for (guint i = 0; i < devices->len; i++) {
5351 FuDevice *device_tmp = g_ptr_array_index (devices, i);
5352 if (g_strcmp0 (fu_device_get_proxy_guid (device_tmp), guid) == 0) {
5353 g_debug ("adding proxy of %s to %s for %s",
5354 fu_device_get_id (device),
5355 fu_device_get_id (device_tmp),
5356 guid);
5357 fu_device_set_proxy (device_tmp, device);
5358 return;
5359 }
5360 }
5361 }
5362
5363 /* nothing found */
5364 g_warning ("did not find proxy device %s",
5365 fu_device_get_proxy_guid (device));
5366}
5367
5368static void
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005369fu_engine_device_inherit_history (FuEngine *self, FuDevice *device)
5370{
5371 g_autoptr(FuDevice) device_history = NULL;
5372
5373 /* any success or failed update? */
5374 device_history = fu_history_get_device_by_id (self->history,
5375 fu_device_get_id (device),
5376 NULL);
5377 if (device_history == NULL)
5378 return;
5379
5380 /* the device is still running the old firmware version and so if it
5381 * required activation before, it still requires it now -- note:
5382 * we can't just check for version_new=version to allow for re-installs */
Mario Limonciello0f50a312021-04-06 11:30:26 -05005383 if (fu_device_has_internal_flag (device, FU_DEVICE_INTERNAL_FLAG_INHERIT_ACTIVATION) &&
5384 fu_device_has_flag (device_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005385 FwupdRelease *release = fu_device_get_release_default (device_history);
Richard Hughes9a680842020-02-20 11:11:13 +00005386 if (fu_common_vercmp_full (fu_device_get_version (device),
5387 fwupd_release_get_version (release),
5388 fu_device_get_version_format (device)) != 0) {
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005389 g_debug ("inheriting needs-activation for %s as version %s != %s",
5390 fu_device_get_name (device),
5391 fu_device_get_version (device),
5392 fwupd_release_get_version (release));
5393 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
5394 }
5395 }
5396}
5397
Richard Hughes0a7e7832017-11-22 11:01:13 +00005398void
5399fu_engine_add_device (FuEngine *self, FuDevice *device)
5400{
Richard Hughes85226fd2020-06-30 14:43:48 +01005401 GPtrArray *disabled_devices;
Richard Hughes32684f22017-07-13 09:32:21 +01005402 GPtrArray *device_guids;
Mario Limoncielloe0384192020-04-16 14:36:49 -05005403 g_autoptr(XbNode) component = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005404
5405 /* device has no GUIDs set! */
Richard Hughes32684f22017-07-13 09:32:21 +01005406 device_guids = fu_device_get_guids (device);
5407 if (device_guids->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005408 g_warning ("no GUIDs for device %s [%s]",
Richard Hughes7e77bf32018-05-08 14:56:46 +01005409 fu_device_get_name (device),
5410 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01005411 return;
5412 }
5413
Richard Hughes85226fd2020-06-30 14:43:48 +01005414 /* is this GUID disabled */
5415 disabled_devices = fu_config_get_disabled_devices (self->config);
5416 for (guint i = 0; i < disabled_devices->len; i++) {
5417 const gchar *disabled_guid = g_ptr_array_index (disabled_devices, i);
Richard Hughes32684f22017-07-13 09:32:21 +01005418 for (guint j = 0; j < device_guids->len; j++) {
5419 const gchar *device_guid = g_ptr_array_index (device_guids, j);
Richard Hughes85226fd2020-06-30 14:43:48 +01005420 if (g_strcmp0 (disabled_guid, device_guid) == 0) {
5421 g_debug ("%s [%s] is disabled [%s], ignoring from %s",
Richard Hughes7e77bf32018-05-08 14:56:46 +01005422 fu_device_get_name (device),
5423 fu_device_get_id (device),
5424 device_guid,
Richard Hughes0a7e7832017-11-22 11:01:13 +00005425 fu_device_get_plugin (device));
Richard Hughes32684f22017-07-13 09:32:21 +01005426 return;
5427 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005428 }
5429 }
5430
Richard Hughes7f765002019-12-20 09:41:29 +00005431 /* does the device not have an assigned protocol */
5432 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
Richard Hughesb3f98412021-03-01 13:17:57 +00005433 fu_device_get_protocols (device)->len == 0) {
Mario Limonciello33b40ed2020-01-08 15:11:34 -06005434 g_warning ("device %s [%s] does not define an update protocol",
Richard Hughes7f765002019-12-20 09:41:29 +00005435 fu_device_get_id (device),
5436 fu_device_get_name (device));
5437 }
5438
Richard Hughesf3077752018-06-27 12:45:06 +01005439 /* if this device is locked get some metadata from AppStream */
Mario Limoncielloe0384192020-04-16 14:36:49 -05005440 component = fu_engine_get_component_by_guids (self, device);
Richard Hughesf3077752018-06-27 12:45:06 +01005441 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01005442 if (component != NULL) {
5443 g_autoptr(XbNode) release = NULL;
5444 release = xb_node_query_first (component,
5445 "releases/release",
5446 NULL);
Richard Hughesf3077752018-06-27 12:45:06 +01005447 if (release != NULL) {
5448 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01005449 g_autoptr(GError) error_local = NULL;
5450 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01005451 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01005452 rel,
5453 component,
5454 release,
5455 &error_local)) {
5456 g_warning ("failed to set AppStream release: %s",
5457 error_local->message);
5458 } else {
5459 fu_device_add_release (device, rel);
5460 }
Richard Hughesf3077752018-06-27 12:45:06 +01005461 }
5462 }
5463 }
5464
Richard Hughes5e447292018-04-27 14:25:54 +01005465 /* adopt any required children, which may or may not already exist */
5466 fu_engine_adopt_children (self, device);
5467
Richard Hughes35ca4cb2020-04-14 17:09:01 +01005468 /* set the proxy device if specified by GUID */
5469 fu_engine_set_proxy_device (self, device);
5470
Richard Hughese48351e2018-06-22 12:32:39 +01005471 /* set any alternate objects on the device from the ID */
5472 if (fu_device_get_alternate_id (device) != NULL) {
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01005473 g_autoptr(FuDevice) device_alt = NULL;
Richard Hughese48351e2018-06-22 12:32:39 +01005474 device_alt = fu_device_list_get_by_id (self->device_list,
5475 fu_device_get_alternate_id (device),
5476 NULL);
5477 if (device_alt != NULL)
5478 fu_device_set_alternate (device, device_alt);
5479 }
5480
Richard Hughes5079f262019-04-29 09:34:27 +01005481 if (fu_device_get_version_format (device) == FWUPD_VERSION_FORMAT_UNKNOWN &&
5482 fu_common_version_guess_format (fu_device_get_version (device)) == FWUPD_VERSION_FORMAT_NUMBER) {
Mario Limonciello253b8252019-04-25 10:48:17 -04005483 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
5484 fu_device_set_update_error (device, "VersionFormat is ambiguous for this device");
5485 }
5486
Richard Hughesd8ea8da2020-02-19 15:04:49 +00005487 /* no vendor-id, and so no way to lock it down! */
5488 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
Richard Hugheseddaed02021-01-03 12:08:38 +00005489 fu_device_get_vendor_ids(device)->len == 0) {
Richard Hughesd8ea8da2020-02-19 15:04:49 +00005490 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
5491 fu_device_set_update_error (device, "No vendor ID set");
5492 }
5493
Richard Hughese1fd34d2017-08-24 14:19:51 +01005494 /* notify all plugins about this new device */
5495 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED))
5496 fu_engine_plugin_device_register (self, device);
5497
Richard Hughes7f765002019-12-20 09:41:29 +00005498 /* does the device *still* not have a vendor ID? */
5499 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
Richard Hugheseddaed02021-01-03 12:08:38 +00005500 fu_device_get_vendor_ids(device)->len == 0) {
Richard Hughes7f765002019-12-20 09:41:29 +00005501 g_warning ("device %s [%s] does not define a vendor-id!",
5502 fu_device_get_id (device),
5503 fu_device_get_name (device));
5504 }
5505
Richard Hughesd76ed3d2018-11-15 15:05:36 +00005506 /* create new device */
5507 fu_device_list_add (self->device_list, device);
5508
Richard Hughes81f95522020-09-13 15:12:57 +01005509 /* fix order */
5510 fu_device_list_depsolve_order (self->device_list, device);
5511
Mario Limoncielloe0384192020-04-16 14:36:49 -05005512 /* fixup the name and format as needed from cached metadata */
5513 if (component != NULL)
5514 fu_engine_md_refresh_device_from_component (self, device, component);
5515
Richard Hughes1ee18002020-03-31 21:46:49 +01005516 /* match the metadata so clients can tell if the device is worthy */
5517 fu_engine_ensure_device_supported (self, device);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005518
5519 /* sometimes inherit flags from recent history */
5520 fu_engine_device_inherit_history (self, device);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06005521
5522 fu_engine_emit_changed (self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005523}
5524
5525static void
Richard Hughes75b965d2018-11-15 13:51:21 +00005526fu_engine_plugin_rules_changed_cb (FuPlugin *plugin, gpointer user_data)
5527{
5528 FuEngine *self = FU_ENGINE (user_data);
5529 GPtrArray *rules = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE);
Richard Hughes11c59412020-06-22 15:29:48 +01005530 if (rules == NULL)
5531 return;
Richard Hughes75b965d2018-11-15 13:51:21 +00005532 for (guint j = 0; j < rules->len; j++) {
5533 const gchar *tmp = g_ptr_array_index (rules, j);
5534 fu_idle_inhibit (self->idle, tmp);
5535 }
5536}
5537
5538static void
Richard Hughesb333e002021-04-01 10:40:02 +01005539fu_engine_context_security_changed_cb (FuContext *ctx, gpointer user_data)
Richard Hughes399859e2020-05-11 19:44:03 +01005540{
5541 FuEngine *self = FU_ENGINE (user_data);
Richard Hughesfb0a9382020-06-23 16:56:28 +01005542
5543 /* invalidate host security attributes */
5544 g_clear_pointer (&self->host_security_id, g_free);
5545
5546 /* make UI refresh */
Richard Hughes399859e2020-05-11 19:44:03 +01005547 fu_engine_emit_changed (self);
5548}
5549
5550static void
Richard Hughes9945edb2017-06-19 10:03:55 +01005551fu_engine_plugin_device_removed_cb (FuPlugin *plugin,
5552 FuDevice *device,
5553 gpointer user_data)
5554{
5555 FuEngine *self = (FuEngine *) user_data;
Richard Hughes34834102017-11-21 21:55:00 +00005556 FuPlugin *plugin_old;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01005557 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005558 g_autoptr(GError) error = NULL;
5559
Richard Hughes40127542018-01-12 20:25:55 +00005560 device_tmp = fu_device_list_get_by_id (self->device_list,
5561 fu_device_get_id (device),
5562 &error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00005563 if (device_tmp == NULL) {
Richard Hughes521c2e22020-10-01 14:12:41 +01005564 g_debug ("failed to find device %s: %s",
5565 fu_device_get_id (device),
5566 error->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01005567 return;
5568 }
5569
Richard Hughes34834102017-11-21 21:55:00 +00005570 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00005571 plugin_old = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00005572 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00005573 &error);
Richard Hughes34834102017-11-21 21:55:00 +00005574 if (plugin_old == NULL) {
Richard Hughes521c2e22020-10-01 14:12:41 +01005575 g_debug ("failed to find plugin %s: %s",
5576 fu_device_get_plugin (device),
5577 error->message);
Richard Hughes34834102017-11-21 21:55:00 +00005578 return;
5579 }
5580
Richard Hughes9945edb2017-06-19 10:03:55 +01005581 /* check this came from the same plugin */
5582 if (g_strcmp0 (fu_plugin_get_name (plugin),
Richard Hughes34834102017-11-21 21:55:00 +00005583 fu_plugin_get_name (plugin_old)) != 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005584 g_debug ("ignoring duplicate removal from %s",
5585 fu_plugin_get_name (plugin));
5586 return;
5587 }
5588
5589 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +00005590 fu_device_list_remove (self->device_list, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01005591 fu_engine_emit_changed (self);
5592}
5593
Richard Hughesf425d292019-01-18 17:57:39 +00005594/* this is called by the self tests as well */
Richard Hughes34834102017-11-21 21:55:00 +00005595void
5596fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin)
5597{
Mario Limonciello52e75ba2019-11-22 13:21:19 -06005598 if (fu_plugin_is_open (plugin)) {
5599 /* plugin does not match built version */
5600 if (fu_plugin_get_build_hash (plugin) == NULL) {
5601 const gchar *name = fu_plugin_get_name (plugin);
5602 g_warning ("%s should call fu_plugin_set_build_hash()",
5603 name);
5604 self->tainted = TRUE;
5605 } else if (g_strcmp0 (fu_plugin_get_build_hash (plugin),
5606 FU_BUILD_HASH) != 0) {
5607 const gchar *name = fu_plugin_get_name (plugin);
5608 g_warning ("%s has incorrect built version %s",
5609 name, fu_plugin_get_build_hash (plugin));
5610 self->tainted = TRUE;
5611 }
Richard Hughesf425d292019-01-18 17:57:39 +00005612 }
5613
Richard Hughese7e95452017-11-22 09:05:53 +00005614 fu_plugin_list_add (self->plugin_list, plugin);
Richard Hughes34834102017-11-21 21:55:00 +00005615}
5616
Richard Hughes9945edb2017-06-19 10:03:55 +01005617static gboolean
Richard Hughes85226fd2020-06-30 14:43:48 +01005618fu_engine_is_plugin_name_disabled (FuEngine *self, const gchar *name)
Richard Hughes1e456bc2018-05-10 20:16:16 +01005619{
Richard Hughes85226fd2020-06-30 14:43:48 +01005620 GPtrArray *disabled = fu_config_get_disabled_plugins (self->config);
5621 for (guint i = 0; i < disabled->len; i++) {
5622 const gchar *name_tmp = g_ptr_array_index (disabled, i);
Richard Hughes1e456bc2018-05-10 20:16:16 +01005623 if (g_strcmp0 (name_tmp, name) == 0)
5624 return TRUE;
5625 }
5626 return FALSE;
5627}
5628
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005629static gboolean
Richard Hughes85226fd2020-06-30 14:43:48 +01005630fu_engine_is_plugin_name_enabled (FuEngine *self, const gchar *name)
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005631{
5632 if (self->plugin_filter->len == 0)
5633 return TRUE;
5634 for (guint i = 0; i < self->plugin_filter->len; i++) {
5635 const gchar *name_tmp = g_ptr_array_index (self->plugin_filter, i);
Richard Hughes5c508de2019-11-22 09:57:34 +00005636 if (fu_common_fnmatch (name_tmp, name))
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005637 return TRUE;
5638 }
5639 return FALSE;
5640}
5641
5642void
5643fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob)
5644{
Mario Limonciello1a520512020-05-26 10:44:53 -05005645 GString *str;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005646 g_return_if_fail (FU_IS_ENGINE (self));
5647 g_return_if_fail (plugin_glob != NULL);
Mario Limonciello1a520512020-05-26 10:44:53 -05005648 str = g_string_new (plugin_glob);
5649 fu_common_string_replace (str, "-", "_");
5650 g_ptr_array_add (self->plugin_filter, g_string_free (str, FALSE));
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005651}
5652
Richard Hughesaabdc372018-11-14 10:11:08 +00005653static gboolean
5654fu_engine_plugin_check_supported_cb (FuPlugin *plugin, const gchar *guid, FuEngine *self)
5655{
5656 g_autoptr(XbNode) n = NULL;
5657 g_autofree gchar *xpath = NULL;
Mario Limonciello4fa95a72020-03-28 10:50:57 -05005658
5659 if (fu_config_get_enumerate_all_devices (self->config))
5660 return TRUE;
5661
Richard Hughes4cbe99c2020-11-22 13:14:33 +00005662 xpath = g_strdup_printf ("components/component[@type='firmware']/"
Richard Hughesaabdc372018-11-14 10:11:08 +00005663 "provides/firmware[@type='flashed'][text()='%s']",
5664 guid);
5665 n = xb_silo_query_first (self->silo, xpath, NULL);
5666 return n != NULL;
5667}
5668
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005669gboolean
Richard Hughesf425d292019-01-18 17:57:39 +00005670fu_engine_get_tainted (FuEngine *self)
5671{
5672 return self->tainted;
5673}
5674
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005675const gchar *
5676fu_engine_get_host_product (FuEngine *self)
5677{
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06005678 const gchar *result = NULL;
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005679 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
Richard Hughesb333e002021-04-01 10:40:02 +01005680 result = fu_context_get_hwid_value (self->ctx, FU_HWIDS_KEY_PRODUCT_NAME);
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06005681 return result != NULL ? result : "Unknown Product";
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05005682}
5683
Richard Hughes0917fb62019-09-21 12:55:37 +01005684const gchar *
5685fu_engine_get_host_machine_id (FuEngine *self)
5686{
5687 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
5688 return self->host_machine_id;
5689}
5690
Richard Hughesad451582020-05-11 22:14:47 +01005691static void
Richard Hughesfb0a9382020-06-23 16:56:28 +01005692fu_engine_ensure_security_attrs_tainted (FuEngine *self)
Richard Hughesad451582020-05-11 22:14:47 +01005693{
Mario Limoncielloa8342842020-05-13 09:47:16 -05005694 gboolean disabled_plugins = FALSE;
Richard Hughes85226fd2020-06-30 14:43:48 +01005695 GPtrArray *disabled = fu_config_get_disabled_plugins (self->config);
Richard Hughesb246bca2020-05-18 14:31:35 +01005696 g_autoptr(FwupdSecurityAttr) attr = fwupd_security_attr_new (FWUPD_SECURITY_ATTR_ID_FWUPD_PLUGINS);
Richard Hughescae111d2020-05-15 15:20:24 +01005697 fwupd_security_attr_set_plugin (attr, "core");
Richard Hughesad451582020-05-11 22:14:47 +01005698 fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE);
Richard Hughesfb0a9382020-06-23 16:56:28 +01005699
5700 fu_security_attrs_append (self->host_security_attrs, attr);
Richard Hughes85226fd2020-06-30 14:43:48 +01005701 for (guint i = 0; i < disabled->len; i++) {
5702 const gchar *name_tmp = g_ptr_array_index (disabled, i);
Richard Hughes21c44d12021-03-03 10:04:00 +00005703 if (!g_str_has_prefix (name_tmp, "test") &&
Mario Limoncielloa8342842020-05-13 09:47:16 -05005704 g_strcmp0 (name_tmp, "invalid") != 0) {
5705 disabled_plugins = TRUE;
5706 break;
5707 }
5708 }
Richard Hughesad451582020-05-11 22:14:47 +01005709 if (self->tainted) {
Richard Hughesb246bca2020-05-18 14:31:35 +01005710 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_TAINTED);
5711 return;
Richard Hughesad451582020-05-11 22:14:47 +01005712 }
Richard Hughesb246bca2020-05-18 14:31:35 +01005713 if (self->plugin_filter->len > 0 || disabled_plugins) {
5714 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_ENABLED);
5715 return;
5716 }
5717
5718 /* success */
5719 fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
5720 fwupd_security_attr_set_result (attr, FWUPD_SECURITY_ATTR_RESULT_NOT_TAINTED);
Richard Hughesad451582020-05-11 22:14:47 +01005721}
5722
Richard Hughes377feea2020-10-17 15:52:03 +01005723static gchar *
5724fu_engine_attrs_calculate_hsi_for_chassis (FuEngine *self)
5725{
5726 guint val;
Richard Hughes377feea2020-10-17 15:52:03 +01005727
5728 /* get chassis type from SMBIOS data */
Richard Hughesb333e002021-04-01 10:40:02 +01005729 val = fu_context_get_smbios_integer (self->ctx,
5730 FU_SMBIOS_STRUCTURE_TYPE_CHASSIS,
5731 0x05);
5732 if (val == G_MAXUINT)
Richard Hughes377feea2020-10-17 15:52:03 +01005733 return g_strdup ("HSI-INVALID:chassis");
Richard Hughes377feea2020-10-17 15:52:03 +01005734
5735 /* verify HSI makes sense for this chassis type */
5736 switch (val) {
5737 case FU_SMBIOS_CHASSIS_KIND_DESKTOP:
5738 case FU_SMBIOS_CHASSIS_KIND_LOW_PROFILE_DESKTOP:
5739 case FU_SMBIOS_CHASSIS_KIND_MINI_TOWER:
5740 case FU_SMBIOS_CHASSIS_KIND_TOWER:
5741 case FU_SMBIOS_CHASSIS_KIND_PORTABLE:
5742 case FU_SMBIOS_CHASSIS_KIND_LAPTOP:
5743 case FU_SMBIOS_CHASSIS_KIND_NOTEBOOK:
5744 case FU_SMBIOS_CHASSIS_KIND_ALL_IN_ONE:
5745 case FU_SMBIOS_CHASSIS_KIND_SUB_NOTEBOOK:
5746 case FU_SMBIOS_CHASSIS_KIND_LUNCH_BOX:
5747 case FU_SMBIOS_CHASSIS_KIND_MAIN_SERVER:
5748 case FU_SMBIOS_CHASSIS_KIND_TABLET:
5749 case FU_SMBIOS_CHASSIS_KIND_CONVERTIBLE:
5750 case FU_SMBIOS_CHASSIS_KIND_DETACHABLE:
5751 case FU_SMBIOS_CHASSIS_KIND_IOT_GATEWAY:
5752 case FU_SMBIOS_CHASSIS_KIND_EMBEDDED_PC:
5753 case FU_SMBIOS_CHASSIS_KIND_MINI_PC:
5754 case FU_SMBIOS_CHASSIS_KIND_STICK_PC:
5755 return fu_security_attrs_calculate_hsi (self->host_security_attrs,
5756 FU_SECURITY_ATTRS_FLAG_ADD_VERSION);
5757 default:
5758 break;
5759 }
5760
5761 /* failed */
5762 return g_strdup_printf ("HSI-INVALID:chassis[0x%02x]", val);
5763}
5764
5765
Richard Hughesfb0a9382020-06-23 16:56:28 +01005766static void
5767fu_engine_ensure_security_attrs (FuEngine *self)
Richard Hughes196c6c62020-05-11 19:42:47 +01005768{
5769 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughesdd616892021-03-17 21:42:28 +00005770 g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list);
Richard Hughesb246bca2020-05-18 14:31:35 +01005771 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01005772
Richard Hughesfb0a9382020-06-23 16:56:28 +01005773 /* already valid */
5774 if (self->host_security_id != NULL)
5775 return;
5776
5777 /* clear old values */
5778 fu_security_attrs_remove_all (self->host_security_attrs);
5779
Richard Hughesad451582020-05-11 22:14:47 +01005780 /* built in */
Richard Hughesfb0a9382020-06-23 16:56:28 +01005781 fu_engine_ensure_security_attrs_tainted (self);
Richard Hughesad451582020-05-11 22:14:47 +01005782
Richard Hughesdd616892021-03-17 21:42:28 +00005783 /* call into devices */
5784 for (guint i = 0; i < devices->len; i++) {
5785 FuDevice *device = g_ptr_array_index (devices, i);
5786 fu_device_add_security_attrs (device, self->host_security_attrs);
5787 }
5788
Richard Hughesad451582020-05-11 22:14:47 +01005789 /* call into plugins */
Richard Hughes196c6c62020-05-11 19:42:47 +01005790 for (guint j = 0; j < plugins->len; j++) {
5791 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
Richard Hughesfb0a9382020-06-23 16:56:28 +01005792 fu_plugin_runner_add_security_attrs (plugin_tmp, self->host_security_attrs);
Richard Hughes196c6c62020-05-11 19:42:47 +01005793 }
5794
Richard Hughesb246bca2020-05-18 14:31:35 +01005795 /* set the fallback names for clients without native translations */
Richard Hughesfb0a9382020-06-23 16:56:28 +01005796 items = fu_security_attrs_get_all (self->host_security_attrs);
Richard Hughesb246bca2020-05-18 14:31:35 +01005797 for (guint i = 0; i < items->len; i++) {
5798 FwupdSecurityAttr *attr = g_ptr_array_index (items, i);
5799 if (fwupd_security_attr_get_name (attr) == NULL) {
Richard Hughesb99df2e2020-07-01 19:28:35 +01005800 g_autofree gchar *name_tmp = fu_security_attr_get_name (attr);
Richard Hughesb246bca2020-05-18 14:31:35 +01005801 if (name_tmp == NULL) {
5802 g_warning ("failed to get fallback for %s",
5803 fwupd_security_attr_get_appstream_id (attr));
5804 continue;
5805 }
5806 fwupd_security_attr_set_name (attr, name_tmp);
5807 }
5808 }
5809
Richard Hughes196c6c62020-05-11 19:42:47 +01005810 /* set the obsoletes flag for each attr */
Richard Hughesfb0a9382020-06-23 16:56:28 +01005811 fu_security_attrs_depsolve (self->host_security_attrs);
Richard Hughesb246bca2020-05-18 14:31:35 +01005812
Richard Hughesfb0a9382020-06-23 16:56:28 +01005813 /* distil into one simple string */
5814 g_free (self->host_security_id);
Richard Hughes377feea2020-10-17 15:52:03 +01005815 self->host_security_id = fu_engine_attrs_calculate_hsi_for_chassis (self);
Richard Hughes196c6c62020-05-11 19:42:47 +01005816}
5817
5818const gchar *
5819fu_engine_get_host_security_id (FuEngine *self)
5820{
5821 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
Richard Hughesfb0a9382020-06-23 16:56:28 +01005822 fu_engine_ensure_security_attrs (self);
Richard Hughes196c6c62020-05-11 19:42:47 +01005823 return self->host_security_id;
5824}
5825
Richard Hughesfb0a9382020-06-23 16:56:28 +01005826FuSecurityAttrs *
5827fu_engine_get_host_security_attrs (FuEngine *self)
5828{
5829 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
5830 fu_engine_ensure_security_attrs (self);
5831 return g_object_ref (self->host_security_attrs);
5832}
5833
Richard Hughesf425d292019-01-18 17:57:39 +00005834gboolean
Richard Hughes9945edb2017-06-19 10:03:55 +01005835fu_engine_load_plugins (FuEngine *self, GError **error)
5836{
5837 const gchar *fn;
5838 g_autoptr(GDir) dir = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +01005839 g_autofree gchar *plugin_path = NULL;
Richard Hughes00d6f472019-11-22 17:04:02 +00005840 g_autofree gchar *suffix = g_strdup_printf (".%s", G_MODULE_SUFFIX);
Richard Hughes24281472020-10-05 11:45:47 +01005841 g_autoptr(GPtrArray) plugins_disabled = g_ptr_array_new_with_free_func (g_free);
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005842 g_autoptr(GPtrArray) plugins_disabled_rt = g_ptr_array_new_with_free_func (g_free);
Richard Hughesf77d7062017-11-27 12:06:36 +00005843
Richard Hughes9945edb2017-06-19 10:03:55 +01005844 /* search */
Richard Hughes4be17d12018-05-30 20:36:29 +01005845 plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG);
5846 dir = g_dir_open (plugin_path, 0, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01005847 if (dir == NULL)
5848 return FALSE;
5849 while ((fn = g_dir_read_name (dir)) != NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005850 g_autofree gchar *filename = NULL;
Richard Hughes1e456bc2018-05-10 20:16:16 +01005851 g_autofree gchar *name = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005852 g_autoptr(FuPlugin) plugin = NULL;
5853 g_autoptr(GError) error_local = NULL;
Richard Hughesb333e002021-04-01 10:40:02 +01005854 g_autoptr(GPtrArray) firmware_gtypes = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005855
5856 /* ignore non-plugins */
Richard Hughes00d6f472019-11-22 17:04:02 +00005857 if (!g_str_has_suffix (fn, suffix))
Richard Hughes9945edb2017-06-19 10:03:55 +01005858 continue;
5859
Richard Hughes85226fd2020-06-30 14:43:48 +01005860 /* is disabled */
Richard Hughes1e456bc2018-05-10 20:16:16 +01005861 name = fu_plugin_guess_name_from_fn (fn);
5862 if (name == NULL)
5863 continue;
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005864 if (fu_engine_is_plugin_name_disabled (self, name) ||
5865 !fu_engine_is_plugin_name_enabled (self, name)) {
Richard Hughes24281472020-10-05 11:45:47 +01005866 g_ptr_array_add (plugins_disabled, g_steal_pointer (&name));
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005867 continue;
5868 }
Richard Hughes1e456bc2018-05-10 20:16:16 +01005869
Richard Hughes9945edb2017-06-19 10:03:55 +01005870 /* open module */
Richard Hughes4be17d12018-05-30 20:36:29 +01005871 filename = g_build_filename (plugin_path, fn, NULL);
Richard Hughesb333e002021-04-01 10:40:02 +01005872 plugin = fu_plugin_new (self->ctx);
Richard Hughes1e456bc2018-05-10 20:16:16 +01005873 fu_plugin_set_name (plugin, name);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005874
5875 /* if loaded from fu_engine_load() open the plugin */
Richard Hughesb333e002021-04-01 10:40:02 +01005876 firmware_gtypes = fu_context_get_firmware_gtype_ids (self->ctx);
5877 if (firmware_gtypes->len > 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005878 if (!fu_plugin_open (plugin, filename, &error_local)) {
Richard Hughes521c2e22020-10-01 14:12:41 +01005879 g_warning ("cannot load: %s", error_local->message);
Mario Limoncielloc3a81732020-10-20 09:16:18 -05005880 fu_engine_add_plugin (self, plugin);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01005881 continue;
5882 }
Richard Hughes9945edb2017-06-19 10:03:55 +01005883 }
5884
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005885 /* runtime disabled */
5886 if (fu_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_DISABLED)) {
5887 g_ptr_array_add (plugins_disabled_rt, g_steal_pointer (&name));
Richard Hughes9945edb2017-06-19 10:03:55 +01005888 continue;
5889 }
5890
5891 /* watch for changes */
5892 g_signal_connect (plugin, "device-added",
5893 G_CALLBACK (fu_engine_plugin_device_added_cb),
5894 self);
5895 g_signal_connect (plugin, "device-removed",
5896 G_CALLBACK (fu_engine_plugin_device_removed_cb),
5897 self);
Richard Hughese1fd34d2017-08-24 14:19:51 +01005898 g_signal_connect (plugin, "device-register",
5899 G_CALLBACK (fu_engine_plugin_device_register_cb),
5900 self);
Richard Hughesaabdc372018-11-14 10:11:08 +00005901 g_signal_connect (plugin, "check-supported",
5902 G_CALLBACK (fu_engine_plugin_check_supported_cb),
5903 self);
Richard Hughes75b965d2018-11-15 13:51:21 +00005904 g_signal_connect (plugin, "rules-changed",
5905 G_CALLBACK (fu_engine_plugin_rules_changed_cb),
5906 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005907
5908 /* add */
Richard Hughesf425d292019-01-18 17:57:39 +00005909 fu_engine_add_plugin (self, plugin);
Richard Hughes9945edb2017-06-19 10:03:55 +01005910 }
5911
Richard Hughes24281472020-10-05 11:45:47 +01005912 /* show list */
5913 if (plugins_disabled->len > 0) {
5914 g_autofree gchar *str = NULL;
5915 g_ptr_array_add (plugins_disabled, NULL);
5916 str = g_strjoinv (", ", (gchar **) plugins_disabled->pdata);
5917 g_debug ("plugins disabled: %s", str);
5918 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005919 if (plugins_disabled_rt->len > 0) {
Richard Hughes24281472020-10-05 11:45:47 +01005920 g_autofree gchar *str = NULL;
Richard Hughes7bcb8d42020-10-08 15:47:47 +01005921 g_ptr_array_add (plugins_disabled_rt, NULL);
5922 str = g_strjoinv (", ", (gchar **) plugins_disabled_rt->pdata);
5923 g_debug ("plugins runtime-disabled: %s", str);
Richard Hughes24281472020-10-05 11:45:47 +01005924 }
5925
Richard Hughese7e95452017-11-22 09:05:53 +00005926 /* depsolve into the correct order */
5927 if (!fu_plugin_list_depsolve (self->plugin_list, error))
5928 return FALSE;
Richard Hughes08a37992017-09-12 12:57:43 +01005929
Richard Hughese7e95452017-11-22 09:05:53 +00005930 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01005931 return TRUE;
5932}
5933
Richard Hughes9945edb2017-06-19 10:03:55 +01005934static gboolean
5935fu_engine_cleanup_state (GError **error)
5936{
5937 const gchar *filenames[] = {
5938 "/var/cache/app-info/xmls/fwupd-verify.xml",
5939 "/var/cache/app-info/xmls/fwupd.xml",
5940 NULL };
5941 for (guint i = 0; filenames[i] != NULL; i++) {
5942 g_autoptr(GFile) file = g_file_new_for_path (filenames[i]);
5943 if (g_file_query_exists (file, NULL)) {
5944 if (!g_file_delete (file, NULL, error))
5945 return FALSE;
5946 }
5947 }
5948 return TRUE;
5949}
5950
Richard Hughesc7bbbc22018-01-02 22:22:25 +00005951guint64
5952fu_engine_get_archive_size_max (FuEngine *self)
5953{
5954 return fu_config_get_archive_size_max (self->config);
5955}
5956
Richard Hughes104f6512017-11-24 11:44:57 +00005957static void
Richard Hughes117f8572021-02-09 20:02:48 +00005958fu_engine_backend_device_removed_cb (FuBackend *backend, FuDevice *device, FuEngine *self)
Richard Hughes104f6512017-11-24 11:44:57 +00005959{
5960 g_autoptr(GPtrArray) devices = NULL;
5961
Richard Hughesf3880bc2019-11-26 12:02:30 +00005962 /* debug */
5963 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
Richard Hughes117f8572021-02-09 20:02:48 +00005964 g_debug ("%s removed %s",
5965 fu_backend_get_name (backend),
Richard Hughes253e6e42021-02-24 14:59:24 +00005966 fu_device_get_backend_id (device));
Richard Hughesf3880bc2019-11-26 12:02:30 +00005967 }
5968
Richard Hughes104f6512017-11-24 11:44:57 +00005969 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01005970 devices = fu_device_list_get_all (self->device_list);
Richard Hughes104f6512017-11-24 11:44:57 +00005971 for (guint i = 0; i < devices->len; i++) {
Richard Hughes117f8572021-02-09 20:02:48 +00005972 FuDevice *device_tmp = g_ptr_array_index (devices, i);
Richard Hughes253e6e42021-02-24 14:59:24 +00005973 if (g_strcmp0 (fu_device_get_backend_id (device_tmp),
5974 fu_device_get_backend_id (device)) == 0) {
5975 g_debug ("auto-removing backend device");
Richard Hughes117f8572021-02-09 20:02:48 +00005976 fu_device_list_remove (self->device_list, device_tmp);
Richard Hughesc125ec02018-09-05 19:35:17 +01005977 }
Richard Hughes104f6512017-11-24 11:44:57 +00005978 }
5979}
5980
5981static void
Richard Hughes117f8572021-02-09 20:02:48 +00005982fu_engine_backend_device_added_cb (FuBackend *backend, FuDevice *device, FuEngine *self)
Richard Hughes104f6512017-11-24 11:44:57 +00005983{
Richard Hughes6dec4012018-08-27 19:13:00 +01005984 g_autoptr(GError) error_local = NULL;
Richard Hughes45a18152019-10-30 15:29:04 +00005985 g_autoptr(GPtrArray) possible_plugins = NULL;
Richard Hughes6dec4012018-08-27 19:13:00 +01005986
Richard Hughes117f8572021-02-09 20:02:48 +00005987 /* super useful for plugin development */
Richard Hughesf3880bc2019-11-26 12:02:30 +00005988 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
Richard Hughes117f8572021-02-09 20:02:48 +00005989 g_autofree gchar *str = fu_device_to_string (FU_DEVICE (device));
5990 g_debug ("%s added %s", fu_backend_get_name (backend), str);
Richard Hughesf3880bc2019-11-26 12:02:30 +00005991 }
5992
Richard Hughes6dec4012018-08-27 19:13:00 +01005993 /* add any extra quirks */
Richard Hughesb333e002021-04-01 10:40:02 +01005994 fu_device_set_context (device, self->ctx);
Richard Hughes117f8572021-02-09 20:02:48 +00005995 if (!fu_device_probe (device, &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01005996 g_warning ("failed to probe device %s: %s",
Richard Hughes253e6e42021-02-24 14:59:24 +00005997 fu_device_get_backend_id (device),
Richard Hughes6dec4012018-08-27 19:13:00 +01005998 error_local->message);
5999 return;
6000 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01006001
Richard Hughesf3c3adb2021-02-18 18:50:28 +00006002 /* super useful for plugin development */
6003 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
6004 g_autofree gchar *str = fu_device_to_string (FU_DEVICE (device));
6005 g_debug ("%s added %s", fu_backend_get_name (backend), str);
6006 }
6007
Richard Hughes23170a82018-08-29 09:11:52 +01006008 /* can be specified using a quirk */
Richard Hughes117f8572021-02-09 20:02:48 +00006009 possible_plugins = fu_device_get_possible_plugins (device);
Richard Hughes45a18152019-10-30 15:29:04 +00006010 for (guint i = 0; i < possible_plugins->len; i++) {
6011 FuPlugin *plugin;
6012 const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
6013 g_autoptr(GError) error = NULL;
Mario Limonciello0e500322019-10-17 18:41:04 -05006014
Richard Hughes9ed4d472020-10-05 11:37:26 +01006015 plugin = fu_plugin_list_find_by_name (self->plugin_list, plugin_name, NULL);
6016 if (plugin == NULL)
Richard Hughes45a18152019-10-30 15:29:04 +00006017 continue;
Richard Hughes117f8572021-02-09 20:02:48 +00006018 if (!fu_plugin_runner_backend_device_added (plugin, device, &error)) {
Richard Hughes45a18152019-10-30 15:29:04 +00006019 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
6020 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
6021 g_debug ("%s ignoring: %s",
6022 fu_plugin_get_name (plugin),
6023 error->message);
Mario Limonciello0e500322019-10-17 18:41:04 -05006024 }
Mario Limonciello0e500322019-10-17 18:41:04 -05006025 continue;
6026 }
Richard Hughes117f8572021-02-09 20:02:48 +00006027 g_warning ("failed to add device %s: %s",
Richard Hughes253e6e42021-02-24 14:59:24 +00006028 fu_device_get_backend_id (device),
Richard Hughes45a18152019-10-30 15:29:04 +00006029 error->message);
6030 continue;
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01006031 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01006032 }
Richard Hughes104f6512017-11-24 11:44:57 +00006033}
Richard Hughes117f8572021-02-09 20:02:48 +00006034
6035static void
6036fu_engine_backend_device_changed_cb (FuBackend *backend, FuDevice *device, FuEngine *self)
6037{
6038 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
6039 g_autoptr(GPtrArray) devices = NULL;
6040
6041 /* debug */
6042 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
6043 g_debug ("%s changed %s",
6044 fu_backend_get_name (backend),
6045 fu_device_get_physical_id (device));
6046 }
6047
6048 /* emit changed on any that match */
6049 devices = fu_device_list_get_all (self->device_list);
6050 for (guint i = 0; i < devices->len; i++) {
6051 FuDevice *device_tmp = g_ptr_array_index (devices, i);
6052 if (!FU_IS_UDEV_DEVICE (device_tmp))
6053 continue;
6054 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device_tmp)),
6055 fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device))) == 0) {
6056 fu_udev_device_emit_changed (FU_UDEV_DEVICE (device));
6057 }
6058 }
6059
6060 /* run all plugins */
6061 for (guint j = 0; j < plugins->len; j++) {
6062 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
6063 g_autoptr(GError) error = NULL;
6064 if (!fu_plugin_runner_backend_device_changed (plugin_tmp, device, &error)) {
6065 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
6066 g_debug ("%s ignoring: %s",
6067 fu_plugin_get_name (plugin_tmp),
6068 error->message);
6069 continue;
6070 }
6071 g_warning ("%s failed to change udev device %s: %s",
6072 fu_plugin_get_name (plugin_tmp),
6073 fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
6074 error->message);
6075 }
6076 }
6077}
Richard Hughes104f6512017-11-24 11:44:57 +00006078
Richard Hughesf77d7062017-11-27 12:06:36 +00006079static void
Richard Hughesd94286b2021-03-01 21:12:18 +00006080fu_engine_load_quirks_for_hwid (FuEngine *self, const gchar *hwid)
6081{
6082 FuPlugin *plugin;
6083 const gchar *value;
Richard Hughesd94286b2021-03-01 21:12:18 +00006084 g_auto(GStrv) plugins = NULL;
6085
6086 /* does prefixed quirk exist */
Richard Hughesb333e002021-04-01 10:40:02 +01006087 value = fu_context_lookup_quirk_by_id (self->ctx, hwid, FU_QUIRKS_PLUGIN);
Richard Hughesd94286b2021-03-01 21:12:18 +00006088 if (value == NULL)
6089 return;
6090 plugins = g_strsplit (value, ",", -1);
6091 for (guint i = 0; plugins[i] != NULL; i++) {
6092 g_autoptr(GError) error_local = NULL;
6093 plugin = fu_plugin_list_find_by_name (self->plugin_list,
6094 plugins[i], &error_local);
6095 if (plugin == NULL) {
6096 g_debug ("no %s plugin for HwId %s: %s",
6097 plugins[i], hwid, error_local->message);
6098 continue;
6099 }
6100 g_debug ("enabling %s due to HwId %s", plugins[i], hwid);
6101 fu_plugin_remove_flag (plugin, FWUPD_PLUGIN_FLAG_REQUIRE_HWID);
6102 }
6103}
6104
6105static void
Richard Hughes51a869a2019-10-07 11:23:42 +01006106fu_engine_load_quirks (FuEngine *self, FuQuirksLoadFlags quirks_flags)
Richard Hughesf77d7062017-11-27 12:06:36 +00006107{
Richard Hughesb333e002021-04-01 10:40:02 +01006108 GPtrArray *guids = fu_context_get_hwid_guids (self->ctx);
Richard Hughesf77d7062017-11-27 12:06:36 +00006109 g_autoptr(GError) error = NULL;
Richard Hughesd94286b2021-03-01 21:12:18 +00006110
6111 /* rebuild silo if required */
Richard Hughesb333e002021-04-01 10:40:02 +01006112 if (!fu_context_load_quirks (self->ctx, quirks_flags, &error)) {
Richard Hughesf77d7062017-11-27 12:06:36 +00006113 g_warning ("Failed to load quirks: %s", error->message);
Richard Hughesd94286b2021-03-01 21:12:18 +00006114 return;
6115 }
6116
6117 /* search each hwid */
Richard Hughesb333e002021-04-01 10:40:02 +01006118 for (guint i = 0; i < guids->len; i++) {
6119 const gchar *hwid = g_ptr_array_index (guids, i);
Richard Hughesd94286b2021-03-01 21:12:18 +00006120 fu_engine_load_quirks_for_hwid (self, hwid);
6121 }
Richard Hughesf77d7062017-11-27 12:06:36 +00006122}
6123
Richard Hughesa2f8e452018-01-11 10:11:17 +00006124static gboolean
6125fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError **error)
6126{
Richard Hughesa2f8e452018-01-11 10:11:17 +00006127 FuPlugin *plugin;
6128 FwupdRelease *rel_history;
Richard Hughesf2711422018-01-12 09:49:05 +00006129 g_autofree gchar *btime = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01006130 g_autoptr(FuDevice) dev = NULL;
Richard Hughesb1146612020-06-16 14:56:16 +01006131 g_autoptr(GHashTable) metadata_device = NULL;
Richard Hughesa2f8e452018-01-11 10:11:17 +00006132
6133 /* is in the device list */
Richard Hughes40127542018-01-12 20:25:55 +00006134 dev = fu_device_list_get_by_id (self->device_list,
6135 fu_device_get_id (dev_history),
6136 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00006137 if (dev == NULL)
6138 return FALSE;
6139
6140 /* does the installed version match what we tried to install
6141 * before fwupd was restarted */
6142 rel_history = fu_device_get_release_default (dev_history);
6143 if (rel_history == NULL) {
6144 g_set_error_literal (error,
6145 FWUPD_ERROR,
6146 FWUPD_ERROR_INTERNAL,
6147 "no release for history FuDevice");
6148 return FALSE;
6149 }
6150
Richard Hughesf2711422018-01-12 09:49:05 +00006151 /* is this the same boot time as when we scheduled the update,
6152 * i.e. has fwupd been restarted before we rebooted */
6153 btime = fu_engine_get_boot_time ();
6154 if (g_strcmp0 (fwupd_release_get_metadata_item (rel_history, "BootTime"),
6155 btime) == 0) {
6156 g_debug ("service restarted, but no reboot has taken place");
Richard Hughes60441f92021-01-13 14:03:42 +00006157
6158 /* if it needed reboot then, it also needs it now... */
6159 if (fu_device_get_update_state (dev_history) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
6160 g_debug ("inheriting needs-reboot for %s",
6161 fu_device_get_name (dev));
6162 fu_device_set_update_state (dev, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
6163 }
Richard Hughesf2711422018-01-12 09:49:05 +00006164 return TRUE;
6165 }
6166
Richard Hughesb1146612020-06-16 14:56:16 +01006167 /* save any additional report metadata */
6168 metadata_device = fu_device_report_metadata_post (dev);
6169 if (metadata_device != NULL && g_hash_table_size (metadata_device) > 0) {
6170 fwupd_release_add_metadata (rel_history, metadata_device);
6171 if (!fu_history_set_device_metadata (self->history,
6172 fu_device_get_id (dev_history),
6173 fwupd_release_get_metadata (rel_history),
6174 error)) {
6175 g_prefix_error (error, "failed to set metadata: ");
6176 return FALSE;
6177 }
6178 }
6179
Richard Hughesa2f8e452018-01-11 10:11:17 +00006180 /* the system is running with the new firmware version */
Richard Hughes9a680842020-02-20 11:11:13 +00006181 if (fu_common_vercmp_full (fu_device_get_version (dev),
6182 fwupd_release_get_version (rel_history),
6183 fu_device_get_version_format (dev)) == 0) {
Richard Hughes4e886a42018-12-12 10:33:26 +00006184 GPtrArray *checksums;
Richard Hughesa2f8e452018-01-11 10:11:17 +00006185 g_debug ("installed version %s matching history %s",
6186 fu_device_get_version (dev),
6187 fwupd_release_get_version (rel_history));
Richard Hughes4e886a42018-12-12 10:33:26 +00006188
6189 /* copy over runtime checksums if set from probe() */
6190 checksums = fu_device_get_checksums (dev);
6191 for (guint i = 0; i < checksums->len; i++) {
6192 const gchar *csum = g_ptr_array_index (checksums, i);
6193 fu_device_add_checksum (dev_history, csum);
6194 }
Richard Hughesf50ff2c2020-02-25 09:45:15 +00006195 fu_device_set_version_format (dev_history, fu_device_get_version_format (dev));
6196 fu_device_set_version (dev_history, fu_device_get_version (dev));
Mario Limonciello96a0dd52019-02-25 13:50:03 -06006197 fu_device_remove_flag (dev_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
Richard Hughesc0cd0232018-01-31 15:02:00 +00006198 fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS);
Richard Hughese261bb62020-06-10 19:03:00 +01006199 fu_device_set_update_error (dev_history, NULL);
Richard Hughes0bbef292019-11-01 12:15:15 +00006200 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00006201 }
6202
Richard Hughes7e070c92018-01-12 16:50:05 +00006203 /* does the plugin know the update failure */
Richard Hughesa2f8e452018-01-11 10:11:17 +00006204 plugin = fu_plugin_list_find_by_name (self->plugin_list,
6205 fu_device_get_plugin (dev),
6206 error);
6207 if (plugin == NULL)
6208 return FALSE;
Richard Hughesa2f8e452018-01-11 10:11:17 +00006209 if (!fu_plugin_runner_get_results (plugin, dev, error))
6210 return FALSE;
Richard Hughes7e070c92018-01-12 16:50:05 +00006211
6212 /* the plugin either can't tell us the error, or doesn't know itself */
Richard Hughescce6a1c2019-04-16 17:25:48 +01006213 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED &&
6214 fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) {
Richard Hughes7e070c92018-01-12 16:50:05 +00006215 g_debug ("falling back to generic failure");
Richard Hughese261bb62020-06-10 19:03:00 +01006216 fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_FAILED);
Richard Hughesc0cd0232018-01-31 15:02:00 +00006217 fu_device_set_update_error (dev_history, "failed to run update on reboot");
Richard Hughese261bb62020-06-10 19:03:00 +01006218 } else {
6219 fu_device_set_update_state (dev_history, fu_device_get_update_state (dev));
6220 fu_device_set_update_error (dev_history, fu_device_get_update_error (dev));
Richard Hughesa2f8e452018-01-11 10:11:17 +00006221 }
Richard Hughes7e070c92018-01-12 16:50:05 +00006222
6223 /* update the state in the database */
Richard Hughes0bbef292019-11-01 12:15:15 +00006224 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00006225}
6226
6227static gboolean
6228fu_engine_update_history_database (FuEngine *self, GError **error)
6229{
6230 g_autoptr(GPtrArray) devices = NULL;
6231
6232 /* get any devices */
6233 devices = fu_history_get_devices (self->history, error);
6234 if (devices == NULL)
6235 return FALSE;
6236 for (guint i = 0; i < devices->len; i++) {
6237 FuDevice *dev = g_ptr_array_index (devices, i);
6238 g_autoptr(GError) error_local = NULL;
6239
6240 /* not in the required state */
Richard Hughesb2a1d972021-01-30 17:03:01 +00006241 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_NEEDS_REBOOT &&
6242 fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_PENDING)
Richard Hughesa2f8e452018-01-11 10:11:17 +00006243 continue;
6244
6245 /* try to save the new update-state, but ignoring any error */
Richard Hughes521c2e22020-10-01 14:12:41 +01006246 if (!fu_engine_update_history_device (self, dev, &error_local)) {
6247 g_warning ("failed to update history database: %s",
6248 error_local->message);
6249 }
Richard Hughesa2f8e452018-01-11 10:11:17 +00006250 }
6251 return TRUE;
6252}
6253
Richard Hughesf3c7d422019-03-12 10:29:42 +00006254static void
6255fu_engine_ensure_client_certificate (FuEngine *self)
6256{
Richard Hughesf3c7d422019-03-12 10:29:42 +00006257 g_autoptr(GBytes) blob = g_bytes_new_static ("test\0", 5);
Richard Hughesf3c7d422019-03-12 10:29:42 +00006258 g_autoptr(GError) error = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00006259 g_autoptr(JcatBlob) jcat_sig = NULL;
6260 g_autoptr(JcatEngine) jcat_engine = NULL;
Richard Hughesf3c7d422019-03-12 10:29:42 +00006261
6262 /* create keyring and sign dummy data to ensure certificate exists */
Richard Hughesd5aab652020-02-25 12:47:50 +00006263 jcat_engine = jcat_context_get_engine (self->jcat_context,
6264 JCAT_BLOB_KIND_PKCS7,
6265 &error);
6266 if (jcat_engine == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00006267 g_message ("failed to create keyring: %s", error->message);
6268 return;
6269 }
Richard Hughesd5aab652020-02-25 12:47:50 +00006270 jcat_sig = jcat_engine_self_sign (jcat_engine, blob, JCAT_SIGN_FLAG_NONE, &error);
6271 if (jcat_sig == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00006272 g_message ("failed to sign using keyring: %s", error->message);
6273 return;
6274 }
6275 g_debug ("client certificate exists and working");
6276}
6277
Richard Hughes9945edb2017-06-19 10:03:55 +01006278/**
6279 * fu_engine_load:
6280 * @self: A #FuEngine
Richard Hughesc7d870a2020-12-10 10:05:35 +00006281 * @flags: #FuEngineLoadFlags, e.g. %FU_ENGINE_LOAD_FLAG_READONLY
Richard Hughes9945edb2017-06-19 10:03:55 +01006282 * @error: A #GError, or %NULL
6283 *
6284 * Load the firmware update engine so it is ready for use.
6285 *
6286 * Returns: %TRUE for success
6287 **/
6288gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00006289fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01006290{
Richard Hughes51a869a2019-10-07 11:23:42 +01006291 FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE;
Richard Hughes404f0522021-02-16 18:25:32 +00006292 guint backend_cnt = 0;
Richard Hughes31206832020-07-27 15:31:11 +01006293 g_autoptr(GPtrArray) checksums_approved = NULL;
6294 g_autoptr(GPtrArray) checksums_blocked = NULL;
Norbert Kamińskia863e6a2021-04-02 15:50:35 +02006295#ifndef _WIN32
Daniel Campellob5ae60c2020-02-27 17:06:28 -07006296 g_autoptr(GError) error_local = NULL;
6297#endif
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006298
Richard Hughes9945edb2017-06-19 10:03:55 +01006299 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
6300 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
6301
Mario Limonciello46aaee82019-01-10 12:58:00 -06006302 /* avoid re-loading a second time if fu-tool or fu-util request to */
6303 if (self->loaded)
6304 return TRUE;
6305
Richard Hughes45a00732019-11-22 16:57:14 +00006306/* TODO: Read registry key [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography] "MachineGuid" */
6307#ifndef _WIN32
Richard Hughes0917fb62019-09-21 12:55:37 +01006308 /* cache machine ID so we can use it from a sandboxed app */
Daniel Campellob5ae60c2020-02-27 17:06:28 -07006309 self->host_machine_id = fwupd_build_machine_id ("fwupd", &error_local);
Richard Hughes0917fb62019-09-21 12:55:37 +01006310 if (self->host_machine_id == NULL)
Richard Hughes521c2e22020-10-01 14:12:41 +01006311 g_debug ("failed to build machine-id: %s", error_local->message);
Richard Hughes45a00732019-11-22 16:57:14 +00006312#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01006313 /* read config file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00006314 if (!fu_config_load (self->config, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01006315 g_prefix_error (error, "Failed to load config: ");
6316 return FALSE;
6317 }
6318
Richard Hughesd1808aa2019-12-10 15:20:30 +00006319 /* read remotes */
Richard Hughesc7d870a2020-12-10 10:05:35 +00006320 if (flags & FU_ENGINE_LOAD_FLAG_REMOTES) {
6321 FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE;
6322 if (flags & FU_ENGINE_LOAD_FLAG_READONLY)
6323 remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS;
6324 if (!fu_remote_list_load (self->remote_list, remote_list_flags, error)) {
6325 g_prefix_error (error, "Failed to load remotes: ");
6326 return FALSE;
6327 }
Richard Hughesd1808aa2019-12-10 15:20:30 +00006328 }
6329
Richard Hughesf3c7d422019-03-12 10:29:42 +00006330 /* create client certificate */
6331 fu_engine_ensure_client_certificate (self);
6332
Richard Hughes31206832020-07-27 15:31:11 +01006333 /* get hardcoded approved and blocked firmware */
6334 checksums_approved = fu_config_get_approved_firmware (self->config);
6335 for (guint i = 0; i < checksums_approved->len; i++) {
6336 const gchar *csum = g_ptr_array_index (checksums_approved, i);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006337 fu_engine_add_approved_firmware (self, csum);
6338 }
Richard Hughes31206832020-07-27 15:31:11 +01006339 checksums_blocked = fu_config_get_blocked_firmware (self->config);
6340 for (guint i = 0; i < checksums_blocked->len; i++) {
6341 const gchar *csum = g_ptr_array_index (checksums_blocked, i);
6342 fu_engine_add_blocked_firmware (self, csum);
6343 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006344
6345 /* get extra firmware saved to the database */
Richard Hughes31206832020-07-27 15:31:11 +01006346 checksums_approved = fu_history_get_approved_firmware (self->history, error);
6347 if (checksums_approved == NULL)
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006348 return FALSE;
Richard Hughes31206832020-07-27 15:31:11 +01006349 for (guint i = 0; i < checksums_approved->len; i++) {
6350 const gchar *csum = g_ptr_array_index (checksums_approved, i);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006351 fu_engine_add_approved_firmware (self, csum);
6352 }
Richard Hughes31206832020-07-27 15:31:11 +01006353 checksums_blocked = fu_history_get_blocked_firmware (self->history, error);
6354 if (checksums_blocked == NULL)
6355 return FALSE;
6356 for (guint i = 0; i < checksums_blocked->len; i++) {
6357 const gchar *csum = g_ptr_array_index (checksums_blocked, i);
6358 fu_engine_add_blocked_firmware (self, csum);
6359 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00006360
Richard Hughes75b965d2018-11-15 13:51:21 +00006361 /* set up idle exit */
6362 if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0)
6363 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (self->config));
6364
Richard Hughes05217332021-03-01 15:51:15 +00006365 /* load SMBIOS and the hwids */
Richard Hughesb333e002021-04-01 10:40:02 +01006366 if (flags & FU_ENGINE_LOAD_FLAG_HWINFO)
6367 fu_context_load_hwinfo (self->ctx, NULL);
Richard Hughes9c028f02017-10-28 21:14:28 +01006368
Richard Hughes9945edb2017-06-19 10:03:55 +01006369 /* load AppStream metadata */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00006370 if (!fu_engine_load_metadata_store (self, flags, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01006371 g_prefix_error (error, "Failed to load AppStream data: ");
6372 return FALSE;
6373 }
6374
Richard Hughes95c98a92019-10-22 16:03:15 +01006375 /* add the "built-in" firmware types */
Richard Hughesb333e002021-04-01 10:40:02 +01006376 fu_context_add_firmware_gtype (self->ctx, "raw", FU_TYPE_FIRMWARE);
6377 fu_context_add_firmware_gtype (self->ctx, "dfu", FU_TYPE_DFU_FIRMWARE);
6378 fu_context_add_firmware_gtype (self->ctx, "dfuse", FU_TYPE_DFUSE_FIRMWARE);
6379 fu_context_add_firmware_gtype (self->ctx, "fmap", FU_TYPE_FMAP_FIRMWARE);
6380 fu_context_add_firmware_gtype (self->ctx, "ihex", FU_TYPE_IHEX_FIRMWARE);
6381 fu_context_add_firmware_gtype (self->ctx, "srec", FU_TYPE_SREC_FIRMWARE);
6382 fu_context_add_firmware_gtype (self->ctx, "smbios", FU_TYPE_SMBIOS);
Richard Hughes95c98a92019-10-22 16:03:15 +01006383
Richard Hughes117f8572021-02-09 20:02:48 +00006384 /* set up backends */
6385 for (guint i = 0; i < self->backends->len; i++) {
6386 FuBackend *backend = g_ptr_array_index (self->backends, i);
Richard Hughes404f0522021-02-16 18:25:32 +00006387 g_autoptr(GError) error_backend = NULL;
6388 if (!fu_backend_setup (backend, &error_backend)) {
6389 g_debug ("failed to setup backend %s: %s",
6390 fu_backend_get_name (backend),
6391 error_backend->message);
6392 continue;
6393 }
6394 backend_cnt++;
6395 }
6396 if (backend_cnt == 0) {
6397 g_set_error_literal (error,
6398 FWUPD_ERROR,
6399 FWUPD_ERROR_NOT_SUPPORTED,
6400 "all backends failed setup");
6401 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01006402 }
6403
Richard Hughes9945edb2017-06-19 10:03:55 +01006404 /* delete old data files */
6405 if (!fu_engine_cleanup_state (error)) {
6406 g_prefix_error (error, "Failed to clean up: ");
6407 return FALSE;
6408 }
6409
6410 /* load plugin */
6411 if (!fu_engine_load_plugins (self, error)) {
6412 g_prefix_error (error, "Failed to load plugins: ");
6413 return FALSE;
6414 }
6415
Richard Hughes05217332021-03-01 15:51:15 +00006416 /* on a read-only filesystem don't care about the cache GUID */
6417 if (flags & FU_ENGINE_LOAD_FLAG_READONLY)
6418 quirks_flags |= FU_QUIRKS_LOAD_FLAG_READONLY_FS;
6419 fu_engine_load_quirks (self, quirks_flags);
6420
Richard Hughes170c0c12017-11-22 11:26:24 +00006421 /* watch the device list for updates and proxy */
6422 g_signal_connect (self->device_list, "added",
6423 G_CALLBACK (fu_engine_device_added_cb),
6424 self);
6425 g_signal_connect (self->device_list, "removed",
6426 G_CALLBACK (fu_engine_device_removed_cb),
6427 self);
6428 g_signal_connect (self->device_list, "changed",
6429 G_CALLBACK (fu_engine_device_changed_cb),
6430 self);
Mario Limonciello6463f312018-08-09 13:28:46 -05006431 fu_engine_set_status (self, FWUPD_STATUS_LOADING);
6432
Richard Hughes9945edb2017-06-19 10:03:55 +01006433 /* add devices */
6434 fu_engine_plugins_setup (self);
Richard Hughesc7d870a2020-12-10 10:05:35 +00006435 if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG)
Richard Hughes4ae7b5e2021-04-01 14:43:43 +01006436 fu_engine_plugins_coldplug (self);
Richard Hughes9945edb2017-06-19 10:03:55 +01006437
Richard Hughes117f8572021-02-09 20:02:48 +00006438 /* coldplug backends */
6439 if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) {
6440 for (guint i = 0; i < self->backends->len; i++) {
6441 FuBackend *backend = g_ptr_array_index (self->backends, i);
Richard Hughes404f0522021-02-16 18:25:32 +00006442 g_autoptr(GError) error_backend = NULL;
6443 if (!fu_backend_get_enabled (backend))
6444 continue;
Richard Hughes117f8572021-02-09 20:02:48 +00006445 g_signal_connect (backend, "device-added",
6446 G_CALLBACK (fu_engine_backend_device_added_cb),
6447 self);
6448 g_signal_connect (backend, "device-removed",
6449 G_CALLBACK (fu_engine_backend_device_removed_cb),
6450 self);
6451 g_signal_connect (backend, "device-changed",
6452 G_CALLBACK (fu_engine_backend_device_changed_cb),
6453 self);
Richard Hughes404f0522021-02-16 18:25:32 +00006454 if (!fu_backend_coldplug (backend, &error_backend)) {
6455 g_warning ("failed to coldplug backend %s: %s",
6456 fu_backend_get_name (backend),
6457 error_backend->message);
6458 continue;
6459 }
Richard Hughes117f8572021-02-09 20:02:48 +00006460 }
6461 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01006462
Richard Hughesf43381f2020-02-24 10:11:31 +00006463 /* set device properties from the metadata */
6464 fu_engine_md_refresh_devices (self);
6465
Richard Hughesa2f8e452018-01-11 10:11:17 +00006466 /* update the db for devices that were updated during the reboot */
6467 if (!fu_engine_update_history_database (self, error))
6468 return FALSE;
6469
Mario Limonciello6463f312018-08-09 13:28:46 -05006470 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello46aaee82019-01-10 12:58:00 -06006471 self->loaded = TRUE;
Mario Limonciello6463f312018-08-09 13:28:46 -05006472
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06006473 /* let clients know engine finished starting up */
6474 fu_engine_emit_changed (self);
6475
Richard Hughes9945edb2017-06-19 10:03:55 +01006476 /* success */
6477 return TRUE;
6478}
6479
6480static void
6481fu_engine_class_init (FuEngineClass *klass)
6482{
6483 GObjectClass *object_class = G_OBJECT_CLASS (klass);
6484 object_class->finalize = fu_engine_finalize;
6485
6486 signals[SIGNAL_CHANGED] =
6487 g_signal_new ("changed",
6488 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6489 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
6490 G_TYPE_NONE, 0);
6491 signals[SIGNAL_DEVICE_ADDED] =
6492 g_signal_new ("device-added",
6493 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6494 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
6495 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
6496 signals[SIGNAL_DEVICE_REMOVED] =
6497 g_signal_new ("device-removed",
6498 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6499 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
6500 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughesa5bb4d82017-06-19 20:22:25 +01006501 signals[SIGNAL_DEVICE_CHANGED] =
6502 g_signal_new ("device-changed",
6503 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6504 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
6505 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughes9945edb2017-06-19 10:03:55 +01006506 signals[SIGNAL_STATUS_CHANGED] =
6507 g_signal_new ("status-changed",
6508 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6509 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
6510 G_TYPE_NONE, 1, G_TYPE_UINT);
6511 signals[SIGNAL_PERCENTAGE_CHANGED] =
6512 g_signal_new ("percentage-changed",
6513 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
6514 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
6515 G_TYPE_NONE, 1, G_TYPE_UINT);
6516}
6517
Richard Hughes0eb123b2018-04-19 12:00:04 +01006518void
6519fu_engine_add_runtime_version (FuEngine *self,
6520 const gchar *component_id,
6521 const gchar *version)
6522{
Richard Hughesb333e002021-04-01 10:40:02 +01006523 fu_context_add_runtime_version (self->ctx, component_id, version);
Richard Hughes0eb123b2018-04-19 12:00:04 +01006524}
6525
Richard Hughesf517c9a2019-03-22 19:50:35 +00006526void
6527fu_engine_add_app_flag (FuEngine *self, FuAppFlags app_flags)
6528{
6529 g_return_if_fail (FU_IS_ENGINE (self));
6530 self->app_flags |= app_flags;
6531}
6532
Richard Hughes9945edb2017-06-19 10:03:55 +01006533static void
Richard Hughes4d76d182021-04-06 13:35:15 +01006534fu_engine_context_battery_changed_cb (FuContext *ctx, GParamSpec *pspec, FuEngine *self)
6535{
6536 g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list);
6537
6538 /* apply policy on any existing devices */
6539 for (guint i = 0; i < devices->len; i++) {
6540 FuDevice *device = g_ptr_array_index (devices, i);
6541 fu_engine_ensure_device_battery_inhibit (self, device);
6542 }
6543}
6544
6545static void
Richard Hughes75b965d2018-11-15 13:51:21 +00006546fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self)
6547{
6548 FwupdStatus status = fu_idle_get_status (idle);
6549 if (status == FWUPD_STATUS_SHUTDOWN)
6550 fu_engine_set_status (self, status);
6551}
6552
6553static void
Richard Hughes9945edb2017-06-19 10:03:55 +01006554fu_engine_init (FuEngine *self)
6555{
Richard Hughesfc1e2672019-11-22 08:53:33 +00006556#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01006557 struct utsname uname_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00006558#endif
Richard Hughesd5aab652020-02-25 12:47:50 +00006559 g_autofree gchar *keyring_path = NULL;
6560 g_autofree gchar *pkidir_fw = NULL;
6561 g_autofree gchar *pkidir_md = NULL;
6562 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01006563 self->percentage = 0;
6564 self->status = FWUPD_STATUS_IDLE;
6565 self->config = fu_config_new ();
Richard Hughesd1808aa2019-12-10 15:20:30 +00006566 self->remote_list = fu_remote_list_new ();
Richard Hughes0a7e7832017-11-22 11:01:13 +00006567 self->device_list = fu_device_list_new ();
Richard Hughesb333e002021-04-01 10:40:02 +01006568 self->ctx = fu_context_new ();
Richard Hughes75b965d2018-11-15 13:51:21 +00006569 self->idle = fu_idle_new ();
Richard Hughesbc3a4e12018-01-06 22:41:47 +00006570 self->history = fu_history_new ();
Richard Hughese7e95452017-11-22 09:05:53 +00006571 self->plugin_list = fu_plugin_list_new ();
Richard Hughesc02ee4d2018-05-22 15:46:03 +01006572 self->plugin_filter = g_ptr_array_new_with_free_func (g_free);
Richard Hughesfb0a9382020-06-23 16:56:28 +01006573 self->host_security_attrs = fu_security_attrs_new ();
Richard Hughes117f8572021-02-09 20:02:48 +00006574 self->backends = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes0eb123b2018-04-19 12:00:04 +01006575 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 +01006576 self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughesb333e002021-04-01 10:40:02 +01006577
6578 fu_context_set_runtime_versions (self->ctx, self->runtime_versions);
6579 fu_context_set_compile_versions (self->ctx, self->compile_versions);
6580
6581 g_signal_connect (self->ctx, "security-changed",
6582 G_CALLBACK (fu_engine_context_security_changed_cb),
6583 self);
Richard Hughes4d76d182021-04-06 13:35:15 +01006584 g_signal_connect (self->ctx, "notify::battery-state",
6585 G_CALLBACK (fu_engine_context_battery_changed_cb),
6586 self);
6587 g_signal_connect (self->ctx, "notify::battery-level",
6588 G_CALLBACK (fu_engine_context_battery_changed_cb),
6589 self);
6590 g_signal_connect (self->ctx, "notify::battery-threshold",
6591 G_CALLBACK (fu_engine_context_battery_changed_cb),
6592 self);
Richard Hughes0eb123b2018-04-19 12:00:04 +01006593
Mario Limonciello263cab92019-08-20 17:16:00 -05006594 g_signal_connect (self->config, "changed",
6595 G_CALLBACK (fu_engine_config_changed_cb),
6596 self);
Richard Hughesd1808aa2019-12-10 15:20:30 +00006597 g_signal_connect (self->remote_list, "changed",
6598 G_CALLBACK (fu_engine_remote_list_changed_cb),
6599 self);
Mario Limonciello263cab92019-08-20 17:16:00 -05006600
Richard Hughes75b965d2018-11-15 13:51:21 +00006601 g_signal_connect (self->idle, "notify::status",
6602 G_CALLBACK (fu_engine_idle_status_notify_cb), self);
6603
Richard Hughes117f8572021-02-09 20:02:48 +00006604 /* backends */
6605#ifdef HAVE_GUSB
6606 g_ptr_array_add (self->backends, fu_usb_backend_new ());
6607#endif
6608#ifdef HAVE_GUDEV
Richard Hughesb333e002021-04-01 10:40:02 +01006609 g_ptr_array_add (self->backends, fu_udev_backend_new (fu_context_get_udev_subsystems (self->ctx)));
Richard Hughes117f8572021-02-09 20:02:48 +00006610#endif
Ricardo Cañuelo536fb852021-02-12 12:48:58 +01006611#ifdef HAVE_BLUEZ
6612 g_ptr_array_add (self->backends, fu_bluez_backend_new ());
6613#endif
Richard Hughes117f8572021-02-09 20:02:48 +00006614
Richard Hughesd5aab652020-02-25 12:47:50 +00006615 /* setup Jcat context */
6616 self->jcat_context = jcat_context_new ();
6617 keyring_path = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
6618 jcat_context_set_keyring_path (self->jcat_context, keyring_path);
6619 sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
6620 pkidir_fw = g_build_filename (sysconfdir, "pki", "fwupd", NULL);
6621 jcat_context_add_public_keys (self->jcat_context, pkidir_fw);
6622 pkidir_md = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
6623 jcat_context_add_public_keys (self->jcat_context, pkidir_md);
6624
Richard Hughes0eb123b2018-04-19 12:00:04 +01006625 /* add some runtime versions of things the daemon depends on */
6626 fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION);
Richard Hughes2d37c3f2018-08-04 15:18:25 +01006627 fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12");
Richard Hughes416ade72018-10-10 20:36:53 +01006628 fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", "0.7.14");
Richard Hughes0eb123b2018-04-19 12:00:04 +01006629#if G_USB_CHECK_VERSION(0,3,1)
6630 fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ());
6631#endif
Richard Hughes34e0dab2018-04-20 16:43:00 +01006632
Richard Hughesf679e5e2019-08-28 15:31:34 +01006633 /* optional kernel version */
Richard Hughesfc1e2672019-11-22 08:53:33 +00006634#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01006635 memset (&uname_tmp, 0, sizeof(uname_tmp));
6636 if (uname (&uname_tmp) >= 0)
6637 fu_engine_add_runtime_version (self, "org.kernel", uname_tmp.release);
Richard Hughesfc1e2672019-11-22 08:53:33 +00006638#endif
Richard Hughesf679e5e2019-08-28 15:31:34 +01006639
Richard Hughes34e0dab2018-04-20 16:43:00 +01006640 g_hash_table_insert (self->compile_versions,
Richard Hughes2d37c3f2018-08-04 15:18:25 +01006641 g_strdup ("com.redhat.fwupdate"),
6642 g_strdup ("12"));
6643 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01006644 g_strdup ("org.freedesktop.fwupd"),
6645 g_strdup (VERSION));
Richard Hughes1a3d3b32021-01-13 18:43:44 +00006646#ifdef HAVE_GUSB
Richard Hughes34e0dab2018-04-20 16:43:00 +01006647 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01006648 g_strdup ("org.freedesktop.gusb"),
6649 g_strdup_printf ("%i.%i.%i",
6650 G_USB_MAJOR_VERSION,
6651 G_USB_MINOR_VERSION,
6652 G_USB_MICRO_VERSION));
Richard Hughes1a3d3b32021-01-13 18:43:44 +00006653#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01006654}
6655
6656static void
6657fu_engine_finalize (GObject *obj)
6658{
6659 FuEngine *self = FU_ENGINE (obj);
6660
Richard Hughes481aa2a2018-09-18 20:51:46 +01006661 if (self->silo != NULL)
6662 g_object_unref (self->silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01006663 if (self->coldplug_id != 0)
6664 g_source_remove (self->coldplug_id);
Richard Hughes3444cf62020-06-22 15:10:28 +01006665 if (self->approved_firmware != NULL)
6666 g_hash_table_unref (self->approved_firmware);
Richard Hughes31206832020-07-27 15:31:11 +01006667 if (self->blocked_firmware != NULL)
6668 g_hash_table_unref (self->blocked_firmware);
Richard Hughes9945edb2017-06-19 10:03:55 +01006669
Richard Hughes0917fb62019-09-21 12:55:37 +01006670 g_free (self->host_machine_id);
Richard Hughes196c6c62020-05-11 19:42:47 +01006671 g_free (self->host_security_id);
Richard Hughesfb0a9382020-06-23 16:56:28 +01006672 g_object_unref (self->host_security_attrs);
Richard Hughes75b965d2018-11-15 13:51:21 +00006673 g_object_unref (self->idle);
Richard Hughes9945edb2017-06-19 10:03:55 +01006674 g_object_unref (self->config);
Richard Hughesd1808aa2019-12-10 15:20:30 +00006675 g_object_unref (self->remote_list);
Richard Hughesb333e002021-04-01 10:40:02 +01006676 g_object_unref (self->ctx);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00006677 g_object_unref (self->history);
Richard Hughes0a7e7832017-11-22 11:01:13 +00006678 g_object_unref (self->device_list);
Richard Hughesd5aab652020-02-25 12:47:50 +00006679 g_object_unref (self->jcat_context);
Richard Hughesc02ee4d2018-05-22 15:46:03 +01006680 g_ptr_array_unref (self->plugin_filter);
Richard Hughes117f8572021-02-09 20:02:48 +00006681 g_ptr_array_unref (self->backends);
Richard Hughes0eb123b2018-04-19 12:00:04 +01006682 g_hash_table_unref (self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01006683 g_hash_table_unref (self->compile_versions);
Mario Limonciello3f9a1c12018-06-06 14:06:40 -05006684 g_object_unref (self->plugin_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01006685
6686 G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj);
6687}
6688
6689FuEngine *
Richard Hughes5b5f6552018-05-18 10:22:39 +01006690fu_engine_new (FuAppFlags app_flags)
Richard Hughes9945edb2017-06-19 10:03:55 +01006691{
6692 FuEngine *self;
6693 self = g_object_new (FU_TYPE_ENGINE, NULL);
Richard Hughes5b5f6552018-05-18 10:22:39 +01006694 self->app_flags = app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +01006695 return FU_ENGINE (self);
6696}