blob: f3f2a5b2767727f040a21f25fc0eaade67724b82 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes2de8f132018-01-17 09:12:02 +00002 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
Richard Hughes9945edb2017-06-19 10:03:55 +01003 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes9945edb2017-06-19 10:03:55 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuEngine"
8
Richard Hughes9945edb2017-06-19 10:03:55 +01009#include "config.h"
10
Richard Hughes9945edb2017-06-19 10:03:55 +010011#include <gio/gio.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000012#ifdef HAVE_GIO_UNIX
Richard Hughes9945edb2017-06-19 10:03:55 +010013#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000014#endif
Richard Hughes9945edb2017-06-19 10:03:55 +010015#include <glib-object.h>
Richard Hughes16658372019-11-22 09:23:59 +000016#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +010017#include <gudev/gudev.h>
Richard Hughes16658372019-11-22 09:23:59 +000018#endif
Richard Hughesbd4d2852017-09-13 14:05:14 +010019#include <string.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000020#ifdef HAVE_UTSNAME_H
Richard Hughes473c5202018-01-11 21:06:16 +000021#include <sys/utsname.h>
Richard Hughesfc1e2672019-11-22 08:53:33 +000022#endif
Richard Hughes019a1bc2019-11-26 10:19:33 +000023#include <errno.h>
Richard Hughes9945edb2017-06-19 10:03:55 +010024
25#include "fwupd-common-private.h"
26#include "fwupd-enums-private.h"
27#include "fwupd-error.h"
28#include "fwupd-release-private.h"
29#include "fwupd-remote-private.h"
30#include "fwupd-resources.h"
31
Richard Hughesc6eb4162020-02-27 10:02:36 +000032#include "fu-cabinet.h"
Richard Hughesdeea2da2017-12-15 15:40:44 +000033#include "fu-common-cab.h"
Richard Hughes943d2c92017-06-21 09:04:39 +010034#include "fu-common.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010035#include "fu-config.h"
36#include "fu-debug.h"
Richard Hughes0a7e7832017-11-22 11:01:13 +000037#include "fu-device-list.h"
Richard Hughes9dde04f2017-09-13 12:07:15 +010038#include "fu-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010039#include "fu-engine.h"
Mario Limonciellod81ea2e2020-01-13 14:11:43 -060040#include "fu-engine-helper.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010041#include "fu-hwids.h"
Richard Hughes75b965d2018-11-15 13:51:21 +000042#include "fu-idle.h"
Richard Hughes7383ce22018-05-08 14:14:35 +010043#include "fu-keyring-utils.h"
Richard Hughesf425d292019-01-18 17:57:39 +000044#include "fu-hash.h"
Richard Hughesbc3a4e12018-01-06 22:41:47 +000045#include "fu-history.h"
Richard Hughes37d09432018-09-09 10:39:45 +010046#include "fu-mutex.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010047#include "fu-plugin.h"
Richard Hughese7e95452017-11-22 09:05:53 +000048#include "fu-plugin-list.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010049#include "fu-plugin-private.h"
50#include "fu-quirks.h"
Richard Hughesd1808aa2019-12-10 15:20:30 +000051#include "fu-remote-list.h"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060052#include "fu-smbios-private.h"
Richard Hughes405baeb2018-09-06 14:23:08 +010053#include "fu-udev-device-private.h"
Richard Hughesc125ec02018-09-05 19:35:17 +010054#include "fu-usb-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010055
Richard Hughes95c98a92019-10-22 16:03:15 +010056#include "fu-dfu-firmware.h"
57#include "fu-ihex-firmware.h"
58#include "fu-srec-firmware.h"
59
Richard Hughes3d005222019-05-17 14:02:41 +010060#ifdef HAVE_SYSTEMD
61#include "fu-systemd.h"
62#endif
63
Richard Hughes9945edb2017-06-19 10:03:55 +010064static void fu_engine_finalize (GObject *obj);
65
66struct _FuEngine
67{
68 GObject parent_instance;
Richard Hughes5b5f6552018-05-18 10:22:39 +010069 FuAppFlags app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +010070 GUsbContext *usb_ctx;
Richard Hughes16658372019-11-22 09:23:59 +000071#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +010072 GUdevClient *gudev_client;
Richard Hughes16658372019-11-22 09:23:59 +000073#endif
Richard Hughes9945edb2017-06-19 10:03:55 +010074 FuConfig *config;
Richard Hughesd1808aa2019-12-10 15:20:30 +000075 FuRemoteList *remote_list;
Richard Hughes0a7e7832017-11-22 11:01:13 +000076 FuDeviceList *device_list;
Richard Hughes9945edb2017-06-19 10:03:55 +010077 FwupdStatus status;
Richard Hughesf425d292019-01-18 17:57:39 +000078 gboolean tainted;
Richard Hughes9945edb2017-06-19 10:03:55 +010079 guint percentage;
Richard Hughesbc3a4e12018-01-06 22:41:47 +000080 FuHistory *history;
Richard Hughes75b965d2018-11-15 13:51:21 +000081 FuIdle *idle;
Richard Hughes481aa2a2018-09-18 20:51:46 +010082 XbSilo *silo;
Richard Hughes9945edb2017-06-19 10:03:55 +010083 gboolean coldplug_running;
84 guint coldplug_id;
85 guint coldplug_delay;
Richard Hughese7e95452017-11-22 09:05:53 +000086 FuPluginList *plugin_list;
Richard Hughesc02ee4d2018-05-22 15:46:03 +010087 GPtrArray *plugin_filter;
Richard Hughes9d6e0e72018-08-24 20:20:17 +010088 GPtrArray *udev_subsystems;
Richard Hughes16658372019-11-22 09:23:59 +000089#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +010090 GHashTable *udev_changed_ids; /* sysfs:FuEngineUdevChangedHelper */
Richard Hughes16658372019-11-22 09:23:59 +000091#endif
Richard Hughes49e5e052017-09-03 12:15:41 +010092 FuSmbios *smbios;
Richard Hughesd7704d42017-08-08 20:29:09 +010093 FuHwids *hwids;
Richard Hughes9c028f02017-10-28 21:14:28 +010094 FuQuirks *quirks;
Richard Hughes0eb123b2018-04-19 12:00:04 +010095 GHashTable *runtime_versions;
Richard Hughes34e0dab2018-04-20 16:43:00 +010096 GHashTable *compile_versions;
Richard Hughes8dd4c1c2019-03-03 18:27:57 +000097 GHashTable *approved_firmware;
Richard Hughes95c98a92019-10-22 16:03:15 +010098 GHashTable *firmware_gtypes;
Richard Hughes0917fb62019-09-21 12:55:37 +010099 gchar *host_machine_id;
Richard Hughesd5aab652020-02-25 12:47:50 +0000100 JcatContext *jcat_context;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600101 gboolean loaded;
Richard Hughes9945edb2017-06-19 10:03:55 +0100102};
103
104enum {
105 SIGNAL_CHANGED,
106 SIGNAL_DEVICE_ADDED,
107 SIGNAL_DEVICE_REMOVED,
108 SIGNAL_DEVICE_CHANGED,
109 SIGNAL_STATUS_CHANGED,
110 SIGNAL_PERCENTAGE_CHANGED,
111 SIGNAL_LAST
112};
113
114static guint signals[SIGNAL_LAST] = { 0 };
115
116G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT)
117
Richard Hughes9945edb2017-06-19 10:03:55 +0100118static void
119fu_engine_emit_changed (FuEngine *self)
120{
121 g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
Richard Hughes75b965d2018-11-15 13:51:21 +0000122 fu_engine_idle_reset (self);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600123
124 /* update the motd */
125 if (self->loaded &&
126 fu_config_get_update_motd (self->config)) {
127 g_autoptr(GError) error_local = NULL;
128 if (!fu_engine_update_motd (self, &error_local))
129 g_debug ("%s", error_local->message);
130 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100131}
132
133static void
Richard Hughes170c0c12017-11-22 11:26:24 +0000134fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
135{
136 g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
137}
138
Richard Hughes95c98a92019-10-22 16:03:15 +0100139GPtrArray *
140fu_engine_get_firmware_gtype_ids (FuEngine *self)
141{
142 GPtrArray *firmware_gtypes = g_ptr_array_new_with_free_func (g_free);
143 g_autoptr(GList) keys = g_hash_table_get_keys (self->firmware_gtypes);
144 for (GList *l = keys; l != NULL; l = l->next) {
145 const gchar *id = l->data;
146 g_ptr_array_add (firmware_gtypes, g_strdup (id));
147 }
148 return firmware_gtypes;
149}
150
151GType
152fu_engine_get_firmware_gtype_by_id (FuEngine *self, const gchar *id)
153{
154 return GPOINTER_TO_SIZE (g_hash_table_lookup (self->firmware_gtypes, id));
155}
156
157static void
158fu_engine_add_firmware_gtype (FuEngine *self, const gchar *id, GType gtype)
159{
160 g_hash_table_insert (self->firmware_gtypes, g_strdup (id), GSIZE_TO_POINTER (gtype));
161}
162
Richard Hughes9945edb2017-06-19 10:03:55 +0100163/**
164 * fu_engine_get_status:
165 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +0100166 *
167 * Gets the current engine status.
168 *
169 * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING
170 **/
171FwupdStatus
172fu_engine_get_status (FuEngine *self)
173{
174 g_return_val_if_fail (FU_IS_ENGINE (self), 0);
175 return self->status;
176}
177
Richard Hughes9945edb2017-06-19 10:03:55 +0100178static void
179fu_engine_set_status (FuEngine *self, FwupdStatus status)
180{
181 if (self->status == status)
182 return;
183 self->status = status;
184
185 /* emit changed */
186 g_debug ("Emitting PropertyChanged('Status'='%s')",
187 fwupd_status_to_string (status));
188 g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status);
189}
190
191static void
192fu_engine_set_percentage (FuEngine *self, guint percentage)
193{
194 if (self->percentage == percentage)
195 return;
196 self->percentage = percentage;
197
198 /* emit changed */
199 g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
200}
201
202static void
Richard Hughes4a036012017-11-30 16:33:24 +0000203fu_engine_progress_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
204{
Richard Hughes2a77afc2019-01-30 11:19:48 +0000205 if (fu_device_get_status (device) == FWUPD_STATUS_UNKNOWN)
206 return;
Richard Hughes4a036012017-11-30 16:33:24 +0000207 fu_engine_set_percentage (self, fu_device_get_progress (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100208 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000209}
210
211static void
212fu_engine_status_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
213{
214 fu_engine_set_status (self, fu_device_get_status (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100215 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000216}
217
218static void
Richard Hughesfbcebe02017-12-11 16:37:19 +0000219fu_engine_watch_device (FuEngine *self, FuDevice *device)
Richard Hughes4a036012017-11-30 16:33:24 +0000220{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100221 g_autoptr(FuDevice) device_old = fu_device_list_get_old (self->device_list, device);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000222 if (device_old != NULL) {
223 g_signal_handlers_disconnect_by_func (device_old,
224 fu_engine_progress_notify_cb,
225 self);
226 g_signal_handlers_disconnect_by_func (device_old,
227 fu_engine_status_notify_cb,
228 self);
229 }
Richard Hughes4a036012017-11-30 16:33:24 +0000230 g_signal_connect (device, "notify::progress",
231 G_CALLBACK (fu_engine_progress_notify_cb), self);
232 g_signal_connect (device, "notify::status",
233 G_CALLBACK (fu_engine_status_notify_cb), self);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000234}
235
236static void
237fu_engine_device_added_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
238{
239 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000240 g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device);
241}
242
243static void
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500244fu_engine_device_runner_device_removed (FuEngine *self, FuDevice *device)
245{
246 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
247 for (guint j = 0; j < plugins->len; j++) {
248 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
249 fu_plugin_runner_device_removed (plugin_tmp, device);
250 }
251}
252
253static void
Richard Hughes4a036012017-11-30 16:33:24 +0000254fu_engine_device_removed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
255{
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500256 fu_engine_device_runner_device_removed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000257 g_signal_handlers_disconnect_by_data (device, self);
258 g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device);
259}
260
261static void
262fu_engine_device_changed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
263{
Richard Hughesfbcebe02017-12-11 16:37:19 +0000264 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000265 fu_engine_emit_device_changed (self, device);
266}
267
Richard Hughes481aa2a2018-09-18 20:51:46 +0100268/* convert hex and decimal versions to dotted style */
269static gchar *
Richard Hughes2c40b372019-04-17 13:41:47 +0100270fu_engine_get_release_version (FuEngine *self, FuDevice *dev, XbNode *rel, GError **error)
Richard Hughes611f1a92018-01-11 11:54:28 +0000271{
Mario Limonciellof675c212020-02-25 11:12:35 -0600272 FwupdVersionFormat fmt = fu_device_get_version_format (dev);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100273 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100274 guint64 ver_uint32;
275
276 /* get version */
277 version = xb_node_get_attr (rel, "version");
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100278 if (version == NULL) {
279 g_set_error_literal (error,
280 FWUPD_ERROR,
281 FWUPD_ERROR_NOT_SUPPORTED,
282 "version unset");
Richard Hughes611f1a92018-01-11 11:54:28 +0000283 return NULL;
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100284 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100285
286 /* already dotted notation */
287 if (g_strstr_len (version, -1, ".") != NULL)
288 return g_strdup (version);
289
Richard Hughes481aa2a2018-09-18 20:51:46 +0100290 /* don't touch my version! */
Richard Hughesc84b36c2019-04-27 09:24:42 +0100291 if (fmt == FWUPD_VERSION_FORMAT_PLAIN)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100292 return g_strdup (version);
293
294 /* parse as integer */
295 ver_uint32 = fu_common_strtoull (version);
Mario Limonciellof675c212020-02-25 11:12:35 -0600296 if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN ||
297 ver_uint32 == 0 || ver_uint32 > G_MAXUINT32)
Richard Hughes481aa2a2018-09-18 20:51:46 +0100298 return g_strdup (version);
299
300 /* convert to dotted decimal */
301 return fu_common_version_from_uint32 ((guint32) ver_uint32, fmt);
Richard Hughes611f1a92018-01-11 11:54:28 +0000302}
303
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100304static gboolean
Richard Hughesbd4d2852017-09-13 14:05:14 +0100305fu_engine_set_release_from_appstream (FuEngine *self,
Richard Hughes2c40b372019-04-17 13:41:47 +0100306 FuDevice *dev,
Richard Hughesbd4d2852017-09-13 14:05:14 +0100307 FwupdRelease *rel,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100308 XbNode *component,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100309 XbNode *release,
310 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +0100311{
Richard Hughesc6afb512017-08-22 10:22:20 +0100312 FwupdRemote *remote = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100313 const gchar *tmp;
Richard Hughes611f1a92018-01-11 11:54:28 +0000314 const gchar *remote_id;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100315 guint64 tmp64;
316 g_autofree gchar *version_rel = NULL;
Richard Hughes02ac92c2019-03-29 17:56:46 +0000317 g_autoptr(GPtrArray) cats = NULL;
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100318 g_autoptr(GPtrArray) issues = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100319 g_autoptr(XbNode) description = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100320
Richard Hughes481aa2a2018-09-18 20:51:46 +0100321 /* set from the component */
322 tmp = xb_node_query_text (component, "id", NULL);
323 if (tmp != NULL)
324 fwupd_release_set_appstream_id (rel, tmp);
325 tmp = xb_node_query_text (component, "url[@type='homepage']", NULL);
326 if (tmp != NULL)
327 fwupd_release_set_homepage (rel, tmp);
328 tmp = xb_node_query_text (component, "project_license", NULL);
329 if (tmp != NULL)
330 fwupd_release_set_license (rel, tmp);
331 tmp = xb_node_query_text (component, "name", NULL);
332 if (tmp != NULL)
333 fwupd_release_set_name (rel, tmp);
334 tmp = xb_node_query_text (component, "summary", NULL);
335 if (tmp != NULL)
336 fwupd_release_set_summary (rel, tmp);
337 tmp = xb_node_query_text (component, "developer_name", NULL);
338 if (tmp != NULL)
339 fwupd_release_set_vendor (rel, tmp);
340
Mario Limonciello92ff9c72020-03-09 16:54:56 -0500341 /* refresh the device and release to the new version format too */
342 fu_engine_md_refresh_device_from_component (self, dev, component);
343
Richard Hughes481aa2a2018-09-18 20:51:46 +0100344 /* the version is fixed up at runtime */
Richard Hughes2c40b372019-04-17 13:41:47 +0100345 version_rel = fu_engine_get_release_version (self, dev, release, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100346 if (version_rel == NULL)
347 return FALSE;
348 fwupd_release_set_version (rel, version_rel);
Richard Hughes74fa2ca2018-01-10 21:33:39 +0000349
Richard Hughesc6afb512017-08-22 10:22:20 +0100350 /* find the remote */
Richard Hughes33171fd2018-11-09 13:29:11 +0000351 remote_id = xb_node_query_text (component, "../custom/value[@key='fwupd::RemoteId']", NULL);
Richard Hughes611f1a92018-01-11 11:54:28 +0000352 if (remote_id != NULL) {
353 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughesd1808aa2019-12-10 15:20:30 +0000354 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100355 if (remote == NULL)
356 g_warning ("no remote found for release %s", version_rel);
Richard Hughesbd4d2852017-09-13 14:05:14 +0100357 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100358 description = xb_node_query_first (release, "description", NULL);
359 if (description != NULL) {
360 g_autofree gchar *xml = NULL;
361 xml = xb_node_export (description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL);
362 if (xml != NULL)
363 fwupd_release_set_description (rel, xml);
364 }
365 tmp = xb_node_query_text (release, "location", NULL);
Richard Hughesc6afb512017-08-22 10:22:20 +0100366 if (tmp != NULL) {
367 g_autofree gchar *uri = NULL;
368 if (remote != NULL)
369 uri = fwupd_remote_build_firmware_uri (remote, tmp, NULL);
370 if (uri == NULL)
371 uri = g_strdup (tmp);
372 fwupd_release_set_uri (rel, uri);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600373 } else if (remote != NULL &&
374 fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
375 g_autofree gchar *uri = NULL;
376 tmp = xb_node_query_text (component, "../custom/value[@key='fwupd::FilenameCache']", NULL);
377 if (tmp != NULL) {
378 uri = g_strdup_printf ("file://%s", tmp);
379 fwupd_release_set_uri (rel, uri);
380 }
Richard Hughesc6afb512017-08-22 10:22:20 +0100381 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100382 tmp = xb_node_query_text (release, "checksum[@target='content']", NULL);
383 if (tmp != NULL)
384 fwupd_release_set_filename (rel, tmp);
Richard Hughes71858282019-01-26 12:15:55 +0000385 tmp = xb_node_query_text (release, "url[@type='details']", NULL);
386 if (tmp != NULL)
387 fwupd_release_set_details_url (rel, tmp);
388 tmp = xb_node_query_text (release, "url[@type='source']", NULL);
389 if (tmp != NULL)
390 fwupd_release_set_source_url (rel, tmp);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100391 tmp = xb_node_query_text (release, "checksum[@target='container']", NULL);
392 if (tmp != NULL)
393 fwupd_release_add_checksum (rel, tmp);
394 tmp64 = xb_node_query_text_as_uint (release, "size[@type='installed']", NULL);
395 if (tmp64 != G_MAXUINT64) {
396 fwupd_release_set_size (rel, tmp64);
397 } else {
398 GBytes *sz = xb_node_get_data (release, "fwupd::ReleaseSize");
399 if (sz != NULL) {
400 const guint64 *sizeptr = g_bytes_get_data (sz, NULL);
401 fwupd_release_set_size (rel, *sizeptr);
402 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100403 }
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000404 tmp64 = xb_node_get_attr_as_uint (release, "install_duration");
Richard Hughes319dbcb2018-12-18 17:59:05 +0000405 if (tmp64 != G_MAXUINT64)
Richard Hughes6e0c8f82018-11-11 21:46:41 +0000406 fwupd_release_set_install_duration (rel, tmp64);
Richard Hughes02ac92c2019-03-29 17:56:46 +0000407 cats = xb_node_query (component, "categories/category", 0, NULL);
408 if (cats != NULL) {
409 for (guint i = 0; i < cats->len; i++) {
410 XbNode *n = g_ptr_array_index (cats, i);
411 fwupd_release_add_category (rel, xb_node_get_text (n));
412 }
413 }
Richard Hughes0ad59cb2019-09-16 10:33:05 +0100414 issues = xb_node_query (component, "issues/issue", 0, NULL);
415 if (issues != NULL) {
416 for (guint i = 0; i < issues->len; i++) {
417 XbNode *n = g_ptr_array_index (issues, i);
418 fwupd_release_add_issue (rel, xb_node_get_text (n));
419 }
420 }
Richard Hughes868db4e2019-09-26 14:56:57 +0100421 tmp = xb_node_query_text (component, "screenshots/screenshot/caption", NULL);
422 if (tmp != NULL)
423 fwupd_release_set_detach_caption (rel, tmp);
424 tmp = xb_node_query_text (component, "screenshots/screenshot/image", NULL);
425 if (tmp != NULL)
426 fwupd_release_set_detach_image (rel, tmp);
Richard Hughesadeb2b12018-12-14 11:56:41 +0000427 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateProtocol']", NULL);
428 if (tmp != NULL)
429 fwupd_release_set_protocol (rel, tmp);
Mario Limonciello32241f42019-01-24 10:12:41 -0600430 tmp = xb_node_query_text (component, "custom/value[@key='LVFS::UpdateMessage']", NULL);
431 if (tmp != NULL)
432 fwupd_release_set_update_message (rel, tmp);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +0100433 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100434}
435
Richard Hughes481aa2a2018-09-18 20:51:46 +0100436/* finds the remote-id for the first firmware in the silo that matches this
Richard Hughes534255c2018-01-28 19:51:56 +0000437 * container checksum */
438static const gchar *
439fu_engine_get_remote_id_for_checksum (FuEngine *self, const gchar *csum)
440{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100441 g_autofree gchar *xpath = NULL;
442 g_autoptr(XbNode) key = NULL;
Richard Hughes33171fd2018-11-09 13:29:11 +0000443 xpath = g_strdup_printf ("components/component/releases/release/"
444 "checksum[@target='container'][text()='%s']/../../"
445 "../../custom/value[@key='fwupd::RemoteId']", csum);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100446 key = xb_silo_query_first (self->silo, xpath, NULL);
447 if (key == NULL)
448 return NULL;
449 return xb_node_get_text (key);
Richard Hughes534255c2018-01-28 19:51:56 +0000450}
451
Richard Hughes9945edb2017-06-19 10:03:55 +0100452/**
453 * fu_engine_unlock:
454 * @self: A #FuEngine
455 * @device_id: A device ID
456 * @error: A #GError, or %NULL
457 *
458 * Unlocks a device.
459 *
460 * Returns: %TRUE for success
461 **/
462gboolean
463fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error)
464{
Richard Hughes34834102017-11-21 21:55:00 +0000465 FuPlugin *plugin;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100466 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100467
468 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
469 g_return_val_if_fail (device_id != NULL, FALSE);
470 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
471
472 /* check the device exists */
Richard Hughes40127542018-01-12 20:25:55 +0000473 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000474 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100475 return FALSE;
476
Richard Hughes34834102017-11-21 21:55:00 +0000477 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000478 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000479 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000480 error);
Richard Hughes34834102017-11-21 21:55:00 +0000481 if (plugin == NULL)
482 return FALSE;
483
Richard Hughes9945edb2017-06-19 10:03:55 +0100484 /* run the correct plugin that added this */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000485 if (!fu_plugin_runner_unlock (plugin, device, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100486 return FALSE;
487
488 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000489 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100490 fu_engine_emit_changed (self);
491 return TRUE;
492}
493
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100494gboolean
495fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, GError **error)
496{
497 const gchar *keys[] = {
498 "ArchiveSizeMax",
499 "BlacklistDevices",
500 "BlacklistPlugins",
501 "IdleTimeout",
Mario Limonciello38027e62019-04-17 15:16:07 +0100502 "VerboseDomains",
Mario Limonciellod81ea2e2020-01-13 14:11:43 -0600503 "UpdateMotd",
Mario Limonciello4fa95a72020-03-28 10:50:57 -0500504 "EnumerateAllDevices",
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100505 NULL };
506
507 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
508 g_return_val_if_fail (key != NULL, FALSE);
509 g_return_val_if_fail (value != NULL, FALSE);
510 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
511
512 /* check keys are valid */
513 if (!g_strv_contains (keys, key)) {
514 g_set_error (error,
515 FWUPD_ERROR,
516 FWUPD_ERROR_NOT_FOUND,
517 "key %s not supported", key);
518 return FALSE;
519 }
520
521 /* modify, effective next reboot */
Richard Hughesd1808aa2019-12-10 15:20:30 +0000522 return fu_config_set_key_value (self->config, key, value, error);
Mario Limonciellobfcf75b2019-04-17 15:05:39 +0100523}
524
Richard Hughes9945edb2017-06-19 10:03:55 +0100525/**
Richard Hughesa6bd5582017-09-07 14:32:22 +0100526 * fu_engine_modify_remote:
527 * @self: A #FuEngine
528 * @remote_id: A remote ID
Richard Hughes4eada342017-10-03 21:20:32 +0100529 * @key: the key, e.g. `Enabled`
530 * @value: the key, e.g. `true`
Richard Hughesa6bd5582017-09-07 14:32:22 +0100531 * @error: A #GError, or %NULL
532 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100533 * Updates the verification silo entry for a specific device.
Richard Hughesa6bd5582017-09-07 14:32:22 +0100534 *
535 * Returns: %TRUE for success
536 **/
537gboolean
538fu_engine_modify_remote (FuEngine *self,
539 const gchar *remote_id,
540 const gchar *key,
541 const gchar *value,
542 GError **error)
543{
Mario Limonciellod7026062019-10-06 22:25:07 -0500544 const gchar *keys[] = { "Enabled", "MetadataURI", "FirmwareBaseURI", "ReportURI", "AutomaticReports", NULL };
Richard Hughesa6bd5582017-09-07 14:32:22 +0100545
546 /* check keys are valid */
547 if (!g_strv_contains (keys, key)) {
548 g_set_error (error,
549 FWUPD_ERROR,
550 FWUPD_ERROR_NOT_FOUND,
551 "key %s not supported", key);
552 return FALSE;
553 }
Richard Hughesd1808aa2019-12-10 15:20:30 +0000554 return fu_remote_list_set_key_value (self->remote_list, remote_id, key, value, error);
Richard Hughesa6bd5582017-09-07 14:32:22 +0100555}
556
557/**
Richard Hughes6b222952018-01-11 10:20:48 +0000558 * fu_engine_modify_device:
559 * @self: A #FuEngine
560 * @device_id: A device ID
561 * @key: the key, e.g. `Flags`
562 * @value: the key, e.g. `reported`
563 * @error: A #GError, or %NULL
564 *
565 * Sets the reported flag for a specific device. This ensures that other
566 * front-end clients for fwupd do not report the same event.
567 *
568 * Returns: %TRUE for success
569 **/
570gboolean
571fu_engine_modify_device (FuEngine *self,
572 const gchar *device_id,
573 const gchar *key,
574 const gchar *value,
575 GError **error)
576{
577 g_autoptr(FuDevice) device = NULL;
578
579 /* find the correct device */
Richard Hughes0b9d9962018-01-12 16:31:28 +0000580 device = fu_history_get_device_by_id (self->history, device_id, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000581 if (device == NULL)
582 return FALSE;
583
584 /* support adding a subset of the device flags */
585 if (g_strcmp0 (key, "Flags") == 0) {
586 FwupdDeviceFlags flag = fwupd_device_flag_from_string (value);
587 if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) {
588 g_set_error (error,
589 FWUPD_ERROR,
590 FWUPD_ERROR_NOT_SUPPORTED,
591 "key %s not a valid flag", key);
592 return FALSE;
593 }
Richard Hughesad54f652018-01-30 17:22:37 +0000594 if (flag != FWUPD_DEVICE_FLAG_REPORTED &&
595 flag != FWUPD_DEVICE_FLAG_NOTIFIED) {
Richard Hughes6b222952018-01-11 10:20:48 +0000596 g_set_error (error,
597 FWUPD_ERROR,
598 FWUPD_ERROR_NOT_SUPPORTED,
599 "flag %s cannot be set from client", key);
600 return FALSE;
601 }
Richard Hughesc0cd0232018-01-31 15:02:00 +0000602 fu_device_add_flag (device, flag);
Richard Hughes0bbef292019-11-01 12:15:15 +0000603 return fu_history_modify_device (self->history, device, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000604 }
605
606 /* others invalid */
607 g_set_error (error,
608 FWUPD_ERROR,
609 FWUPD_ERROR_NOT_SUPPORTED,
610 "key %s not supported", key);
611 return FALSE;
612}
613
Richard Hughes481aa2a2018-09-18 20:51:46 +0100614static const gchar *
615fu_engine_checksum_type_to_string (GChecksumType checksum_type)
616{
617 if (checksum_type == G_CHECKSUM_SHA1)
618 return "sha1";
619 if (checksum_type == G_CHECKSUM_SHA256)
620 return "sha256";
621 if (checksum_type == G_CHECKSUM_SHA512)
622 return "sha512";
623 return "sha1";
624}
625
Richard Hughes6b222952018-01-11 10:20:48 +0000626/**
Richard Hughes9945edb2017-06-19 10:03:55 +0100627 * fu_engine_verify_update:
628 * @self: A #FuEngine
629 * @device_id: A device ID
630 * @error: A #GError, or %NULL
631 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100632 * Updates the verification silo entry for a specific device.
Richard Hughes9945edb2017-06-19 10:03:55 +0100633 *
634 * Returns: %TRUE for success
635 **/
636gboolean
637fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error)
638{
Richard Hughes34834102017-11-21 21:55:00 +0000639 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100640 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100641 GPtrArray *guids;
642 g_autofree gchar *fn = NULL;
643 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100644 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100645 g_autoptr(GFile) file = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100646 g_autoptr(XbBuilder) builder = xb_builder_new ();
647 g_autoptr(XbBuilderNode) component = NULL;
648 g_autoptr(XbBuilderNode) provides = NULL;
649 g_autoptr(XbBuilderNode) release = NULL;
650 g_autoptr(XbBuilderNode) releases = NULL;
651 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100652
653 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
654 g_return_val_if_fail (device_id != NULL, FALSE);
655 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
656
657 /* check the devices still exists */
Richard Hughes40127542018-01-12 20:25:55 +0000658 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000659 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100660 return FALSE;
661
Richard Hughes34834102017-11-21 21:55:00 +0000662 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000663 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000664 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000665 error);
Richard Hughes34834102017-11-21 21:55:00 +0000666 if (plugin == NULL)
667 return FALSE;
668
Richard Hughes9945edb2017-06-19 10:03:55 +0100669 /* get the checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000670 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100671 if (checksums->len == 0) {
Richard Hughes0a7e7832017-11-22 11:01:13 +0000672 if (!fu_plugin_runner_verify (plugin, device,
Richard Hughes9945edb2017-06-19 10:03:55 +0100673 FU_PLUGIN_VERIFY_FLAG_NONE,
674 error))
675 return FALSE;
Richard Hughes0a7e7832017-11-22 11:01:13 +0000676 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100677 }
678
679 /* we got nothing */
680 if (checksums->len == 0) {
681 g_set_error_literal (error,
682 FWUPD_ERROR,
683 FWUPD_ERROR_NOT_SUPPORTED,
684 "device verification not supported");
685 return FALSE;
686 }
687
Richard Hughes481aa2a2018-09-18 20:51:46 +0100688 /* build XML */
689 component = xb_builder_node_insert (NULL, "component",
690 "type", "firmware",
691 NULL);
692 provides = xb_builder_node_insert (component, "provides", NULL);
693 guids = fu_device_get_guids (device);
694 for (guint i = 0; i < guids->len; i++) {
695 const gchar *guid = g_ptr_array_index (guids, i);
696 g_autoptr(XbBuilderNode) provide = NULL;
697 provide = xb_builder_node_insert (provides, "firmware",
698 "type", "flashed",
699 NULL);
700 xb_builder_node_set_text (provide, guid, -1);
701 }
702 releases = xb_builder_node_insert (component, "releases", NULL);
703 release = xb_builder_node_insert (releases, "release",
704 "version", fu_device_get_version (device),
705 NULL);
706 for (guint i = 0; i < checksums->len; i++) {
707 const gchar *checksum = g_ptr_array_index (checksums, i);
708 GChecksumType kind = fwupd_checksum_guess_kind (checksum);
709 g_autoptr(XbBuilderNode) csum = NULL;
Richard Hughesaaa60c62018-11-07 11:05:50 +0000710 csum = xb_builder_node_insert (release, "checksum",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100711 "type", fu_engine_checksum_type_to_string (kind),
712 "target", "content",
713 NULL);
714 xb_builder_node_set_text (csum, checksum, -1);
715 }
716 xb_builder_import_node (builder, component);
717
718 /* save silo */
719 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
720 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
Richard Hughes11f612c2019-03-15 16:27:18 +0000721 if (!fu_common_mkdir_parent (fn, error))
722 return FALSE;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100723 file = g_file_new_for_path (fn);
724 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
725 if (silo == NULL)
726 return FALSE;
727 if (!xb_silo_export_file (silo, file,
728 XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE,
729 NULL, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100730 return FALSE;
731
Richard Hughes481aa2a2018-09-18 20:51:46 +0100732 /* success */
733 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100734}
735
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600736XbNode *
737fu_engine_get_component_by_guids (FuEngine *self, FuDevice *device)
Richard Hughes9945edb2017-06-19 10:03:55 +0100738{
739 GPtrArray *guids = fu_device_get_guids (device);
Richard Hughesab1bc892018-11-15 15:04:35 +0000740 g_autoptr(GString) xpath = g_string_new (NULL);
741 g_autoptr(XbNode) component = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100742 for (guint i = 0; i < guids->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100743 const gchar *guid = g_ptr_array_index (guids, i);
Richard Hughesab1bc892018-11-15 15:04:35 +0000744 xb_string_append_union (xpath,
745 "components/component/"
746 "provides/firmware[@type='flashed'][text()='%s']/"
747 "../..", guid);
Richard Hughes9945edb2017-06-19 10:03:55 +0100748 }
Mario Limonciello4f24d0b2019-01-26 01:19:59 -0600749 component = xb_silo_query_first (self->silo, xpath->str, NULL);
Richard Hughesab1bc892018-11-15 15:04:35 +0000750 if (component != NULL)
751 return g_steal_pointer (&component);
Richard Hughes9945edb2017-06-19 10:03:55 +0100752 return NULL;
753}
754
755/**
756 * fu_engine_verify:
757 * @self: A #FuEngine
758 * @device_id: A device ID
759 * @error: A #GError, or %NULL
760 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100761 * Verifies a device firmware checksum using the verification silo entry.
Richard Hughes9945edb2017-06-19 10:03:55 +0100762 *
763 * Returns: %TRUE for success
764 **/
765gboolean
766fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error)
767{
Richard Hughes34834102017-11-21 21:55:00 +0000768 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100769 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100770 const gchar *version;
771 g_autofree gchar *fn = NULL;
772 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100773 g_autoptr(FuDevice) device = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100774 g_autoptr(GFile) file = NULL;
Richard Hughes45bbfc92018-12-12 10:57:08 +0000775 g_autoptr(GString) xpath_csum = g_string_new (NULL);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100776 g_autoptr(XbNode) csum = NULL;
777 g_autoptr(XbNode) release = NULL;
778 g_autoptr(XbSilo) silo = xb_silo_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +0100779
780 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
781 g_return_val_if_fail (device_id != NULL, FALSE);
782 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
783
784 /* check the id exists */
Richard Hughes40127542018-01-12 20:25:55 +0000785 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000786 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100787 return FALSE;
788
Richard Hughes34834102017-11-21 21:55:00 +0000789 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000790 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000791 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000792 error);
Richard Hughes34834102017-11-21 21:55:00 +0000793 if (plugin == NULL)
794 return FALSE;
795
Mario Limonciello8fa0b382019-10-14 07:36:04 -0500796 /* update the device firmware hashes if possible */
797 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) {
798 if (!fu_plugin_runner_verify (plugin, device,
799 FU_PLUGIN_VERIFY_FLAG_NONE, error))
800 return FALSE;
801 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100802
803 /* find component in metadata */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100804 version = fu_device_get_version (device);
805 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
806 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
807 file = g_file_new_for_path (fn);
808 if (g_file_query_exists (file, NULL)) {
809 g_autofree gchar *xpath = NULL;
810 g_autoptr(XbBuilder) builder = xb_builder_new ();
811 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
812 if (!xb_builder_source_load_file (source, file,
813 XB_BUILDER_SOURCE_FLAG_NONE,
814 NULL, error))
815 return FALSE;
816 xb_builder_import_source (builder, source);
817 silo = xb_builder_compile (builder,
818 XB_BUILDER_COMPILE_FLAG_NONE,
819 NULL, error);
820 if (silo == NULL)
821 return FALSE;
822 xpath = g_strdup_printf ("component/releases/release[@version='%s']", version);
823 release = xb_silo_query_first (silo, xpath, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +0100824 }
825
Richard Hughes481aa2a2018-09-18 20:51:46 +0100826 /* try again with the system metadata */
Richard Hughes9945edb2017-06-19 10:03:55 +0100827 if (release == NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100828 GPtrArray *guids = fu_device_get_guids (device);
Mario Limonciello91d36092019-10-14 08:44:39 -0500829 FwupdVersionFormat fmt = fu_device_get_version_format (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100830 for (guint i = 0; i < guids->len; i++) {
831 const gchar *guid = g_ptr_array_index (guids, i);
832 g_autofree gchar *xpath2 = NULL;
Mario Limonciello91d36092019-10-14 08:44:39 -0500833 g_autoptr(GPtrArray) releases = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100834 xpath2 = g_strdup_printf ("components/component/"
835 "provides/firmware[@type='flashed'][text()='%s']/"
Mario Limonciello91d36092019-10-14 08:44:39 -0500836 "../../releases/release",
837 guid);
838 releases = xb_silo_query (self->silo, xpath2, 0, error);
839 if (releases == NULL)
840 return FALSE;
841 for (guint j = 0; j < releases->len; j++) {
842 XbNode *rel = g_ptr_array_index (releases, j);
843 const gchar *rel_ver = xb_node_get_attr (rel, "version");
844 g_autofree gchar *tmp_ver = fu_common_version_parse_from_format (rel_ver, fmt);
Richard Hughes9a680842020-02-20 11:11:13 +0000845 if (fu_common_vercmp_full (tmp_ver, version, fmt) == 0) {
Mario Limonciello91d36092019-10-14 08:44:39 -0500846 release = g_object_ref (rel);
847 break;
848 }
849 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100850 if (release != NULL)
851 break;
Richard Hughes9945edb2017-06-19 10:03:55 +0100852 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100853 }
854 if (release == NULL) {
855 g_set_error (error,
856 FWUPD_ERROR,
857 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello8fa0b382019-10-14 07:36:04 -0500858 "No release found for version %s", version);
Richard Hughes9945edb2017-06-19 10:03:55 +0100859 return FALSE;
860 }
861
Richard Hughes9945edb2017-06-19 10:03:55 +0100862 /* get the matching checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000863 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100864 if (checksums->len == 0) {
865 g_set_error (error,
866 FWUPD_ERROR,
867 FWUPD_ERROR_NOT_FOUND,
868 "No device checksums for %s", version);
869 return FALSE;
870 }
Richard Hughes45bbfc92018-12-12 10:57:08 +0000871
872 /* do any of the checksums in the release match any in the device */
Richard Hughes9945edb2017-06-19 10:03:55 +0100873 for (guint j = 0; j < checksums->len; j++) {
874 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
Richard Hughes45bbfc92018-12-12 10:57:08 +0000875 xb_string_append_union (xpath_csum,
876 "checksum[@target='device'][text()='%s']",
877 hash_tmp);
878 xb_string_append_union (xpath_csum,
879 "checksum[@target='content'][text()='%s']",
880 hash_tmp);
881 }
882 csum = xb_node_query_first (release, xpath_csum->str, NULL);
883 if (csum == NULL) {
884 g_autoptr(GString) checksums_device = g_string_new (NULL);
885 g_autoptr(GString) checksums_metadata = g_string_new (NULL);
886 g_autoptr(GPtrArray) csums = NULL;
887 g_autoptr(GString) xpath = g_string_new (NULL);
888
889 /* get all checksums to display a useful error */
890 xb_string_append_union (xpath, "checksum[@target='device']");
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500891 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE))
892 xb_string_append_union (xpath, "checksum[@target='content']");
Richard Hughes45bbfc92018-12-12 10:57:08 +0000893 csums = xb_node_query (release, xpath->str, 0, NULL);
894 if (csums == NULL) {
895 g_set_error (error,
896 FWUPD_ERROR,
897 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500898 "No stored checksums for %s",
Richard Hughes45bbfc92018-12-12 10:57:08 +0000899 version);
900 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100901 }
Richard Hughes45bbfc92018-12-12 10:57:08 +0000902 for (guint i = 0; i < csums->len; i++) {
903 XbNode *csum_tmp = g_ptr_array_index (csums, i);
904 xb_string_append_union (checksums_metadata,
905 "%s", xb_node_get_text (csum_tmp));
906 }
907 for (guint i = 0; i < checksums->len; i++) {
908 const gchar *hash_tmp = g_ptr_array_index (checksums, i);
909 xb_string_append_union (checksums_device, "%s", hash_tmp);
910 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100911 g_set_error (error,
912 FWUPD_ERROR,
913 FWUPD_ERROR_NOT_FOUND,
Mario Limonciello2a0e20b2019-10-14 12:09:35 -0500914 "For %s %s expected %s, got %s",
915 fu_device_get_name (device),
Richard Hughes9945edb2017-06-19 10:03:55 +0100916 version,
Richard Hughes45bbfc92018-12-12 10:57:08 +0000917 checksums_metadata->str,
918 checksums_device->str);
Richard Hughes9945edb2017-06-19 10:03:55 +0100919 return FALSE;
920 }
921
922 /* success */
923 return TRUE;
924}
925
Richard Hughes481aa2a2018-09-18 20:51:46 +0100926static gboolean
Richard Hughes9a680842020-02-20 11:11:13 +0000927fu_engine_require_vercmp (XbNode *req,
928 const gchar *version,
929 FwupdVersionFormat fmt,
930 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +0000931{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100932 gboolean ret = FALSE;
933 gint rc = 0;
934 const gchar *tmp = xb_node_get_attr (req, "compare");
935 const gchar *version_req = xb_node_get_attr (req, "version");
936
937 if (g_strcmp0 (tmp, "eq") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000938 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100939 ret = rc == 0;
940 } else if (g_strcmp0 (tmp, "ne") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000941 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100942 ret = rc != 0;
943 } else if (g_strcmp0 (tmp, "lt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000944 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100945 ret = rc < 0;
946 } else if (g_strcmp0 (tmp, "gt") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000947 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100948 ret = rc > 0;
949 } else if (g_strcmp0 (tmp, "le") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000950 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100951 ret = rc <= 0;
952 } else if (g_strcmp0 (tmp, "ge") == 0) {
Richard Hughes9a680842020-02-20 11:11:13 +0000953 rc = fu_common_vercmp_full (version, version_req, fmt);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100954 ret = rc >= 0;
955 } else if (g_strcmp0 (tmp, "glob") == 0) {
Richard Hughes5c508de2019-11-22 09:57:34 +0000956 ret = fu_common_fnmatch (version_req, version);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100957 } else if (g_strcmp0 (tmp, "regex") == 0) {
958 ret = g_regex_match_simple (version_req, version, 0, 0);
959 } else {
960 g_set_error (error,
961 FWUPD_ERROR,
962 FWUPD_ERROR_NOT_SUPPORTED,
963 "failed to compare [%s] and [%s]",
964 version_req,
965 version);
966 return FALSE;
Richard Hughes650dade2017-12-14 14:43:11 +0000967 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100968
969 /* set error */
970 if (!ret) {
971 g_set_error (error,
972 FWUPD_ERROR,
973 FWUPD_ERROR_INTERNAL,
974 "failed predicate [%s %s %s]",
975 version_req, tmp, version);
976 }
977 return ret;
Richard Hughes650dade2017-12-14 14:43:11 +0000978}
979
Richard Hughes9945edb2017-06-19 10:03:55 +0100980static gboolean
Richard Hughesb62c3a42019-04-08 12:13:47 +0100981fu_engine_check_requirement_not_child (FuEngine *self, XbNode *req,
982 FuDevice *device, GError **error)
983{
984 GPtrArray *children = fu_device_get_children (device);
985
986 /* only <firmware> supported */
987 if (g_strcmp0 (xb_node_get_element (req), "firmware") != 0) {
988 g_set_error (error,
989 FWUPD_ERROR,
990 FWUPD_ERROR_NOT_SUPPORTED,
991 "cannot handle not-child %s requirement",
992 xb_node_get_element (req));
993 return FALSE;
994 }
995
996 /* check each child */
997 for (guint i = 0; i < children->len; i++) {
998 FuDevice *child = g_ptr_array_index (children, i);
999 const gchar *version = fu_device_get_version (child);
Richard Hughesec14e4b2019-11-01 12:07:10 +00001000 if (version == NULL) {
1001 g_set_error (error,
1002 FWUPD_ERROR,
1003 FWUPD_ERROR_NOT_SUPPORTED,
1004 "no version provided by %s, child of %s",
1005 fu_device_get_name (child),
1006 fu_device_get_name (device));
1007 return FALSE;
1008 }
Richard Hughes9a680842020-02-20 11:11:13 +00001009 if (fu_engine_require_vercmp (req, version,
1010 fu_device_get_version_format (child),
1011 NULL)) {
Richard Hughesb62c3a42019-04-08 12:13:47 +01001012 g_set_error (error,
1013 FWUPD_ERROR,
1014 FWUPD_ERROR_NOT_SUPPORTED,
1015 "Not compatible with child device version %s",
1016 version);
1017 return FALSE;
1018 }
1019 }
1020 return TRUE;
1021}
1022
1023static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001024fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
Richard Hughes0eb123b2018-04-19 12:00:04 +01001025 FuDevice *device, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001026{
Richard Hughesf7006d22019-11-14 13:42:52 +00001027 guint64 depth;
1028 g_autoptr(FuDevice) device_actual = g_object_ref (device);
Richard Hughes88adcbe2017-11-21 14:33:56 +00001029 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001030
Richard Hughesf7006d22019-11-14 13:42:52 +00001031 /* look at the parent device */
1032 depth = xb_node_get_attr_as_uint (req, "depth");
1033 if (depth != G_MAXUINT64) {
1034 for (guint64 i = 0; i < depth; i++) {
1035 FuDevice *device_tmp = fu_device_get_parent (device_actual);
1036 if (device_actual == NULL) {
1037 g_set_error (error,
1038 FWUPD_ERROR,
1039 FWUPD_ERROR_NOT_SUPPORTED,
1040 "No parent device for %s "
1041 "(%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ")",
1042 fu_device_get_name (device_actual), i, depth);
1043 return FALSE;
1044 }
1045 g_set_object (&device_actual, device_tmp);
1046 }
1047 }
1048
Richard Hughes0eb123b2018-04-19 12:00:04 +01001049 /* old firmware version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001050 if (xb_node_get_text (req) == NULL) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001051 const gchar *version = fu_device_get_version (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001052 if (!fu_engine_require_vercmp (req, version,
1053 fu_device_get_version_format (device_actual),
1054 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001055 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001056 g_set_error (error,
1057 FWUPD_ERROR,
1058 FWUPD_ERROR_INVALID_FILE,
1059 "Not compatible with firmware version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001060 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +00001061 } else {
1062 g_set_error (error,
1063 FWUPD_ERROR,
1064 FWUPD_ERROR_INVALID_FILE,
1065 "Not compatible with firmware version: %s",
1066 error_local->message);
1067 }
1068 return FALSE;
1069 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001070 return TRUE;
1071 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001072
Richard Hughes0eb123b2018-04-19 12:00:04 +01001073 /* bootloader version */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001074 if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001075 const gchar *version = fu_device_get_version_bootloader (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001076 if (!fu_engine_require_vercmp (req, version,
1077 fu_device_get_version_format (device_actual),
1078 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001079 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +00001080 g_set_error (error,
1081 FWUPD_ERROR,
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001082 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes88adcbe2017-11-21 14:33:56 +00001083 "Not compatible with bootloader version %s, requires >= %s",
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001084 version, xb_node_get_attr (req, "version"));
1085
Richard Hughes88adcbe2017-11-21 14:33:56 +00001086 } else {
Mario Limoncielloc23e6122019-12-12 14:30:27 -06001087 g_debug ("Bootloader is not compatible: %s", error_local->message);
1088 g_set_error_literal (error,
1089 FWUPD_ERROR,
1090 FWUPD_ERROR_NOT_SUPPORTED,
1091 "Bootloader is not compatible");
Richard Hughes88adcbe2017-11-21 14:33:56 +00001092 }
1093 return FALSE;
1094 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001095 return TRUE;
1096 }
Richard Hughes88adcbe2017-11-21 14:33:56 +00001097
Richard Hughes0eb123b2018-04-19 12:00:04 +01001098 /* vendor ID */
Richard Hughes12843af2019-12-09 10:30:19 +00001099 if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0 &&
1100 fu_device_get_vendor_id (device_actual) != NULL) {
Richard Hughesf7006d22019-11-14 13:42:52 +00001101 const gchar *version = fu_device_get_vendor_id (device_actual);
Richard Hughes9a680842020-02-20 11:11:13 +00001102 if (!fu_engine_require_vercmp (req, version,
1103 fu_device_get_version_format (device_actual),
1104 &error_local)) {
Richard Hughes38857892019-12-09 10:32:00 +00001105 g_set_error (error,
1106 FWUPD_ERROR,
1107 FWUPD_ERROR_INVALID_FILE,
1108 "Not compatible with vendor: %s",
1109 error_local->message);
Richard Hughes88adcbe2017-11-21 14:33:56 +00001110 return FALSE;
1111 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001112 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001113 }
1114
Richard Hughesb62c3a42019-04-08 12:13:47 +01001115 /* child version */
1116 if (g_strcmp0 (xb_node_get_text (req), "not-child") == 0)
Richard Hughesf7006d22019-11-14 13:42:52 +00001117 return fu_engine_check_requirement_not_child (self, req, device_actual, error);
Richard Hughesb62c3a42019-04-08 12:13:47 +01001118
Richard Hughes12c84992018-10-02 11:07:28 +01001119 /* another device */
Richard Hughes592baed2019-02-03 18:30:24 +00001120 if (fwupd_guid_is_valid (xb_node_get_text (req))) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001121 const gchar *guid = xb_node_get_text (req);
Richard Hughes12c84992018-10-02 11:07:28 +01001122 const gchar *version;
Richard Hughes12c84992018-10-02 11:07:28 +01001123
1124 /* find if the other device exists */
Richard Hughesf7006d22019-11-14 13:42:52 +00001125 if (depth == G_MAXUINT64) {
1126 g_autoptr(FuDevice) device_tmp = NULL;
1127 device_tmp = fu_device_list_get_by_guid (self->device_list, guid, error);
1128 if (device_tmp == NULL)
1129 return FALSE;
1130 g_set_object (&device_actual, device_tmp);
1131
1132 /* verify the parent device has the GUID */
1133 } else {
1134 if (!fu_device_has_guid (device_actual, guid)) {
1135 g_set_error (error,
1136 FWUPD_ERROR,
1137 FWUPD_ERROR_NOT_SUPPORTED,
1138 "No GUID of %s on parent device %s",
1139 guid, fu_device_get_name (device_actual));
1140 return FALSE;
1141 }
1142 }
Richard Hughes12c84992018-10-02 11:07:28 +01001143
1144 /* get the version of the other device */
Richard Hughesf7006d22019-11-14 13:42:52 +00001145 version = fu_device_get_version (device_actual);
Richard Hughesaaf0ce72019-07-16 08:43:57 +01001146 if (version != NULL &&
Richard Hughesf7006d22019-11-14 13:42:52 +00001147 xb_node_get_attr (req, "compare") != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00001148 !fu_engine_require_vercmp (req, version,
1149 fu_device_get_version_format (device_actual),
1150 &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001151 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes12c84992018-10-02 11:07:28 +01001152 g_set_error (error,
1153 FWUPD_ERROR,
1154 FWUPD_ERROR_INVALID_FILE,
1155 "Not compatible with %s version %s, requires >= %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001156 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001157 version,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001158 xb_node_get_attr (req, "version"));
Richard Hughes12c84992018-10-02 11:07:28 +01001159 } else {
1160 g_set_error (error,
1161 FWUPD_ERROR,
1162 FWUPD_ERROR_INVALID_FILE,
1163 "Not compatible with %s: %s",
Richard Hughesf7006d22019-11-14 13:42:52 +00001164 fu_device_get_name (device_actual),
Richard Hughes12c84992018-10-02 11:07:28 +01001165 error_local->message);
1166 }
1167 return FALSE;
1168 }
1169 return TRUE;
1170
1171 }
1172
Richard Hughes0eb123b2018-04-19 12:00:04 +01001173 /* not supported */
1174 g_set_error (error,
1175 FWUPD_ERROR,
1176 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001177 "cannot handle firmware requirement '%s'",
1178 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001179 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001180}
Richard Hughes9945edb2017-06-19 10:03:55 +01001181
1182static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001183fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error)
Richard Hughes2ec78d62017-11-03 21:48:54 +00001184{
Richard Hughes0eb123b2018-04-19 12:00:04 +01001185 g_autoptr(GError) error_local = NULL;
1186 const gchar *version = g_hash_table_lookup (self->runtime_versions,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001187 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001188 if (version == NULL) {
1189 g_set_error (error,
1190 FWUPD_ERROR,
1191 FWUPD_ERROR_NOT_FOUND,
1192 "no version available for %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001193 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001194 return FALSE;
1195 }
Richard Hughes9a680842020-02-20 11:11:13 +00001196 if (!fu_engine_require_vercmp (req, version, FWUPD_VERSION_FORMAT_UNKNOWN, &error_local)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001197 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes2ec78d62017-11-03 21:48:54 +00001198 g_set_error (error,
1199 FWUPD_ERROR,
1200 FWUPD_ERROR_INVALID_FILE,
Richard Hughes0eb123b2018-04-19 12:00:04 +01001201 "Not compatible with %s version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001202 xb_node_get_text (req), version,
1203 xb_node_get_attr (req, "version"));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001204 } else {
1205 g_set_error (error,
1206 FWUPD_ERROR,
1207 FWUPD_ERROR_INVALID_FILE,
1208 "Not compatible with %s version: %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001209 xb_node_get_text (req), error_local->message);
Richard Hughes2ec78d62017-11-03 21:48:54 +00001210 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001211 return FALSE;
Richard Hughes2ec78d62017-11-03 21:48:54 +00001212 }
1213
Richard Hughes0eb123b2018-04-19 12:00:04 +01001214 g_debug ("requirement %s %s %s on %s passed",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001215 xb_node_get_attr (req, "version"),
1216 xb_node_get_attr (req, "compare"),
1217 version, xb_node_get_text (req));
Richard Hughes2ec78d62017-11-03 21:48:54 +00001218 return TRUE;
1219}
Richard Hughes2ec78d62017-11-03 21:48:54 +00001220
1221static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001222fu_engine_check_requirement_hardware (FuEngine *self, XbNode *req, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001223{
Richard Hughes3d71c162018-04-30 16:40:44 +01001224 g_auto(GStrv) hwid_split = NULL;
1225
1226 /* split and treat as OR */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001227 hwid_split = g_strsplit (xb_node_get_text (req), "|", -1);
Richard Hughes3d71c162018-04-30 16:40:44 +01001228 for (guint i = 0; hwid_split[i] != NULL; i++) {
1229 if (fu_hwids_has_guid (self->hwids, hwid_split[i])) {
1230 g_debug ("HWID provided %s", hwid_split[i]);
1231 return TRUE;
1232 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001233 }
Richard Hughes3d71c162018-04-30 16:40:44 +01001234
1235 /* nothing matched */
1236 g_set_error (error,
1237 FWUPD_ERROR,
1238 FWUPD_ERROR_INVALID_FILE,
1239 "no HWIDs matched %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001240 xb_node_get_text (req));
Richard Hughes3d71c162018-04-30 16:40:44 +01001241 return FALSE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001242}
1243
1244static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001245fu_engine_check_requirement (FuEngine *self, XbNode *req, FuDevice *device, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001246{
1247 /* ensure component requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001248 if (g_strcmp0 (xb_node_get_element (req), "id") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001249 return fu_engine_check_requirement_id (self, req, error);
1250
1251 /* ensure firmware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001252 if (g_strcmp0 (xb_node_get_element (req), "firmware") == 0) {
Richard Hughes881f6242018-08-06 11:03:06 +01001253 if (device == NULL)
1254 return TRUE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001255 return fu_engine_check_requirement_firmware (self, req, device, error);
Richard Hughes881f6242018-08-06 11:03:06 +01001256 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001257
1258 /* ensure hardware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001259 if (g_strcmp0 (xb_node_get_element (req), "hardware") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001260 return fu_engine_check_requirement_hardware (self, req, error);
1261
1262 /* not supported */
1263 g_set_error (error,
1264 FWUPD_ERROR,
1265 FWUPD_ERROR_NOT_SUPPORTED,
1266 "cannot handle requirement type %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001267 xb_node_get_element (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001268 return FALSE;
1269}
1270
1271gboolean
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001272fu_engine_check_requirements (FuEngine *self, FuInstallTask *task,
1273 FwupdInstallFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001274{
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001275 FuDevice *device = fu_install_task_get_device (task);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001276 g_autoptr(GError) error_local = NULL;
1277 g_autoptr(GPtrArray) reqs = NULL;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001278
1279 /* all install task checks require a device */
1280 if (device != NULL) {
1281 if (!fu_install_task_check_requirements (task, flags, error))
1282 return FALSE;
1283 }
1284
1285 /* do engine checks */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001286 reqs = xb_node_query (fu_install_task_get_component (task),
1287 "requires/*", 0, &error_local);
1288 if (reqs == NULL) {
1289 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1290 return TRUE;
1291 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
1292 return TRUE;
1293 g_propagate_error (error, g_steal_pointer (&error_local));
1294 return FALSE;
1295 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001296 for (guint i = 0; i < reqs->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001297 XbNode *req = g_ptr_array_index (reqs, i);
Richard Hughes0eb123b2018-04-19 12:00:04 +01001298 if (!fu_engine_check_requirement (self, req, device, error))
1299 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001300 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001301 return TRUE;
1302}
1303
Richard Hughes75b965d2018-11-15 13:51:21 +00001304void
1305fu_engine_idle_reset (FuEngine *self)
1306{
1307 fu_idle_reset (self->idle);
1308}
1309
Richard Hughes9945edb2017-06-19 10:03:55 +01001310static gchar *
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001311fu_engine_get_boot_time (void)
1312{
1313 g_autofree gchar *buf = NULL;
1314 g_auto(GStrv) lines = NULL;
1315 if (!g_file_get_contents ("/proc/stat", &buf, NULL, NULL))
1316 return NULL;
1317 lines = g_strsplit (buf, "\n", -1);
1318 for (guint i = 0; lines[i] != NULL; i++) {
1319 if (g_str_has_prefix (lines[i], "btime "))
1320 return g_strdup (lines[i] + 6);
1321 }
1322 return NULL;
1323}
1324
Richard Hughes473c5202018-01-11 21:06:16 +00001325static GHashTable *
1326fu_engine_get_report_metadata (FuEngine *self)
1327{
1328 GHashTable *hash;
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001329 gchar *btime;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001330#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001331 struct utsname name_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00001332#endif
Richard Hughes34e0dab2018-04-20 16:43:00 +01001333 g_autoptr(GList) compile_keys = g_hash_table_get_keys (self->compile_versions);
1334 g_autoptr(GList) runtime_keys = g_hash_table_get_keys (self->runtime_versions);
Richard Hughes473c5202018-01-11 21:06:16 +00001335
Richard Hughes34e0dab2018-04-20 16:43:00 +01001336 /* convert all the runtime and compile-time versions */
Richard Hughes473c5202018-01-11 21:06:16 +00001337 hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes34e0dab2018-04-20 16:43:00 +01001338 for (GList *l = compile_keys; l != NULL; l = l->next) {
1339 const gchar *id = l->data;
1340 const gchar *version = g_hash_table_lookup (self->compile_versions, id);
1341 g_hash_table_insert (hash,
1342 g_strdup_printf ("CompileVersion(%s)", id),
1343 g_strdup (version));
1344 }
1345 for (GList *l = runtime_keys; l != NULL; l = l->next) {
1346 const gchar *id = l->data;
1347 const gchar *version = g_hash_table_lookup (self->runtime_versions, id);
1348 g_hash_table_insert (hash,
1349 g_strdup_printf ("RuntimeVersion(%s)", id),
1350 g_strdup (version));
1351 }
Richard Hughes473c5202018-01-11 21:06:16 +00001352
1353 /* kernel version is often important for debugging failures */
Richard Hughesfc1e2672019-11-22 08:53:33 +00001354#ifdef HAVE_UTSNAME_H
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001355 memset (&name_tmp, 0, sizeof (struct utsname));
Richard Hughes473c5202018-01-11 21:06:16 +00001356 if (uname (&name_tmp) >= 0) {
1357 g_hash_table_insert (hash,
1358 g_strdup ("CpuArchitecture"),
1359 g_strdup (name_tmp.machine));
Richard Hughes473c5202018-01-11 21:06:16 +00001360 }
Richard Hughesfc1e2672019-11-22 08:53:33 +00001361#endif
Richard Hughes473c5202018-01-11 21:06:16 +00001362
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001363 /* add the kernel boot time so we can detect a reboot */
1364 btime = fu_engine_get_boot_time ();
1365 if (btime != NULL)
1366 g_hash_table_insert (hash, g_strdup ("BootTime"), btime);
1367
Richard Hughes473c5202018-01-11 21:06:16 +00001368 return hash;
1369}
1370
Richard Hughes9945edb2017-06-19 10:03:55 +01001371/**
Richard Hughesdbd8c762018-06-15 20:31:40 +01001372 * fu_engine_composite_prepare:
1373 * @self: A #FuEngine
1374 * @devices: (element-type #FuDevice): devices that will be updated
1375 * @error: A #GError, or %NULL
1376 *
1377 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1378 *
1379 * Any failure in any plugin will abort all of the actions before they are started.
1380 *
1381 * Returns: %TRUE for success
1382 **/
1383gboolean
1384fu_engine_composite_prepare (FuEngine *self, GPtrArray *devices, GError **error)
1385{
1386 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1387 for (guint j = 0; j < plugins->len; j++) {
1388 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1389 if (!fu_plugin_runner_composite_prepare (plugin_tmp, devices, error))
1390 return FALSE;
1391 }
1392 return TRUE;
1393}
1394
1395/**
1396 * fu_engine_composite_cleanup:
1397 * @self: A #FuEngine
1398 * @devices: (element-type #FuDevice): devices that will be updated
1399 * @error: A #GError, or %NULL
1400 *
1401 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1402 *
1403 * Returns: %TRUE for success
1404 **/
1405gboolean
1406fu_engine_composite_cleanup (FuEngine *self, GPtrArray *devices, GError **error)
1407{
1408 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1409 for (guint j = 0; j < plugins->len; j++) {
1410 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1411 if (!fu_plugin_runner_composite_cleanup (plugin_tmp, devices, error))
1412 return FALSE;
1413 }
1414 return TRUE;
1415}
1416
1417/**
1418 * fu_engine_install_tasks:
1419 * @self: A #FuEngine
1420 * @install_tasks: (element-type FuInstallTask): A #FuDevice
1421 * @blob_cab: The #GBytes of the .cab file
1422 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
1423 * @error: A #GError, or %NULL
1424 *
1425 * Installs a specific firmware file on one or more install tasks.
1426 *
1427 * By this point all the requirements and tests should have been done in
1428 * fu_engine_check_requirements() so this should not fail before running
1429 * the plugin loader.
1430 *
1431 * Returns: %TRUE for success
1432 **/
1433gboolean
1434fu_engine_install_tasks (FuEngine *self,
1435 GPtrArray *install_tasks,
1436 GBytes *blob_cab,
1437 FwupdInstallFlags flags,
1438 GError **error)
1439{
Richard Hughes75b965d2018-11-15 13:51:21 +00001440 g_autoptr(FuIdleLocker) locker = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001441 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001442 g_autoptr(GPtrArray) devices_new = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001443
Richard Hughes75b965d2018-11-15 13:51:21 +00001444 /* do not allow auto-shutdown during this time */
1445 locker = fu_idle_locker_new (self->idle, "performing update");
1446 g_assert (locker != NULL);
1447
Richard Hughesdbd8c762018-06-15 20:31:40 +01001448 /* notify the plugins about the composite action */
1449 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1450 for (guint i = 0; i < install_tasks->len; i++) {
1451 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1452 g_ptr_array_add (devices, g_object_ref (fu_install_task_get_device (task)));
1453 }
1454 if (!fu_engine_composite_prepare (self, devices, error)) {
1455 g_prefix_error (error, "failed to prepare composite action: ");
1456 return FALSE;
1457 }
1458
1459 /* all authenticated, so install all the things */
1460 for (guint i = 0; i < install_tasks->len; i++) {
1461 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1462 if (!fu_engine_install (self, task, blob_cab, flags, error)) {
1463 g_autoptr(GError) error_local = NULL;
1464 if (!fu_engine_composite_cleanup (self, devices, &error_local)) {
1465 g_warning ("failed to cleanup failed composite action: %s",
1466 error_local->message);
1467 }
1468 return FALSE;
1469 }
1470 }
1471
Richard Hughes96019e82019-01-30 11:12:57 +00001472 /* set all the device statuses back to unknown */
1473 for (guint i = 0; i < install_tasks->len; i++) {
1474 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1475 FuDevice *device = fu_install_task_get_device (task);
1476 fu_device_set_status (device, FWUPD_STATUS_UNKNOWN);
1477 }
1478
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001479 /* get a new list of devices in case they replugged */
1480 devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1481 for (guint i = 0; i < devices->len; i++) {
1482 FuDevice *device;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001483 g_autoptr(FuDevice) device_new = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001484 g_autoptr(GError) error_local = NULL;
1485 device = g_ptr_array_index (devices, i);
1486 device_new = fu_device_list_get_by_id (self->device_list,
1487 fu_device_get_id (device),
1488 &error_local);
1489 if (device_new == NULL) {
Mario Limonciello769d7682018-09-28 08:43:37 -05001490 g_debug ("failed to find new device: %s",
1491 error_local->message);
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001492 continue;
1493 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001494 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001495 }
1496
Richard Hughesdbd8c762018-06-15 20:31:40 +01001497 /* notify the plugins about the composite action */
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001498 if (!fu_engine_composite_cleanup (self, devices_new, error)) {
Richard Hughesdbd8c762018-06-15 20:31:40 +01001499 g_prefix_error (error, "failed to cleanup composite action: ");
1500 return FALSE;
1501 }
1502
1503 /* success */
1504 return TRUE;
1505}
1506
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001507static FwupdRelease *
1508fu_engine_create_release_metadata (FuEngine *self, FuPlugin *plugin, GError **error)
1509{
Richard Hughesdad35972019-12-06 11:00:25 +00001510 GPtrArray *metadata_sources;
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001511 const gchar *tmp;
1512 g_autoptr(FwupdRelease) release = fwupd_release_new ();
1513 g_autoptr(GHashTable) metadata_hash = NULL;
1514 g_autoptr(GHashTable) os_release = NULL;
1515
1516 /* add release data from os-release */
1517 os_release = fwupd_get_os_release (error);
1518 if (os_release == NULL)
1519 return NULL;
1520
1521 /* build the version metadata */
1522 metadata_hash = fu_engine_get_report_metadata (self);
1523 fwupd_release_add_metadata (release, metadata_hash);
1524 fwupd_release_add_metadata (release, fu_plugin_get_report_metadata (plugin));
1525
Richard Hughesdad35972019-12-06 11:00:25 +00001526 /* allow other plugins to contribute metadata too */
1527 metadata_sources = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_METADATA_SOURCE);
1528 for (guint i = 0; i < metadata_sources->len; i++) {
1529 FuPlugin *plugin_tmp;
1530 const gchar *plugin_name = g_ptr_array_index (metadata_sources, i);
1531 g_autoptr(GError) error_local = NULL;
1532
1533 plugin_tmp = fu_plugin_list_find_by_name (self->plugin_list,
1534 plugin_name,
1535 &error_local);
1536 if (plugin_tmp == NULL) {
1537 g_warning ("could not add metadata for %s: %s",
1538 plugin_name,
1539 error_local->message);
1540 continue;
1541 }
1542 fwupd_release_add_metadata (release,
1543 fu_plugin_get_report_metadata (plugin_tmp));
1544 }
1545
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001546 /* add details from os-release as metadata */
1547 tmp = g_hash_table_lookup (os_release, "ID");
1548 if (tmp != NULL)
1549 fwupd_release_add_metadata_item (release, "DistroId", tmp);
1550 tmp = g_hash_table_lookup (os_release, "VERSION_ID");
1551 if (tmp != NULL)
1552 fwupd_release_add_metadata_item (release, "DistroVersion", tmp);
1553 tmp = g_hash_table_lookup (os_release, "VARIANT_ID");
1554 if (tmp != NULL)
1555 fwupd_release_add_metadata_item (release, "DistroVariant", tmp);
1556 return g_steal_pointer (&release);
1557}
1558
Richard Hughes3d005222019-05-17 14:02:41 +01001559static gboolean
1560fu_engine_is_running_offline (FuEngine *self)
1561{
1562#ifdef HAVE_SYSTEMD
1563 g_autofree gchar *default_target = NULL;
1564 g_autoptr(GError) error = NULL;
1565 default_target = fu_systemd_get_default_target (&error);
1566 if (default_target == NULL) {
1567 g_warning ("failed to get default.target: %s", error->message);
1568 return FALSE;
1569 }
1570 return g_strcmp0 (default_target, "system-update.target") == 0;
1571#else
1572 return FALSE;
1573#endif
1574}
1575
Richard Hughes019a1bc2019-11-26 10:19:33 +00001576static gboolean
1577fu_engine_offline_setup (GError **error)
1578{
Richard Hughes9e5675e2019-11-22 09:35:03 +00001579#ifdef HAVE_GIO_UNIX
Richard Hughes019a1bc2019-11-26 10:19:33 +00001580 gint rc;
1581 g_autofree gchar *filename = NULL;
1582 g_autofree gchar *symlink_target = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1583 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
1584
1585 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1586
1587 /* does already exist */
1588 filename = fu_common_realpath (trigger, NULL);
1589 if (g_strcmp0 (filename, symlink_target) == 0) {
1590 g_debug ("%s already points to %s, skipping creation",
1591 trigger, symlink_target);
1592 return TRUE;
1593 }
1594
1595 /* create symlink for the systemd-system-update-generator */
1596 rc = symlink (symlink_target, trigger);
1597 if (rc < 0) {
1598 g_set_error (error,
1599 FWUPD_ERROR,
1600 FWUPD_ERROR_INTERNAL,
1601 "Failed to create symlink %s to %s: %s",
1602 trigger, symlink_target, strerror (errno));
1603 return FALSE;
1604 }
1605 return TRUE;
Richard Hughes9e5675e2019-11-22 09:35:03 +00001606#else
1607 g_set_error (error,
1608 FWUPD_ERROR,
1609 FWUPD_ERROR_NOT_SUPPORTED,
1610 "Not supported as <gio-unix.h> not available");
1611 return FALSE;
1612#endif
1613
Richard Hughes019a1bc2019-11-26 10:19:33 +00001614}
1615
1616static gboolean
1617fu_engine_offline_invalidate (GError **error)
1618{
1619 g_autofree gchar *trigger = fu_common_get_path (FU_PATH_KIND_OFFLINE_TRIGGER);
1620 g_autoptr(GError) error_local = NULL;
1621 g_autoptr(GFile) file1 = NULL;
1622
1623 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1624
1625 file1 = g_file_new_for_path (trigger);
1626 if (!g_file_query_exists (file1, NULL))
1627 return TRUE;
1628 if (!g_file_delete (file1, NULL, &error_local)) {
1629 g_set_error (error,
1630 FWUPD_ERROR,
1631 FWUPD_ERROR_INTERNAL,
1632 "Cannot delete %s: %s",
1633 trigger,
1634 error_local->message);
1635 return FALSE;
1636 }
1637 return TRUE;
1638}
1639
1640/**
1641 * fu_engine_schedule_update:
1642 * @self: a #FuEngine
1643 * @device: a #FuDevice
1644 * @release: A #FwupdRelease
1645 * @blob_cab: A #GBytes
1646 * @flags: #FwupdInstallFlags
1647 * @error: A #GError or NULL
1648 *
1649 * Schedule an offline update for the device
1650 *
1651 * Returns: #TRUE for success, #FALSE for failure
1652 *
1653 * Since: 1.3.5
1654 **/
1655gboolean
1656fu_engine_schedule_update (FuEngine *self,
1657 FuDevice *device,
1658 FwupdRelease *release,
1659 GBytes *blob_cab,
1660 FwupdInstallFlags flags,
1661 GError **error)
1662{
1663 gchar tmpname[] = {"XXXXXX.cab"};
1664 g_autofree gchar *dirname = NULL;
1665 g_autofree gchar *filename = NULL;
1666 g_autoptr(FuHistory) history = NULL;
1667 g_autoptr(GFile) file = NULL;
1668
1669 /* id already exists */
1670 history = fu_history_new ();
1671 if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
1672 g_autoptr(FuDevice) res_tmp = NULL;
1673 res_tmp = fu_history_get_device_by_id (history, fu_device_get_id (device), NULL);
1674 if (res_tmp != NULL &&
1675 fu_device_get_update_state (res_tmp) == FWUPD_UPDATE_STATE_PENDING) {
1676 g_set_error (error,
1677 FWUPD_ERROR,
1678 FWUPD_ERROR_ALREADY_PENDING,
1679 "%s is already scheduled to be updated",
1680 fu_device_get_id (device));
1681 return FALSE;
1682 }
1683 }
1684
1685 /* create directory */
1686 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1687 file = g_file_new_for_path (dirname);
1688 if (!g_file_query_exists (file, NULL)) {
1689 if (!g_file_make_directory_with_parents (file, NULL, error))
1690 return FALSE;
1691 }
1692
1693 /* get a random filename */
1694 for (guint i = 0; i < 6; i++)
1695 tmpname[i] = (gchar) g_random_int_range ('A', 'Z');
1696 filename = g_build_filename (dirname, tmpname, NULL);
1697
1698 /* just copy to the temp file */
1699 fu_device_set_status (device, FWUPD_STATUS_SCHEDULING);
1700 if (!g_file_set_contents (filename,
1701 g_bytes_get_data (blob_cab, NULL),
1702 (gssize) g_bytes_get_size (blob_cab),
1703 error))
1704 return FALSE;
1705
1706 /* schedule for next boot */
1707 g_debug ("schedule %s to be installed to %s on next boot",
1708 filename, fu_device_get_id (device));
1709 fwupd_release_set_filename (release, filename);
1710
1711 /* add to database */
1712 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
1713 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_PENDING);
1714 if (!fu_history_add_device (history, device, release, error))
1715 return FALSE;
1716
1717 /* next boot we run offline */
1718 fu_device_set_progress (device, 100);
1719 return fu_engine_offline_setup (error);
1720}
1721
Richard Hughes4bd2e042019-12-22 12:19:52 +00001722static gboolean
1723fu_engine_install_release (FuEngine *self,
Richard Hughes293a64d2020-02-14 12:08:08 +00001724 FuDevice *device_orig,
Richard Hughes4bd2e042019-12-22 12:19:52 +00001725 XbNode *component,
1726 XbNode *rel,
1727 FwupdInstallFlags flags,
1728 GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001729{
Richard Hughesafca2032019-02-01 18:05:30 +00001730 FuPlugin *plugin;
Richard Hughes9a680842020-02-20 11:11:13 +00001731 FwupdVersionFormat fmt;
Richard Hughes9945edb2017-06-19 10:03:55 +01001732 GBytes *blob_fw;
Richard Hughesd5aab652020-02-25 12:47:50 +00001733 const gchar *tmp;
Richard Hughes84af6e72019-02-01 18:19:41 +00001734 g_autofree gchar *version_orig = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001735 g_autofree gchar *version_rel = NULL;
Richard Hughes68db74b2019-03-14 09:52:05 +00001736 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes293a64d2020-02-14 12:08:08 +00001737 g_autoptr(FuDevice) device = g_object_ref (device_orig);
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001738 g_autoptr(GBytes) blob_fw2 = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001739 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001740
Richard Hughesd5aab652020-02-25 12:47:50 +00001741 /* get per-release firmware blob */
1742 blob_fw = xb_node_get_data (rel, "fwupd::FirmwareBlob");
Richard Hughes9945edb2017-06-19 10:03:55 +01001743 if (blob_fw == NULL) {
Richard Hughesd5aab652020-02-25 12:47:50 +00001744 g_set_error_literal (error,
1745 FWUPD_ERROR,
1746 FWUPD_ERROR_INTERNAL,
1747 "Failed to get firmware blob from release");
Richard Hughes9945edb2017-06-19 10:03:55 +01001748 return FALSE;
1749 }
1750
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001751 /* use a bubblewrap helper script to build the firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001752 tmp = g_object_get_data (G_OBJECT (component), "fwupd::BuilderScript");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001753 if (tmp != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001754 const gchar *tmp2 = g_object_get_data (G_OBJECT (component), "fwupd::BuilderOutput");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001755 if (tmp2 == NULL)
1756 tmp2 = "firmware.bin";
1757 blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error);
1758 if (blob_fw2 == NULL)
1759 return FALSE;
1760 } else {
1761 blob_fw2 = g_bytes_ref (blob_fw);
1762 }
1763
Richard Hughesafca2032019-02-01 18:05:30 +00001764 /* get the plugin */
1765 plugin = fu_plugin_list_find_by_name (self->plugin_list,
1766 fu_device_get_plugin (device),
1767 error);
1768 if (plugin == NULL)
1769 return FALSE;
1770
Richard Hughesf8e353e2019-03-25 14:26:57 +00001771 /* schedule this for the next reboot if not in system-update.target,
1772 * but first check if allowed on battery power */
Richard Hughes2c40b372019-04-17 13:41:47 +01001773 version_rel = fu_engine_get_release_version (self, device, rel, error);
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01001774 if (version_rel == NULL) {
1775 g_prefix_error (error, "failed to get release version: ");
1776 return FALSE;
1777 }
Richard Hughesf8e353e2019-03-25 14:26:57 +00001778
Richard Hughesafca2032019-02-01 18:05:30 +00001779 /* add device to database */
Richard Hughesafca2032019-02-01 18:05:30 +00001780 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) {
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001781 g_autoptr(FwupdRelease) release_tmp = NULL;
1782 release_tmp = fu_engine_create_release_metadata (self, plugin, error);
1783 if (release_tmp == NULL)
Richard Hughesafca2032019-02-01 18:05:30 +00001784 return FALSE;
Richard Hughesafca2032019-02-01 18:05:30 +00001785 tmp = xb_node_query_text (component,
1786 "releases/release/checksum[@target='container']",
1787 NULL);
Richard Hughesb9bbe4c2019-03-25 14:03:27 +00001788 if (tmp != NULL)
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001789 fwupd_release_add_checksum (release_tmp, tmp);
1790 fwupd_release_set_version (release_tmp, version_rel);
Richard Hughesafca2032019-02-01 18:05:30 +00001791 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes81d7a5c2019-03-25 14:26:04 +00001792 if (!fu_history_add_device (self->history, device, release_tmp, error))
Richard Hughesafca2032019-02-01 18:05:30 +00001793 return FALSE;
1794 }
1795
1796 /* install firmware blob */
Richard Hughes84af6e72019-02-01 18:19:41 +00001797 version_orig = g_strdup (fu_device_get_version (device));
Richard Hughes03df0d52019-02-01 18:31:39 +00001798 if (!fu_engine_install_blob (self, device, blob_fw2, flags, &error_local)) {
1799 fu_device_set_status (device, FWUPD_STATUS_IDLE);
Richard Hughescce6a1c2019-04-16 17:25:48 +01001800 if (g_error_matches (error_local,
1801 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01001802 FWUPD_ERROR_AC_POWER_REQUIRED) ||
1803 g_error_matches (error_local,
1804 FWUPD_ERROR,
Richard Hughes4266ac42019-07-11 16:49:50 +01001805 FWUPD_ERROR_BATTERY_LEVEL_TOO_LOW) ||
1806 g_error_matches (error_local,
1807 FWUPD_ERROR,
Mario Limonciello2e06dcd2019-10-30 19:28:52 -05001808 FWUPD_ERROR_NEEDS_USER_ACTION) ||
1809 g_error_matches (error_local,
1810 FWUPD_ERROR,
Richard Hughes7886c082019-04-18 10:00:17 +01001811 FWUPD_ERROR_BROKEN_SYSTEM)) {
Richard Hughescce6a1c2019-04-16 17:25:48 +01001812 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
1813 } else {
1814 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
1815 }
Richard Hughes03df0d52019-02-01 18:31:39 +00001816 fu_device_set_update_error (device, error_local->message);
1817 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001818 !fu_history_modify_device (self->history, device, error)) {
Richard Hughes03df0d52019-02-01 18:31:39 +00001819 return FALSE;
1820 }
1821 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes84af6e72019-02-01 18:19:41 +00001822 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00001823 }
Richard Hughes84af6e72019-02-01 18:19:41 +00001824
Richard Hughes68db74b2019-03-14 09:52:05 +00001825 /* the device may have changed */
1826 device_tmp = fu_device_list_get_by_id (self->device_list,
1827 fu_device_get_id (device),
1828 error);
1829 if (device_tmp == NULL) {
1830 g_prefix_error (error, "failed to get device after install: ");
1831 return FALSE;
1832 }
1833 g_set_object (&device, device_tmp);
1834
Richard Hughes84af6e72019-02-01 18:19:41 +00001835 /* update database */
1836 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) ||
1837 fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) {
1838 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
1839 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001840 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001841 return FALSE;
1842 /* success */
1843 return TRUE;
1844 }
1845
1846 /* for online updates, verify the version changed if not a re-install */
Richard Hughes9a680842020-02-20 11:11:13 +00001847 fmt = fu_device_get_version_format (device);
Richard Hughes84af6e72019-02-01 18:19:41 +00001848 if (version_rel != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00001849 fu_common_vercmp_full (version_orig, version_rel, fmt) != 0 &&
1850 fu_common_vercmp_full (version_orig, fu_device_get_version (device), fmt) == 0) {
Richard Hughes68db74b2019-03-14 09:52:05 +00001851 g_autofree gchar *str = NULL;
Richard Hughes84af6e72019-02-01 18:19:41 +00001852 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
Richard Hughes68db74b2019-03-14 09:52:05 +00001853 str = g_strdup_printf ("device version not updated on success, %s != %s",
1854 version_rel, fu_device_get_version (device));
1855 fu_device_set_update_error (device, str);
Richard Hughes84af6e72019-02-01 18:19:41 +00001856 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001857 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001858 return FALSE;
1859 /* success */
1860 return TRUE;
1861 }
1862
1863 /* ensure the new version matched what we expected */
1864 if (version_rel != NULL &&
1865 g_strcmp0 (fu_device_get_version (device), version_rel) != 0) {
1866 g_warning ("new device version '%s' was is not '%s', fixing up",
1867 fu_device_get_version (device), version_rel);
Richard Hughesf50ff2c2020-02-25 09:45:15 +00001868 fu_device_set_version_format (device, fu_device_get_version_format (device));
1869 fu_device_set_version (device, version_rel);
Richard Hughes84af6e72019-02-01 18:19:41 +00001870 }
1871
1872 /* success */
Richard Hughes84af6e72019-02-01 18:19:41 +00001873 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
Richard Hughes0bbef292019-11-01 12:15:15 +00001874 !fu_history_modify_device (self->history, device, error))
Richard Hughes84af6e72019-02-01 18:19:41 +00001875 return FALSE;
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06001876
1877 /* make the UI update */
1878 fu_engine_emit_changed (self);
1879
Richard Hughes84af6e72019-02-01 18:19:41 +00001880 return TRUE;
Richard Hughes6e7419d2018-05-18 10:11:36 +01001881}
1882
Richard Hughesaf140732019-12-22 18:42:12 +00001883typedef struct {
1884 gboolean ret;
1885 GError **error;
1886 FuEngine *self;
1887 FuDevice *device;
1888} FuEngineSortHelper;
1889
1890static gint
1891fu_engine_sort_release_versions_cb (gconstpointer a, gconstpointer b, gpointer user_data)
1892{
1893 FuEngineSortHelper *helper = (FuEngineSortHelper *) user_data;
1894 XbNode *na = *((XbNode **) a);
1895 XbNode *nb = *((XbNode **) b);
1896 g_autofree gchar *va = NULL;
1897 g_autofree gchar *vb = NULL;
1898
1899 /* already failed */
1900 if (!helper->ret)
1901 return 0;
1902
1903 /* get the semver from the release */
1904 va = fu_engine_get_release_version (helper->self, helper->device, na, helper->error);
1905 if (va == NULL) {
1906 g_prefix_error (helper->error, "failed to get release version: ");
1907 return 0;
1908 }
1909 vb = fu_engine_get_release_version (helper->self, helper->device, nb, helper->error);
1910 if (vb == NULL) {
1911 g_prefix_error (helper->error, "failed to get release version: ");
1912 return 0;
1913 }
Richard Hughes9a680842020-02-20 11:11:13 +00001914 return fu_common_vercmp_full (va, vb, fu_device_get_version_format (helper->device));
Richard Hughesaf140732019-12-22 18:42:12 +00001915}
1916
1917static gboolean
1918fu_engine_sort_releases (FuEngine *self, FuDevice *device, GPtrArray *rels, GError **error)
1919{
1920 FuEngineSortHelper helper = {
1921 .ret = TRUE,
1922 .self = self,
1923 .device = device,
1924 .error = error,
1925 };
1926 g_ptr_array_sort_with_data (rels, fu_engine_sort_release_versions_cb, &helper);
1927 return helper.ret;
1928}
1929
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001930/**
Richard Hughes4bd2e042019-12-22 12:19:52 +00001931 * fu_engine_install:
1932 * @self: A #FuEngine
1933 * @task: A #FuInstallTask
1934 * @blob_cab: The #GBytes of the .cab file
1935 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
1936 * @error: A #GError, or %NULL
1937 *
1938 * Installs a specific firmware file on a device.
1939 *
1940 * By this point all the requirements and tests should have been done in
1941 * fu_engine_check_requirements() so this should not fail before running
1942 * the plugin loader.
1943 *
1944 * Returns: %TRUE for success
1945 **/
1946gboolean
1947fu_engine_install (FuEngine *self,
1948 FuInstallTask *task,
1949 GBytes *blob_cab,
1950 FwupdInstallFlags flags,
1951 GError **error)
1952{
1953 XbNode *component = fu_install_task_get_component (task);
1954 g_autoptr(FuDevice) device = NULL;
1955 g_autoptr(GError) error_local = NULL;
1956 g_autoptr(XbNode) rel_newest = NULL;
1957
1958 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
1959 g_return_val_if_fail (XB_IS_NODE (component), FALSE);
1960 g_return_val_if_fail (blob_cab != NULL, FALSE);
1961 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1962
1963 /* not in bootloader mode */
1964 device = g_object_ref (fu_install_task_get_device (task));
1965 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
1966 const gchar *caption = NULL;
1967 caption = xb_node_query_text (component,
1968 "screenshots/screenshot/caption",
1969 NULL);
1970 if (caption != NULL) {
1971 g_set_error (error,
1972 FWUPD_ERROR,
1973 FWUPD_ERROR_NEEDS_USER_ACTION,
1974 "Device %s needs to manually be put in update mode: %s",
1975 fu_device_get_name (device), caption);
1976 } else {
1977 g_set_error (error,
1978 FWUPD_ERROR,
1979 FWUPD_ERROR_NEEDS_USER_ACTION,
1980 "Device %s needs to manually be put in update mode",
1981 fu_device_get_name (device));
1982 }
1983 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT);
1984 if (error != NULL)
1985 fu_device_set_update_error (device, (*error)->message);
1986 return FALSE;
1987 }
1988
1989 /* get the newest version */
1990 rel_newest = xb_node_query_first (component, "releases/release", &error_local);
1991 if (rel_newest == NULL) {
1992 g_set_error (error,
1993 FWUPD_ERROR,
1994 FWUPD_ERROR_INVALID_FILE,
1995 "No releases in the firmware component: %s",
1996 error_local->message);
1997 return FALSE;
1998 }
1999
2000 /* schedule this for the next reboot if not in system-update.target,
2001 * but first check if allowed on battery power */
2002 if ((flags & FWUPD_INSTALL_FLAG_OFFLINE) > 0 &&
2003 !fu_engine_is_running_offline (self)) {
2004 FuPlugin *plugin;
2005 g_autoptr(FwupdRelease) release_tmp = NULL;
2006 g_autofree gchar *version_rel = NULL;
2007 version_rel = fu_engine_get_release_version (self, device, rel_newest, error);
2008 if (version_rel == NULL) {
2009 g_prefix_error (error, "failed to get release version: ");
2010 return FALSE;
2011 }
2012 plugin = fu_plugin_list_find_by_name (self->plugin_list, "upower", NULL);
2013 if (plugin != NULL) {
2014 if (!fu_plugin_runner_update_prepare (plugin, flags, device, error))
2015 return FALSE;
2016 }
2017 release_tmp = fu_engine_create_release_metadata (self, plugin, error);
2018 if (release_tmp == NULL)
2019 return FALSE;
2020 fwupd_release_set_version (release_tmp, version_rel);
2021 return fu_engine_schedule_update (self, device, release_tmp,
2022 blob_cab, flags, error);
2023 }
2024
Richard Hughesaf140732019-12-22 18:42:12 +00002025 /* install each intermediate release, or install only the newest version */
2026 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) {
2027 g_autoptr(GPtrArray) rels = NULL;
2028 rels = xb_node_query (component, "releases/release", 0, &error_local);
2029 if (rels == NULL) {
2030 g_set_error (error,
2031 FWUPD_ERROR,
2032 FWUPD_ERROR_INVALID_FILE,
2033 "No releases in the firmware component: %s",
2034 error_local->message);
2035 return FALSE;
2036 }
2037 if (!fu_engine_sort_releases (self, device, rels, error))
2038 return FALSE;
2039 for (guint i = 0; i < rels->len; i++) {
2040 XbNode *rel = g_ptr_array_index (rels, i);
2041 if (!fu_engine_install_release (self, device, component, rel, flags, error))
2042 return FALSE;
2043 }
2044 } else {
2045 if (!fu_engine_install_release (self, device, component, rel_newest, flags, error))
2046 return FALSE;
2047 }
Richard Hughes4bd2e042019-12-22 12:19:52 +00002048
2049 /* success */
2050 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2051 return TRUE;
2052}
2053
2054/**
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002055 * fu_engine_get_plugins:
2056 * @self: A #FuPluginList
2057 *
2058 * Gets all the plugins that have been added.
2059 *
2060 * Returns: (transfer none) (element-type FuPlugin): the plugins
2061 *
2062 * Since: 1.0.8
2063 **/
2064GPtrArray *
2065fu_engine_get_plugins (FuEngine *self)
2066{
2067 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2068 return fu_plugin_list_get_all (self->plugin_list);
2069}
2070
Richard Hughesd583baf2018-11-23 13:57:22 +00002071static FuDevice *
2072fu_engine_get_device_by_id (FuEngine *self, const gchar *device_id, GError **error)
2073{
2074 g_autoptr(FuDevice) device1 = NULL;
2075 g_autoptr(FuDevice) device2 = NULL;
Richard Hughesc3afed32020-03-10 16:05:29 +00002076 g_autoptr(FuDevice) root = NULL;
Richard Hughesd583baf2018-11-23 13:57:22 +00002077
2078 /* find device */
2079 device1 = fu_device_list_get_by_id (self->device_list, device_id, error);
2080 if (device1 == NULL)
2081 return NULL;
2082
Richard Hughesd583baf2018-11-23 13:57:22 +00002083 /* wait for device to disconnect and reconnect */
Richard Hughesc3afed32020-03-10 16:05:29 +00002084 root = fu_device_get_root (device1);
2085 if (fu_device_has_flag (device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2086 if (!fu_device_list_wait_for_replug (self->device_list, device1, error)) {
2087 g_prefix_error (error, "failed to wait for detach replug: ");
2088 return NULL;
2089 }
2090 } else if (fu_device_has_flag (root, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2091 if (!fu_device_list_wait_for_replug (self->device_list, root, error)) {
2092 g_prefix_error (error, "failed to wait for detach replug: ");
2093 return NULL;
2094 }
2095 } else {
2096 /* no replug required */
2097 return g_steal_pointer (&device1);
Richard Hughesd583baf2018-11-23 13:57:22 +00002098 }
2099
2100 /* get the new device */
2101 device2 = fu_device_list_get_by_id (self->device_list, device_id, error);
2102 if (device2 == NULL) {
2103 g_prefix_error (error, "failed to get device after replug: ");
2104 return NULL;
2105 }
2106
2107 /* success */
2108 return g_steal_pointer (&device2);
2109}
2110
Richard Hughes2031ce32019-10-30 14:16:24 +00002111/* same as FuDevice->prepare, but with the device open */
2112static gboolean
2113fu_engine_device_prepare (FuEngine *self,
2114 FuDevice *device,
2115 FwupdInstallFlags flags,
2116 GError **error)
2117{
2118 g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error);
2119 if (locker == NULL)
2120 return FALSE;
2121 return fu_device_prepare (device, flags, error);
2122}
2123
2124/* same as FuDevice->cleanup, but with the device open */
2125static gboolean
2126fu_engine_device_cleanup (FuEngine *self,
2127 FuDevice *device,
2128 FwupdInstallFlags flags,
2129 GError **error)
2130{
Mario Limoncielloe2b8a272019-11-09 22:10:35 -06002131 g_autoptr(FuDeviceLocker) locker = NULL;
2132
2133 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2134 g_debug ("skipping device cleanup due to will-disappear flag");
2135 return TRUE;
2136 }
2137
2138 locker = fu_device_locker_new (device, error);
Richard Hughes2031ce32019-10-30 14:16:24 +00002139 if (locker == NULL)
2140 return FALSE;
2141 return fu_device_cleanup (device, flags, error);
2142}
2143
Richard Hughes03df0d52019-02-01 18:31:39 +00002144static gboolean
2145fu_engine_update_prepare (FuEngine *self,
2146 FwupdInstallFlags flags,
2147 const gchar *device_id,
2148 GError **error)
2149{
2150 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002151 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002152 g_autoptr(FuDevice) device = NULL;
2153
2154 /* the device and plugin both may have changed */
2155 device = fu_engine_get_device_by_id (self, device_id, error);
2156 if (device == NULL)
2157 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002158 str = fu_device_to_string (device);
2159 g_debug ("performing prepare on %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002160 if (!fu_engine_device_prepare (self, device, flags, error))
Mario Limonciello44b9e462019-10-22 14:32:10 -05002161 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002162 for (guint j = 0; j < plugins->len; j++) {
2163 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2164 if (!fu_plugin_runner_update_prepare (plugin_tmp, flags, device, error))
2165 return FALSE;
2166 }
Richard Hughes802bb312019-10-17 13:32:23 +01002167
2168 /* wait for device to disconnect and reconnect */
2169 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2170 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2171 g_prefix_error (error, "failed to wait for prepare replug: ");
2172 return FALSE;
2173 }
2174 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002175 return TRUE;
2176}
2177
2178static gboolean
2179fu_engine_update_cleanup (FuEngine *self,
2180 FwupdInstallFlags flags,
2181 const gchar *device_id,
2182 GError **error)
2183{
2184 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes791c91b2019-10-17 13:33:30 +01002185 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002186 g_autoptr(FuDevice) device = NULL;
2187
2188 /* the device and plugin both may have changed */
2189 device = fu_engine_get_device_by_id (self, device_id, error);
2190 if (device == NULL)
2191 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002192 str = fu_device_to_string (device);
2193 g_debug ("performing cleanup on %s", str);
Richard Hughes2031ce32019-10-30 14:16:24 +00002194 if (!fu_engine_device_cleanup (self, device, flags, error))
Richard Hughes791c91b2019-10-17 13:33:30 +01002195 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002196 for (guint j = 0; j < plugins->len; j++) {
2197 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
2198 if (!fu_plugin_runner_update_cleanup (plugin_tmp, flags, device, error))
2199 return FALSE;
2200 }
Richard Hughes802bb312019-10-17 13:32:23 +01002201
2202 /* wait for device to disconnect and reconnect */
2203 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) {
2204 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
2205 g_prefix_error (error, "failed to wait for cleanup replug: ");
2206 return FALSE;
2207 }
2208 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002209 return TRUE;
2210}
2211
2212static gboolean
2213fu_engine_update_detach (FuEngine *self, const gchar *device_id, GError **error)
2214{
2215 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002216 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002217 g_autoptr(FuDevice) device = NULL;
2218
2219 /* the device and plugin both may have changed */
2220 device = fu_engine_get_device_by_id (self, device_id, error);
2221 if (device == NULL)
2222 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002223 str = fu_device_to_string (device);
2224 g_debug ("performing detach on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002225 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2226 fu_device_get_plugin (device),
2227 error);
2228 if (plugin == NULL)
2229 return FALSE;
2230 if (!fu_plugin_runner_update_detach (plugin, device, error))
2231 return FALSE;
2232 return TRUE;
2233}
2234
2235static gboolean
2236fu_engine_update_attach (FuEngine *self, const gchar *device_id, GError **error)
2237{
2238 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002239 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002240 g_autoptr(FuDevice) device = NULL;
2241
2242 /* the device and plugin both may have changed */
2243 device = fu_engine_get_device_by_id (self, device_id, error);
2244 if (device == NULL) {
2245 g_prefix_error (error, "failed to get device after update: ");
2246 return FALSE;
2247 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002248 str = fu_device_to_string (device);
2249 g_debug ("performing attach on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002250 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2251 fu_device_get_plugin (device),
2252 error);
2253 if (plugin == NULL)
2254 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002255
2256 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2257 g_debug ("skipping attach due to will-disappear flag");
2258 return TRUE;
2259 }
2260
Richard Hughes03df0d52019-02-01 18:31:39 +00002261 if (!fu_plugin_runner_update_attach (plugin, device, error))
2262 return FALSE;
2263 return TRUE;
2264}
2265
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002266gboolean
2267fu_engine_activate (FuEngine *self, const gchar *device_id, GError **error)
2268{
2269 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002270 g_autofree gchar *str = NULL;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002271 g_autoptr(FuDevice) device = NULL;
2272
2273 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2274 g_return_val_if_fail (device_id != NULL, FALSE);
2275 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2276
2277 /* check the device exists */
2278 device = fu_device_list_get_by_id (self->device_list, device_id, error);
2279 if (device == NULL)
2280 return FALSE;
Richard Hughes791c91b2019-10-17 13:33:30 +01002281 str = fu_device_to_string (device);
2282 g_debug ("performing activate on %s", str);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002283 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2284 fu_device_get_plugin (device),
2285 error);
2286 if (plugin == NULL)
2287 return FALSE;
2288 g_debug ("Activating %s", fu_device_get_name (device));
2289
2290 if (!fu_plugin_runner_activate (plugin, device, error))
2291 return FALSE;
2292
2293 fu_engine_emit_device_changed (self, device);
2294 fu_engine_emit_changed (self);
2295
2296 return TRUE;
2297}
2298
Richard Hughes03df0d52019-02-01 18:31:39 +00002299static gboolean
2300fu_engine_update_reload (FuEngine *self, const gchar *device_id, GError **error)
2301{
2302 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002303 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002304 g_autoptr(FuDevice) device = NULL;
2305
2306 /* the device and plugin both may have changed */
2307 device = fu_engine_get_device_by_id (self, device_id, error);
2308 if (device == NULL) {
2309 g_prefix_error (error, "failed to get device after update: ");
2310 return FALSE;
2311 }
Richard Hughes791c91b2019-10-17 13:33:30 +01002312 str = fu_device_to_string (device);
2313 g_debug ("performing reload on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002314 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2315 fu_device_get_plugin (device),
2316 error);
2317 if (plugin == NULL)
2318 return FALSE;
Mario Limoncielloe1b4d8e2019-10-12 11:19:10 -05002319
2320 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) {
2321 g_debug ("skipping reload due to will-disappear flag");
2322 return TRUE;
2323 }
2324
Richard Hughes03df0d52019-02-01 18:31:39 +00002325 if (!fu_plugin_runner_update_reload (plugin, device, error)) {
2326 g_prefix_error (error, "failed to reload device: ");
2327 return FALSE;
2328 }
2329 return TRUE;
2330}
2331
2332static gboolean
2333fu_engine_update (FuEngine *self,
2334 const gchar *device_id,
2335 GBytes *blob_fw2,
2336 FwupdInstallFlags flags,
2337 GError **error)
2338{
2339 FuPlugin *plugin;
Richard Hughes791c91b2019-10-17 13:33:30 +01002340 g_autofree gchar *str = NULL;
Richard Hughes03df0d52019-02-01 18:31:39 +00002341 g_autoptr(FuDevice) device = NULL;
Richard Hughes019a1bc2019-11-26 10:19:33 +00002342 g_autoptr(FuDevice) device_pending = NULL;
2343
2344 /* cancel the pending action */
2345 if (!fu_engine_offline_invalidate (error))
2346 return FALSE;
Richard Hughes03df0d52019-02-01 18:31:39 +00002347
2348 /* the device and plugin both may have changed */
2349 device = fu_engine_get_device_by_id (self, device_id, error);
2350 if (device == NULL) {
2351 g_prefix_error (error, "failed to get device after detach: ");
2352 return FALSE;
2353 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002354 device_pending = fu_history_get_device_by_id (self->history, device_id, NULL);
Richard Hughes791c91b2019-10-17 13:33:30 +01002355 str = fu_device_to_string (device);
2356 g_debug ("performing update on %s", str);
Richard Hughes03df0d52019-02-01 18:31:39 +00002357 plugin = fu_plugin_list_find_by_name (self->plugin_list,
2358 fu_device_get_plugin (device),
2359 error);
2360 if (plugin == NULL)
2361 return FALSE;
2362 if (!fu_plugin_runner_update (plugin, device, blob_fw2, flags, error)) {
2363 g_autoptr(GError) error_attach = NULL;
2364 g_autoptr(GError) error_cleanup = NULL;
2365
2366 /* attack back into runtime then cleanup */
2367 if (!fu_plugin_runner_update_attach (plugin,
2368 device,
2369 &error_attach)) {
2370 g_warning ("failed to attach device after failed update: %s",
2371 error_attach->message);
2372 }
2373 if (!fu_engine_update_cleanup (self, flags, device_id, &error_cleanup)) {
2374 g_warning ("failed to update-cleanup after failed update: %s",
2375 error_cleanup->message);
2376 }
2377 return FALSE;
2378 }
Richard Hughes019a1bc2019-11-26 10:19:33 +00002379
2380 /* cleanup */
2381 if (device_pending != NULL) {
2382 const gchar *tmp;
2383 FwupdRelease *release;
2384
2385 /* update history database */
2386 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
2387 if (!fu_history_modify_device (self->history, device, error))
2388 return FALSE;
2389
2390 /* delete cab file */
2391 release = fu_device_get_release_default (device_pending);
2392 tmp = fwupd_release_get_filename (release);
2393 if (tmp != NULL && g_str_has_prefix (tmp, FWUPD_LIBEXECDIR)) {
2394 g_autoptr(GError) error_delete = NULL;
2395 g_autoptr(GFile) file = NULL;
2396 file = g_file_new_for_path (tmp);
2397 if (!g_file_delete (file, NULL, &error_delete)) {
2398 g_set_error (error,
2399 FWUPD_ERROR,
2400 FWUPD_ERROR_INVALID_FILE,
2401 "Failed to delete %s: %s",
2402 tmp, error_delete->message);
2403 return FALSE;
2404 }
2405 }
2406 }
Richard Hughes03df0d52019-02-01 18:31:39 +00002407 return TRUE;
2408}
2409
Richard Hughesa58510b2019-10-30 10:03:12 +00002410GBytes *
2411fu_engine_firmware_read (FuEngine *self,
2412 FuDevice *device,
2413 FwupdInstallFlags flags,
2414 GError **error)
2415{
2416 g_autoptr(FuDeviceLocker) locker = NULL;
2417 g_autoptr(FuFirmware) firmware = NULL;
2418
2419 /* open, detach, read, attach, serialize */
2420 locker = fu_device_locker_new (device, error);
2421 if (locker == NULL)
2422 return NULL;
2423 if (!fu_device_detach (device, error))
2424 return NULL;
2425 firmware = fu_device_read_firmware (device, error);
2426 if (firmware == NULL) {
2427 g_autoptr(GError) error_local = NULL;
2428 if (!fu_device_attach (device, &error_local)) {
2429 g_warning ("failed to attach after read image failure: %s",
2430 error_local->message);
2431 }
2432 return NULL;
2433 }
2434 if (!fu_device_attach (device, error))
2435 return NULL;
2436 return fu_firmware_write (firmware, error);
2437}
2438
Richard Hughes6e7419d2018-05-18 10:11:36 +01002439gboolean
2440fu_engine_install_blob (FuEngine *self,
Richard Hughes03df0d52019-02-01 18:31:39 +00002441 FuDevice *device,
2442 GBytes *blob_fw,
Richard Hughes6e7419d2018-05-18 10:11:36 +01002443 FwupdInstallFlags flags,
2444 GError **error)
2445{
Richard Hughes52976782019-02-01 21:23:01 +00002446 guint retries = 0;
Richard Hughes03df0d52019-02-01 18:31:39 +00002447 g_autofree gchar *device_id = NULL;
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05002448 g_autoptr(GTimer) timer = g_timer_new ();
Richard Hughes6e7419d2018-05-18 10:11:36 +01002449
Richard Hughesadcc16a2017-08-21 12:26:46 +01002450 /* test the firmware is not an empty blob */
Richard Hughes03df0d52019-02-01 18:31:39 +00002451 if (g_bytes_get_size (blob_fw) == 0) {
Richard Hughesadcc16a2017-08-21 12:26:46 +01002452 g_set_error (error,
2453 FWUPD_ERROR,
2454 FWUPD_ERROR_INVALID_FILE,
2455 "Firmware is invalid as has zero size");
2456 return FALSE;
2457 }
2458
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002459 /* mark this as modified even if we actually fail to do the update */
2460 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
2461
Richard Hughes52976782019-02-01 21:23:01 +00002462 /* plugins can set FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED to run again, but they
2463 * must return TRUE rather than an error */
Richard Hughes03df0d52019-02-01 18:31:39 +00002464 device_id = g_strdup (fu_device_get_id (device));
Richard Hughes52976782019-02-01 21:23:01 +00002465 do {
2466 /* check for a loop */
2467 if (++retries > 5) {
2468 g_set_error_literal (error,
2469 FWUPD_ERROR,
2470 FWUPD_ERROR_INTERNAL,
2471 "aborting device write loop, limit 5");
2472 return FALSE;
2473 }
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002474
Richard Hughes52976782019-02-01 21:23:01 +00002475 /* don't rely on a plugin clearing this */
2476 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002477
Richard Hughes52976782019-02-01 21:23:01 +00002478 /* signal to all the plugins the update is about to happen */
2479 if (!fu_engine_update_prepare (self, flags, device_id, error))
2480 return FALSE;
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002481
Richard Hughes52976782019-02-01 21:23:01 +00002482 /* detach to bootloader mode */
2483 if (!fu_engine_update_detach (self, device_id, error))
2484 return FALSE;
2485
2486 /* install */
2487 if (!fu_engine_update (self, device_id, blob_fw, flags, error))
2488 return FALSE;
2489
2490 /* attach into runtime mode */
2491 if (!fu_engine_update_attach (self, device_id, error))
2492 return FALSE;
2493
2494 } while (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED));
Aleksander Morgado37b985c2019-01-20 11:23:32 +01002495
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002496 /* get the new version number */
Richard Hughes03df0d52019-02-01 18:31:39 +00002497 if (!fu_engine_update_reload (self, device_id, error))
Richard Hughes0d7fdb32017-11-11 20:23:14 +00002498 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002499
2500 /* signal to all the plugins the update has happened */
Richard Hughes03df0d52019-02-01 18:31:39 +00002501 if (!fu_engine_update_cleanup (self, flags, device_id, error))
2502 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002503
2504 /* make the UI update */
Richard Hughes7772dcf2018-09-10 15:49:59 +01002505 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05002506 g_debug ("Updating %s took %f seconds", fu_device_get_name (device),
2507 g_timer_elapsed (timer, NULL));
Richard Hughes76e0f942018-05-14 16:24:00 +01002508 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01002509}
2510
Richard Hughes0a7e7832017-11-22 11:01:13 +00002511static FuDevice *
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002512fu_engine_get_item_by_id_fallback_history (FuEngine *self, const gchar *id, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002513{
Richard Hughes9945edb2017-06-19 10:03:55 +01002514 g_autoptr(GPtrArray) devices = NULL;
2515
2516 /* not a wildcard */
Richard Hughes65e44ca2018-01-30 17:26:30 +00002517 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
2518 g_autoptr(FuDevice) dev = NULL;
2519 g_autoptr(GError) error_local = NULL;
2520
2521 /* get this one device */
2522 dev = fu_history_get_device_by_id (self->history, id, &error_local);
2523 if (dev == NULL) {
2524 g_set_error (error,
2525 FWUPD_ERROR,
2526 FWUPD_ERROR_NOTHING_TO_DO,
2527 "Failed to find %s in history database: %s",
2528 id, error_local->message);
2529 return NULL;
2530 }
2531
2532 /* only useful */
2533 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01002534 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00002535 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
2536 return g_steal_pointer (&dev);
2537 }
2538
2539 /* nothing in database */
2540 g_set_error (error,
2541 FWUPD_ERROR,
2542 FWUPD_ERROR_NOTHING_TO_DO,
2543 "Device %s has no results to report",
2544 fu_device_get_id (dev));
2545 return NULL;
2546 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002547
2548 /* allow '*' for any */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002549 devices = fu_history_get_devices (self->history, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01002550 if (devices == NULL)
2551 return NULL;
2552 for (guint i = 0; i < devices->len; i++) {
Richard Hughes65e44ca2018-01-30 17:26:30 +00002553 FuDevice *dev = g_ptr_array_index (devices, i);
2554 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
Richard Hughescce6a1c2019-04-16 17:25:48 +01002555 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED_TRANSIENT ||
Richard Hughes65e44ca2018-01-30 17:26:30 +00002556 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED)
2557 return g_object_ref (dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01002558 }
Richard Hughes65e44ca2018-01-30 17:26:30 +00002559 g_set_error_literal (error,
2560 FWUPD_ERROR,
2561 FWUPD_ERROR_NOTHING_TO_DO,
2562 "Failed to find any useful results to report");
2563 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002564}
2565
Richard Hughes481aa2a2018-09-18 20:51:46 +01002566/* for the self tests */
2567void
2568fu_engine_set_silo (FuEngine *self, XbSilo *silo)
Richard Hughesbd4d2852017-09-13 14:05:14 +01002569{
Richard Hughes481aa2a2018-09-18 20:51:46 +01002570 g_return_if_fail (FU_IS_ENGINE (self));
2571 g_return_if_fail (XB_IS_SILO (silo));
2572 g_set_object (&self->silo, silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01002573}
2574
2575static gboolean
Richard Hughesa8997132018-01-12 14:25:39 +00002576fu_engine_is_device_supported (FuEngine *self, FuDevice *device)
2577{
Richard Hughes481aa2a2018-09-18 20:51:46 +01002578 g_autoptr(XbNode) component = NULL;
2579
2580 /* sanity check */
2581 if (self->silo == NULL) {
2582 g_critical ("FuEngine silo not set up");
2583 return FALSE;
2584 }
Richard Hughesa8997132018-01-12 14:25:39 +00002585
2586 /* no device version */
2587 if (fu_device_get_version (device) == NULL)
2588 return FALSE;
2589
2590 /* match the GUIDs in the XML */
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002591 component = fu_engine_get_component_by_guids (self, device);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002592 if (component == NULL)
Richard Hughesa8997132018-01-12 14:25:39 +00002593 return FALSE;
2594
2595 /* success */
2596 return TRUE;
2597}
2598
2599static gboolean
Richard Hughes1af48b12018-12-03 11:51:17 +00002600fu_engine_appstream_upgrade_cb (XbBuilderFixup *self,
2601 XbBuilderNode *bn,
2602 gpointer user_data,
2603 GError **error)
2604{
2605 if (g_strcmp0 (xb_builder_node_get_element (bn), "metadata") == 0)
2606 xb_builder_node_set_element (bn, "custom");
2607 return TRUE;
2608}
2609
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002610static XbBuilderSource *
2611fu_engine_create_metadata_builder_source (FuEngine *self,
2612 const gchar *fn,
2613 GError **error)
2614{
2615 g_autoptr(GBytes) blob = NULL;
2616 g_autoptr(XbSilo) silo = NULL;
2617 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2618 g_autofree gchar *xml = NULL;
2619
2620 g_debug ("building metadata for %s", fn);
2621 blob = fu_common_get_contents_bytes (fn, error);
2622 if (blob == NULL)
2623 return NULL;
2624
2625 /* convert the silo for the CAB into a XbBuilderSource */
2626 silo = fu_engine_get_silo_from_blob (self, blob, error);
2627 if (silo == NULL)
2628 return NULL;
2629 xml = xb_silo_export (silo, XB_NODE_EXPORT_FLAG_NONE, error);
2630 if (xml == NULL)
2631 return NULL;
2632 if (!xb_builder_source_load_xml (source, xml,
2633 XB_BUILDER_SOURCE_FLAG_NONE,
2634 error))
2635 return NULL;
2636 return g_steal_pointer (&source);
2637}
2638
2639static gboolean
2640fu_engine_create_metadata (FuEngine *self, XbBuilder *builder,
2641 FwupdRemote *remote, GError **error)
2642{
2643 g_autoptr(GPtrArray) files = NULL;
2644 const gchar *path;
2645
2646 /* find all files in directory */
2647 path = fwupd_remote_get_filename_cache (remote);
2648 files = fu_common_get_files_recursive (path, error);
2649 if (files == NULL)
2650 return FALSE;
2651
2652 /* add each source */
2653 for (guint i = 0; i < files->len; i++) {
2654 g_autoptr(XbBuilderNode) custom = NULL;
2655 g_autoptr(XbBuilderSource) source = NULL;
2656 g_autoptr(GError) error_local = NULL;
2657 const gchar *fn = g_ptr_array_index (files, i);
Crag Wangae422982020-03-05 23:42:53 +08002658 g_autofree gchar *fn_lowercase = g_ascii_strdown (fn, -1);
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002659
2660 /* check is cab file */
Crag Wangae422982020-03-05 23:42:53 +08002661 if (!g_str_has_suffix (fn_lowercase, ".cab")) {
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002662 g_debug ("ignoring: %s", fn);
2663 continue;
2664 }
2665
2666 /* build source for file */
2667 source = fu_engine_create_metadata_builder_source (self, fn, &error_local);
2668 if (source == NULL) {
2669 g_warning ("%s", error_local->message);
2670 continue;
2671 }
2672
2673 /* add metadata */
2674 custom = xb_builder_node_new ("custom");
2675 xb_builder_node_insert_text (custom,
2676 "value", fn,
2677 "key", "fwupd::FilenameCache",
2678 NULL);
2679 xb_builder_node_insert_text (custom,
2680 "value", fwupd_remote_get_id (remote),
2681 "key", "fwupd::RemoteId",
2682 NULL);
2683 xb_builder_source_set_info (source, custom);
2684 xb_builder_import_source (builder, source);
2685 }
2686 return TRUE;
2687}
2688
Richard Hughesf43381f2020-02-24 10:11:31 +00002689static void
2690fu_engine_md_refresh_device_supported (FuEngine *self, FuDevice *device, XbNode *component)
2691{
2692 /* was supported, now unsupported */
2693 if (component == NULL) {
2694 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
2695 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
2696 fu_engine_emit_device_changed (self, device);
2697 }
2698 return;
2699 }
2700
2701 /* was unsupported, now supported */
2702 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
2703 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
2704 fu_engine_emit_device_changed (self, device);
2705 }
2706}
2707
2708static void
2709fu_engine_md_refresh_device_name (FuEngine *self, FuDevice *device, XbNode *component)
2710{
2711 const gchar *name = NULL;
2712
2713 /* require data */
2714 if (component == NULL)
2715 return;
2716
2717 /* copy 1:1 */
2718 name = xb_node_query_text (component, "name", NULL);
2719 if (name != NULL)
2720 fu_device_set_name (device, name);
2721}
2722
2723static const gchar *
2724fu_common_device_category_to_name (const gchar *cat)
2725{
2726 if (g_strcmp0 (cat, "X-EmbeddedController") == 0)
2727 return "Embedded Controller";
2728 if (g_strcmp0 (cat, "X-ManagementEngine") == 0)
2729 return "Intel Management Engine";
2730 if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0)
2731 return "Intel Management Engine";
2732 if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0)
2733 return "Intel Management Engine";
2734 if (g_strcmp0 (cat, "X-ThunderboltController") == 0)
2735 return "Thunderbolt Controller";
2736 if (g_strcmp0 (cat, "X-PlatformSecurityProcessor") == 0)
2737 return "Platform Security Processor";
2738 return NULL;
2739}
2740
2741static void
2742fu_engine_md_refresh_device_name_category (FuEngine *self, FuDevice *device, XbNode *component)
2743{
2744 const gchar *name = NULL;
2745 g_autoptr(GPtrArray) cats = NULL;
2746
2747 /* require data */
2748 if (component == NULL)
2749 return;
2750
2751 /* get AppStream and safe-compat categories */
2752 cats = xb_node_query (component, "categories/category|X-categories/category", 0, NULL);
2753 if (cats == NULL)
2754 return;
2755 for (guint i = 0; i < cats->len; i++) {
2756 XbNode *n = g_ptr_array_index (cats, i);
2757 name = fu_common_device_category_to_name (xb_node_get_text (n));
2758 if (name != NULL)
2759 break;
2760 }
2761 if (name != NULL)
2762 fu_device_set_name (device, name);
2763}
2764
2765static void
Richard Hughesb0976032020-02-24 14:17:04 +00002766_g_ptr_array_reverse (GPtrArray *array)
2767{
2768 guint last_idx = array->len - 1;
2769 for (guint i = 0; i < array->len / 2; i++) {
2770 gpointer tmp = array->pdata[i];
2771 array->pdata[i] = array->pdata[last_idx - i];
2772 array->pdata[last_idx - i] = tmp;
2773 }
2774}
2775
2776static void
2777fu_engine_md_refresh_device_verfmt (FuEngine *self, FuDevice *device, XbNode *component)
2778{
2779 FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN;
2780 g_autoptr(GPtrArray) verfmts = NULL;
2781
2782 /* require data */
2783 if (component == NULL)
2784 return;
2785
2786 /* get metadata */
2787 verfmts = xb_node_query (component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL);
2788 if (verfmts == NULL)
2789 return;
2790 _g_ptr_array_reverse (verfmts);
2791 for (guint i = 0; i < verfmts->len; i++) {
2792 XbNode *value = g_ptr_array_index (verfmts, i);
2793 verfmt = fwupd_version_format_from_string (xb_node_get_text (value));
2794 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN)
2795 break;
2796 }
2797
2798 /* found and different to existing */
2799 if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN &&
2800 fu_device_get_version_format (device) != verfmt) {
2801 fu_device_set_version_format (device, verfmt);
2802 if (fu_device_get_version_raw (device) != 0x0) {
2803 g_autofree gchar *version = NULL;
2804 version = fu_common_version_from_uint32 (fu_device_get_version_raw (device), verfmt);
2805 fu_device_set_version (device, version);
2806 }
2807 if (fu_device_get_version_lowest_raw (device) != 0x0) {
2808 g_autofree gchar *version = NULL;
2809 version = fu_common_version_from_uint32 (fu_device_get_version_lowest_raw (device), verfmt);
2810 fu_device_set_version_lowest (device, version);
2811 }
2812 if (fu_device_get_version_bootloader_raw (device) != 0x0) {
2813 g_autofree gchar *version = NULL;
2814 version = fu_common_version_from_uint32 (fu_device_get_version_bootloader_raw (device), verfmt);
2815 fu_device_set_version_bootloader (device, version);
2816 }
2817 }
2818}
2819
Mario Limonciello537da0e2020-03-09 15:38:17 -05002820void
Mario Limonciellof430da02020-03-09 14:03:03 -05002821fu_engine_md_refresh_device_from_component (FuEngine *self, FuDevice *device, XbNode *component)
Richard Hughesf43381f2020-02-24 10:11:31 +00002822{
Richard Hughesf43381f2020-02-24 10:11:31 +00002823 /* set the name */
2824 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME))
2825 fu_engine_md_refresh_device_name (self, device, component);
2826 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_NAME_CATEGORY))
2827 fu_engine_md_refresh_device_name_category (self, device, component);
Richard Hughesb0976032020-02-24 14:17:04 +00002828
2829 /* fix the version */
2830 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_MD_SET_VERFMT))
2831 fu_engine_md_refresh_device_verfmt (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00002832}
2833
2834static void
2835fu_engine_md_refresh_devices (FuEngine *self)
2836{
2837 g_autoptr(GPtrArray) devices = fu_device_list_get_all (self->device_list);
2838 for (guint i = 0; i < devices->len; i++) {
2839 FuDevice *device = g_ptr_array_index (devices, i);
Mario Limonciellof430da02020-03-09 14:03:03 -05002840 g_autoptr(XbNode) component = fu_engine_get_component_by_guids (self, device);
Mario Limonciello537da0e2020-03-09 15:38:17 -05002841
2842 /* set or clear the SUPPORTED flag */
2843 fu_engine_md_refresh_device_supported (self, device, component);
2844
2845 /* fixup the name and format as needed */
Mario Limonciellof430da02020-03-09 14:03:03 -05002846 fu_engine_md_refresh_device_from_component (self, device, component);
Richard Hughesf43381f2020-02-24 10:11:31 +00002847 }
2848}
2849
Richard Hughes1af48b12018-12-03 11:51:17 +00002850static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00002851fu_engine_load_metadata_store (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002852{
Richard Hughes9945edb2017-06-19 10:03:55 +01002853 GPtrArray *remotes;
Richard Hughesc8cc77c2019-03-07 11:57:24 +00002854 XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002855 g_autofree gchar *cachedirpkg = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002856 g_autofree gchar *xmlbfn = NULL;
2857 g_autoptr(GFile) xmlb = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002858 g_autoptr(GPtrArray) components = NULL;
2859 g_autoptr(XbBuilder) builder = xb_builder_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01002860
Richard Hughes481aa2a2018-09-18 20:51:46 +01002861 /* clear existing silo */
2862 g_clear_object (&self->silo);
2863
2864 /* verbose profiling */
2865 if (g_getenv ("FWUPD_VERBOSE") != NULL) {
2866 xb_builder_set_profile_flags (builder,
2867 XB_SILO_PROFILE_FLAG_XPATH |
2868 XB_SILO_PROFILE_FLAG_DEBUG);
2869 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002870
2871 /* load each enabled metadata file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00002872 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01002873 for (guint i = 0; i < remotes->len; i++) {
2874 const gchar *path = NULL;
Richard Hughes5f733f22017-11-26 16:14:20 +00002875 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002876 g_autoptr(GFile) file = NULL;
Richard Hughes1af48b12018-12-03 11:51:17 +00002877 g_autoptr(XbBuilderFixup) fixup = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002878 g_autoptr(XbBuilderNode) custom = NULL;
2879 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2880
Richard Hughes9945edb2017-06-19 10:03:55 +01002881 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2882 if (!fwupd_remote_get_enabled (remote)) {
2883 g_debug ("remote %s not enabled, so skipping",
2884 fwupd_remote_get_id (remote));
2885 continue;
2886 }
2887 path = fwupd_remote_get_filename_cache (remote);
2888 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
2889 g_debug ("no %s, so skipping", path);
2890 continue;
2891 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01002892
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06002893 /* generate all metadata on demand */
2894 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
2895 g_debug ("building metadata for remote '%s'",
2896 fwupd_remote_get_id (remote));
2897 if (!fu_engine_create_metadata (self, builder, remote, &error_local)) {
2898 g_warning ("failed to generate remote %s: %s",
2899 fwupd_remote_get_id (remote),
2900 error_local->message);
2901 }
2902 continue;
2903 }
2904
Richard Hughes481aa2a2018-09-18 20:51:46 +01002905 /* save the remote-id in the custom metadata space */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002906 file = g_file_new_for_path (path);
2907 if (!xb_builder_source_load_file (source, file,
2908 XB_BUILDER_SOURCE_FLAG_NONE,
2909 NULL, &error_local)) {
Richard Hughes068d3432017-09-16 08:26:46 +01002910 g_warning ("failed to load remote %s: %s",
2911 fwupd_remote_get_id (remote),
2912 error_local->message);
2913 continue;
2914 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01002915
Richard Hughes1af48b12018-12-03 11:51:17 +00002916 /* fix up any legacy installed files */
2917 fixup = xb_builder_fixup_new ("AppStreamUpgrade",
2918 fu_engine_appstream_upgrade_cb,
2919 self, NULL);
2920 xb_builder_fixup_set_max_depth (fixup, 3);
2921 xb_builder_source_add_fixup (source, fixup);
2922
Richard Hughes33171fd2018-11-09 13:29:11 +00002923 /* add metadata */
2924 custom = xb_builder_node_new ("custom");
2925 xb_builder_node_insert_text (custom,
2926 "value", path,
2927 "key", "fwupd::FilenameCache",
2928 NULL);
2929 xb_builder_node_insert_text (custom,
2930 "value", fwupd_remote_get_id (remote),
2931 "key", "fwupd::RemoteId",
2932 NULL);
2933 xb_builder_source_set_info (source, custom);
2934
Richard Hughes481aa2a2018-09-18 20:51:46 +01002935 /* we need to watch for changes? */
2936 xb_builder_import_source (builder, source);
Richard Hughes9945edb2017-06-19 10:03:55 +01002937 }
2938
Richard Hughes88dc0f42019-03-07 11:58:11 +00002939 /* on a read-only filesystem don't care about the cache GUID */
2940 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
2941 compile_flags |= XB_BUILDER_COMPILE_FLAG_IGNORE_GUID;
Richard Hughes88dc0f42019-03-07 11:58:11 +00002942
Richard Hughes481aa2a2018-09-18 20:51:46 +01002943 /* ensure silo is up to date */
2944 cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
2945 xmlbfn = g_build_filename (cachedirpkg, "metadata.xmlb", NULL);
2946 xmlb = g_file_new_for_path (xmlbfn);
Richard Hughesc8cc77c2019-03-07 11:57:24 +00002947 self->silo = xb_builder_ensure (builder, xmlb, compile_flags, NULL, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002948 if (self->silo == NULL)
2949 return FALSE;
2950
Richard Hughes9945edb2017-06-19 10:03:55 +01002951 /* print what we've got */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002952 components = xb_silo_query (self->silo, "components/component", 0, NULL);
2953 if (components != NULL)
2954 g_debug ("%u components now in silo", components->len);
Richard Hughes9945edb2017-06-19 10:03:55 +01002955
Richard Hughes634da032018-11-05 11:42:20 +00002956 /* build the index */
2957 if (!xb_silo_query_build_index (self->silo,
2958 "components/component/provides/firmware",
2959 "type", error))
2960 return FALSE;
2961 if (!xb_silo_query_build_index (self->silo,
2962 "components/component/provides/firmware",
2963 NULL, error))
2964 return FALSE;
2965
Richard Hughesf43381f2020-02-24 10:11:31 +00002966 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01002967 return TRUE;
2968}
2969
Mario Limonciello263cab92019-08-20 17:16:00 -05002970static void
2971fu_engine_config_changed_cb (FuConfig *config, FuEngine *self)
2972{
Richard Hughesd1808aa2019-12-10 15:20:30 +00002973 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (config));
2974}
2975
2976static void
2977fu_engine_remote_list_changed_cb (FuRemoteList *remote_list, FuEngine *self)
2978{
Mario Limonciello263cab92019-08-20 17:16:00 -05002979 g_autoptr(GError) error_local = NULL;
2980 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE,
2981 &error_local))
2982 g_warning ("Failed to reload metadata store: %s",
2983 error_local->message);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06002984
Richard Hughesf43381f2020-02-24 10:11:31 +00002985 /* set device properties from the metadata */
2986 fu_engine_md_refresh_devices (self);
2987
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06002988 /* make the UI update */
2989 fu_engine_emit_changed (self);
Mario Limonciello263cab92019-08-20 17:16:00 -05002990}
2991
Richard Hughesd5aab652020-02-25 12:47:50 +00002992static gint
2993fu_engine_sort_jcat_results_timestamp_cb (gconstpointer a, gconstpointer b)
2994{
2995 JcatResult *ra = *((JcatResult **) a);
2996 JcatResult *rb = *((JcatResult **) b);
2997 if (jcat_result_get_timestamp (ra) < jcat_result_get_timestamp (rb))
2998 return -1;
2999 if (jcat_result_get_timestamp (ra) > jcat_result_get_timestamp (rb))
3000 return 1;
3001 return 0;
3002}
3003
3004static JcatResult *
3005fu_engine_get_system_jcat_result (FuEngine *self, FwupdRemote *remote, GError **error)
Richard Hughesf69a4812017-08-16 12:27:51 +01003006{
3007 g_autoptr(GBytes) blob = NULL;
3008 g_autoptr(GBytes) blob_sig = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003009 g_autoptr(GInputStream) istream = NULL;
3010 g_autoptr(GPtrArray) results = NULL;
3011 g_autoptr(JcatItem) jcat_item = NULL;
3012 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
3013
Richard Hughesf69a4812017-08-16 12:27:51 +01003014 blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
3015 if (blob == NULL)
3016 return NULL;
3017 blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
3018 if (blob_sig == NULL)
3019 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003020 istream = g_memory_input_stream_new_from_bytes (blob_sig);
3021 if (!jcat_file_import_stream (jcat_file, istream,
3022 JCAT_IMPORT_FLAG_NONE,
3023 NULL, error))
3024 return NULL;
3025 jcat_item = jcat_file_get_item_default (jcat_file, error);
3026 if (jcat_item == NULL)
3027 return NULL;
3028 results = jcat_context_verify_item (self->jcat_context,
3029 blob, jcat_item,
3030 JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
3031 JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
3032 error);
3033 if (results == NULL)
3034 return NULL;
3035 g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
3036
3037 /* return the newest one */
3038 return g_object_ref (g_ptr_array_index (results, 0));
3039}
3040
3041static gboolean
3042fu_engine_validate_result_timestamp (JcatResult *jcat_result,
3043 JcatResult *jcat_result_old,
3044 GError **error)
3045{
3046 gint64 delta = 0;
3047
3048 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result), FALSE);
3049 g_return_val_if_fail (JCAT_IS_RESULT (jcat_result_old), FALSE);
3050
3051 if (jcat_result_get_timestamp (jcat_result) > 0 &&
3052 jcat_result_get_timestamp (jcat_result_old) > 0) {
3053 delta = jcat_result_get_timestamp (jcat_result) -
3054 jcat_result_get_timestamp (jcat_result_old);
3055 }
3056 if (delta < 0) {
3057 g_set_error (error,
3058 FWUPD_ERROR,
3059 FWUPD_ERROR_INVALID_FILE,
3060 "new signing timestamp was %"
3061 G_GINT64_FORMAT " seconds older",
3062 -delta);
3063 return FALSE;
3064 }
3065 if (delta > 0)
3066 g_debug ("timestamp increased, so no rollback");
3067 return TRUE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003068}
3069
Richard Hughes9945edb2017-06-19 10:03:55 +01003070/**
Richard Hughesbe5b0192020-01-17 14:32:23 +00003071 * fu_engine_update_metadata_bytes:
Richard Hughes9945edb2017-06-19 10:03:55 +01003072 * @self: A #FuEngine
Richard Hughes4eada342017-10-03 21:20:32 +01003073 * @remote_id: A remote ID, e.g. `lvfs`
Richard Hughesbe5b0192020-01-17 14:32:23 +00003074 * @bytes_raw: Blob of metadata
Richard Hughesd5aab652020-02-25 12:47:50 +00003075 * @bytes_sig: Blob of metadata signature, typically Jcat binary format
Richard Hughes9945edb2017-06-19 10:03:55 +01003076 * @error: A #GError, or %NULL
3077 *
3078 * Updates the metadata for a specific remote.
3079 *
Richard Hughes9945edb2017-06-19 10:03:55 +01003080 * Returns: %TRUE for success
3081 **/
3082gboolean
Richard Hughesbe5b0192020-01-17 14:32:23 +00003083fu_engine_update_metadata_bytes (FuEngine *self, const gchar *remote_id,
3084 GBytes *bytes_raw, GBytes *bytes_sig, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003085{
Richard Hughes7403dc52017-08-10 15:34:10 +01003086 FwupdKeyringKind keyring_kind;
Richard Hughes9945edb2017-06-19 10:03:55 +01003087 FwupdRemote *remote;
Mario Limonciellocf63aec2018-06-11 12:10:54 -05003088 g_autofree gchar *pki_dir = NULL;
3089 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003090
3091 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3092 g_return_val_if_fail (remote_id != NULL, FALSE);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003093 g_return_val_if_fail (bytes_raw != NULL, FALSE);
3094 g_return_val_if_fail (bytes_sig != NULL, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003095 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3096
Richard Hughes9945edb2017-06-19 10:03:55 +01003097 /* check remote is valid */
Richard Hughesd1808aa2019-12-10 15:20:30 +00003098 remote = fu_remote_list_get_by_id (self->remote_list, remote_id);
Richard Hughes9945edb2017-06-19 10:03:55 +01003099 if (remote == NULL) {
3100 g_set_error (error,
3101 FWUPD_ERROR,
3102 FWUPD_ERROR_NOT_FOUND,
3103 "remote %s not found", remote_id);
3104 return FALSE;
3105 }
3106 if (!fwupd_remote_get_enabled (remote)) {
3107 g_set_error (error,
3108 FWUPD_ERROR,
3109 FWUPD_ERROR_NOT_SUPPORTED,
3110 "remote %s not enabled", remote_id);
3111 return FALSE;
3112 }
3113
Richard Hughes9945edb2017-06-19 10:03:55 +01003114 /* verify file */
Richard Hughes7403dc52017-08-10 15:34:10 +01003115 keyring_kind = fwupd_remote_get_keyring_kind (remote);
3116 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
Richard Hughesd5aab652020-02-25 12:47:50 +00003117 JcatResult *jcat_result;
Richard Hughesf69a4812017-08-16 12:27:51 +01003118 g_autoptr(GError) error_local = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00003119 g_autoptr(GInputStream) istream = NULL;
3120 g_autoptr(GPtrArray) results = NULL;
3121 g_autoptr(JcatFile) jcat_file = jcat_file_new ();
3122 g_autoptr(JcatItem) jcat_item = NULL;
3123 g_autoptr(JcatResult) jcat_result_old = NULL;
3124
3125 /* load Jcat file */
3126 istream = g_memory_input_stream_new_from_bytes (bytes_sig);
3127 if (!jcat_file_import_stream (jcat_file, istream,
3128 JCAT_IMPORT_FLAG_NONE,
3129 NULL, error))
Richard Hughes7403dc52017-08-10 15:34:10 +01003130 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003131
3132 /* this should only be signing one thing */
3133 jcat_item = jcat_file_get_item_default (jcat_file, error);
3134 if (jcat_item == NULL)
Richard Hughes14047d72017-08-18 10:58:47 +01003135 return FALSE;
Richard Hughesd5aab652020-02-25 12:47:50 +00003136 results = jcat_context_verify_item (self->jcat_context,
3137 bytes_raw, jcat_item,
3138 JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM |
3139 JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE,
Richard Hughesf28abe72019-03-07 16:01:02 +00003140 error);
Richard Hughesd5aab652020-02-25 12:47:50 +00003141 if (results == NULL)
Richard Hughes7403dc52017-08-10 15:34:10 +01003142 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003143
Richard Hughesd5aab652020-02-25 12:47:50 +00003144 /* return the newest one */
3145 g_ptr_array_sort (results, fu_engine_sort_jcat_results_timestamp_cb);
3146 jcat_result = g_ptr_array_index (results, 0);
3147
Richard Hughesf69a4812017-08-16 12:27:51 +01003148 /* verify the metadata was signed later than the existing
3149 * metadata for this remote to mitigate a rollback attack */
Richard Hughesd5aab652020-02-25 12:47:50 +00003150 jcat_result_old = fu_engine_get_system_jcat_result (self, remote, &error_local);
3151 if (jcat_result_old == NULL) {
Richard Hughesf69a4812017-08-16 12:27:51 +01003152 if (g_error_matches (error_local,
3153 G_FILE_ERROR,
3154 G_FILE_ERROR_NOENT)) {
3155 g_debug ("no existing valid keyrings: %s",
3156 error_local->message);
3157 } else {
3158 g_warning ("could not get existing keyring result: %s",
3159 error_local->message);
3160 }
3161 } else {
Richard Hughesd5aab652020-02-25 12:47:50 +00003162 if (!fu_engine_validate_result_timestamp (jcat_result,
3163 jcat_result_old,
3164 error))
Richard Hughesf69a4812017-08-16 12:27:51 +01003165 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01003166 }
Richard Hughes7403dc52017-08-10 15:34:10 +01003167 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003168
Richard Hughes99e621d2017-08-16 12:24:18 +01003169 /* save XML and signature to remotes.d */
Richard Hughes943d2c92017-06-21 09:04:39 +01003170 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache (remote),
3171 bytes_raw, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003172 return FALSE;
Richard Hughes99e621d2017-08-16 12:24:18 +01003173 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
3174 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache_sig (remote),
3175 bytes_sig, error))
3176 return FALSE;
3177 }
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003178 if (!fu_engine_load_metadata_store (self, FU_ENGINE_LOAD_FLAG_NONE, error))
3179 return FALSE;
Richard Hughesf43381f2020-02-24 10:11:31 +00003180 fu_engine_md_refresh_devices (self);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003181 fu_engine_emit_changed (self);
3182 return TRUE;
Richard Hughesbe5b0192020-01-17 14:32:23 +00003183}
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003184
Richard Hughesbe5b0192020-01-17 14:32:23 +00003185/**
3186 * fu_engine_update_metadata:
3187 * @self: A #FuEngine
3188 * @remote_id: A remote ID, e.g. `lvfs`
3189 * @fd: file descriptor of the metadata
3190 * @fd_sig: file descriptor of the metadata signature
3191 * @error: A #GError, or %NULL
3192 *
3193 * Updates the metadata for a specific remote.
3194 *
3195 * Note: this will close the fds when done
3196 *
3197 * Returns: %TRUE for success
3198 **/
3199gboolean
3200fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
3201 gint fd, gint fd_sig, GError **error)
3202{
3203#ifdef HAVE_GIO_UNIX
3204 g_autoptr(GBytes) bytes_raw = NULL;
3205 g_autoptr(GBytes) bytes_sig = NULL;
3206 g_autoptr(GInputStream) stream_fd = NULL;
3207 g_autoptr(GInputStream) stream_sig = NULL;
3208
3209 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3210 g_return_val_if_fail (remote_id != NULL, FALSE);
3211 g_return_val_if_fail (fd > 0, FALSE);
3212 g_return_val_if_fail (fd_sig > 0, FALSE);
3213 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3214
3215 /* ensures the fd's are closed on error */
3216 stream_fd = g_unix_input_stream_new (fd, TRUE);
3217 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
3218
3219 /* read the entire file into memory */
3220 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
3221 if (bytes_raw == NULL)
3222 return FALSE;
3223
3224 /* read signature */
Richard Hughes64d868a2020-01-17 17:40:00 +00003225 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x100000, NULL, error);
Richard Hughesbe5b0192020-01-17 14:32:23 +00003226 if (bytes_sig == NULL)
3227 return FALSE;
3228
3229 /* update with blobs */
3230 return fu_engine_update_metadata_bytes (self, remote_id,
3231 bytes_raw, bytes_sig,
3232 error);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003233#else
3234 g_set_error (error,
3235 FWUPD_ERROR,
3236 FWUPD_ERROR_NOT_SUPPORTED,
3237 "Not supported as <glib-unix.h> is unavailable");
3238 return FALSE;
3239#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01003240}
3241
Richard Hughes9945edb2017-06-19 10:03:55 +01003242/**
Richard Hughes481aa2a2018-09-18 20:51:46 +01003243 * fu_engine_get_silo_from_blob:
Richard Hughes9945edb2017-06-19 10:03:55 +01003244 * @self: A #FuEngine
3245 * @blob_cab: A #GBytes
3246 * @error: A #GError, or %NULL
3247 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003248 * Creates a silo from a .cab file blob.
Richard Hughes9945edb2017-06-19 10:03:55 +01003249 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01003250 * Returns: (transfer container): a #XbSilo, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01003251 **/
Richard Hughes481aa2a2018-09-18 20:51:46 +01003252XbSilo *
3253fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003254{
Richard Hughesc6eb4162020-02-27 10:02:36 +00003255 g_autoptr(FuCabinet) cabinet = fu_cabinet_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003256 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003257
3258 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3259 g_return_val_if_fail (blob_cab != NULL, NULL);
3260 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3261
3262 /* load file */
Richard Hughes9945edb2017-06-19 10:03:55 +01003263 fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003264 fu_cabinet_set_size_max (cabinet, fu_engine_get_archive_size_max (self));
Richard Hughesd5aab652020-02-25 12:47:50 +00003265 fu_cabinet_set_jcat_context (cabinet, self->jcat_context);
Richard Hughesc6eb4162020-02-27 10:02:36 +00003266 if (!fu_cabinet_parse (cabinet, blob_cab, FU_CABINET_PARSE_FLAG_NONE, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003267 return NULL;
Richard Hughesc6eb4162020-02-27 10:02:36 +00003268 silo = fu_cabinet_get_silo (cabinet);
Mario Limonciello5735fd62017-07-13 15:47:52 -05003269 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003270 return g_steal_pointer (&silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01003271}
3272
Richard Hughes75449d92019-04-17 13:36:31 +01003273static FuDevice *
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003274fu_engine_get_result_from_component (FuEngine *self, XbNode *component, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003275{
Richard Hughes245885c2019-03-04 08:46:02 +00003276 FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003277 g_autoptr(FuInstallTask) task = NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003278 g_autoptr(FuDevice) dev = NULL;
Richard Hughes93b15762017-09-15 11:05:23 +01003279 g_autoptr(FwupdRelease) rel = NULL;
Salud Lemus980f2d92018-08-28 09:25:40 -07003280 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003281 g_autoptr(GPtrArray) provides = NULL;
3282 g_autoptr(XbNode) description = NULL;
3283 g_autoptr(XbNode) release = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003284
Richard Hughes75449d92019-04-17 13:36:31 +01003285 dev = fu_device_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003286 provides = xb_node_query (component,
Richard Hughes634da032018-11-05 11:42:20 +00003287 "provides/firmware[@type=$'flashed']",
Richard Hughes481aa2a2018-09-18 20:51:46 +01003288 0, &error_local);
3289 if (provides == NULL) {
3290 g_set_error (error,
3291 FWUPD_ERROR,
3292 FWUPD_ERROR_INTERNAL,
3293 "failed to get release: %s",
3294 error_local->message);
3295 return NULL;
3296 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003297 for (guint i = 0; i < provides->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01003298 XbNode *prov = XB_NODE (g_ptr_array_index (provides, i));
Richard Hughes9945edb2017-06-19 10:03:55 +01003299 const gchar *guid;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003300 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003301
Richard Hughes9945edb2017-06-19 10:03:55 +01003302 /* is a online or offline update appropriate */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003303 guid = xb_node_get_text (prov);
Richard Hughes9945edb2017-06-19 10:03:55 +01003304 if (guid == NULL)
3305 continue;
Richard Hughes40127542018-01-12 20:25:55 +00003306 device = fu_device_list_get_by_guid (self->device_list, guid, NULL);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003307 if (device != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01003308 fu_device_set_name (dev, fu_device_get_name (device));
3309 fu_device_set_flags (dev, fu_device_get_flags (device));
3310 fu_device_set_id (dev, fu_device_get_id (device));
Mario Limonciellof430da02020-03-09 14:03:03 -05003311 fu_device_set_version_raw (dev, fu_device_get_version_raw (device));
Mario Limonciello8bcdfaa2020-02-25 11:18:15 -06003312 fu_device_set_version_format (dev, fu_device_get_version_format (device));
3313 fu_device_set_version (dev, fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01003314 }
3315
3316 /* add GUID */
Richard Hughes75449d92019-04-17 13:36:31 +01003317 fu_device_add_guid (dev, guid);
Richard Hughes9945edb2017-06-19 10:03:55 +01003318 }
Richard Hughes75449d92019-04-17 13:36:31 +01003319 if (fu_device_get_guids(dev)->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003320 g_set_error_literal (error,
3321 FWUPD_ERROR,
3322 FWUPD_ERROR_INTERNAL,
3323 "component has no GUIDs");
3324 return NULL;
3325 }
3326
3327 /* check we can install it */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003328 task = fu_install_task_new (NULL, component);
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01003329 if (!fu_engine_check_requirements (self, task,
3330 FWUPD_INSTALL_FLAG_NONE,
3331 error))
Richard Hughes9945edb2017-06-19 10:03:55 +01003332 return NULL;
3333
3334 /* verify trust */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003335 release = xb_node_query_first (component,
3336 "releases/release",
3337 &error_local);
3338 if (release == NULL) {
3339 g_set_error (error,
3340 FWUPD_ERROR,
3341 FWUPD_ERROR_INTERNAL,
3342 "failed to get release: %s",
3343 error_local->message);
3344 return NULL;
3345 }
Richard Hughes245885c2019-03-04 08:46:02 +00003346 if (!fu_keyring_get_release_flags (release,
3347 &release_flags,
3348 &error_local)) {
Salud Lemus980f2d92018-08-28 09:25:40 -07003349 if (g_error_matches (error_local,
3350 FWUPD_ERROR,
3351 FWUPD_ERROR_NOT_SUPPORTED)) {
3352 g_warning ("Ignoring verification: %s",
3353 error_local->message);
3354 } else {
3355 g_propagate_error (error, g_steal_pointer (&error_local));
3356 return NULL;
3357 }
3358 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003359
Richard Hughes9945edb2017-06-19 10:03:55 +01003360 /* create a result with all the metadata in */
Richard Hughes481aa2a2018-09-18 20:51:46 +01003361 description = xb_node_query_first (component, "description", NULL);
3362 if (description != NULL) {
3363 g_autofree gchar *xml = NULL;
3364 xml = xb_node_export (description,
3365 XB_NODE_EXPORT_FLAG_ONLY_CHILDREN,
3366 NULL);
3367 if (xml != NULL)
Richard Hughes75449d92019-04-17 13:36:31 +01003368 fu_device_set_description (dev, xml);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003369 }
Richard Hughes93b15762017-09-15 11:05:23 +01003370 rel = fwupd_release_new ();
Richard Hughes245885c2019-03-04 08:46:02 +00003371 fwupd_release_set_flags (rel, release_flags);
Richard Hughes75449d92019-04-17 13:36:31 +01003372 if (!fu_engine_set_release_from_appstream (self, dev, rel, component, release, error))
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003373 return NULL;
Richard Hughes75449d92019-04-17 13:36:31 +01003374 fu_device_add_release (dev, rel);
Richard Hughes93b15762017-09-15 11:05:23 +01003375 return g_steal_pointer (&dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003376}
3377
3378/**
Richard Hughes07f963a2017-09-15 14:28:47 +01003379 * fu_engine_get_details:
Richard Hughes9945edb2017-06-19 10:03:55 +01003380 * @self: A #FuEngine
3381 * @fd: A file descriptor
3382 * @error: A #GError, or %NULL
3383 *
3384 * Gets the details about a local file.
3385 *
3386 * Note: this will close the fd when done
3387 *
Richard Hughes75449d92019-04-17 13:36:31 +01003388 * Returns: (transfer container) (element-type FuDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01003389 **/
3390GPtrArray *
Richard Hughes07f963a2017-09-15 14:28:47 +01003391fu_engine_get_details (FuEngine *self, gint fd, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01003392{
Richard Hughes534255c2018-01-28 19:51:56 +00003393 const gchar *remote_id;
3394 g_autofree gchar *csum = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003395 g_autoptr(GBytes) blob = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003396 g_autoptr(GError) error_local = NULL;
3397 g_autoptr(GPtrArray) components = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003398 g_autoptr(GPtrArray) details = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003399 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003400
3401 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3402 g_return_val_if_fail (fd > 0, NULL);
3403 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3404
Richard Hughes481aa2a2018-09-18 20:51:46 +01003405 /* get all components */
Richard Hughesc7bbbc22018-01-02 22:22:25 +00003406 blob = fu_common_get_contents_fd (fd,
3407 fu_engine_get_archive_size_max (self),
3408 error);
Richard Hughes9945edb2017-06-19 10:03:55 +01003409 if (blob == NULL)
3410 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003411 silo = fu_engine_get_silo_from_blob (self, blob, error);
3412 if (silo == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01003413 return NULL;
Mario Limonciello51ddf182019-01-26 00:31:58 -06003414 components = xb_silo_query (silo, "components/component", 0, &error_local);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003415 if (components == NULL) {
3416 g_set_error (error,
3417 FWUPD_ERROR,
3418 FWUPD_ERROR_INVALID_FILE,
3419 "no components: %s",
3420 error_local->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01003421 return NULL;
3422 }
3423
Richard Hughesec6190c2018-11-25 12:19:33 +00003424 /* build the index */
Mario Limonciello51ddf182019-01-26 00:31:58 -06003425 if (!xb_silo_query_build_index (silo, "components/component/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00003426 "type", error))
Richard Hughes2eee2582019-03-11 13:17:39 +00003427 return NULL;
Mario Limonciello51ddf182019-01-26 00:31:58 -06003428 if (!xb_silo_query_build_index (silo, "components/component/provides/firmware",
Richard Hughesec6190c2018-11-25 12:19:33 +00003429 NULL, error))
Richard Hughes2eee2582019-03-11 13:17:39 +00003430 return NULL;
Richard Hughesec6190c2018-11-25 12:19:33 +00003431
Richard Hughes534255c2018-01-28 19:51:56 +00003432 /* does this exist in any enabled remote */
3433 csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
3434 remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
3435
Richard Hughes9945edb2017-06-19 10:03:55 +01003436 /* create results with all the metadata in */
3437 details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003438 for (guint i = 0; i < components->len; i++) {
3439 XbNode *component = g_ptr_array_index (components, i);
Richard Hughes75449d92019-04-17 13:36:31 +01003440 FuDevice *dev;
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003441 dev = fu_engine_get_result_from_component (self, component, error);
Richard Hughes534255c2018-01-28 19:51:56 +00003442 if (dev == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01003443 return NULL;
Richard Hughes534255c2018-01-28 19:51:56 +00003444 if (remote_id != NULL) {
Richard Hughes75449d92019-04-17 13:36:31 +01003445 FwupdRelease *rel = fu_device_get_release_default (dev);
Richard Hughes534255c2018-01-28 19:51:56 +00003446 fwupd_release_set_remote_id (rel, remote_id);
Richard Hughes75449d92019-04-17 13:36:31 +01003447 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes534255c2018-01-28 19:51:56 +00003448 }
Mario Limonciellof430da02020-03-09 14:03:03 -05003449 fu_engine_md_refresh_device_from_component (self, dev, component);
Richard Hughes534255c2018-01-28 19:51:56 +00003450 g_ptr_array_add (details, dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01003451 }
3452 return g_steal_pointer (&details);
3453}
3454
Mario Limonciello343095d2018-10-23 17:21:13 -05003455static gint
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003456fu_engine_sort_devices_by_priority_name (gconstpointer a, gconstpointer b)
Mario Limonciello343095d2018-10-23 17:21:13 -05003457{
3458 FuDevice *dev_a = *((FuDevice **) a);
3459 FuDevice *dev_b = *((FuDevice **) b);
3460 gint prio_a = fu_device_get_priority (dev_a);
3461 gint prio_b = fu_device_get_priority (dev_b);
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003462 const gchar *name_a = fu_device_get_name (dev_a);
3463 const gchar *name_b = fu_device_get_name (dev_b);
Mario Limonciello343095d2018-10-23 17:21:13 -05003464
3465 if (prio_a > prio_b)
3466 return -1;
3467 if (prio_a < prio_b)
3468 return 1;
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003469 if (g_strcmp0 (name_a, name_b) > 0)
3470 return 1;
3471 if (g_strcmp0 (name_a, name_b) < 0)
3472 return -1;
Mario Limonciello343095d2018-10-23 17:21:13 -05003473 return 0;
3474}
3475
Richard Hughes9945edb2017-06-19 10:03:55 +01003476/**
3477 * fu_engine_get_devices:
3478 * @self: A #FuEngine
3479 * @error: A #GError, or %NULL
3480 *
3481 * Gets the list of devices.
3482 *
3483 * Returns: (transfer container) (element-type FwupdDevice): results
3484 **/
3485GPtrArray *
3486fu_engine_get_devices (FuEngine *self, GError **error)
3487{
Richard Hughesf2eccde2017-09-20 11:18:03 +01003488 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003489
3490 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3491 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3492
Richard Hughes70425fe2017-11-22 12:33:27 +00003493 devices = fu_device_list_get_active (self->device_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003494 if (devices->len == 0) {
3495 g_set_error_literal (error,
3496 FWUPD_ERROR,
3497 FWUPD_ERROR_NOTHING_TO_DO,
3498 "No detected devices");
3499 return NULL;
3500 }
Mario Limonciello6ff8a162019-08-23 12:55:10 -05003501 g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority_name);
Richard Hughesf2eccde2017-09-20 11:18:03 +01003502 return g_steal_pointer (&devices);
Richard Hughes9945edb2017-06-19 10:03:55 +01003503}
3504
Richard Hughes12040b52018-05-14 13:32:10 +01003505/**
3506 * fu_engine_get_device:
3507 * @self: A #FuEngine
3508 * @device_id: A device ID
3509 * @error: A #GError, or %NULL
3510 *
3511 * Gets a specific device.
3512 *
3513 * Returns: (transfer full): a device, or %NULL if not found
3514 **/
Richard Hughes4ad41f02018-05-08 14:35:36 +01003515FuDevice *
3516fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error)
3517{
Richard Hughes12040b52018-05-14 13:32:10 +01003518 FuDevice *device;
Richard Hughes37d09432018-09-09 10:39:45 +01003519
Richard Hughes12040b52018-05-14 13:32:10 +01003520 device = fu_device_list_get_by_id (self->device_list, device_id, error);
3521 if (device == NULL)
3522 return NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003523 return device;
Richard Hughes4ad41f02018-05-08 14:35:36 +01003524}
3525
Richard Hughes9945edb2017-06-19 10:03:55 +01003526/**
Richard Hughes476363a2018-01-11 10:08:58 +00003527 * fu_engine_get_history:
3528 * @self: A #FuEngine
3529 * @error: A #GError, or %NULL
3530 *
3531 * Gets the list of history.
3532 *
3533 * Returns: (transfer container) (element-type FwupdDevice): results
3534 **/
3535GPtrArray *
3536fu_engine_get_history (FuEngine *self, GError **error)
3537{
3538 g_autoptr(GPtrArray) devices = NULL;
3539
3540 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3541 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3542
3543 devices = fu_history_get_devices (self->history, error);
3544 if (devices == NULL)
3545 return NULL;
3546 if (devices->len == 0) {
3547 g_set_error_literal (error,
3548 FWUPD_ERROR,
3549 FWUPD_ERROR_NOTHING_TO_DO,
3550 "No history");
3551 return NULL;
3552 }
Richard Hughes611f1a92018-01-11 11:54:28 +00003553
3554 /* try to set the remote ID for each device */
3555 for (guint i = 0; i < devices->len; i++) {
3556 FuDevice *dev = g_ptr_array_index (devices, i);
3557 FwupdRelease *rel;
3558 GPtrArray *csums;
3559
3560 /* get the checksums */
3561 rel = fu_device_get_release_default (dev);
3562 if (rel == NULL)
3563 continue;
3564
3565 /* find the checksum that matches */
3566 csums = fwupd_release_get_checksums (rel);
3567 for (guint j = 0; j < csums->len; j++) {
3568 const gchar *csum = g_ptr_array_index (csums, j);
3569 const gchar *remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
3570 if (remote_id != NULL) {
3571 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
3572 fwupd_release_set_remote_id (rel, remote_id);
3573 break;
3574 }
3575 }
3576 }
3577
Richard Hughes476363a2018-01-11 10:08:58 +00003578 return g_steal_pointer (&devices);
3579}
3580
3581/**
Richard Hughes9945edb2017-06-19 10:03:55 +01003582 * fu_engine_get_remotes:
3583 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +01003584 * @error: A #GError, or %NULL
3585 *
3586 * Gets the list of remotes in use by the engine.
3587 *
3588 * Returns: (transfer container) (element-type FwupdRemote): results
3589 **/
3590GPtrArray *
3591fu_engine_get_remotes (FuEngine *self, GError **error)
3592{
3593 GPtrArray *remotes;
3594
3595 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3596 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3597
Richard Hughesd1808aa2019-12-10 15:20:30 +00003598 remotes = fu_remote_list_get_all (self->remote_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003599 if (remotes->len == 0) {
3600 g_set_error (error,
3601 FWUPD_ERROR,
3602 FWUPD_ERROR_INTERNAL,
3603 "No remotes configured");
3604 return NULL;
3605 }
3606 return g_ptr_array_ref (remotes);
3607}
3608
Mario Limonciello46aaee82019-01-10 12:58:00 -06003609/**
3610 * fu_engine_get_remote_by_id:
3611 * @self: A #FuEngine
3612 * @remote_id: A string representation of a remote
3613 * @error: A #GError, or %NULL
3614 *
3615 * Gets the FwupdRemote object.
3616 *
3617 * Returns: FwupdRemote
3618 **/
3619FwupdRemote *
3620fu_engine_get_remote_by_id (FuEngine *self, const gchar *remote_id, GError **error)
3621{
3622 g_autoptr(GPtrArray) remotes = NULL;
3623
3624 remotes = fu_engine_get_remotes (self, error);
3625 if (remotes == NULL)
3626 return NULL;
3627
3628 for (guint i = 0; i < remotes->len; i++) {
3629 FwupdRemote *remote = g_ptr_array_index (remotes, i);
3630 if (g_strcmp0 (remote_id, fwupd_remote_get_id (remote)) == 0)
3631 return remote;
3632 }
3633
3634 g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
3635 "Couldn't find remote %s", remote_id);
3636
3637 return NULL;
3638}
3639
3640
Richard Hughes225f3a92017-09-13 19:38:51 +01003641static gint
Richard Hughes9a680842020-02-20 11:11:13 +00003642fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b, gpointer user_data)
Richard Hughes225f3a92017-09-13 19:38:51 +01003643{
Richard Hughes9a680842020-02-20 11:11:13 +00003644 FuDevice *device = FU_DEVICE (user_data);
Richard Hughes225f3a92017-09-13 19:38:51 +01003645 FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a));
3646 FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b));
Richard Hughes9a680842020-02-20 11:11:13 +00003647 return fu_common_vercmp_full (fwupd_release_get_version (rel_b),
3648 fwupd_release_get_version (rel_a),
3649 fu_device_get_version_format (device));
Richard Hughes225f3a92017-09-13 19:38:51 +01003650}
3651
Richard Hughes481aa2a2018-09-18 20:51:46 +01003652static gboolean
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003653fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel)
3654{
3655 GPtrArray *csums = fwupd_release_get_checksums (rel);
3656 for (guint i = 0; i < csums->len; i++) {
3657 const gchar *csum = g_ptr_array_index (csums, i);
3658 g_debug ("checking %s against approved list", csum);
3659 if (g_hash_table_lookup (self->approved_firmware, csum) != NULL)
3660 return TRUE;
3661 }
3662 return FALSE;
3663}
3664
3665static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01003666fu_engine_add_releases_for_device_component (FuEngine *self,
3667 FuDevice *device,
3668 XbNode *component,
3669 GPtrArray *releases,
3670 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +00003671{
Richard Hughes9a680842020-02-20 11:11:13 +00003672 FwupdVersionFormat fmt = fu_device_get_version_format (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003673 g_autoptr(GError) error_local = NULL;
3674 g_autoptr(FuInstallTask) task = fu_install_task_new (device, component);
3675 g_autoptr(GPtrArray) releases_tmp = NULL;
Richard Hughes650dade2017-12-14 14:43:11 +00003676
Richard Hughes481aa2a2018-09-18 20:51:46 +01003677 if (!fu_engine_check_requirements (self, task,
Richard Hughesce756272019-03-24 12:21:34 +00003678 FWUPD_INSTALL_FLAG_OFFLINE |
Richard Hughes481aa2a2018-09-18 20:51:46 +01003679 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
3680 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
3681 error))
3682 return FALSE;
3683
3684 /* get all releases */
3685 releases_tmp = xb_node_query (component, "releases/release", 0, &error_local);
3686 if (releases_tmp == NULL) {
3687 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
3688 return TRUE;
3689 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
3690 return TRUE;
3691 g_propagate_error (error, g_steal_pointer (&error_local));
3692 return FALSE;
3693 }
3694 for (guint i = 0; i < releases_tmp->len; i++) {
3695 XbNode *release = g_ptr_array_index (releases_tmp, i);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003696 const gchar *remote_id;
Mario Limonciello32241f42019-01-24 10:12:41 -06003697 const gchar *update_message;
Richard Hughes8e0cc802019-03-04 14:10:17 +00003698 gint vercmp;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003699 GPtrArray *checksums;
3700 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003701 g_autoptr(GError) error_loop = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003702
3703 /* create new FwupdRelease for the XbNode */
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003704 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01003705 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01003706 rel,
3707 component,
3708 release,
3709 &error_loop)) {
3710 g_warning ("failed to set release for component: %s",
3711 error_loop->message);
3712 continue;
3713 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003714
Richard Hughes6e0c8f82018-11-11 21:46:41 +00003715 /* fall back to quirk-provided value */
3716 if (fwupd_release_get_install_duration (rel) == 0)
3717 fwupd_release_set_install_duration (rel, fu_device_get_install_duration (device));
3718
Richard Hughes481aa2a2018-09-18 20:51:46 +01003719 /* invalid */
3720 if (fwupd_release_get_uri (rel) == NULL)
Richard Hughes650dade2017-12-14 14:43:11 +00003721 continue;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003722 checksums = fwupd_release_get_checksums (rel);
3723 if (checksums->len == 0)
3724 continue;
3725
Richard Hughes8e0cc802019-03-04 14:10:17 +00003726 /* test for upgrade or downgrade */
Richard Hughes9a680842020-02-20 11:11:13 +00003727 vercmp = fu_common_vercmp_full (fwupd_release_get_version (rel),
3728 fu_device_get_version (device),
3729 fmt);
Richard Hughes8e0cc802019-03-04 14:10:17 +00003730 if (vercmp > 0)
3731 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_UPGRADE);
3732 else if (vercmp < 0)
3733 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_IS_DOWNGRADE);
3734
3735 /* lower than allowed to downgrade to */
3736 if (fu_device_get_version_lowest (device) != NULL &&
Richard Hughes9a680842020-02-20 11:11:13 +00003737 fu_common_vercmp_full (fwupd_release_get_version (rel),
3738 fu_device_get_version_lowest (device),
3739 fmt) < 0) {
Richard Hughes8e0cc802019-03-04 14:10:17 +00003740 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION);
3741 }
3742
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003743 /* check if remote is whitelisting firmware */
3744 remote_id = fwupd_release_get_remote_id (rel);
3745 if (remote_id != NULL) {
3746 FwupdRemote *remote = fu_engine_get_remote_by_id (self, remote_id, NULL);
3747 if (remote != NULL &&
3748 fwupd_remote_get_approval_required (remote) &&
3749 !fu_engine_check_release_is_approved (self, rel)) {
3750 fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL);
3751 }
3752 }
3753
Mario Limonciello32241f42019-01-24 10:12:41 -06003754 /* add update message if exists but device doesn't already have one */
3755 update_message = fwupd_release_get_update_message (rel);
3756 if (fwupd_device_get_update_message (FWUPD_DEVICE (device)) == NULL &&
3757 update_message != NULL) {
3758 fwupd_device_set_update_message (FWUPD_DEVICE (device), update_message);
3759 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003760 /* success */
3761 g_ptr_array_add (releases, g_steal_pointer (&rel));
Richard Hughes650dade2017-12-14 14:43:11 +00003762 }
3763
Richard Hughes481aa2a2018-09-18 20:51:46 +01003764 /* success */
3765 return TRUE;
Richard Hughes650dade2017-12-14 14:43:11 +00003766}
3767
Filipe Laínsd3a4fd32020-03-30 21:05:14 +01003768GPtrArray *
Richard Hughes97284b12017-09-13 17:07:58 +01003769fu_engine_get_releases_for_device (FuEngine *self, FuDevice *device, GError **error)
3770{
3771 GPtrArray *device_guids;
3772 GPtrArray *releases;
3773 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003774 g_autoptr(GError) error_all = NULL;
3775 g_autoptr(GError) error_local = NULL;
3776 g_autoptr(GPtrArray) components = NULL;
3777 g_autoptr(GString) xpath = g_string_new (NULL);
Richard Hughes97284b12017-09-13 17:07:58 +01003778
3779 /* get device version */
3780 version = fu_device_get_version (device);
3781 if (version == NULL) {
3782 g_set_error (error,
3783 FWUPD_ERROR,
3784 FWUPD_ERROR_NOT_SUPPORTED,
3785 "no version set");
3786 return NULL;
3787 }
3788
3789 /* only show devices that can be updated */
3790 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003791 g_set_error_literal (error,
3792 FWUPD_ERROR,
3793 FWUPD_ERROR_NOT_SUPPORTED,
3794 "is not updatable");
Richard Hughes97284b12017-09-13 17:07:58 +01003795 return NULL;
3796 }
3797
Richard Hughes481aa2a2018-09-18 20:51:46 +01003798 /* get all the components that provide any of these GUIDs */
Richard Hughes97284b12017-09-13 17:07:58 +01003799 device_guids = fu_device_get_guids (device);
3800 for (guint i = 0; i < device_guids->len; i++) {
Richard Hughes97284b12017-09-13 17:07:58 +01003801 const gchar *guid = g_ptr_array_index (device_guids, i);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003802 xb_string_append_union (xpath,
3803 "components/component/"
Richard Hughes634da032018-11-05 11:42:20 +00003804 "provides/firmware[@type=$'flashed'][text()=$'%s']/"
Richard Hughes481aa2a2018-09-18 20:51:46 +01003805 "../..", guid);
3806 }
3807 components = xb_silo_query (self->silo, xpath->str, 0, &error_local);
3808 if (components == NULL) {
Richard Hughesa07a80f2018-12-03 14:57:33 +00003809 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
3810 g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003811 g_set_error_literal (error,
3812 FWUPD_ERROR,
3813 FWUPD_ERROR_NOTHING_TO_DO,
3814 "No releases found");
Richard Hughesb095df62018-11-05 11:41:36 +00003815 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01003816 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01003817 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughesb095df62018-11-05 11:41:36 +00003818 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01003819 }
3820
3821 /* find all the releases that pass all the requirements */
3822 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
3823 for (guint i = 0; i < components->len; i++) {
3824 XbNode *component = XB_NODE (g_ptr_array_index (components, i));
3825 g_autoptr(GError) error_tmp = NULL;
3826 if (!fu_engine_add_releases_for_device_component (self,
3827 device,
3828 component,
3829 releases,
3830 &error_tmp)) {
3831 if (error_all == NULL) {
3832 error_all = g_steal_pointer (&error_tmp);
3833 continue;
3834 }
3835
3836 /* assume the domain and code is the same */
3837 g_prefix_error (&error_all, "%s, ", error_tmp->message);
3838 }
3839 }
3840
3841 /* return the compound error */
3842 if (releases->len == 0) {
Richard Hughesd56ad5b2018-11-12 10:48:06 +00003843 if (error_all != NULL) {
Richard Hughes5b715e12019-04-08 13:33:59 +01003844 g_propagate_prefixed_error (error, g_steal_pointer (&error_all),
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003845 "No releases found: ");
Richard Hughesd56ad5b2018-11-12 10:48:06 +00003846 return NULL;
3847 }
3848 g_set_error (error,
3849 FWUPD_ERROR,
3850 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003851 "No releases found");
Richard Hughes481aa2a2018-09-18 20:51:46 +01003852 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01003853 }
3854 return releases;
3855}
3856
Richard Hughes9945edb2017-06-19 10:03:55 +01003857/**
3858 * fu_engine_get_releases:
3859 * @self: A #FuEngine
3860 * @device_id: A device ID
3861 * @error: A #GError, or %NULL
3862 *
3863 * Gets the releases available for a specific device.
3864 *
Richard Hughes93b15762017-09-15 11:05:23 +01003865 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01003866 **/
3867GPtrArray *
3868fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error)
3869{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003870 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003871 g_autoptr(GPtrArray) releases = NULL;
3872
3873 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3874 g_return_val_if_fail (device_id != NULL, NULL);
3875 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3876
3877 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00003878 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003879 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01003880 return NULL;
3881
3882 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00003883 releases = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01003884 if (releases == NULL)
3885 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003886 if (releases->len == 0) {
3887 g_set_error_literal (error,
3888 FWUPD_ERROR,
3889 FWUPD_ERROR_NOTHING_TO_DO,
3890 "No releases for device");
3891 return NULL;
3892 }
Richard Hughes9a680842020-02-20 11:11:13 +00003893 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes97284b12017-09-13 17:07:58 +01003894 return g_steal_pointer (&releases);
3895}
3896
3897/**
3898 * fu_engine_get_downgrades:
3899 * @self: A #FuEngine
3900 * @device_id: A device ID
3901 * @error: A #GError, or %NULL
3902 *
3903 * Gets the downgrades available for a specific device.
3904 *
Richard Hughes93b15762017-09-15 11:05:23 +01003905 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes97284b12017-09-13 17:07:58 +01003906 **/
3907GPtrArray *
3908fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error)
3909{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003910 g_autoptr(FuDevice) device = NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01003911 g_autoptr(GPtrArray) releases = NULL;
3912 g_autoptr(GPtrArray) releases_tmp = NULL;
3913 g_autoptr(GString) error_str = g_string_new (NULL);
3914
3915 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
3916 g_return_val_if_fail (device_id != NULL, NULL);
3917 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
3918
3919 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00003920 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003921 if (device == NULL)
Richard Hughes97284b12017-09-13 17:07:58 +01003922 return NULL;
3923
3924 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00003925 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01003926 if (releases_tmp == NULL)
3927 return NULL;
3928 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
3929 for (guint i = 0; i < releases_tmp->len; i++) {
3930 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughes97284b12017-09-13 17:07:58 +01003931
Richard Hughes8e0cc802019-03-04 14:10:17 +00003932 /* same as installed */
3933 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
3934 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01003935 g_string_append_printf (error_str, "%s=same, ",
3936 fwupd_release_get_version (rel_tmp));
3937 g_debug ("ignoring %s as the same as %s",
3938 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00003939 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01003940 continue;
3941 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00003942
3943 /* newer than current */
3944 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE)) {
Richard Hughes97284b12017-09-13 17:07:58 +01003945 g_string_append_printf (error_str, "%s=newer, ",
3946 fwupd_release_get_version (rel_tmp));
3947 g_debug ("ignoring %s as newer than %s",
3948 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00003949 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01003950 continue;
3951 }
3952
Richard Hughesdce91202019-04-08 12:47:45 +01003953 /* don't show releases we are not allowed to downgrade to */
Richard Hughes8e0cc802019-03-04 14:10:17 +00003954 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_VERSION)) {
3955 g_string_append_printf (error_str, "%s=lowest, ",
3956 fwupd_release_get_version (rel_tmp));
3957 g_debug ("ignoring %s as older than lowest %s",
3958 fwupd_release_get_version (rel_tmp),
3959 fu_device_get_version_lowest (device));
3960 continue;
Richard Hughes97284b12017-09-13 17:07:58 +01003961 }
3962 g_ptr_array_add (releases, g_object_ref (rel_tmp));
3963 }
3964 if (error_str->len > 2)
3965 g_string_truncate (error_str, error_str->len - 2);
3966 if (releases->len == 0) {
3967 if (error_str->len > 0) {
3968 g_set_error (error,
3969 FWUPD_ERROR,
3970 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003971 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00003972 fu_device_get_version (device),
Richard Hughes97284b12017-09-13 17:07:58 +01003973 error_str->str);
3974 } else {
3975 g_set_error (error,
3976 FWUPD_ERROR,
3977 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06003978 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00003979 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01003980 }
3981 return NULL;
3982 }
Richard Hughes9a680842020-02-20 11:11:13 +00003983 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01003984 return g_steal_pointer (&releases);
3985}
3986
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00003987GPtrArray *
3988fu_engine_get_approved_firmware (FuEngine *self)
3989{
3990 GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free);
3991 g_autoptr(GList) keys = g_hash_table_get_keys (self->approved_firmware);
3992 for (GList *l = keys; l != NULL; l = l->next) {
3993 const gchar *csum = l->data;
3994 g_ptr_array_add (checksums, g_strdup (csum));
3995 }
3996 return checksums;
3997}
3998
3999void
4000fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum)
4001{
4002 g_hash_table_add (self->approved_firmware, g_strdup (checksum));
4003}
4004
Richard Hughes3d607622019-03-07 16:59:27 +00004005gchar *
4006fu_engine_self_sign (FuEngine *self,
4007 const gchar *value,
Richard Hughesd5aab652020-02-25 12:47:50 +00004008 JcatSignFlags flags,
Richard Hughes3d607622019-03-07 16:59:27 +00004009 GError **error)
4010{
Richard Hughesd5aab652020-02-25 12:47:50 +00004011 g_autoptr(JcatBlob) jcat_signature = NULL;
4012 g_autoptr(JcatEngine) jcat_engine = NULL;
4013 g_autoptr(JcatResult) jcat_result = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004014 g_autoptr(GBytes) payload = NULL;
Richard Hughes3d607622019-03-07 16:59:27 +00004015
4016 /* create detached signature and verify */
Richard Hughesd5aab652020-02-25 12:47:50 +00004017 jcat_engine = jcat_context_get_engine (self->jcat_context,
4018 JCAT_BLOB_KIND_PKCS7,
4019 error);
4020 if (jcat_engine == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004021 return NULL;
4022 payload = g_bytes_new (value, strlen (value));
Richard Hughesd5aab652020-02-25 12:47:50 +00004023 jcat_signature = jcat_engine_self_sign (jcat_engine, payload, flags, error);
4024 if (jcat_signature == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004025 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004026 jcat_result = jcat_engine_self_verify (jcat_engine, payload,
4027 jcat_blob_get_data (jcat_signature),
4028 JCAT_VERIFY_FLAG_NONE, error);
4029 if (jcat_result == NULL)
Richard Hughes3d607622019-03-07 16:59:27 +00004030 return NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00004031 return jcat_blob_get_data_as_string (jcat_signature);
Richard Hughes3d607622019-03-07 16:59:27 +00004032}
4033
Richard Hughes9945edb2017-06-19 10:03:55 +01004034/**
Richard Hughesa96413a2017-09-13 17:19:59 +01004035 * fu_engine_get_upgrades:
4036 * @self: A #FuEngine
4037 * @device_id: A device ID
4038 * @error: A #GError, or %NULL
4039 *
4040 * Gets the upgrades available for a specific device.
4041 *
Richard Hughes93b15762017-09-15 11:05:23 +01004042 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughesa96413a2017-09-13 17:19:59 +01004043 **/
4044GPtrArray *
4045fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error)
4046{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004047 g_autoptr(FuDevice) device = NULL;
Richard Hughesa96413a2017-09-13 17:19:59 +01004048 g_autoptr(GPtrArray) releases = NULL;
4049 g_autoptr(GPtrArray) releases_tmp = NULL;
4050 g_autoptr(GString) error_str = g_string_new (NULL);
4051
4052 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4053 g_return_val_if_fail (device_id != NULL, NULL);
4054 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4055
4056 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00004057 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004058 if (device == NULL)
Richard Hughesa96413a2017-09-13 17:19:59 +01004059 return NULL;
4060
Mario Limonciellofc139352018-09-13 10:06:39 -05004061 /* don't show upgrades again until we reboot */
4062 if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004063 g_set_error_literal (error,
4064 FWUPD_ERROR,
4065 FWUPD_ERROR_NOTHING_TO_DO,
4066 "A reboot is pending");
Mario Limonciellofc139352018-09-13 10:06:39 -05004067 return NULL;
4068 }
4069
Richard Hughesa96413a2017-09-13 17:19:59 +01004070 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004071 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughesa96413a2017-09-13 17:19:59 +01004072 if (releases_tmp == NULL)
4073 return NULL;
4074 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
4075 for (guint i = 0; i < releases_tmp->len; i++) {
4076 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
Richard Hughesa96413a2017-09-13 17:19:59 +01004077
Richard Hughes8e0cc802019-03-04 14:10:17 +00004078 /* same as installed */
4079 if (!fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_UPGRADE) &&
4080 !fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01004081 g_string_append_printf (error_str, "%s=same, ",
4082 fwupd_release_get_version (rel_tmp));
4083 g_debug ("ignoring %s as the same as %s",
4084 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004085 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004086 continue;
4087 }
Richard Hughes8e0cc802019-03-04 14:10:17 +00004088
4089 /* older than current */
4090 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) {
Richard Hughesa96413a2017-09-13 17:19:59 +01004091 g_string_append_printf (error_str, "%s=older, ",
4092 fwupd_release_get_version (rel_tmp));
4093 g_debug ("ignoring %s as older than %s",
4094 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00004095 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004096 continue;
4097 }
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00004098
4099 /* not approved */
4100 if (fwupd_release_has_flag (rel_tmp, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL)) {
4101 g_string_append_printf (error_str, "%s=not-approved, ",
4102 fwupd_release_get_version (rel_tmp));
4103 g_debug ("ignoring %s as not approved as required by %s",
4104 fwupd_release_get_version (rel_tmp),
4105 fwupd_release_get_remote_id (rel_tmp));
4106 continue;
4107 }
4108
Richard Hughesa96413a2017-09-13 17:19:59 +01004109 g_ptr_array_add (releases, g_object_ref (rel_tmp));
4110 }
4111 if (error_str->len > 2)
4112 g_string_truncate (error_str, error_str->len - 2);
4113 if (releases->len == 0) {
4114 if (error_str->len > 0) {
4115 g_set_error (error,
4116 FWUPD_ERROR,
4117 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004118 "current version is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004119 fu_device_get_version (device),
Richard Hughesa96413a2017-09-13 17:19:59 +01004120 error_str->str);
4121 } else {
4122 g_set_error (error,
4123 FWUPD_ERROR,
4124 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello7e4949c2019-12-09 10:21:20 -06004125 "current version is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00004126 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01004127 }
4128 return NULL;
4129 }
Richard Hughes9a680842020-02-20 11:11:13 +00004130 g_ptr_array_sort_with_data (releases, fu_engine_sort_releases_cb, device);
Richard Hughesa96413a2017-09-13 17:19:59 +01004131 return g_steal_pointer (&releases);
4132}
4133
4134/**
Richard Hughes9945edb2017-06-19 10:03:55 +01004135 * fu_engine_clear_results:
4136 * @self: A #FuEngine
4137 * @device_id: A device ID
4138 * @error: A #GError, or %NULL
4139 *
4140 * Clear the historical state of a specific device operation.
4141 *
4142 * Returns: %TRUE for success
4143 **/
4144gboolean
4145fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error)
4146{
Richard Hughes65e44ca2018-01-30 17:26:30 +00004147 g_autoptr(FuDevice) device = NULL;
Richard Hughes34834102017-11-21 21:55:00 +00004148 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +01004149
4150 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
4151 g_return_val_if_fail (device_id != NULL, FALSE);
4152 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
4153
4154 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00004155 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004156 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01004157 return FALSE;
4158
Richard Hughes65e44ca2018-01-30 17:26:30 +00004159 /* already set on the database */
4160 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
4161 g_set_error_literal (error,
4162 FWUPD_ERROR,
4163 FWUPD_ERROR_NOT_SUPPORTED,
4164 "device already has notified flag");
4165 return FALSE;
4166 }
4167
4168 /* call into the plugin if it still exists */
Richard Hughese7e95452017-11-22 09:05:53 +00004169 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004170 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00004171 error);
Richard Hughes65e44ca2018-01-30 17:26:30 +00004172 if (plugin != NULL) {
4173 if (!fu_plugin_runner_clear_results (plugin, device, error))
4174 return FALSE;
4175 }
Richard Hughes34834102017-11-21 21:55:00 +00004176
Richard Hughes65e44ca2018-01-30 17:26:30 +00004177 /* override */
Richard Hughesc0cd0232018-01-31 15:02:00 +00004178 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED);
Richard Hughes0bbef292019-11-01 12:15:15 +00004179 return fu_history_modify_device (self->history, device, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01004180}
4181
4182/**
4183 * fu_engine_get_results:
4184 * @self: A #FuEngine
4185 * @device_id: A device ID
4186 * @error: A #GError, or %NULL
4187 *
4188 * Gets the historical state of a specific device operation.
4189 *
Richard Hughes93b15762017-09-15 11:05:23 +01004190 * Returns: (transfer container): a #FwupdDevice, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01004191 **/
Richard Hughes93b15762017-09-15 11:05:23 +01004192FwupdDevice *
Richard Hughes9945edb2017-06-19 10:03:55 +01004193fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error)
4194{
Richard Hughes65e44ca2018-01-30 17:26:30 +00004195 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004196
4197 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4198 g_return_val_if_fail (device_id != NULL, NULL);
4199 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
4200
4201 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00004202 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004203 if (device == NULL)
Richard Hughesfe221dc2018-05-29 09:33:44 +01004204 return NULL;
Richard Hughes34834102017-11-21 21:55:00 +00004205
Richard Hughes65e44ca2018-01-30 17:26:30 +00004206 /* the notification has already been shown to the user */
4207 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
4208 g_set_error (error,
4209 FWUPD_ERROR,
4210 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes7e77bf32018-05-08 14:56:46 +01004211 "User has already been notified about %s [%s]",
4212 fu_device_get_name (device),
Richard Hughes65e44ca2018-01-30 17:26:30 +00004213 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004214 return NULL;
Richard Hughes65e44ca2018-01-30 17:26:30 +00004215 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004216
Richard Hughes65e44ca2018-01-30 17:26:30 +00004217 /* success */
Richard Hugheseec8a3c2018-01-02 20:37:31 +00004218 return g_object_ref (FWUPD_DEVICE (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004219}
4220
4221static void
4222fu_engine_plugins_setup (FuEngine *self)
4223{
Richard Hughese671c052018-09-18 10:17:15 +01004224 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughese7e95452017-11-22 09:05:53 +00004225 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004226 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004227 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004228 if (!fu_plugin_runner_startup (plugin, &error)) {
4229 fu_plugin_set_enabled (plugin, FALSE);
4230 g_message ("disabling plugin because: %s", error->message);
4231 }
4232 }
4233}
4234
4235static void
Richard Hughes2de8f132018-01-17 09:12:02 +00004236fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug)
Richard Hughes9945edb2017-06-19 10:03:55 +01004237{
Richard Hughese7e95452017-11-22 09:05:53 +00004238 GPtrArray *plugins;
Richard Hughes535664c2017-07-24 10:30:09 +01004239 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01004240
4241 /* don't allow coldplug to be scheduled when in coldplug */
4242 self->coldplug_running = TRUE;
4243
4244 /* prepare */
Richard Hughese7e95452017-11-22 09:05:53 +00004245 plugins = fu_plugin_list_get_all (self->plugin_list);
4246 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004247 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004248 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004249 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
4250 g_warning ("failed to prepare coldplug: %s", error->message);
4251 }
4252
4253 /* do this in one place */
4254 if (self->coldplug_delay > 0) {
4255 g_debug ("sleeping for %ums", self->coldplug_delay);
4256 g_usleep (self->coldplug_delay * 1000);
4257 }
4258
4259 /* exec */
Richard Hughese7e95452017-11-22 09:05:53 +00004260 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004261 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004262 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes2de8f132018-01-17 09:12:02 +00004263 if (is_recoldplug) {
4264 if (!fu_plugin_runner_recoldplug (plugin, &error))
4265 g_message ("failed recoldplug: %s", error->message);
4266 } else {
4267 if (!fu_plugin_runner_coldplug (plugin, &error)) {
4268 fu_plugin_set_enabled (plugin, FALSE);
4269 g_message ("disabling plugin because: %s",
4270 error->message);
4271 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004272 }
4273 }
4274
4275 /* cleanup */
Richard Hughese7e95452017-11-22 09:05:53 +00004276 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004277 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00004278 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01004279 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
4280 g_warning ("failed to cleanup coldplug: %s", error->message);
4281 }
4282
Richard Hughes535664c2017-07-24 10:30:09 +01004283 /* print what we do have */
Richard Hughese7e95452017-11-22 09:05:53 +00004284 for (guint i = 0; i < plugins->len; i++) {
4285 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes535664c2017-07-24 10:30:09 +01004286 if (!fu_plugin_get_enabled (plugin))
4287 continue;
4288 g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin));
4289 }
4290 if (str->len > 2) {
4291 g_string_truncate (str, str->len - 2);
Mario Limonciello59cd4702018-08-09 12:43:47 -05004292 g_debug ("using plugins: %s", str->str);
Richard Hughes535664c2017-07-24 10:30:09 +01004293 }
4294
Richard Hughes9945edb2017-06-19 10:03:55 +01004295 /* we can recoldplug from this point on */
4296 self->coldplug_running = FALSE;
4297}
4298
4299static void
Richard Hughese1fd34d2017-08-24 14:19:51 +01004300fu_engine_plugin_device_register (FuEngine *self, FuDevice *device)
4301{
Richard Hughese7e95452017-11-22 09:05:53 +00004302 GPtrArray *plugins;
Richard Hughese1fd34d2017-08-24 14:19:51 +01004303 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) {
4304 g_warning ("already registered %s, ignoring",
4305 fu_device_get_id (device));
4306 return;
4307 }
Richard Hughese7e95452017-11-22 09:05:53 +00004308 plugins = fu_plugin_list_get_all (self->plugin_list);
4309 for (guint i = 0; i < plugins->len; i++) {
4310 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughese1fd34d2017-08-24 14:19:51 +01004311 fu_plugin_runner_device_register (plugin, device);
4312 }
4313 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REGISTERED);
4314}
4315
4316static void
4317fu_engine_plugin_device_register_cb (FuPlugin *plugin,
4318 FuDevice *device,
4319 gpointer user_data)
4320{
4321 FuEngine *self = FU_ENGINE (user_data);
4322 fu_engine_plugin_device_register (self, device);
4323}
4324
4325static void
Richard Hughes9945edb2017-06-19 10:03:55 +01004326fu_engine_plugin_device_added_cb (FuPlugin *plugin,
4327 FuDevice *device,
4328 gpointer user_data)
4329{
4330 FuEngine *self = (FuEngine *) user_data;
Mario Limonciello387f0392018-10-23 17:21:40 -05004331 gint priority = fu_plugin_get_priority (plugin);
4332 GPtrArray *children = fu_device_get_children (device);
4333 /* set the priority to 1 greater than biggest child */
4334 for (guint i = 0; i < children->len; i++) {
4335 FuDevice *child = g_ptr_array_index (children, i);
4336 gint child_priority = fu_device_get_priority (child);
4337 if (child_priority >= priority)
4338 priority = child_priority + 1;
4339 }
4340 fu_device_set_priority (device, priority);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004341 fu_engine_add_device (self, device);
4342}
4343
Richard Hughes5e447292018-04-27 14:25:54 +01004344static void
4345fu_engine_adopt_children (FuEngine *self, FuDevice *device)
4346{
4347 GPtrArray *guids;
4348 g_autoptr(GPtrArray) devices = fu_device_list_get_active (self->device_list);
4349
4350 /* find the parent GUID in any existing device */
4351 guids = fu_device_get_parent_guids (device);
4352 for (guint j = 0; j < guids->len; j++) {
4353 const gchar *guid = g_ptr_array_index (guids, j);
4354 for (guint i = 0; i < devices->len; i++) {
4355 FuDevice *device_tmp = g_ptr_array_index (devices, i);
4356 if (fu_device_get_parent (device) != NULL)
4357 continue;
4358 if (fu_device_has_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004359 g_debug ("setting parent of %s [%s] to be %s [%s]",
4360 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01004361 fu_device_get_id (device),
Richard Hughes7e77bf32018-05-08 14:56:46 +01004362 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01004363 fu_device_get_id (device_tmp));
4364 fu_device_add_child (device_tmp, device);
4365 break;
4366 }
4367 }
4368 }
4369
4370 /* the new device is the parent to an existing child */
4371 guids = fu_device_get_guids (device);
4372 for (guint j = 0; j < guids->len; j++) {
4373 const gchar *guid = g_ptr_array_index (guids, j);
4374 for (guint i = 0; i < devices->len; i++) {
4375 FuDevice *device_tmp = g_ptr_array_index (devices, i);
4376 if (fu_device_get_parent (device_tmp) != NULL)
4377 continue;
4378 if (fu_device_has_parent_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004379 g_debug ("setting parent of %s [%s] to be %s [%s]",
4380 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01004381 fu_device_get_id (device_tmp),
Richard Hughes7e77bf32018-05-08 14:56:46 +01004382 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01004383 fu_device_get_id (device));
4384 fu_device_add_child (device, device_tmp);
4385 }
4386 }
4387 }
4388}
4389
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004390static void
4391fu_engine_device_inherit_history (FuEngine *self, FuDevice *device)
4392{
4393 g_autoptr(FuDevice) device_history = NULL;
4394
4395 /* any success or failed update? */
4396 device_history = fu_history_get_device_by_id (self->history,
4397 fu_device_get_id (device),
4398 NULL);
4399 if (device_history == NULL)
4400 return;
4401
4402 /* the device is still running the old firmware version and so if it
4403 * required activation before, it still requires it now -- note:
4404 * we can't just check for version_new=version to allow for re-installs */
4405 if (fu_device_has_flag (device_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
4406 FwupdRelease *release = fu_device_get_release_default (device_history);
Richard Hughes9a680842020-02-20 11:11:13 +00004407 if (fu_common_vercmp_full (fu_device_get_version (device),
4408 fwupd_release_get_version (release),
4409 fu_device_get_version_format (device)) != 0) {
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004410 g_debug ("inheriting needs-activation for %s as version %s != %s",
4411 fu_device_get_name (device),
4412 fu_device_get_version (device),
4413 fwupd_release_get_version (release));
4414 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
4415 }
4416 }
4417}
4418
Richard Hughes0a7e7832017-11-22 11:01:13 +00004419void
4420fu_engine_add_device (FuEngine *self, FuDevice *device)
4421{
Richard Hughes9945edb2017-06-19 10:03:55 +01004422 GPtrArray *blacklisted_devices;
Richard Hughes32684f22017-07-13 09:32:21 +01004423 GPtrArray *device_guids;
Richard Hughes9945edb2017-06-19 10:03:55 +01004424
4425 /* device has no GUIDs set! */
Richard Hughes32684f22017-07-13 09:32:21 +01004426 device_guids = fu_device_get_guids (device);
4427 if (device_guids->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004428 g_warning ("no GUIDs for device %s [%s]",
Richard Hughes7e77bf32018-05-08 14:56:46 +01004429 fu_device_get_name (device),
4430 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01004431 return;
4432 }
4433
4434 /* is this GUID blacklisted */
4435 blacklisted_devices = fu_config_get_blacklist_devices (self->config);
4436 for (guint i = 0; i < blacklisted_devices->len; i++) {
Richard Hughes32684f22017-07-13 09:32:21 +01004437 const gchar *blacklisted_guid = g_ptr_array_index (blacklisted_devices, i);
4438 for (guint j = 0; j < device_guids->len; j++) {
4439 const gchar *device_guid = g_ptr_array_index (device_guids, j);
4440 if (g_strcmp0 (blacklisted_guid, device_guid) == 0) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01004441 g_debug ("%s [%s] is blacklisted [%s], ignoring from %s",
4442 fu_device_get_name (device),
4443 fu_device_get_id (device),
4444 device_guid,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004445 fu_device_get_plugin (device));
Richard Hughes32684f22017-07-13 09:32:21 +01004446 return;
4447 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004448 }
4449 }
4450
Richard Hughes7f765002019-12-20 09:41:29 +00004451 /* does the device not have an assigned protocol */
4452 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4453 fu_device_get_protocol (device) == NULL) {
Mario Limonciello33b40ed2020-01-08 15:11:34 -06004454 g_warning ("device %s [%s] does not define an update protocol",
Richard Hughes7f765002019-12-20 09:41:29 +00004455 fu_device_get_id (device),
4456 fu_device_get_name (device));
4457 }
4458
Richard Hughesf3077752018-06-27 12:45:06 +01004459 /* if this device is locked get some metadata from AppStream */
4460 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Mario Limonciello4f24d0b2019-01-26 01:19:59 -06004461 g_autoptr(XbNode) component = fu_engine_get_component_by_guids (self, device);
Richard Hughes481aa2a2018-09-18 20:51:46 +01004462 if (component != NULL) {
4463 g_autoptr(XbNode) release = NULL;
4464 release = xb_node_query_first (component,
4465 "releases/release",
4466 NULL);
Richard Hughesf3077752018-06-27 12:45:06 +01004467 if (release != NULL) {
4468 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004469 g_autoptr(GError) error_local = NULL;
4470 if (!fu_engine_set_release_from_appstream (self,
Richard Hughes2c40b372019-04-17 13:41:47 +01004471 device,
Richard Hughes0b3e9fd2019-04-08 11:13:20 +01004472 rel,
4473 component,
4474 release,
4475 &error_local)) {
4476 g_warning ("failed to set AppStream release: %s",
4477 error_local->message);
4478 } else {
4479 fu_device_add_release (device, rel);
4480 }
Richard Hughesf3077752018-06-27 12:45:06 +01004481 }
4482 }
4483 }
4484
Richard Hughes5e447292018-04-27 14:25:54 +01004485 /* adopt any required children, which may or may not already exist */
4486 fu_engine_adopt_children (self, device);
4487
Richard Hughese48351e2018-06-22 12:32:39 +01004488 /* set any alternate objects on the device from the ID */
4489 if (fu_device_get_alternate_id (device) != NULL) {
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004490 g_autoptr(FuDevice) device_alt = NULL;
Richard Hughese48351e2018-06-22 12:32:39 +01004491 device_alt = fu_device_list_get_by_id (self->device_list,
4492 fu_device_get_alternate_id (device),
4493 NULL);
4494 if (device_alt != NULL)
4495 fu_device_set_alternate (device, device_alt);
4496 }
4497
Richard Hughes5079f262019-04-29 09:34:27 +01004498 if (fu_device_get_version_format (device) == FWUPD_VERSION_FORMAT_UNKNOWN &&
4499 fu_common_version_guess_format (fu_device_get_version (device)) == FWUPD_VERSION_FORMAT_NUMBER) {
Mario Limonciello253b8252019-04-25 10:48:17 -04004500 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
4501 fu_device_set_update_error (device, "VersionFormat is ambiguous for this device");
4502 }
4503
Richard Hughesd8ea8da2020-02-19 15:04:49 +00004504 /* no vendor-id, and so no way to lock it down! */
4505 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4506 fu_device_get_vendor_id (device) == NULL) {
4507 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE);
4508 fu_device_set_update_error (device, "No vendor ID set");
4509 }
4510
Richard Hughese1fd34d2017-08-24 14:19:51 +01004511 /* notify all plugins about this new device */
4512 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED))
4513 fu_engine_plugin_device_register (self, device);
4514
Richard Hughes7f765002019-12-20 09:41:29 +00004515 /* does the device *still* not have a vendor ID? */
4516 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) &&
4517 fu_device_get_vendor_id (device) == NULL) {
4518 g_warning ("device %s [%s] does not define a vendor-id!",
4519 fu_device_get_id (device),
4520 fu_device_get_name (device));
4521 }
4522
Richard Hughesd76ed3d2018-11-15 15:05:36 +00004523 /* create new device */
4524 fu_device_list_add (self->device_list, device);
4525
Richard Hughes9945edb2017-06-19 10:03:55 +01004526 /* match the metadata at this point so clients can tell if the
4527 * device is worthy */
Richard Hughes1cf88d62017-11-07 20:02:15 +00004528 if (fu_engine_is_device_supported (self, device))
4529 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06004530
4531 /* sometimes inherit flags from recent history */
4532 fu_engine_device_inherit_history (self, device);
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06004533
4534 fu_engine_emit_changed (self);
Richard Hughes9945edb2017-06-19 10:03:55 +01004535}
4536
4537static void
Richard Hughes95c98a92019-10-22 16:03:15 +01004538fu_engine_plugin_add_firmware_gtype_cb (FuPlugin *plugin,
4539 const gchar *id,
4540 GType gtype,
4541 gpointer user_data)
4542{
4543 FuEngine *self = FU_ENGINE (user_data);
4544 fu_engine_add_firmware_gtype (self, id, gtype);
4545}
4546
4547static void
Richard Hughes75b965d2018-11-15 13:51:21 +00004548fu_engine_plugin_rules_changed_cb (FuPlugin *plugin, gpointer user_data)
4549{
4550 FuEngine *self = FU_ENGINE (user_data);
4551 GPtrArray *rules = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_INHIBITS_IDLE);
4552 for (guint j = 0; j < rules->len; j++) {
4553 const gchar *tmp = g_ptr_array_index (rules, j);
4554 fu_idle_inhibit (self->idle, tmp);
4555 }
4556}
4557
4558static void
Richard Hughes9945edb2017-06-19 10:03:55 +01004559fu_engine_plugin_device_removed_cb (FuPlugin *plugin,
4560 FuDevice *device,
4561 gpointer user_data)
4562{
4563 FuEngine *self = (FuEngine *) user_data;
Richard Hughes34834102017-11-21 21:55:00 +00004564 FuPlugin *plugin_old;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01004565 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004566 g_autoptr(GError) error = NULL;
4567
Richard Hughes40127542018-01-12 20:25:55 +00004568 device_tmp = fu_device_list_get_by_id (self->device_list,
4569 fu_device_get_id (device),
4570 &error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00004571 if (device_tmp == NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004572 g_debug ("%s", error->message);
4573 return;
4574 }
4575
Richard Hughes34834102017-11-21 21:55:00 +00004576 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00004577 plugin_old = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00004578 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00004579 &error);
Richard Hughes34834102017-11-21 21:55:00 +00004580 if (plugin_old == NULL) {
4581 g_debug ("%s", error->message);
4582 return;
4583 }
4584
Richard Hughes9945edb2017-06-19 10:03:55 +01004585 /* check this came from the same plugin */
4586 if (g_strcmp0 (fu_plugin_get_name (plugin),
Richard Hughes34834102017-11-21 21:55:00 +00004587 fu_plugin_get_name (plugin_old)) != 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004588 g_debug ("ignoring duplicate removal from %s",
4589 fu_plugin_get_name (plugin));
4590 return;
4591 }
4592
4593 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +00004594 fu_device_list_remove (self->device_list, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01004595 fu_engine_emit_changed (self);
4596}
4597
Richard Hughes9945edb2017-06-19 10:03:55 +01004598static gboolean
4599fu_engine_recoldplug_delay_cb (gpointer user_data)
4600{
4601 FuEngine *self = (FuEngine *) user_data;
4602 g_debug ("performing a recoldplug");
Richard Hughes2de8f132018-01-17 09:12:02 +00004603 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes9945edb2017-06-19 10:03:55 +01004604 self->coldplug_id = 0;
4605 return FALSE;
4606}
4607
Richard Hughes16658372019-11-22 09:23:59 +00004608#ifdef HAVE_GUDEV
Richard Hughes9945edb2017-06-19 10:03:55 +01004609static void
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004610fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device)
4611{
Richard Hughesff704412018-09-04 11:28:32 +01004612 g_autoptr(FuUdevDevice) device = fu_udev_device_new (udev_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01004613 g_autoptr(GError) error_local = NULL;
Richard Hughes51a869a2019-10-07 11:23:42 +01004614 g_autoptr(GPtrArray) possible_plugins = NULL;
Richard Hughes6dec4012018-08-27 19:13:00 +01004615
Richard Hughesf3880bc2019-11-26 12:02:30 +00004616 /* debug */
4617 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4618 g_debug ("UDEV %s added",
4619 g_udev_device_get_sysfs_path (udev_device));
4620 }
4621
Richard Hughes6dec4012018-08-27 19:13:00 +01004622 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01004623 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
4624 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01004625 g_warning ("failed to probe device %s: %s",
4626 g_udev_device_get_sysfs_path (udev_device),
4627 error_local->message);
4628 return;
4629 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004630
Richard Hughes23170a82018-08-29 09:11:52 +01004631 /* can be specified using a quirk */
Richard Hughes51a869a2019-10-07 11:23:42 +01004632 possible_plugins = fu_device_get_possible_plugins (FU_DEVICE (device));
4633 for (guint i = 0; i < possible_plugins->len; i++) {
4634 FuPlugin *plugin;
4635 const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
4636 g_autoptr(GError) error = NULL;
Mario Limonciello0e500322019-10-17 18:41:04 -05004637
Richard Hughes51a869a2019-10-07 11:23:42 +01004638 plugin = fu_plugin_list_find_by_name (self->plugin_list,
4639 plugin_name, &error);
4640 if (plugin == NULL) {
Richard Hughesa1f95352020-02-21 08:14:54 +00004641 g_debug ("failed to find specified plugin %s: %s",
4642 plugin_name, error->message);
Richard Hughes51a869a2019-10-07 11:23:42 +01004643 continue;
4644 }
4645 if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) {
4646 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
4647 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4648 g_debug ("%s ignoring: %s",
4649 fu_plugin_get_name (plugin),
4650 error->message);
Mario Limonciello0e500322019-10-17 18:41:04 -05004651 }
Mario Limonciello0e500322019-10-17 18:41:04 -05004652 continue;
4653 }
Richard Hughes51a869a2019-10-07 11:23:42 +01004654 g_warning ("failed to add udev device %s: %s",
4655 g_udev_device_get_sysfs_path (udev_device),
4656 error->message);
4657 continue;
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004658 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004659 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004660}
4661
4662static void
4663fu_engine_udev_device_remove (FuEngine *self, GUdevDevice *udev_device)
4664{
4665 g_autoptr(GPtrArray) devices = NULL;
4666
Richard Hughesf3880bc2019-11-26 12:02:30 +00004667 /* debug */
4668 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
4669 g_debug ("UDEV %s removed",
4670 g_udev_device_get_sysfs_path (udev_device));
4671 }
4672
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004673 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01004674 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004675 for (guint i = 0; i < devices->len; i++) {
4676 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01004677 if (!FU_IS_UDEV_DEVICE (device))
4678 continue;
4679 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
4680 g_udev_device_get_sysfs_path (udev_device)) == 0) {
4681 g_debug ("auto-removing GUdevDevice");
4682 fu_device_list_remove (self->device_list, device);
4683 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004684 }
4685}
4686
Richard Hughes5e952ce2019-08-26 11:09:46 +01004687typedef struct {
4688 FuEngine *self;
4689 GUdevDevice *udev_device;
4690 guint idle_id;
4691} FuEngineUdevChangedHelper;
4692
4693static void
4694fu_engine_udev_changed_helper_free (FuEngineUdevChangedHelper *helper)
4695{
4696 if (helper->idle_id != 0)
4697 g_source_remove (helper->idle_id);
4698 g_object_unref (helper->self);
4699 g_object_unref (helper->udev_device);
4700 g_free (helper);
4701}
4702
4703static FuEngineUdevChangedHelper *
4704fu_engine_udev_changed_helper_new (FuEngine *self, GUdevDevice *udev_device)
4705{
4706 FuEngineUdevChangedHelper *helper = g_new0 (FuEngineUdevChangedHelper, 1);
4707 helper->self = g_object_ref (self);
4708 helper->udev_device = g_object_ref (udev_device);
4709 return helper;
4710}
4711
4712static gboolean
4713fu_engine_udev_changed_cb (gpointer user_data)
4714{
4715 FuEngineUdevChangedHelper *helper = (FuEngineUdevChangedHelper *) user_data;
4716 GPtrArray *plugins = fu_plugin_list_get_all (helper->self->plugin_list);
4717 g_autoptr(FuUdevDevice) device = fu_udev_device_new (helper->udev_device);
4718
4719 /* run all plugins */
4720 for (guint j = 0; j < plugins->len; j++) {
4721 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
4722 g_autoptr(GError) error = NULL;
4723 if (!fu_plugin_runner_udev_device_changed (plugin_tmp, device, &error)) {
4724 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
4725 g_debug ("%s ignoring: %s",
4726 fu_plugin_get_name (plugin_tmp),
4727 error->message);
4728 continue;
4729 }
4730 g_warning ("%s failed to change udev device %s: %s",
4731 fu_plugin_get_name (plugin_tmp),
4732 g_udev_device_get_sysfs_path (helper->udev_device),
4733 error->message);
4734 }
4735 }
4736
4737 /* device done, so remove ref */
4738 helper->idle_id = 0;
4739 g_hash_table_remove (helper->self->udev_changed_ids,
4740 g_udev_device_get_sysfs_path (helper->udev_device));
4741 return FALSE;
4742}
4743
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004744static void
4745fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device)
4746{
Richard Hughes5e952ce2019-08-26 11:09:46 +01004747 const gchar *sysfs_path = g_udev_device_get_sysfs_path (udev_device);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004748 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes5e952ce2019-08-26 11:09:46 +01004749 FuEngineUdevChangedHelper *helper;
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004750
4751 /* emit changed on any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01004752 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004753 for (guint i = 0; i < devices->len; i++) {
4754 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01004755 if (!FU_IS_UDEV_DEVICE (device))
4756 continue;
4757 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
Richard Hughes5e952ce2019-08-26 11:09:46 +01004758 sysfs_path) == 0) {
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004759 fu_udev_device_emit_changed (FU_UDEV_DEVICE (device));
Richard Hughesc125ec02018-09-05 19:35:17 +01004760 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004761 }
Richard Hughes5e952ce2019-08-26 11:09:46 +01004762
4763 /* run all plugins, with per-device rate limiting */
4764 if (g_hash_table_remove (self->udev_changed_ids, sysfs_path)) {
4765 g_debug ("re-adding rate-limited timeout for %s", sysfs_path);
4766 } else {
4767 g_debug ("adding rate-limited timeout for %s", sysfs_path);
4768 }
4769 helper = fu_engine_udev_changed_helper_new (self, udev_device);
4770 helper->idle_id = g_timeout_add (500, fu_engine_udev_changed_cb, helper);
4771 g_hash_table_insert (self->udev_changed_ids, g_strdup (sysfs_path), helper);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004772}
4773
4774static void
4775fu_engine_enumerate_udev (FuEngine *self)
4776{
4777 /* get all devices of class */
4778 for (guint i = 0; i < self->udev_subsystems->len; i++) {
4779 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
4780 GList *devices = g_udev_client_query_by_subsystem (self->gudev_client,
4781 subsystem);
4782 g_debug ("%u devices with subsystem %s",
4783 g_list_length (devices), subsystem);
4784 for (GList *l = devices; l != NULL; l = l->next) {
4785 GUdevDevice *udev_device = l->data;
4786 fu_engine_udev_device_add (self, udev_device);
4787 }
4788 g_list_foreach (devices, (GFunc) g_object_unref, NULL);
4789 g_list_free (devices);
4790 }
4791}
Richard Hughes16658372019-11-22 09:23:59 +00004792#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004793
4794static void
Richard Hughes9945edb2017-06-19 10:03:55 +01004795fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self)
4796{
4797 if (self->coldplug_running) {
4798 g_warning ("coldplug already running, cannot recoldplug");
4799 return;
4800 }
Richard Hughes5b5f6552018-05-18 10:22:39 +01004801 if (self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) {
4802 g_debug ("doing direct recoldplug");
4803 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes16658372019-11-22 09:23:59 +00004804#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004805 fu_engine_enumerate_udev (self);
Richard Hughes16658372019-11-22 09:23:59 +00004806#endif
Richard Hughes5b5f6552018-05-18 10:22:39 +01004807 return;
4808 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004809 g_debug ("scheduling a recoldplug");
4810 if (self->coldplug_id != 0)
4811 g_source_remove (self->coldplug_id);
4812 self->coldplug_id = g_timeout_add (1500, fu_engine_recoldplug_delay_cb, self);
4813}
4814
4815static void
4816fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngine *self)
4817{
4818 self->coldplug_delay = MAX (self->coldplug_delay, duration);
4819 g_debug ("got coldplug delay of %ums, global maximum is now %ums",
4820 duration, self->coldplug_delay);
4821}
4822
Richard Hughesf425d292019-01-18 17:57:39 +00004823/* this is called by the self tests as well */
Richard Hughes34834102017-11-21 21:55:00 +00004824void
4825fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin)
4826{
Mario Limonciello52e75ba2019-11-22 13:21:19 -06004827 if (fu_plugin_is_open (plugin)) {
4828 /* plugin does not match built version */
4829 if (fu_plugin_get_build_hash (plugin) == NULL) {
4830 const gchar *name = fu_plugin_get_name (plugin);
4831 g_warning ("%s should call fu_plugin_set_build_hash()",
4832 name);
4833 self->tainted = TRUE;
4834 } else if (g_strcmp0 (fu_plugin_get_build_hash (plugin),
4835 FU_BUILD_HASH) != 0) {
4836 const gchar *name = fu_plugin_get_name (plugin);
4837 g_warning ("%s has incorrect built version %s",
4838 name, fu_plugin_get_build_hash (plugin));
4839 self->tainted = TRUE;
4840 }
Richard Hughesf425d292019-01-18 17:57:39 +00004841 }
4842
Richard Hughese7e95452017-11-22 09:05:53 +00004843 fu_plugin_list_add (self->plugin_list, plugin);
Richard Hughes34834102017-11-21 21:55:00 +00004844}
4845
Richard Hughes9945edb2017-06-19 10:03:55 +01004846static gboolean
Richard Hughes1e456bc2018-05-10 20:16:16 +01004847fu_engine_is_plugin_name_blacklisted (FuEngine *self, const gchar *name)
4848{
4849 GPtrArray *blacklist = fu_config_get_blacklist_plugins (self->config);
4850 for (guint i = 0; i < blacklist->len; i++) {
4851 const gchar *name_tmp = g_ptr_array_index (blacklist, i);
4852 if (g_strcmp0 (name_tmp, name) == 0)
4853 return TRUE;
4854 }
4855 return FALSE;
4856}
4857
Richard Hughesc02ee4d2018-05-22 15:46:03 +01004858static gboolean
4859fu_engine_is_plugin_name_whitelisted (FuEngine *self, const gchar *name)
4860{
4861 if (self->plugin_filter->len == 0)
4862 return TRUE;
4863 for (guint i = 0; i < self->plugin_filter->len; i++) {
4864 const gchar *name_tmp = g_ptr_array_index (self->plugin_filter, i);
Richard Hughes5c508de2019-11-22 09:57:34 +00004865 if (fu_common_fnmatch (name_tmp, name))
Richard Hughesc02ee4d2018-05-22 15:46:03 +01004866 return TRUE;
4867 }
4868 return FALSE;
4869}
4870
4871void
4872fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob)
4873{
4874 g_return_if_fail (FU_IS_ENGINE (self));
4875 g_return_if_fail (plugin_glob != NULL);
4876 g_ptr_array_add (self->plugin_filter, g_strdup (plugin_glob));
4877}
4878
Richard Hughesaabdc372018-11-14 10:11:08 +00004879static gboolean
4880fu_engine_plugin_check_supported_cb (FuPlugin *plugin, const gchar *guid, FuEngine *self)
4881{
4882 g_autoptr(XbNode) n = NULL;
4883 g_autofree gchar *xpath = NULL;
Mario Limonciello4fa95a72020-03-28 10:50:57 -05004884
4885 if (fu_config_get_enumerate_all_devices (self->config))
4886 return TRUE;
4887
Richard Hughesaabdc372018-11-14 10:11:08 +00004888 xpath = g_strdup_printf ("components/component/"
4889 "provides/firmware[@type='flashed'][text()='%s']",
4890 guid);
4891 n = xb_silo_query_first (self->silo, xpath, NULL);
4892 return n != NULL;
4893}
4894
Richard Hughes8c71a3f2018-05-22 19:19:52 +01004895gboolean
Richard Hughesf425d292019-01-18 17:57:39 +00004896fu_engine_get_tainted (FuEngine *self)
4897{
4898 return self->tainted;
4899}
4900
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05004901const gchar *
4902fu_engine_get_host_product (FuEngine *self)
4903{
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06004904 const gchar *result = NULL;
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05004905 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
Mario Limonciello8ad4ac02020-01-15 09:58:53 -06004906 result = fu_hwids_get_value (self->hwids, FU_HWIDS_KEY_PRODUCT_NAME);
4907 return result != NULL ? result : "Unknown Product";
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05004908}
4909
Richard Hughes0917fb62019-09-21 12:55:37 +01004910const gchar *
4911fu_engine_get_host_machine_id (FuEngine *self)
4912{
4913 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
4914 return self->host_machine_id;
4915}
4916
Richard Hughesf425d292019-01-18 17:57:39 +00004917gboolean
Richard Hughes9945edb2017-06-19 10:03:55 +01004918fu_engine_load_plugins (FuEngine *self, GError **error)
4919{
4920 const gchar *fn;
4921 g_autoptr(GDir) dir = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +01004922 g_autofree gchar *plugin_path = NULL;
Richard Hughes00d6f472019-11-22 17:04:02 +00004923 g_autofree gchar *suffix = g_strdup_printf (".%s", G_MODULE_SUFFIX);
Richard Hughesf77d7062017-11-27 12:06:36 +00004924
Richard Hughes9945edb2017-06-19 10:03:55 +01004925 /* search */
Richard Hughes4be17d12018-05-30 20:36:29 +01004926 plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG);
4927 dir = g_dir_open (plugin_path, 0, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01004928 if (dir == NULL)
4929 return FALSE;
4930 while ((fn = g_dir_read_name (dir)) != NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01004931 g_autofree gchar *filename = NULL;
Richard Hughes1e456bc2018-05-10 20:16:16 +01004932 g_autofree gchar *name = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01004933 g_autoptr(FuPlugin) plugin = NULL;
4934 g_autoptr(GError) error_local = NULL;
4935
4936 /* ignore non-plugins */
Richard Hughes00d6f472019-11-22 17:04:02 +00004937 if (!g_str_has_suffix (fn, suffix))
Richard Hughes9945edb2017-06-19 10:03:55 +01004938 continue;
4939
Richard Hughes1e456bc2018-05-10 20:16:16 +01004940 /* is blacklisted */
4941 name = fu_plugin_guess_name_from_fn (fn);
4942 if (name == NULL)
4943 continue;
4944 if (fu_engine_is_plugin_name_blacklisted (self, name)) {
Richard Hughesc02ee4d2018-05-22 15:46:03 +01004945 g_debug ("plugin %s is blacklisted", name);
4946 continue;
4947 }
4948 if (!fu_engine_is_plugin_name_whitelisted (self, name)) {
4949 g_debug ("plugin %s is not whitelisted", name);
Richard Hughes1e456bc2018-05-10 20:16:16 +01004950 continue;
4951 }
4952
Richard Hughes9945edb2017-06-19 10:03:55 +01004953 /* open module */
Richard Hughes4be17d12018-05-30 20:36:29 +01004954 filename = g_build_filename (plugin_path, fn, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01004955 plugin = fu_plugin_new ();
Richard Hughes1e456bc2018-05-10 20:16:16 +01004956 fu_plugin_set_name (plugin, name);
Richard Hughes9945edb2017-06-19 10:03:55 +01004957 fu_plugin_set_usb_context (plugin, self->usb_ctx);
4958 fu_plugin_set_hwids (plugin, self->hwids);
Richard Hughes49e5e052017-09-03 12:15:41 +01004959 fu_plugin_set_smbios (plugin, self->smbios);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01004960 fu_plugin_set_udev_subsystems (plugin, self->udev_subsystems);
Richard Hughes9c028f02017-10-28 21:14:28 +01004961 fu_plugin_set_quirks (plugin, self->quirks);
Richard Hughes0eb123b2018-04-19 12:00:04 +01004962 fu_plugin_set_runtime_versions (plugin, self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01004963 fu_plugin_set_compile_versions (plugin, self->compile_versions);
Richard Hughes95c98a92019-10-22 16:03:15 +01004964 g_signal_connect (plugin, "add-firmware-gtype",
4965 G_CALLBACK (fu_engine_plugin_add_firmware_gtype_cb),
4966 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01004967 g_debug ("adding plugin %s", filename);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01004968
4969 /* if loaded from fu_engine_load() open the plugin */
4970 if (self->usb_ctx != NULL) {
4971 if (!fu_plugin_open (plugin, filename, &error_local)) {
Mario Limonciellof5605532019-11-04 07:49:50 -06004972 g_warning ("%s", error_local->message);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01004973 continue;
4974 }
Richard Hughes9945edb2017-06-19 10:03:55 +01004975 }
4976
Richard Hughes1e456bc2018-05-10 20:16:16 +01004977 /* self disabled */
Richard Hughes9945edb2017-06-19 10:03:55 +01004978 if (!fu_plugin_get_enabled (plugin)) {
Richard Hughes1e456bc2018-05-10 20:16:16 +01004979 g_debug ("%s self disabled",
Richard Hughes9945edb2017-06-19 10:03:55 +01004980 fu_plugin_get_name (plugin));
4981 continue;
4982 }
4983
4984 /* watch for changes */
4985 g_signal_connect (plugin, "device-added",
4986 G_CALLBACK (fu_engine_plugin_device_added_cb),
4987 self);
4988 g_signal_connect (plugin, "device-removed",
4989 G_CALLBACK (fu_engine_plugin_device_removed_cb),
4990 self);
Richard Hughese1fd34d2017-08-24 14:19:51 +01004991 g_signal_connect (plugin, "device-register",
4992 G_CALLBACK (fu_engine_plugin_device_register_cb),
4993 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01004994 g_signal_connect (plugin, "recoldplug",
4995 G_CALLBACK (fu_engine_plugin_recoldplug_cb),
4996 self);
4997 g_signal_connect (plugin, "set-coldplug-delay",
4998 G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb),
4999 self);
Richard Hughesaabdc372018-11-14 10:11:08 +00005000 g_signal_connect (plugin, "check-supported",
5001 G_CALLBACK (fu_engine_plugin_check_supported_cb),
5002 self);
Richard Hughes75b965d2018-11-15 13:51:21 +00005003 g_signal_connect (plugin, "rules-changed",
5004 G_CALLBACK (fu_engine_plugin_rules_changed_cb),
5005 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01005006
5007 /* add */
Richard Hughesf425d292019-01-18 17:57:39 +00005008 fu_engine_add_plugin (self, plugin);
Richard Hughes9945edb2017-06-19 10:03:55 +01005009 }
5010
Richard Hughese7e95452017-11-22 09:05:53 +00005011 /* depsolve into the correct order */
5012 if (!fu_plugin_list_depsolve (self->plugin_list, error))
5013 return FALSE;
Richard Hughes08a37992017-09-12 12:57:43 +01005014
Richard Hughese7e95452017-11-22 09:05:53 +00005015 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01005016 return TRUE;
5017}
5018
Richard Hughes9945edb2017-06-19 10:03:55 +01005019static gboolean
5020fu_engine_cleanup_state (GError **error)
5021{
5022 const gchar *filenames[] = {
5023 "/var/cache/app-info/xmls/fwupd-verify.xml",
5024 "/var/cache/app-info/xmls/fwupd.xml",
5025 NULL };
5026 for (guint i = 0; filenames[i] != NULL; i++) {
5027 g_autoptr(GFile) file = g_file_new_for_path (filenames[i]);
5028 if (g_file_query_exists (file, NULL)) {
5029 if (!g_file_delete (file, NULL, error))
5030 return FALSE;
5031 }
5032 }
5033 return TRUE;
5034}
5035
Richard Hughesc7bbbc22018-01-02 22:22:25 +00005036guint64
5037fu_engine_get_archive_size_max (FuEngine *self)
5038{
5039 return fu_config_get_archive_size_max (self->config);
5040}
5041
Richard Hughes104f6512017-11-24 11:44:57 +00005042static void
5043fu_engine_usb_device_removed_cb (GUsbContext *ctx,
5044 GUsbDevice *usb_device,
5045 FuEngine *self)
5046{
5047 g_autoptr(GPtrArray) devices = NULL;
5048
Richard Hughesf3880bc2019-11-26 12:02:30 +00005049 /* debug */
5050 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5051 g_debug ("USB %04x:%04x removed",
5052 g_usb_device_get_vid (usb_device),
5053 g_usb_device_get_pid (usb_device));
5054 }
5055
Richard Hughes104f6512017-11-24 11:44:57 +00005056 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01005057 devices = fu_device_list_get_all (self->device_list);
Richard Hughes104f6512017-11-24 11:44:57 +00005058 for (guint i = 0; i < devices->len; i++) {
5059 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01005060 if (!FU_IS_USB_DEVICE (device))
5061 continue;
5062 if (g_strcmp0 (fu_usb_device_get_platform_id (FU_USB_DEVICE (device)),
5063 g_usb_device_get_platform_id (usb_device)) == 0) {
5064 g_debug ("auto-removing GUsbDevice");
5065 fu_device_list_remove (self->device_list, device);
5066 }
Richard Hughes104f6512017-11-24 11:44:57 +00005067 }
5068}
5069
5070static void
5071fu_engine_usb_device_added_cb (GUsbContext *ctx,
5072 GUsbDevice *usb_device,
5073 FuEngine *self)
5074{
Richard Hughesff704412018-09-04 11:28:32 +01005075 g_autoptr(FuUsbDevice) device = fu_usb_device_new (usb_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01005076 g_autoptr(GError) error_local = NULL;
Richard Hughes45a18152019-10-30 15:29:04 +00005077 g_autoptr(GPtrArray) possible_plugins = NULL;
Richard Hughes6dec4012018-08-27 19:13:00 +01005078
Richard Hughesf3880bc2019-11-26 12:02:30 +00005079 /* debug */
5080 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5081 g_debug ("USB %04x:%04x added",
5082 g_usb_device_get_vid (usb_device),
5083 g_usb_device_get_pid (usb_device));
5084 }
5085
Richard Hughes6dec4012018-08-27 19:13:00 +01005086 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01005087 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
5088 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01005089 g_warning ("failed to probe device %s: %s",
Richard Hughesc125ec02018-09-05 19:35:17 +01005090 fu_device_get_physical_id (FU_DEVICE (device)),
Richard Hughes6dec4012018-08-27 19:13:00 +01005091 error_local->message);
5092 return;
5093 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005094
Richard Hughes23170a82018-08-29 09:11:52 +01005095 /* can be specified using a quirk */
Richard Hughes45a18152019-10-30 15:29:04 +00005096 possible_plugins = fu_device_get_possible_plugins (FU_DEVICE (device));
5097 for (guint i = 0; i < possible_plugins->len; i++) {
5098 FuPlugin *plugin;
5099 const gchar *plugin_name = g_ptr_array_index (possible_plugins, i);
5100 g_autoptr(GError) error = NULL;
Mario Limonciello0e500322019-10-17 18:41:04 -05005101
Richard Hughes45a18152019-10-30 15:29:04 +00005102 plugin = fu_plugin_list_find_by_name (self->plugin_list,
5103 plugin_name, &error);
5104 if (plugin == NULL) {
Richard Hughesa1f95352020-02-21 08:14:54 +00005105 g_debug ("failed to find specified plugin %s: %s",
5106 plugin_name, error->message);
Richard Hughes45a18152019-10-30 15:29:04 +00005107 continue;
5108 }
5109 if (!fu_plugin_runner_usb_device_added (plugin, device, &error)) {
5110 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
5111 if (g_getenv ("FWUPD_PROBE_VERBOSE") != NULL) {
5112 g_debug ("%s ignoring: %s",
5113 fu_plugin_get_name (plugin),
5114 error->message);
Mario Limonciello0e500322019-10-17 18:41:04 -05005115 }
Mario Limonciello0e500322019-10-17 18:41:04 -05005116 continue;
5117 }
Richard Hughes45a18152019-10-30 15:29:04 +00005118 g_warning ("failed to add USB device %04x:%04x: %s",
5119 g_usb_device_get_vid (usb_device),
5120 g_usb_device_get_pid (usb_device),
5121 error->message);
5122 continue;
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005123 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01005124 }
Richard Hughes104f6512017-11-24 11:44:57 +00005125}
5126
Richard Hughesf77d7062017-11-27 12:06:36 +00005127static void
Richard Hughes51a869a2019-10-07 11:23:42 +01005128fu_engine_load_quirks (FuEngine *self, FuQuirksLoadFlags quirks_flags)
Richard Hughesf77d7062017-11-27 12:06:36 +00005129{
Richard Hughesf77d7062017-11-27 12:06:36 +00005130 g_autoptr(GError) error = NULL;
Richard Hughes51a869a2019-10-07 11:23:42 +01005131 if (!fu_quirks_load (self->quirks, quirks_flags, &error))
Richard Hughesf77d7062017-11-27 12:06:36 +00005132 g_warning ("Failed to load quirks: %s", error->message);
5133}
5134
5135static void
5136fu_engine_load_smbios (FuEngine *self)
5137{
Richard Hughesf77d7062017-11-27 12:06:36 +00005138 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00005139 if (!fu_smbios_setup (self->smbios, &error))
5140 g_warning ("Failed to load SMBIOS: %s", error->message);
5141}
5142
5143static void
5144fu_engine_load_hwids (FuEngine *self)
5145{
Richard Hughesf77d7062017-11-27 12:06:36 +00005146 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00005147 if (!fu_hwids_setup (self->hwids, self->smbios, &error))
5148 g_warning ("Failed to load HWIDs: %s", error->message);
5149}
5150
Richard Hughesa2f8e452018-01-11 10:11:17 +00005151static gboolean
5152fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError **error)
5153{
Richard Hughesa2f8e452018-01-11 10:11:17 +00005154 FuPlugin *plugin;
5155 FwupdRelease *rel_history;
Richard Hughesf2711422018-01-12 09:49:05 +00005156 g_autofree gchar *btime = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01005157 g_autoptr(FuDevice) dev = NULL;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005158
5159 /* is in the device list */
Richard Hughes40127542018-01-12 20:25:55 +00005160 dev = fu_device_list_get_by_id (self->device_list,
5161 fu_device_get_id (dev_history),
5162 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005163 if (dev == NULL)
5164 return FALSE;
5165
5166 /* does the installed version match what we tried to install
5167 * before fwupd was restarted */
5168 rel_history = fu_device_get_release_default (dev_history);
5169 if (rel_history == NULL) {
5170 g_set_error_literal (error,
5171 FWUPD_ERROR,
5172 FWUPD_ERROR_INTERNAL,
5173 "no release for history FuDevice");
5174 return FALSE;
5175 }
5176
Richard Hughesf2711422018-01-12 09:49:05 +00005177 /* is this the same boot time as when we scheduled the update,
5178 * i.e. has fwupd been restarted before we rebooted */
5179 btime = fu_engine_get_boot_time ();
5180 if (g_strcmp0 (fwupd_release_get_metadata_item (rel_history, "BootTime"),
5181 btime) == 0) {
5182 g_debug ("service restarted, but no reboot has taken place");
5183 return TRUE;
5184 }
5185
Richard Hughesa2f8e452018-01-11 10:11:17 +00005186 /* the system is running with the new firmware version */
Richard Hughes9a680842020-02-20 11:11:13 +00005187 if (fu_common_vercmp_full (fu_device_get_version (dev),
5188 fwupd_release_get_version (rel_history),
5189 fu_device_get_version_format (dev)) == 0) {
Richard Hughes4e886a42018-12-12 10:33:26 +00005190 GPtrArray *checksums;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005191 g_debug ("installed version %s matching history %s",
5192 fu_device_get_version (dev),
5193 fwupd_release_get_version (rel_history));
Richard Hughes4e886a42018-12-12 10:33:26 +00005194
5195 /* copy over runtime checksums if set from probe() */
5196 checksums = fu_device_get_checksums (dev);
5197 for (guint i = 0; i < checksums->len; i++) {
5198 const gchar *csum = g_ptr_array_index (checksums, i);
5199 fu_device_add_checksum (dev_history, csum);
5200 }
Richard Hughesf50ff2c2020-02-25 09:45:15 +00005201 fu_device_set_version_format (dev_history, fu_device_get_version_format (dev));
5202 fu_device_set_version (dev_history, fu_device_get_version (dev));
Mario Limonciello96a0dd52019-02-25 13:50:03 -06005203 fu_device_remove_flag (dev_history, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION);
Richard Hughesc0cd0232018-01-31 15:02:00 +00005204 fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS);
Richard Hughes0bbef292019-11-01 12:15:15 +00005205 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005206 }
5207
Richard Hughes7e070c92018-01-12 16:50:05 +00005208 /* does the plugin know the update failure */
Richard Hughesa2f8e452018-01-11 10:11:17 +00005209 plugin = fu_plugin_list_find_by_name (self->plugin_list,
5210 fu_device_get_plugin (dev),
5211 error);
5212 if (plugin == NULL)
5213 return FALSE;
Richard Hughesa2f8e452018-01-11 10:11:17 +00005214 if (!fu_plugin_runner_get_results (plugin, dev, error))
5215 return FALSE;
Richard Hughes7e070c92018-01-12 16:50:05 +00005216
5217 /* the plugin either can't tell us the error, or doesn't know itself */
Richard Hughescce6a1c2019-04-16 17:25:48 +01005218 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED &&
5219 fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED_TRANSIENT) {
Richard Hughes7e070c92018-01-12 16:50:05 +00005220 g_debug ("falling back to generic failure");
Richard Hughesc0cd0232018-01-31 15:02:00 +00005221 fu_device_set_update_error (dev_history, "failed to run update on reboot");
Richard Hughesa2f8e452018-01-11 10:11:17 +00005222 }
Richard Hughes7e070c92018-01-12 16:50:05 +00005223
5224 /* update the state in the database */
Richard Hughesc0cd0232018-01-31 15:02:00 +00005225 fu_device_set_update_error (dev_history, fu_device_get_update_error (dev));
Richard Hughes0bbef292019-11-01 12:15:15 +00005226 return fu_history_modify_device (self->history, dev_history, error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00005227}
5228
5229static gboolean
5230fu_engine_update_history_database (FuEngine *self, GError **error)
5231{
5232 g_autoptr(GPtrArray) devices = NULL;
5233
5234 /* get any devices */
5235 devices = fu_history_get_devices (self->history, error);
5236 if (devices == NULL)
5237 return FALSE;
5238 for (guint i = 0; i < devices->len; i++) {
5239 FuDevice *dev = g_ptr_array_index (devices, i);
5240 g_autoptr(GError) error_local = NULL;
5241
5242 /* not in the required state */
5243 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_NEEDS_REBOOT)
5244 continue;
5245
5246 /* try to save the new update-state, but ignoring any error */
5247 if (!fu_engine_update_history_device (self, dev, &error_local))
5248 g_warning ("%s", error_local->message);
5249 }
5250 return TRUE;
5251}
5252
Richard Hughes16658372019-11-22 09:23:59 +00005253#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005254static void
5255fu_engine_udev_uevent_cb (GUdevClient *gudev_client,
5256 const gchar *action,
5257 GUdevDevice *udev_device,
5258 FuEngine *self)
5259{
5260 if (g_strcmp0 (action, "add") == 0) {
5261 fu_engine_udev_device_add (self, udev_device);
5262 return;
5263 }
5264 if (g_strcmp0 (action, "remove") == 0) {
5265 fu_engine_udev_device_remove (self, udev_device);
5266 return;
5267 }
5268 if (g_strcmp0 (action, "change") == 0) {
5269 fu_engine_udev_device_changed (self, udev_device);
5270 return;
5271 }
5272}
Richard Hughes16658372019-11-22 09:23:59 +00005273#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005274
Richard Hughesf3c7d422019-03-12 10:29:42 +00005275static void
5276fu_engine_ensure_client_certificate (FuEngine *self)
5277{
Richard Hughesf3c7d422019-03-12 10:29:42 +00005278 g_autoptr(GBytes) blob = g_bytes_new_static ("test\0", 5);
Richard Hughesf3c7d422019-03-12 10:29:42 +00005279 g_autoptr(GError) error = NULL;
Richard Hughesd5aab652020-02-25 12:47:50 +00005280 g_autoptr(JcatBlob) jcat_sig = NULL;
5281 g_autoptr(JcatEngine) jcat_engine = NULL;
Richard Hughesf3c7d422019-03-12 10:29:42 +00005282
5283 /* create keyring and sign dummy data to ensure certificate exists */
Richard Hughesd5aab652020-02-25 12:47:50 +00005284 jcat_engine = jcat_context_get_engine (self->jcat_context,
5285 JCAT_BLOB_KIND_PKCS7,
5286 &error);
5287 if (jcat_engine == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00005288 g_message ("failed to create keyring: %s", error->message);
5289 return;
5290 }
Richard Hughesd5aab652020-02-25 12:47:50 +00005291 jcat_sig = jcat_engine_self_sign (jcat_engine, blob, JCAT_SIGN_FLAG_NONE, &error);
5292 if (jcat_sig == NULL) {
Richard Hughesf3c7d422019-03-12 10:29:42 +00005293 g_message ("failed to sign using keyring: %s", error->message);
5294 return;
5295 }
5296 g_debug ("client certificate exists and working");
5297}
5298
Richard Hughes9945edb2017-06-19 10:03:55 +01005299/**
5300 * fu_engine_load:
5301 * @self: A #FuEngine
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005302 * @flags: #FuEngineLoadFlags, e.g. %FU_ENGINE_LOAD_FLAG_READONLY_FS
Richard Hughes9945edb2017-06-19 10:03:55 +01005303 * @error: A #GError, or %NULL
5304 *
5305 * Load the firmware update engine so it is ready for use.
5306 *
5307 * Returns: %TRUE for success
5308 **/
5309gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005310fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01005311{
Richard Hughesd1808aa2019-12-10 15:20:30 +00005312 FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE;
Richard Hughes51a869a2019-10-07 11:23:42 +01005313 FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE;
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005314 g_autoptr(GPtrArray) checksums = NULL;
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005315#ifndef _WIN32
5316 g_autoptr(GError) error_local = NULL;
5317#endif
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005318
Richard Hughes9945edb2017-06-19 10:03:55 +01005319 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
5320 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
5321
Mario Limonciello46aaee82019-01-10 12:58:00 -06005322 /* avoid re-loading a second time if fu-tool or fu-util request to */
5323 if (self->loaded)
5324 return TRUE;
5325
Richard Hughes45a00732019-11-22 16:57:14 +00005326/* TODO: Read registry key [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography] "MachineGuid" */
5327#ifndef _WIN32
Richard Hughes0917fb62019-09-21 12:55:37 +01005328 /* cache machine ID so we can use it from a sandboxed app */
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005329 self->host_machine_id = fwupd_build_machine_id ("fwupd", &error_local);
Richard Hughes0917fb62019-09-21 12:55:37 +01005330 if (self->host_machine_id == NULL)
Daniel Campellob5ae60c2020-02-27 17:06:28 -07005331 g_debug ("%s", error_local->message);
Richard Hughes45a00732019-11-22 16:57:14 +00005332#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01005333 /* read config file */
Richard Hughesd1808aa2019-12-10 15:20:30 +00005334 if (!fu_config_load (self->config, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005335 g_prefix_error (error, "Failed to load config: ");
5336 return FALSE;
5337 }
5338
Richard Hughesd1808aa2019-12-10 15:20:30 +00005339 /* read remotes */
5340 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
5341 remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_READONLY_FS;
5342 if (!fu_remote_list_load (self->remote_list, remote_list_flags, error)) {
5343 g_prefix_error (error, "Failed to load remotes: ");
5344 return FALSE;
5345 }
5346
Richard Hughesf3c7d422019-03-12 10:29:42 +00005347 /* create client certificate */
5348 fu_engine_ensure_client_certificate (self);
5349
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005350 /* get hardcoded approved firmware */
5351 checksums = fu_config_get_approved_firmware (self->config);
5352 for (guint i = 0; i < checksums->len; i++) {
5353 const gchar *csum = g_ptr_array_index (checksums, i);
5354 fu_engine_add_approved_firmware (self, csum);
5355 }
5356
5357 /* get extra firmware saved to the database */
5358 checksums = fu_history_get_approved_firmware (self->history, error);
5359 if (checksums == NULL)
5360 return FALSE;
5361 for (guint i = 0; i < checksums->len; i++) {
5362 const gchar *csum = g_ptr_array_index (checksums, i);
5363 fu_engine_add_approved_firmware (self, csum);
5364 }
5365
Richard Hughes75b965d2018-11-15 13:51:21 +00005366 /* set up idle exit */
5367 if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0)
5368 fu_idle_set_timeout (self->idle, fu_config_get_idle_timeout (self->config));
5369
Richard Hughesf77d7062017-11-27 12:06:36 +00005370 /* load quirks, SMBIOS and the hwids */
5371 fu_engine_load_smbios (self);
5372 fu_engine_load_hwids (self);
Richard Hughes51a869a2019-10-07 11:23:42 +01005373 /* on a read-only filesystem don't care about the cache GUID */
5374 if (flags & FU_ENGINE_LOAD_FLAG_READONLY_FS)
5375 quirks_flags |= FU_QUIRKS_LOAD_FLAG_READONLY_FS;
5376 fu_engine_load_quirks (self, quirks_flags);
Richard Hughes9c028f02017-10-28 21:14:28 +01005377
Richard Hughes9945edb2017-06-19 10:03:55 +01005378 /* load AppStream metadata */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00005379 if (!fu_engine_load_metadata_store (self, flags, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01005380 g_prefix_error (error, "Failed to load AppStream data: ");
5381 return FALSE;
5382 }
5383
Richard Hughes95c98a92019-10-22 16:03:15 +01005384 /* add the "built-in" firmware types */
5385 fu_engine_add_firmware_gtype (self, "raw", FU_TYPE_FIRMWARE);
5386 fu_engine_add_firmware_gtype (self, "dfu", FU_TYPE_DFU_FIRMWARE);
5387 fu_engine_add_firmware_gtype (self, "ihex", FU_TYPE_IHEX_FIRMWARE);
5388 fu_engine_add_firmware_gtype (self, "srec", FU_TYPE_SREC_FIRMWARE);
5389
Richard Hughes9945edb2017-06-19 10:03:55 +01005390 /* set shared USB context */
5391 self->usb_ctx = g_usb_context_new (error);
5392 if (self->usb_ctx == NULL) {
5393 g_prefix_error (error, "Failed to get USB context: ");
5394 return FALSE;
5395 }
5396
Richard Hughes9945edb2017-06-19 10:03:55 +01005397 /* delete old data files */
5398 if (!fu_engine_cleanup_state (error)) {
5399 g_prefix_error (error, "Failed to clean up: ");
5400 return FALSE;
5401 }
5402
5403 /* load plugin */
5404 if (!fu_engine_load_plugins (self, error)) {
5405 g_prefix_error (error, "Failed to load plugins: ");
5406 return FALSE;
5407 }
5408
Richard Hughes170c0c12017-11-22 11:26:24 +00005409 /* watch the device list for updates and proxy */
5410 g_signal_connect (self->device_list, "added",
5411 G_CALLBACK (fu_engine_device_added_cb),
5412 self);
5413 g_signal_connect (self->device_list, "removed",
5414 G_CALLBACK (fu_engine_device_removed_cb),
5415 self);
5416 g_signal_connect (self->device_list, "changed",
5417 G_CALLBACK (fu_engine_device_changed_cb),
5418 self);
5419
Richard Hughes16658372019-11-22 09:23:59 +00005420#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005421 /* udev watches can only be set up in _init() so set up client now */
5422 if (self->udev_subsystems->len > 0) {
Richard Hugheseffcc7a2018-08-31 14:32:07 +01005423 g_auto(GStrv) udev_subsystems = g_new0 (gchar *, self->udev_subsystems->len + 1);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005424 for (guint i = 0; i < self->udev_subsystems->len; i++) {
5425 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
5426 udev_subsystems[i] = g_strdup (subsystem);
5427 }
5428 self->gudev_client = g_udev_client_new ((const gchar * const *) udev_subsystems);
5429 g_signal_connect (self->gudev_client, "uevent",
5430 G_CALLBACK (fu_engine_udev_uevent_cb), self);
5431 }
Richard Hughes16658372019-11-22 09:23:59 +00005432#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005433
Mario Limonciello6463f312018-08-09 13:28:46 -05005434 fu_engine_set_status (self, FWUPD_STATUS_LOADING);
5435
Richard Hughes9945edb2017-06-19 10:03:55 +01005436 /* add devices */
5437 fu_engine_plugins_setup (self);
Richard Hughes8fb2e6a2019-10-22 15:37:57 +01005438 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5439 fu_engine_plugins_coldplug (self, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01005440
Richard Hughes634e9222017-11-29 15:50:02 +00005441 /* coldplug USB devices */
Richard Hughes104f6512017-11-24 11:44:57 +00005442 g_signal_connect (self->usb_ctx, "device-added",
5443 G_CALLBACK (fu_engine_usb_device_added_cb),
5444 self);
5445 g_signal_connect (self->usb_ctx, "device-removed",
5446 G_CALLBACK (fu_engine_usb_device_removed_cb),
5447 self);
Richard Hughese3757512019-09-25 10:12:28 +01005448 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5449 g_usb_context_enumerate (self->usb_ctx);
Richard Hughes104f6512017-11-24 11:44:57 +00005450
Richard Hughes16658372019-11-22 09:23:59 +00005451#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005452 /* coldplug udev devices */
Richard Hughese3757512019-09-25 10:12:28 +01005453 if ((flags & FU_ENGINE_LOAD_FLAG_NO_ENUMERATE) == 0)
5454 fu_engine_enumerate_udev (self);
Richard Hughes16658372019-11-22 09:23:59 +00005455#endif
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005456
Richard Hughesf43381f2020-02-24 10:11:31 +00005457 /* set device properties from the metadata */
5458 fu_engine_md_refresh_devices (self);
5459
Richard Hughesa2f8e452018-01-11 10:11:17 +00005460 /* update the db for devices that were updated during the reboot */
5461 if (!fu_engine_update_history_database (self, error))
5462 return FALSE;
5463
Mario Limonciello6463f312018-08-09 13:28:46 -05005464 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Mario Limonciello46aaee82019-01-10 12:58:00 -06005465 self->loaded = TRUE;
Mario Limonciello6463f312018-08-09 13:28:46 -05005466
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06005467 /* let clients know engine finished starting up */
5468 fu_engine_emit_changed (self);
5469
Richard Hughes9945edb2017-06-19 10:03:55 +01005470 /* success */
5471 return TRUE;
5472}
5473
5474static void
5475fu_engine_class_init (FuEngineClass *klass)
5476{
5477 GObjectClass *object_class = G_OBJECT_CLASS (klass);
5478 object_class->finalize = fu_engine_finalize;
5479
5480 signals[SIGNAL_CHANGED] =
5481 g_signal_new ("changed",
5482 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5483 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
5484 G_TYPE_NONE, 0);
5485 signals[SIGNAL_DEVICE_ADDED] =
5486 g_signal_new ("device-added",
5487 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5488 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5489 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
5490 signals[SIGNAL_DEVICE_REMOVED] =
5491 g_signal_new ("device-removed",
5492 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5493 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5494 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughesa5bb4d82017-06-19 20:22:25 +01005495 signals[SIGNAL_DEVICE_CHANGED] =
5496 g_signal_new ("device-changed",
5497 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5498 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
5499 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughes9945edb2017-06-19 10:03:55 +01005500 signals[SIGNAL_STATUS_CHANGED] =
5501 g_signal_new ("status-changed",
5502 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5503 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
5504 G_TYPE_NONE, 1, G_TYPE_UINT);
5505 signals[SIGNAL_PERCENTAGE_CHANGED] =
5506 g_signal_new ("percentage-changed",
5507 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
5508 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
5509 G_TYPE_NONE, 1, G_TYPE_UINT);
5510}
5511
Richard Hughes0eb123b2018-04-19 12:00:04 +01005512void
5513fu_engine_add_runtime_version (FuEngine *self,
5514 const gchar *component_id,
5515 const gchar *version)
5516{
5517 g_hash_table_insert (self->runtime_versions,
5518 g_strdup (component_id),
5519 g_strdup (version));
5520}
5521
Richard Hughesf517c9a2019-03-22 19:50:35 +00005522void
5523fu_engine_add_app_flag (FuEngine *self, FuAppFlags app_flags)
5524{
5525 g_return_if_fail (FU_IS_ENGINE (self));
5526 self->app_flags |= app_flags;
5527}
5528
Richard Hughes9945edb2017-06-19 10:03:55 +01005529static void
Richard Hughes75b965d2018-11-15 13:51:21 +00005530fu_engine_idle_status_notify_cb (FuIdle *idle, GParamSpec *pspec, FuEngine *self)
5531{
5532 FwupdStatus status = fu_idle_get_status (idle);
5533 if (status == FWUPD_STATUS_SHUTDOWN)
5534 fu_engine_set_status (self, status);
5535}
5536
5537static void
Richard Hughes9945edb2017-06-19 10:03:55 +01005538fu_engine_init (FuEngine *self)
5539{
Richard Hughesfc1e2672019-11-22 08:53:33 +00005540#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01005541 struct utsname uname_tmp;
Richard Hughesfc1e2672019-11-22 08:53:33 +00005542#endif
Richard Hughesd5aab652020-02-25 12:47:50 +00005543 g_autofree gchar *keyring_path = NULL;
5544 g_autofree gchar *pkidir_fw = NULL;
5545 g_autofree gchar *pkidir_md = NULL;
5546 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01005547 self->percentage = 0;
5548 self->status = FWUPD_STATUS_IDLE;
5549 self->config = fu_config_new ();
Richard Hughesd1808aa2019-12-10 15:20:30 +00005550 self->remote_list = fu_remote_list_new ();
Richard Hughes0a7e7832017-11-22 11:01:13 +00005551 self->device_list = fu_device_list_new ();
Richard Hughes49e5e052017-09-03 12:15:41 +01005552 self->smbios = fu_smbios_new ();
Richard Hughesd7704d42017-08-08 20:29:09 +01005553 self->hwids = fu_hwids_new ();
Richard Hughes75b965d2018-11-15 13:51:21 +00005554 self->idle = fu_idle_new ();
Richard Hughes9c028f02017-10-28 21:14:28 +01005555 self->quirks = fu_quirks_new ();
Richard Hughesbc3a4e12018-01-06 22:41:47 +00005556 self->history = fu_history_new ();
Richard Hughese7e95452017-11-22 09:05:53 +00005557 self->plugin_list = fu_plugin_list_new ();
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005558 self->plugin_filter = g_ptr_array_new_with_free_func (g_free);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005559 self->udev_subsystems = g_ptr_array_new_with_free_func (g_free);
Richard Hughes16658372019-11-22 09:23:59 +00005560#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +01005561 self->udev_changed_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
5562 g_free, (GDestroyNotify) fu_engine_udev_changed_helper_free);
Richard Hughes16658372019-11-22 09:23:59 +00005563#endif
Richard Hughes0eb123b2018-04-19 12:00:04 +01005564 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 +01005565 self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005566 self->approved_firmware = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Richard Hughes95c98a92019-10-22 16:03:15 +01005567 self->firmware_gtypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Richard Hughes0eb123b2018-04-19 12:00:04 +01005568
Mario Limonciello263cab92019-08-20 17:16:00 -05005569 g_signal_connect (self->config, "changed",
5570 G_CALLBACK (fu_engine_config_changed_cb),
5571 self);
Richard Hughesd1808aa2019-12-10 15:20:30 +00005572 g_signal_connect (self->remote_list, "changed",
5573 G_CALLBACK (fu_engine_remote_list_changed_cb),
5574 self);
Mario Limonciello263cab92019-08-20 17:16:00 -05005575
Richard Hughes75b965d2018-11-15 13:51:21 +00005576 g_signal_connect (self->idle, "notify::status",
5577 G_CALLBACK (fu_engine_idle_status_notify_cb), self);
5578
Richard Hughesd5aab652020-02-25 12:47:50 +00005579 /* setup Jcat context */
5580 self->jcat_context = jcat_context_new ();
5581 keyring_path = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
5582 jcat_context_set_keyring_path (self->jcat_context, keyring_path);
5583 sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
5584 pkidir_fw = g_build_filename (sysconfdir, "pki", "fwupd", NULL);
5585 jcat_context_add_public_keys (self->jcat_context, pkidir_fw);
5586 pkidir_md = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
5587 jcat_context_add_public_keys (self->jcat_context, pkidir_md);
5588
Richard Hughes0eb123b2018-04-19 12:00:04 +01005589 /* add some runtime versions of things the daemon depends on */
5590 fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION);
Richard Hughes2d37c3f2018-08-04 15:18:25 +01005591 fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12");
Richard Hughes416ade72018-10-10 20:36:53 +01005592 fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", "0.7.14");
Richard Hughes0eb123b2018-04-19 12:00:04 +01005593#if G_USB_CHECK_VERSION(0,3,1)
5594 fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ());
5595#endif
Richard Hughes34e0dab2018-04-20 16:43:00 +01005596
Richard Hughesf679e5e2019-08-28 15:31:34 +01005597 /* optional kernel version */
Richard Hughesfc1e2672019-11-22 08:53:33 +00005598#ifdef HAVE_UTSNAME_H
Richard Hughesf679e5e2019-08-28 15:31:34 +01005599 memset (&uname_tmp, 0, sizeof(uname_tmp));
5600 if (uname (&uname_tmp) >= 0)
5601 fu_engine_add_runtime_version (self, "org.kernel", uname_tmp.release);
Richard Hughesfc1e2672019-11-22 08:53:33 +00005602#endif
Richard Hughesf679e5e2019-08-28 15:31:34 +01005603
Richard Hughes34e0dab2018-04-20 16:43:00 +01005604 g_hash_table_insert (self->compile_versions,
Richard Hughes2d37c3f2018-08-04 15:18:25 +01005605 g_strdup ("com.redhat.fwupdate"),
5606 g_strdup ("12"));
5607 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01005608 g_strdup ("org.freedesktop.fwupd"),
5609 g_strdup (VERSION));
5610 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01005611 g_strdup ("org.freedesktop.gusb"),
5612 g_strdup_printf ("%i.%i.%i",
5613 G_USB_MAJOR_VERSION,
5614 G_USB_MINOR_VERSION,
5615 G_USB_MICRO_VERSION));
5616
Richard Hughes9945edb2017-06-19 10:03:55 +01005617}
5618
5619static void
5620fu_engine_finalize (GObject *obj)
5621{
5622 FuEngine *self = FU_ENGINE (obj);
5623
5624 if (self->usb_ctx != NULL)
5625 g_object_unref (self->usb_ctx);
Richard Hughes481aa2a2018-09-18 20:51:46 +01005626 if (self->silo != NULL)
5627 g_object_unref (self->silo);
Richard Hughes16658372019-11-22 09:23:59 +00005628#ifdef HAVE_GUDEV
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005629 if (self->gudev_client != NULL)
5630 g_object_unref (self->gudev_client);
Richard Hughes16658372019-11-22 09:23:59 +00005631#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01005632 if (self->coldplug_id != 0)
5633 g_source_remove (self->coldplug_id);
5634
Richard Hughes0917fb62019-09-21 12:55:37 +01005635 g_free (self->host_machine_id);
Richard Hughes75b965d2018-11-15 13:51:21 +00005636 g_object_unref (self->idle);
Richard Hughes9945edb2017-06-19 10:03:55 +01005637 g_object_unref (self->config);
Richard Hughesd1808aa2019-12-10 15:20:30 +00005638 g_object_unref (self->remote_list);
Richard Hughes49e5e052017-09-03 12:15:41 +01005639 g_object_unref (self->smbios);
Richard Hughes9c028f02017-10-28 21:14:28 +01005640 g_object_unref (self->quirks);
Richard Hughesd7704d42017-08-08 20:29:09 +01005641 g_object_unref (self->hwids);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00005642 g_object_unref (self->history);
Richard Hughes0a7e7832017-11-22 11:01:13 +00005643 g_object_unref (self->device_list);
Richard Hughesd5aab652020-02-25 12:47:50 +00005644 g_object_unref (self->jcat_context);
Richard Hughesc02ee4d2018-05-22 15:46:03 +01005645 g_ptr_array_unref (self->plugin_filter);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01005646 g_ptr_array_unref (self->udev_subsystems);
Richard Hughes16658372019-11-22 09:23:59 +00005647#ifdef HAVE_GUDEV
Richard Hughes5e952ce2019-08-26 11:09:46 +01005648 g_hash_table_unref (self->udev_changed_ids);
Richard Hughes16658372019-11-22 09:23:59 +00005649#endif
Richard Hughes0eb123b2018-04-19 12:00:04 +01005650 g_hash_table_unref (self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01005651 g_hash_table_unref (self->compile_versions);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00005652 g_hash_table_unref (self->approved_firmware);
Richard Hughes95c98a92019-10-22 16:03:15 +01005653 g_hash_table_unref (self->firmware_gtypes);
Mario Limonciello3f9a1c12018-06-06 14:06:40 -05005654 g_object_unref (self->plugin_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01005655
5656 G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj);
5657}
5658
5659FuEngine *
Richard Hughes5b5f6552018-05-18 10:22:39 +01005660fu_engine_new (FuAppFlags app_flags)
Richard Hughes9945edb2017-06-19 10:03:55 +01005661{
5662 FuEngine *self;
5663 self = g_object_new (FU_TYPE_ENGINE, NULL);
Richard Hughes5b5f6552018-05-18 10:22:39 +01005664 self->app_flags = app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +01005665 return FU_ENGINE (self);
5666}