blob: 69baa552d15798e7075e92aab15b1339b638d1f3 [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>
12#include <gio/gunixinputstream.h>
13#include <glib-object.h>
Richard Hughes9d6e0e72018-08-24 20:20:17 +010014#include <gudev/gudev.h>
Richard Hughesc02ee4d2018-05-22 15:46:03 +010015#include <fnmatch.h>
Richard Hughesbd4d2852017-09-13 14:05:14 +010016#include <string.h>
Richard Hughes473c5202018-01-11 21:06:16 +000017#include <sys/utsname.h>
Richard Hughes9945edb2017-06-19 10:03:55 +010018
19#include "fwupd-common-private.h"
20#include "fwupd-enums-private.h"
21#include "fwupd-error.h"
22#include "fwupd-release-private.h"
23#include "fwupd-remote-private.h"
24#include "fwupd-resources.h"
25
Richard Hughesdeea2da2017-12-15 15:40:44 +000026#include "fu-common-cab.h"
Richard Hughesd3d2c2c2018-10-10 20:01:05 +010027#include "fu-common-guid.h"
Richard Hughes943d2c92017-06-21 09:04:39 +010028#include "fu-common.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010029#include "fu-config.h"
30#include "fu-debug.h"
Richard Hughes0a7e7832017-11-22 11:01:13 +000031#include "fu-device-list.h"
Richard Hughes9dde04f2017-09-13 12:07:15 +010032#include "fu-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010033#include "fu-engine.h"
34#include "fu-hwids.h"
Richard Hughes7383ce22018-05-08 14:14:35 +010035#include "fu-keyring-utils.h"
Richard Hughesbc3a4e12018-01-06 22:41:47 +000036#include "fu-history.h"
Richard Hughes37d09432018-09-09 10:39:45 +010037#include "fu-mutex.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010038#include "fu-plugin.h"
Richard Hughese7e95452017-11-22 09:05:53 +000039#include "fu-plugin-list.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010040#include "fu-plugin-private.h"
41#include "fu-quirks.h"
Richard Hughes49e5e052017-09-03 12:15:41 +010042#include "fu-smbios.h"
Richard Hughes405baeb2018-09-06 14:23:08 +010043#include "fu-udev-device-private.h"
Richard Hughesc125ec02018-09-05 19:35:17 +010044#include "fu-usb-device-private.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010045
46static void fu_engine_finalize (GObject *obj);
47
48struct _FuEngine
49{
50 GObject parent_instance;
Richard Hughes5b5f6552018-05-18 10:22:39 +010051 FuAppFlags app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +010052 GUsbContext *usb_ctx;
Richard Hughes9d6e0e72018-08-24 20:20:17 +010053 GUdevClient *gudev_client;
Richard Hughes9945edb2017-06-19 10:03:55 +010054 FuConfig *config;
Richard Hughes0a7e7832017-11-22 11:01:13 +000055 FuDeviceList *device_list;
Richard Hughes9945edb2017-06-19 10:03:55 +010056 FwupdStatus status;
57 guint percentage;
Richard Hughesbc3a4e12018-01-06 22:41:47 +000058 FuHistory *history;
Richard Hughes481aa2a2018-09-18 20:51:46 +010059 XbSilo *silo;
Richard Hughes9945edb2017-06-19 10:03:55 +010060 gboolean coldplug_running;
61 guint coldplug_id;
62 guint coldplug_delay;
Richard Hughese7e95452017-11-22 09:05:53 +000063 FuPluginList *plugin_list;
Richard Hughesc02ee4d2018-05-22 15:46:03 +010064 GPtrArray *plugin_filter;
Richard Hughes1354ea92017-09-19 15:58:31 +010065 GPtrArray *supported_guids;
Richard Hughes9d6e0e72018-08-24 20:20:17 +010066 GPtrArray *udev_subsystems;
Richard Hughes49e5e052017-09-03 12:15:41 +010067 FuSmbios *smbios;
Richard Hughesd7704d42017-08-08 20:29:09 +010068 FuHwids *hwids;
Richard Hughes9c028f02017-10-28 21:14:28 +010069 FuQuirks *quirks;
Richard Hughes0eb123b2018-04-19 12:00:04 +010070 GHashTable *runtime_versions;
Richard Hughes34e0dab2018-04-20 16:43:00 +010071 GHashTable *compile_versions;
Richard Hughes9945edb2017-06-19 10:03:55 +010072};
73
74enum {
75 SIGNAL_CHANGED,
76 SIGNAL_DEVICE_ADDED,
77 SIGNAL_DEVICE_REMOVED,
78 SIGNAL_DEVICE_CHANGED,
79 SIGNAL_STATUS_CHANGED,
80 SIGNAL_PERCENTAGE_CHANGED,
81 SIGNAL_LAST
82};
83
84static guint signals[SIGNAL_LAST] = { 0 };
85
86G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT)
87
Richard Hughes9945edb2017-06-19 10:03:55 +010088static void
89fu_engine_emit_changed (FuEngine *self)
90{
91 g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
92}
93
94static void
Richard Hughes170c0c12017-11-22 11:26:24 +000095fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
96{
97 g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
98}
99
Richard Hughes9945edb2017-06-19 10:03:55 +0100100/**
101 * fu_engine_get_status:
102 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +0100103 *
104 * Gets the current engine status.
105 *
106 * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING
107 **/
108FwupdStatus
109fu_engine_get_status (FuEngine *self)
110{
111 g_return_val_if_fail (FU_IS_ENGINE (self), 0);
112 return self->status;
113}
114
Richard Hughes9945edb2017-06-19 10:03:55 +0100115static void
116fu_engine_set_status (FuEngine *self, FwupdStatus status)
117{
118 if (self->status == status)
119 return;
120 self->status = status;
121
122 /* emit changed */
123 g_debug ("Emitting PropertyChanged('Status'='%s')",
124 fwupd_status_to_string (status));
125 g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status);
126}
127
128static void
129fu_engine_set_percentage (FuEngine *self, guint percentage)
130{
131 if (self->percentage == percentage)
132 return;
133 self->percentage = percentage;
134
135 /* emit changed */
136 g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
137}
138
139static void
Richard Hughes4a036012017-11-30 16:33:24 +0000140fu_engine_progress_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
141{
142 fu_engine_set_percentage (self, fu_device_get_progress (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100143 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000144}
145
146static void
147fu_engine_status_notify_cb (FuDevice *device, GParamSpec *pspec, FuEngine *self)
148{
149 fu_engine_set_status (self, fu_device_get_status (device));
Richard Hughesec85ebc2018-08-10 09:52:30 +0100150 fu_engine_emit_device_changed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000151}
152
153static void
Richard Hughesfbcebe02017-12-11 16:37:19 +0000154fu_engine_watch_device (FuEngine *self, FuDevice *device)
Richard Hughes4a036012017-11-30 16:33:24 +0000155{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100156 g_autoptr(FuDevice) device_old = fu_device_list_get_old (self->device_list, device);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000157 if (device_old != NULL) {
158 g_signal_handlers_disconnect_by_func (device_old,
159 fu_engine_progress_notify_cb,
160 self);
161 g_signal_handlers_disconnect_by_func (device_old,
162 fu_engine_status_notify_cb,
163 self);
164 }
Richard Hughes4a036012017-11-30 16:33:24 +0000165 g_signal_connect (device, "notify::progress",
166 G_CALLBACK (fu_engine_progress_notify_cb), self);
167 g_signal_connect (device, "notify::status",
168 G_CALLBACK (fu_engine_status_notify_cb), self);
Richard Hughesfbcebe02017-12-11 16:37:19 +0000169}
170
171static void
172fu_engine_device_added_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
173{
174 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000175 g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device);
176}
177
178static void
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500179fu_engine_device_runner_device_removed (FuEngine *self, FuDevice *device)
180{
181 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
182 for (guint j = 0; j < plugins->len; j++) {
183 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
184 fu_plugin_runner_device_removed (plugin_tmp, device);
185 }
186}
187
188static void
Richard Hughes4a036012017-11-30 16:33:24 +0000189fu_engine_device_removed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
190{
Mario Limoncielloe260ead2018-09-01 09:19:24 -0500191 fu_engine_device_runner_device_removed (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000192 g_signal_handlers_disconnect_by_data (device, self);
193 g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device);
194}
195
196static void
197fu_engine_device_changed_cb (FuDeviceList *device_list, FuDevice *device, FuEngine *self)
198{
Richard Hughesfbcebe02017-12-11 16:37:19 +0000199 fu_engine_watch_device (self, device);
Richard Hughes4a036012017-11-30 16:33:24 +0000200 fu_engine_emit_device_changed (self, device);
201}
202
Richard Hughes481aa2a2018-09-18 20:51:46 +0100203/* convert hex and decimal versions to dotted style */
204static gchar *
205fu_engine_get_release_version (FuEngine *self, XbNode *component, XbNode *rel)
Richard Hughes611f1a92018-01-11 11:54:28 +0000206{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100207 FuVersionFormat fmt = FU_VERSION_FORMAT_TRIPLET;
208 const gchar *quirk;
209 const gchar *version;
210 const gchar *version_format;
211 guint64 ver_uint32;
212
213 /* get version */
214 version = xb_node_get_attr (rel, "version");
215 if (version == NULL)
Richard Hughes611f1a92018-01-11 11:54:28 +0000216 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100217
218 /* already dotted notation */
219 if (g_strstr_len (version, -1, ".") != NULL)
220 return g_strdup (version);
221
222 /* fall back to the quirk database until all files have metadata */
223 quirk = fu_quirks_lookup_by_id (self->quirks,
224 "DaemonVersionFormat=quad",
225 FU_QUIRKS_DAEMON_VERSION_FORMAT);
226 if (quirk != NULL) {
227 const gchar *id = xb_node_query_text (component, "id", NULL);
228 g_auto(GStrv) globs = g_strsplit (quirk, ",", -1);
229 for (guint i = 0; globs[i] != NULL; i++) {
230 if (fnmatch (globs[i], id, 0) == 0) {
231 fmt = FU_VERSION_FORMAT_QUAD;
232 break;
233 }
234 }
235 }
236
237 /* specified in metadata */
238 version_format = xb_node_query_text (component,
239 "custom/value[@key='LVFS::VersionFormat']",
240 NULL);
241 if (version_format != NULL)
242 fmt = fu_common_version_format_from_string (version_format);
243
244 /* don't touch my version! */
245 if (fmt == FU_VERSION_FORMAT_PLAIN)
246 return g_strdup (version);
247
248 /* parse as integer */
249 ver_uint32 = fu_common_strtoull (version);
250 if (ver_uint32 == 0 || ver_uint32 > G_MAXUINT32)
251 return g_strdup (version);
252
253 /* convert to dotted decimal */
254 return fu_common_version_from_uint32 ((guint32) ver_uint32, fmt);
Richard Hughes611f1a92018-01-11 11:54:28 +0000255}
256
Richard Hughes4a036012017-11-30 16:33:24 +0000257static void
Richard Hughesbd4d2852017-09-13 14:05:14 +0100258fu_engine_set_release_from_appstream (FuEngine *self,
259 FwupdRelease *rel,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100260 XbNode *component,
261 XbNode *release)
Richard Hughes9945edb2017-06-19 10:03:55 +0100262{
Richard Hughesc6afb512017-08-22 10:22:20 +0100263 FwupdRemote *remote = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100264 const gchar *tmp;
Richard Hughes611f1a92018-01-11 11:54:28 +0000265 const gchar *remote_id;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100266 guint64 tmp64;
267 g_autofree gchar *version_rel = NULL;
268 g_autoptr(XbNode) description = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100269
Richard Hughes481aa2a2018-09-18 20:51:46 +0100270 /* set from the component */
271 tmp = xb_node_query_text (component, "id", NULL);
272 if (tmp != NULL)
273 fwupd_release_set_appstream_id (rel, tmp);
274 tmp = xb_node_query_text (component, "url[@type='homepage']", NULL);
275 if (tmp != NULL)
276 fwupd_release_set_homepage (rel, tmp);
277 tmp = xb_node_query_text (component, "project_license", NULL);
278 if (tmp != NULL)
279 fwupd_release_set_license (rel, tmp);
280 tmp = xb_node_query_text (component, "name", NULL);
281 if (tmp != NULL)
282 fwupd_release_set_name (rel, tmp);
283 tmp = xb_node_query_text (component, "summary", NULL);
284 if (tmp != NULL)
285 fwupd_release_set_summary (rel, tmp);
286 tmp = xb_node_query_text (component, "developer_name", NULL);
287 if (tmp != NULL)
288 fwupd_release_set_vendor (rel, tmp);
289
290 /* the version is fixed up at runtime */
291 version_rel = fu_engine_get_release_version (self, component, release);
292 if (version_rel != NULL)
293 fwupd_release_set_version (rel, version_rel);
Richard Hughes74fa2ca2018-01-10 21:33:39 +0000294
Richard Hughesc6afb512017-08-22 10:22:20 +0100295 /* find the remote */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100296 remote_id = xb_node_query_text (component, "../custom/fwupd::RemoteId", NULL);
Richard Hughes611f1a92018-01-11 11:54:28 +0000297 if (remote_id != NULL) {
298 fwupd_release_set_remote_id (rel, remote_id);
299 remote = fu_config_get_remote_by_id (self->config, remote_id);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100300 if (remote == NULL)
301 g_warning ("no remote found for release %s", version_rel);
Richard Hughesbd4d2852017-09-13 14:05:14 +0100302 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100303 description = xb_node_query_first (release, "description", NULL);
304 if (description != NULL) {
305 g_autofree gchar *xml = NULL;
306 xml = xb_node_export (description, XB_NODE_EXPORT_FLAG_ONLY_CHILDREN, NULL);
307 if (xml != NULL)
308 fwupd_release_set_description (rel, xml);
309 }
310 tmp = xb_node_query_text (release, "location", NULL);
Richard Hughesc6afb512017-08-22 10:22:20 +0100311 if (tmp != NULL) {
312 g_autofree gchar *uri = NULL;
313 if (remote != NULL)
314 uri = fwupd_remote_build_firmware_uri (remote, tmp, NULL);
315 if (uri == NULL)
316 uri = g_strdup (tmp);
317 fwupd_release_set_uri (rel, uri);
318 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100319 tmp = xb_node_query_text (release, "checksum[@target='content']", NULL);
320 if (tmp != NULL)
321 fwupd_release_set_filename (rel, tmp);
322 tmp = xb_node_query_text (release, "checksum[@target='container']", NULL);
323 if (tmp != NULL)
324 fwupd_release_add_checksum (rel, tmp);
325 tmp64 = xb_node_query_text_as_uint (release, "size[@type='installed']", NULL);
326 if (tmp64 != G_MAXUINT64) {
327 fwupd_release_set_size (rel, tmp64);
328 } else {
329 GBytes *sz = xb_node_get_data (release, "fwupd::ReleaseSize");
330 if (sz != NULL) {
331 const guint64 *sizeptr = g_bytes_get_data (sz, NULL);
332 fwupd_release_set_size (rel, *sizeptr);
333 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100334 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100335}
336
Richard Hughes481aa2a2018-09-18 20:51:46 +0100337/* finds the remote-id for the first firmware in the silo that matches this
Richard Hughes534255c2018-01-28 19:51:56 +0000338 * container checksum */
339static const gchar *
340fu_engine_get_remote_id_for_checksum (FuEngine *self, const gchar *csum)
341{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100342 g_autofree gchar *xpath = NULL;
343 g_autoptr(XbNode) key = NULL;
344 xpath = g_build_filename ("components", "component", "releases", "release",
345 "checksum[@target='container']", "..", "..",
346 "..", "..", "custom", "fwupd::RemoteId", NULL);
347
348 key = xb_silo_query_first (self->silo, xpath, NULL);
349 if (key == NULL)
350 return NULL;
351 return xb_node_get_text (key);
Richard Hughes534255c2018-01-28 19:51:56 +0000352}
353
Richard Hughes9945edb2017-06-19 10:03:55 +0100354/**
355 * fu_engine_unlock:
356 * @self: A #FuEngine
357 * @device_id: A device ID
358 * @error: A #GError, or %NULL
359 *
360 * Unlocks a device.
361 *
362 * Returns: %TRUE for success
363 **/
364gboolean
365fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error)
366{
Richard Hughes34834102017-11-21 21:55:00 +0000367 FuPlugin *plugin;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100368 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100369
370 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
371 g_return_val_if_fail (device_id != NULL, FALSE);
372 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
373
374 /* check the device exists */
Richard Hughes40127542018-01-12 20:25:55 +0000375 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000376 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100377 return FALSE;
378
Richard Hughes34834102017-11-21 21:55:00 +0000379 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000380 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000381 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000382 error);
Richard Hughes34834102017-11-21 21:55:00 +0000383 if (plugin == NULL)
384 return FALSE;
385
Richard Hughes9945edb2017-06-19 10:03:55 +0100386 /* run the correct plugin that added this */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000387 if (!fu_plugin_runner_unlock (plugin, device, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100388 return FALSE;
389
390 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000391 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100392 fu_engine_emit_changed (self);
393 return TRUE;
394}
395
Richard Hughes9945edb2017-06-19 10:03:55 +0100396/**
Richard Hughesa6bd5582017-09-07 14:32:22 +0100397 * fu_engine_modify_remote:
398 * @self: A #FuEngine
399 * @remote_id: A remote ID
Richard Hughes4eada342017-10-03 21:20:32 +0100400 * @key: the key, e.g. `Enabled`
401 * @value: the key, e.g. `true`
Richard Hughesa6bd5582017-09-07 14:32:22 +0100402 * @error: A #GError, or %NULL
403 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100404 * Updates the verification silo entry for a specific device.
Richard Hughesa6bd5582017-09-07 14:32:22 +0100405 *
406 * Returns: %TRUE for success
407 **/
408gboolean
409fu_engine_modify_remote (FuEngine *self,
410 const gchar *remote_id,
411 const gchar *key,
412 const gchar *value,
413 GError **error)
414{
415 FwupdRemote *remote;
416 const gchar *filename;
417 const gchar *keys[] = { "Enabled", "MetadataURI", "FirmwareBaseURI", NULL };
418 g_autoptr(GKeyFile) keyfile = g_key_file_new ();
419
420 /* check remote is valid */
421 remote = fu_config_get_remote_by_id (self->config, remote_id);
422 if (remote == NULL) {
423 g_set_error (error,
424 FWUPD_ERROR,
425 FWUPD_ERROR_NOT_FOUND,
426 "remote %s not found", remote_id);
427 return FALSE;
428 }
429
430 /* check keys are valid */
431 if (!g_strv_contains (keys, key)) {
432 g_set_error (error,
433 FWUPD_ERROR,
434 FWUPD_ERROR_NOT_FOUND,
435 "key %s not supported", key);
436 return FALSE;
437 }
438
439 /* modify the remote filename */
440 filename = fwupd_remote_get_filename_source (remote);
441 if (!g_key_file_load_from_file (keyfile, filename,
442 G_KEY_FILE_KEEP_COMMENTS,
443 error)) {
444 g_prefix_error (error, "failed to load %s: ", filename);
445 return FALSE;
446 }
447 g_key_file_set_string (keyfile, "fwupd Remote", key, value);
448 return g_key_file_save_to_file (keyfile, filename, error);
449}
450
451/**
Richard Hughes6b222952018-01-11 10:20:48 +0000452 * fu_engine_modify_device:
453 * @self: A #FuEngine
454 * @device_id: A device ID
455 * @key: the key, e.g. `Flags`
456 * @value: the key, e.g. `reported`
457 * @error: A #GError, or %NULL
458 *
459 * Sets the reported flag for a specific device. This ensures that other
460 * front-end clients for fwupd do not report the same event.
461 *
462 * Returns: %TRUE for success
463 **/
464gboolean
465fu_engine_modify_device (FuEngine *self,
466 const gchar *device_id,
467 const gchar *key,
468 const gchar *value,
469 GError **error)
470{
471 g_autoptr(FuDevice) device = NULL;
472
473 /* find the correct device */
Richard Hughes0b9d9962018-01-12 16:31:28 +0000474 device = fu_history_get_device_by_id (self->history, device_id, error);
Richard Hughes6b222952018-01-11 10:20:48 +0000475 if (device == NULL)
476 return FALSE;
477
478 /* support adding a subset of the device flags */
479 if (g_strcmp0 (key, "Flags") == 0) {
480 FwupdDeviceFlags flag = fwupd_device_flag_from_string (value);
481 if (flag == FWUPD_DEVICE_FLAG_UNKNOWN) {
482 g_set_error (error,
483 FWUPD_ERROR,
484 FWUPD_ERROR_NOT_SUPPORTED,
485 "key %s not a valid flag", key);
486 return FALSE;
487 }
Richard Hughesad54f652018-01-30 17:22:37 +0000488 if (flag != FWUPD_DEVICE_FLAG_REPORTED &&
489 flag != FWUPD_DEVICE_FLAG_NOTIFIED) {
Richard Hughes6b222952018-01-11 10:20:48 +0000490 g_set_error (error,
491 FWUPD_ERROR,
492 FWUPD_ERROR_NOT_SUPPORTED,
493 "flag %s cannot be set from client", key);
494 return FALSE;
495 }
Richard Hughesc0cd0232018-01-31 15:02:00 +0000496 fu_device_add_flag (device, flag);
497 return fu_history_modify_device (self->history, device,
498 FU_HISTORY_FLAGS_MATCH_OLD_VERSION |
499 FU_HISTORY_FLAGS_MATCH_NEW_VERSION,
500 error);
Richard Hughes6b222952018-01-11 10:20:48 +0000501 }
502
503 /* others invalid */
504 g_set_error (error,
505 FWUPD_ERROR,
506 FWUPD_ERROR_NOT_SUPPORTED,
507 "key %s not supported", key);
508 return FALSE;
509}
510
Richard Hughes481aa2a2018-09-18 20:51:46 +0100511static const gchar *
512fu_engine_checksum_type_to_string (GChecksumType checksum_type)
513{
514 if (checksum_type == G_CHECKSUM_SHA1)
515 return "sha1";
516 if (checksum_type == G_CHECKSUM_SHA256)
517 return "sha256";
518 if (checksum_type == G_CHECKSUM_SHA512)
519 return "sha512";
520 return "sha1";
521}
522
Richard Hughes6b222952018-01-11 10:20:48 +0000523/**
Richard Hughes9945edb2017-06-19 10:03:55 +0100524 * fu_engine_verify_update:
525 * @self: A #FuEngine
526 * @device_id: A device ID
527 * @error: A #GError, or %NULL
528 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100529 * Updates the verification silo entry for a specific device.
Richard Hughes9945edb2017-06-19 10:03:55 +0100530 *
531 * Returns: %TRUE for success
532 **/
533gboolean
534fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error)
535{
Richard Hughes34834102017-11-21 21:55:00 +0000536 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100537 GPtrArray *checksums;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100538 GPtrArray *guids;
539 g_autofree gchar *fn = NULL;
540 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100541 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100542 g_autoptr(GFile) file = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100543 g_autoptr(XbBuilder) builder = xb_builder_new ();
544 g_autoptr(XbBuilderNode) component = NULL;
545 g_autoptr(XbBuilderNode) provides = NULL;
546 g_autoptr(XbBuilderNode) release = NULL;
547 g_autoptr(XbBuilderNode) releases = NULL;
548 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100549
550 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
551 g_return_val_if_fail (device_id != NULL, FALSE);
552 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
553
554 /* check the devices still exists */
Richard Hughes40127542018-01-12 20:25:55 +0000555 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000556 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100557 return FALSE;
558
Richard Hughes34834102017-11-21 21:55:00 +0000559 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000560 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000561 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000562 error);
Richard Hughes34834102017-11-21 21:55:00 +0000563 if (plugin == NULL)
564 return FALSE;
565
Richard Hughes9945edb2017-06-19 10:03:55 +0100566 /* get the checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000567 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100568 if (checksums->len == 0) {
Richard Hughes0a7e7832017-11-22 11:01:13 +0000569 if (!fu_plugin_runner_verify (plugin, device,
Richard Hughes9945edb2017-06-19 10:03:55 +0100570 FU_PLUGIN_VERIFY_FLAG_NONE,
571 error))
572 return FALSE;
Richard Hughes0a7e7832017-11-22 11:01:13 +0000573 fu_engine_emit_device_changed (self, device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100574 }
575
576 /* we got nothing */
577 if (checksums->len == 0) {
578 g_set_error_literal (error,
579 FWUPD_ERROR,
580 FWUPD_ERROR_NOT_SUPPORTED,
581 "device verification not supported");
582 return FALSE;
583 }
584
Richard Hughes481aa2a2018-09-18 20:51:46 +0100585 /* build XML */
586 component = xb_builder_node_insert (NULL, "component",
587 "type", "firmware",
588 NULL);
589 provides = xb_builder_node_insert (component, "provides", NULL);
590 guids = fu_device_get_guids (device);
591 for (guint i = 0; i < guids->len; i++) {
592 const gchar *guid = g_ptr_array_index (guids, i);
593 g_autoptr(XbBuilderNode) provide = NULL;
594 provide = xb_builder_node_insert (provides, "firmware",
595 "type", "flashed",
596 NULL);
597 xb_builder_node_set_text (provide, guid, -1);
598 }
599 releases = xb_builder_node_insert (component, "releases", NULL);
600 release = xb_builder_node_insert (releases, "release",
601 "version", fu_device_get_version (device),
602 NULL);
603 for (guint i = 0; i < checksums->len; i++) {
604 const gchar *checksum = g_ptr_array_index (checksums, i);
605 GChecksumType kind = fwupd_checksum_guess_kind (checksum);
606 g_autoptr(XbBuilderNode) csum = NULL;
607 csum = xb_builder_node_insert (provides, "checksum",
608 "type", fu_engine_checksum_type_to_string (kind),
609 "target", "content",
610 NULL);
611 xb_builder_node_set_text (csum, checksum, -1);
612 }
613 xb_builder_import_node (builder, component);
614
615 /* save silo */
616 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
617 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
618 file = g_file_new_for_path (fn);
619 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
620 if (silo == NULL)
621 return FALSE;
622 if (!xb_silo_export_file (silo, file,
623 XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE,
624 NULL, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100625 return FALSE;
626
Richard Hughes481aa2a2018-09-18 20:51:46 +0100627 /* success */
628 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100629}
630
Richard Hughes481aa2a2018-09-18 20:51:46 +0100631static XbNode *
632fu_engine_store_get_app_by_guids (XbSilo *silo, FuDevice *device)
Richard Hughes9945edb2017-06-19 10:03:55 +0100633{
634 GPtrArray *guids = fu_device_get_guids (device);
635 for (guint i = 0; i < guids->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100636 g_autoptr(XbNode) component = NULL;
637 g_autofree gchar *xpath = NULL;
638 const gchar *guid = g_ptr_array_index (guids, i);
639 xpath = g_strdup_printf ("components/component/"
640 "provides/firmware[@type='flashed'][text()='%s']/"
641 "../..",
642 guid);
643 component = xb_silo_query_first (silo, xpath, NULL);
644 if (component != NULL)
645 return g_steal_pointer (&component);
Richard Hughes9945edb2017-06-19 10:03:55 +0100646 }
647 return NULL;
648}
649
650/**
651 * fu_engine_verify:
652 * @self: A #FuEngine
653 * @device_id: A device ID
654 * @error: A #GError, or %NULL
655 *
Richard Hughes481aa2a2018-09-18 20:51:46 +0100656 * Verifies a device firmware checksum using the verification silo entry.
Richard Hughes9945edb2017-06-19 10:03:55 +0100657 *
658 * Returns: %TRUE for success
659 **/
660gboolean
661fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error)
662{
Richard Hughes34834102017-11-21 21:55:00 +0000663 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +0100664 GPtrArray *checksums;
665 const gchar *hash = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100666 const gchar *version;
667 g_autofree gchar *fn = NULL;
668 g_autofree gchar *localstatedir = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +0100669 g_autoptr(FuDevice) device = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100670 g_autoptr(GFile) file = NULL;
671 g_autoptr(XbNode) csum = NULL;
672 g_autoptr(XbNode) release = NULL;
673 g_autoptr(XbSilo) silo = xb_silo_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +0100674
675 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
676 g_return_val_if_fail (device_id != NULL, FALSE);
677 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
678
679 /* check the id exists */
Richard Hughes40127542018-01-12 20:25:55 +0000680 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +0000681 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +0100682 return FALSE;
683
Richard Hughes34834102017-11-21 21:55:00 +0000684 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +0000685 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +0000686 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +0000687 error);
Richard Hughes34834102017-11-21 21:55:00 +0000688 if (plugin == NULL)
689 return FALSE;
690
Richard Hughes9945edb2017-06-19 10:03:55 +0100691 /* set the device firmware hash */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000692 if (!fu_plugin_runner_verify (plugin, device,
Richard Hughes9945edb2017-06-19 10:03:55 +0100693 FU_PLUGIN_VERIFY_FLAG_NONE, error))
694 return FALSE;
695
696 /* find component in metadata */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100697 version = fu_device_get_version (device);
698 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
699 fn = g_strdup_printf ("%s/verify/%s.xml", localstatedir, device_id);
700 file = g_file_new_for_path (fn);
701 if (g_file_query_exists (file, NULL)) {
702 g_autofree gchar *xpath = NULL;
703 g_autoptr(XbBuilder) builder = xb_builder_new ();
704 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
705 if (!xb_builder_source_load_file (source, file,
706 XB_BUILDER_SOURCE_FLAG_NONE,
707 NULL, error))
708 return FALSE;
709 xb_builder_import_source (builder, source);
710 silo = xb_builder_compile (builder,
711 XB_BUILDER_COMPILE_FLAG_NONE,
712 NULL, error);
713 if (silo == NULL)
714 return FALSE;
715 xpath = g_strdup_printf ("component/releases/release[@version='%s']", version);
716 release = xb_silo_query_first (silo, xpath, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +0100717 }
718
Richard Hughes481aa2a2018-09-18 20:51:46 +0100719 /* try again with the system metadata */
Richard Hughes9945edb2017-06-19 10:03:55 +0100720 if (release == NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +0100721 GPtrArray *guids = fu_device_get_guids (device);
722 for (guint i = 0; i < guids->len; i++) {
723 const gchar *guid = g_ptr_array_index (guids, i);
724 g_autofree gchar *xpath2 = NULL;
725 xpath2 = g_strdup_printf ("components/component/"
726 "provides/firmware[@type='flashed'][text()='%s']/"
727 "../../releases/release[@version='%s']",
728 guid, version);
729 release = xb_silo_query_first (self->silo, xpath2, NULL);
730 if (release != NULL)
731 break;
Richard Hughes9945edb2017-06-19 10:03:55 +0100732 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100733 }
734 if (release == NULL) {
735 g_set_error (error,
736 FWUPD_ERROR,
737 FWUPD_ERROR_NOT_FOUND,
738 "No version %s", version);
739 return FALSE;
740 }
741
742 /* find checksum */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100743 csum = xb_node_query_first (release, "checksum[@target='content']", NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +0100744 if (csum == NULL) {
745 g_set_error (error,
746 FWUPD_ERROR,
747 FWUPD_ERROR_NOT_FOUND,
748 "No content checksum for %s", version);
749 return FALSE;
750 }
751
752 /* get the matching checksum */
Richard Hughes0a7e7832017-11-22 11:01:13 +0000753 checksums = fu_device_get_checksums (device);
Richard Hughes9945edb2017-06-19 10:03:55 +0100754 if (checksums->len == 0) {
755 g_set_error (error,
756 FWUPD_ERROR,
757 FWUPD_ERROR_NOT_FOUND,
758 "No device checksums for %s", version);
759 return FALSE;
760 }
761 for (guint j = 0; j < checksums->len; j++) {
762 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
763 GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100764 if (fwupd_checksum_guess_kind (xb_node_get_text (csum)) == hash_kind) {
Richard Hughes9945edb2017-06-19 10:03:55 +0100765 hash = hash_tmp;
766 break;
767 }
768 }
769 if (hash == NULL) {
770 g_set_error (error,
771 FWUPD_ERROR,
772 FWUPD_ERROR_NOT_FOUND,
773 "No matching hash kind for %s", version);
774 return FALSE;
775 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100776 if (g_strcmp0 (xb_node_get_text (csum), hash) != 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +0100777 g_set_error (error,
778 FWUPD_ERROR,
779 FWUPD_ERROR_NOT_FOUND,
780 "For v%s expected %s, got %s",
781 version,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100782 xb_node_get_text (csum),
Richard Hughes9945edb2017-06-19 10:03:55 +0100783 hash);
784 return FALSE;
785 }
786
787 /* success */
788 return TRUE;
789}
790
Richard Hughes481aa2a2018-09-18 20:51:46 +0100791static gboolean
792fu_engine_require_vercmp (XbNode *req, const gchar *version, GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +0000793{
Richard Hughes481aa2a2018-09-18 20:51:46 +0100794 gboolean ret = FALSE;
795 gint rc = 0;
796 const gchar *tmp = xb_node_get_attr (req, "compare");
797 const gchar *version_req = xb_node_get_attr (req, "version");
798
799 if (g_strcmp0 (tmp, "eq") == 0) {
800 rc = fu_common_vercmp (version, version_req);
801 ret = rc == 0;
802 } else if (g_strcmp0 (tmp, "ne") == 0) {
803 rc = fu_common_vercmp (version, version_req);
804 ret = rc != 0;
805 } else if (g_strcmp0 (tmp, "lt") == 0) {
806 rc = fu_common_vercmp (version, version_req);
807 ret = rc < 0;
808 } else if (g_strcmp0 (tmp, "gt") == 0) {
809 rc = fu_common_vercmp (version, version_req);
810 ret = rc > 0;
811 } else if (g_strcmp0 (tmp, "le") == 0) {
812 rc = fu_common_vercmp (version, version_req);
813 ret = rc <= 0;
814 } else if (g_strcmp0 (tmp, "ge") == 0) {
815 rc = fu_common_vercmp (version, version_req);
816 ret = rc >= 0;
817 } else if (g_strcmp0 (tmp, "glob") == 0) {
818 ret = fnmatch (version_req, version, 0) == 0;
819 } else if (g_strcmp0 (tmp, "regex") == 0) {
820 ret = g_regex_match_simple (version_req, version, 0, 0);
821 } else {
822 g_set_error (error,
823 FWUPD_ERROR,
824 FWUPD_ERROR_NOT_SUPPORTED,
825 "failed to compare [%s] and [%s]",
826 version_req,
827 version);
828 return FALSE;
Richard Hughes650dade2017-12-14 14:43:11 +0000829 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100830
831 /* set error */
832 if (!ret) {
833 g_set_error (error,
834 FWUPD_ERROR,
835 FWUPD_ERROR_INTERNAL,
836 "failed predicate [%s %s %s]",
837 version_req, tmp, version);
838 }
839 return ret;
Richard Hughes650dade2017-12-14 14:43:11 +0000840}
841
Richard Hughes9945edb2017-06-19 10:03:55 +0100842static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +0100843fu_engine_check_requirement_firmware (FuEngine *self, XbNode *req,
Richard Hughes0eb123b2018-04-19 12:00:04 +0100844 FuDevice *device, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +0100845{
Richard Hughes88adcbe2017-11-21 14:33:56 +0000846 g_autoptr(GError) error_local = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100847
Richard Hughes0eb123b2018-04-19 12:00:04 +0100848 /* old firmware version */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100849 if (xb_node_get_text (req) == NULL) {
Richard Hughes0eb123b2018-04-19 12:00:04 +0100850 const gchar *version = fu_device_get_version (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100851 if (!fu_engine_require_vercmp (req, version, &error_local)) {
852 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +0000853 g_set_error (error,
854 FWUPD_ERROR,
855 FWUPD_ERROR_INVALID_FILE,
856 "Not compatible with firmware version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100857 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +0000858 } else {
859 g_set_error (error,
860 FWUPD_ERROR,
861 FWUPD_ERROR_INVALID_FILE,
862 "Not compatible with firmware version: %s",
863 error_local->message);
864 }
865 return FALSE;
866 }
Richard Hughes0eb123b2018-04-19 12:00:04 +0100867 return TRUE;
868 }
Richard Hughes88adcbe2017-11-21 14:33:56 +0000869
Richard Hughes0eb123b2018-04-19 12:00:04 +0100870 /* bootloader version */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100871 if (g_strcmp0 (xb_node_get_text (req), "bootloader") == 0) {
Richard Hughes0eb123b2018-04-19 12:00:04 +0100872 const gchar *version = fu_device_get_version_bootloader (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100873 if (!fu_engine_require_vercmp (req, version, &error_local)) {
874 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +0000875 g_set_error (error,
876 FWUPD_ERROR,
877 FWUPD_ERROR_INVALID_FILE,
878 "Not compatible with bootloader version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100879 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +0000880 } else {
881 g_set_error (error,
882 FWUPD_ERROR,
883 FWUPD_ERROR_INVALID_FILE,
884 "Not compatible with bootloader version: %s",
885 error_local->message);
886 }
887 return FALSE;
888 }
Richard Hughes0eb123b2018-04-19 12:00:04 +0100889 return TRUE;
890 }
Richard Hughes88adcbe2017-11-21 14:33:56 +0000891
Richard Hughes0eb123b2018-04-19 12:00:04 +0100892 /* vendor ID */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100893 if (g_strcmp0 (xb_node_get_text (req), "vendor-id") == 0) {
Richard Hughes0eb123b2018-04-19 12:00:04 +0100894 const gchar *version = fu_device_get_vendor_id (device);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100895 if (!fu_engine_require_vercmp (req, version, &error_local)) {
896 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes88adcbe2017-11-21 14:33:56 +0000897 g_set_error (error,
898 FWUPD_ERROR,
899 FWUPD_ERROR_INVALID_FILE,
900 "Not compatible with vendor %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100901 version, xb_node_get_attr (req, "version"));
Richard Hughes88adcbe2017-11-21 14:33:56 +0000902 } else {
903 g_set_error (error,
904 FWUPD_ERROR,
905 FWUPD_ERROR_INVALID_FILE,
906 "Not compatible with vendor: %s",
907 error_local->message);
908 }
909 return FALSE;
910 }
Richard Hughes0eb123b2018-04-19 12:00:04 +0100911 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100912 }
913
Richard Hughes12c84992018-10-02 11:07:28 +0100914 /* another device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100915 if (fu_common_guid_is_valid (xb_node_get_text (req))) {
916 const gchar *guid = xb_node_get_text (req);
Richard Hughes12c84992018-10-02 11:07:28 +0100917 const gchar *version;
918 g_autoptr(FuDevice) device2 = NULL;
919
920 /* find if the other device exists */
921 device2 = fu_device_list_get_by_guid (self->device_list, guid, error);
922 if (device2 == NULL)
923 return FALSE;
924
925 /* get the version of the other device */
926 version = fu_device_get_version (device2);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100927 if (!fu_engine_require_vercmp (req, version, &error_local)) {
928 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes12c84992018-10-02 11:07:28 +0100929 g_set_error (error,
930 FWUPD_ERROR,
931 FWUPD_ERROR_INVALID_FILE,
932 "Not compatible with %s version %s, requires >= %s",
933 fu_device_get_name (device2),
934 version,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100935 xb_node_get_attr (req, "version"));
Richard Hughes12c84992018-10-02 11:07:28 +0100936 } else {
937 g_set_error (error,
938 FWUPD_ERROR,
939 FWUPD_ERROR_INVALID_FILE,
940 "Not compatible with %s: %s",
941 fu_device_get_name (device2),
942 error_local->message);
943 }
944 return FALSE;
945 }
946 return TRUE;
947
948 }
949
Richard Hughes0eb123b2018-04-19 12:00:04 +0100950 /* not supported */
951 g_set_error (error,
952 FWUPD_ERROR,
953 FWUPD_ERROR_NOT_SUPPORTED,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100954 "cannot handle firmware requirement '%s'",
955 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +0100956 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +0100957}
Richard Hughes9945edb2017-06-19 10:03:55 +0100958
959static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +0100960fu_engine_check_requirement_id (FuEngine *self, XbNode *req, GError **error)
Richard Hughes2ec78d62017-11-03 21:48:54 +0000961{
Richard Hughes0eb123b2018-04-19 12:00:04 +0100962 g_autoptr(GError) error_local = NULL;
963 const gchar *version = g_hash_table_lookup (self->runtime_versions,
Richard Hughes481aa2a2018-09-18 20:51:46 +0100964 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +0100965 if (version == NULL) {
966 g_set_error (error,
967 FWUPD_ERROR,
968 FWUPD_ERROR_NOT_FOUND,
969 "no version available for %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100970 xb_node_get_text (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +0100971 return FALSE;
972 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100973 if (!fu_engine_require_vercmp (req, version, &error_local)) {
974 if (g_strcmp0 (xb_node_get_attr (req, "compare"), "ge") == 0) {
Richard Hughes2ec78d62017-11-03 21:48:54 +0000975 g_set_error (error,
976 FWUPD_ERROR,
977 FWUPD_ERROR_INVALID_FILE,
Richard Hughes0eb123b2018-04-19 12:00:04 +0100978 "Not compatible with %s version %s, requires >= %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100979 xb_node_get_text (req), version,
980 xb_node_get_attr (req, "version"));
Richard Hughes0eb123b2018-04-19 12:00:04 +0100981 } else {
982 g_set_error (error,
983 FWUPD_ERROR,
984 FWUPD_ERROR_INVALID_FILE,
985 "Not compatible with %s version: %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100986 xb_node_get_text (req), error_local->message);
Richard Hughes2ec78d62017-11-03 21:48:54 +0000987 }
Richard Hughes0eb123b2018-04-19 12:00:04 +0100988 return FALSE;
Richard Hughes2ec78d62017-11-03 21:48:54 +0000989 }
990
Richard Hughes0eb123b2018-04-19 12:00:04 +0100991 g_debug ("requirement %s %s %s on %s passed",
Richard Hughes481aa2a2018-09-18 20:51:46 +0100992 xb_node_get_attr (req, "version"),
993 xb_node_get_attr (req, "compare"),
994 version, xb_node_get_text (req));
Richard Hughes2ec78d62017-11-03 21:48:54 +0000995 return TRUE;
996}
Richard Hughes2ec78d62017-11-03 21:48:54 +0000997
998static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +0100999fu_engine_check_requirement_hardware (FuEngine *self, XbNode *req, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001000{
Richard Hughes3d71c162018-04-30 16:40:44 +01001001 g_auto(GStrv) hwid_split = NULL;
1002
1003 /* split and treat as OR */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001004 hwid_split = g_strsplit (xb_node_get_text (req), "|", -1);
Richard Hughes3d71c162018-04-30 16:40:44 +01001005 for (guint i = 0; hwid_split[i] != NULL; i++) {
1006 if (fu_hwids_has_guid (self->hwids, hwid_split[i])) {
1007 g_debug ("HWID provided %s", hwid_split[i]);
1008 return TRUE;
1009 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001010 }
Richard Hughes3d71c162018-04-30 16:40:44 +01001011
1012 /* nothing matched */
1013 g_set_error (error,
1014 FWUPD_ERROR,
1015 FWUPD_ERROR_INVALID_FILE,
1016 "no HWIDs matched %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001017 xb_node_get_text (req));
Richard Hughes3d71c162018-04-30 16:40:44 +01001018 return FALSE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001019}
1020
1021static gboolean
Richard Hughes481aa2a2018-09-18 20:51:46 +01001022fu_engine_check_requirement (FuEngine *self, XbNode *req, FuDevice *device, GError **error)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001023{
1024 /* ensure component requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001025 if (g_strcmp0 (xb_node_get_element (req), "id") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001026 return fu_engine_check_requirement_id (self, req, error);
1027
1028 /* ensure firmware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001029 if (g_strcmp0 (xb_node_get_element (req), "firmware") == 0) {
Richard Hughes881f6242018-08-06 11:03:06 +01001030 if (device == NULL)
1031 return TRUE;
Richard Hughes0eb123b2018-04-19 12:00:04 +01001032 return fu_engine_check_requirement_firmware (self, req, device, error);
Richard Hughes881f6242018-08-06 11:03:06 +01001033 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001034
1035 /* ensure hardware requirement */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001036 if (g_strcmp0 (xb_node_get_element (req), "hardware") == 0)
Richard Hughes0eb123b2018-04-19 12:00:04 +01001037 return fu_engine_check_requirement_hardware (self, req, error);
1038
1039 /* not supported */
1040 g_set_error (error,
1041 FWUPD_ERROR,
1042 FWUPD_ERROR_NOT_SUPPORTED,
1043 "cannot handle requirement type %s",
Richard Hughes481aa2a2018-09-18 20:51:46 +01001044 xb_node_get_element (req));
Richard Hughes0eb123b2018-04-19 12:00:04 +01001045 return FALSE;
1046}
1047
1048gboolean
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001049fu_engine_check_requirements (FuEngine *self, FuInstallTask *task,
1050 FwupdInstallFlags flags, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001051{
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001052 FuDevice *device = fu_install_task_get_device (task);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001053 g_autoptr(GError) error_local = NULL;
1054 g_autoptr(GPtrArray) reqs = NULL;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001055
1056 /* all install task checks require a device */
1057 if (device != NULL) {
1058 if (!fu_install_task_check_requirements (task, flags, error))
1059 return FALSE;
1060 }
1061
1062 /* do engine checks */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001063 reqs = xb_node_query (fu_install_task_get_component (task),
1064 "requires/*", 0, &error_local);
1065 if (reqs == NULL) {
1066 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
1067 return TRUE;
1068 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
1069 return TRUE;
1070 g_propagate_error (error, g_steal_pointer (&error_local));
1071 return FALSE;
1072 }
Richard Hughes0eb123b2018-04-19 12:00:04 +01001073 for (guint i = 0; i < reqs->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001074 XbNode *req = g_ptr_array_index (reqs, i);
Richard Hughes0eb123b2018-04-19 12:00:04 +01001075 if (!fu_engine_check_requirement (self, req, device, error))
1076 return FALSE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001077 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001078 return TRUE;
1079}
1080
Richard Hughes9945edb2017-06-19 10:03:55 +01001081static gchar *
Richard Hughes481aa2a2018-09-18 20:51:46 +01001082fu_engine_get_guids_from_store (XbSilo *silo)
Richard Hughes9945edb2017-06-19 10:03:55 +01001083{
Richard Hughes481aa2a2018-09-18 20:51:46 +01001084 GString *str;
1085 g_autoptr(GPtrArray) provides = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001086
Richard Hughes481aa2a2018-09-18 20:51:46 +01001087 /* return a string with all the firmware components in the silo */
1088 provides = xb_silo_query (silo, "components/component/provides/firmware[@type='flashed']", 0, NULL);
1089 if (provides == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01001090 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001091 str = g_string_new (NULL);
1092 for (guint i = 0; i < provides->len; i++) {
1093 XbNode *prov = XB_NODE (g_ptr_array_index (provides, i));
1094 g_string_append_printf (str, "%s,", xb_node_get_text (prov));
Richard Hughes9939f1c2018-01-08 16:55:57 +00001095 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001096 g_string_truncate (str, str->len - 1);
1097 return g_string_free (str, FALSE);
1098}
1099
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001100static gchar *
1101fu_engine_get_boot_time (void)
1102{
1103 g_autofree gchar *buf = NULL;
1104 g_auto(GStrv) lines = NULL;
1105 if (!g_file_get_contents ("/proc/stat", &buf, NULL, NULL))
1106 return NULL;
1107 lines = g_strsplit (buf, "\n", -1);
1108 for (guint i = 0; lines[i] != NULL; i++) {
1109 if (g_str_has_prefix (lines[i], "btime "))
1110 return g_strdup (lines[i] + 6);
1111 }
1112 return NULL;
1113}
1114
Richard Hughes473c5202018-01-11 21:06:16 +00001115static GHashTable *
1116fu_engine_get_report_metadata (FuEngine *self)
1117{
1118 GHashTable *hash;
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001119 gchar *btime;
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001120 struct utsname name_tmp;
Richard Hughes34e0dab2018-04-20 16:43:00 +01001121 g_autoptr(GList) compile_keys = g_hash_table_get_keys (self->compile_versions);
1122 g_autoptr(GList) runtime_keys = g_hash_table_get_keys (self->runtime_versions);
Richard Hughes473c5202018-01-11 21:06:16 +00001123
Richard Hughes34e0dab2018-04-20 16:43:00 +01001124 /* convert all the runtime and compile-time versions */
Richard Hughes473c5202018-01-11 21:06:16 +00001125 hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes34e0dab2018-04-20 16:43:00 +01001126 for (GList *l = compile_keys; l != NULL; l = l->next) {
1127 const gchar *id = l->data;
1128 const gchar *version = g_hash_table_lookup (self->compile_versions, id);
1129 g_hash_table_insert (hash,
1130 g_strdup_printf ("CompileVersion(%s)", id),
1131 g_strdup (version));
1132 }
1133 for (GList *l = runtime_keys; l != NULL; l = l->next) {
1134 const gchar *id = l->data;
1135 const gchar *version = g_hash_table_lookup (self->runtime_versions, id);
1136 g_hash_table_insert (hash,
1137 g_strdup_printf ("RuntimeVersion(%s)", id),
1138 g_strdup (version));
1139 }
Richard Hughes473c5202018-01-11 21:06:16 +00001140
1141 /* kernel version is often important for debugging failures */
Mario Limoncielloed1ac2a2018-04-17 14:43:28 -05001142 memset (&name_tmp, 0, sizeof (struct utsname));
Richard Hughes473c5202018-01-11 21:06:16 +00001143 if (uname (&name_tmp) >= 0) {
1144 g_hash_table_insert (hash,
1145 g_strdup ("CpuArchitecture"),
1146 g_strdup (name_tmp.machine));
1147 g_hash_table_insert (hash,
1148 g_strdup ("KernelVersion"),
1149 g_strdup (name_tmp.release));
1150 }
1151
Richard Hughes59c2ebe2018-01-12 09:47:40 +00001152 /* add the kernel boot time so we can detect a reboot */
1153 btime = fu_engine_get_boot_time ();
1154 if (btime != NULL)
1155 g_hash_table_insert (hash, g_strdup ("BootTime"), btime);
1156
Richard Hughes473c5202018-01-11 21:06:16 +00001157 return hash;
1158}
1159
Richard Hughes9945edb2017-06-19 10:03:55 +01001160/**
Richard Hughesdbd8c762018-06-15 20:31:40 +01001161 * fu_engine_composite_prepare:
1162 * @self: A #FuEngine
1163 * @devices: (element-type #FuDevice): devices that will be updated
1164 * @error: A #GError, or %NULL
1165 *
1166 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1167 *
1168 * Any failure in any plugin will abort all of the actions before they are started.
1169 *
1170 * Returns: %TRUE for success
1171 **/
1172gboolean
1173fu_engine_composite_prepare (FuEngine *self, GPtrArray *devices, GError **error)
1174{
1175 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1176 for (guint j = 0; j < plugins->len; j++) {
1177 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1178 if (!fu_plugin_runner_composite_prepare (plugin_tmp, devices, error))
1179 return FALSE;
1180 }
1181 return TRUE;
1182}
1183
1184/**
1185 * fu_engine_composite_cleanup:
1186 * @self: A #FuEngine
1187 * @devices: (element-type #FuDevice): devices that will be updated
1188 * @error: A #GError, or %NULL
1189 *
1190 * Calls into the plugin loader, informing each plugin of the pending upgrade(s).
1191 *
1192 * Returns: %TRUE for success
1193 **/
1194gboolean
1195fu_engine_composite_cleanup (FuEngine *self, GPtrArray *devices, GError **error)
1196{
1197 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
1198 for (guint j = 0; j < plugins->len; j++) {
1199 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
1200 if (!fu_plugin_runner_composite_cleanup (plugin_tmp, devices, error))
1201 return FALSE;
1202 }
1203 return TRUE;
1204}
1205
1206/**
1207 * fu_engine_install_tasks:
1208 * @self: A #FuEngine
1209 * @install_tasks: (element-type FuInstallTask): A #FuDevice
1210 * @blob_cab: The #GBytes of the .cab file
1211 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
1212 * @error: A #GError, or %NULL
1213 *
1214 * Installs a specific firmware file on one or more install tasks.
1215 *
1216 * By this point all the requirements and tests should have been done in
1217 * fu_engine_check_requirements() so this should not fail before running
1218 * the plugin loader.
1219 *
1220 * Returns: %TRUE for success
1221 **/
1222gboolean
1223fu_engine_install_tasks (FuEngine *self,
1224 GPtrArray *install_tasks,
1225 GBytes *blob_cab,
1226 FwupdInstallFlags flags,
1227 GError **error)
1228{
1229 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001230 g_autoptr(GPtrArray) devices_new = NULL;
Richard Hughesdbd8c762018-06-15 20:31:40 +01001231
1232 /* notify the plugins about the composite action */
1233 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1234 for (guint i = 0; i < install_tasks->len; i++) {
1235 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1236 g_ptr_array_add (devices, g_object_ref (fu_install_task_get_device (task)));
1237 }
1238 if (!fu_engine_composite_prepare (self, devices, error)) {
1239 g_prefix_error (error, "failed to prepare composite action: ");
1240 return FALSE;
1241 }
1242
1243 /* all authenticated, so install all the things */
1244 for (guint i = 0; i < install_tasks->len; i++) {
1245 FuInstallTask *task = g_ptr_array_index (install_tasks, i);
1246 if (!fu_engine_install (self, task, blob_cab, flags, error)) {
1247 g_autoptr(GError) error_local = NULL;
1248 if (!fu_engine_composite_cleanup (self, devices, &error_local)) {
1249 g_warning ("failed to cleanup failed composite action: %s",
1250 error_local->message);
1251 }
1252 return FALSE;
1253 }
1254 }
1255
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001256 /* get a new list of devices in case they replugged */
1257 devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1258 for (guint i = 0; i < devices->len; i++) {
1259 FuDevice *device;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001260 g_autoptr(FuDevice) device_new = NULL;
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001261 g_autoptr(GError) error_local = NULL;
1262 device = g_ptr_array_index (devices, i);
1263 device_new = fu_device_list_get_by_id (self->device_list,
1264 fu_device_get_id (device),
1265 &error_local);
1266 if (device_new == NULL) {
Mario Limonciello769d7682018-09-28 08:43:37 -05001267 g_debug ("failed to find new device: %s",
1268 error_local->message);
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001269 continue;
1270 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001271 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001272 }
1273
Richard Hughesdbd8c762018-06-15 20:31:40 +01001274 /* notify the plugins about the composite action */
Mario Limonciellob6fa4732018-09-07 15:12:33 +01001275 if (!fu_engine_composite_cleanup (self, devices_new, error)) {
Richard Hughesdbd8c762018-06-15 20:31:40 +01001276 g_prefix_error (error, "failed to cleanup composite action: ");
1277 return FALSE;
1278 }
1279
1280 /* success */
1281 return TRUE;
1282}
1283
1284/**
Richard Hughes9945edb2017-06-19 10:03:55 +01001285 * fu_engine_install:
1286 * @self: A #FuEngine
Richard Hughes828c0392018-09-04 13:42:26 +01001287 * @task: A #FuInstallTask
Richard Hughes9945edb2017-06-19 10:03:55 +01001288 * @blob_cab: The #GBytes of the .cab file
Richard Hughesa785a1c2017-08-25 16:00:58 +01001289 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_UPDATABLE
Richard Hughes9945edb2017-06-19 10:03:55 +01001290 * @error: A #GError, or %NULL
1291 *
1292 * Installs a specfic firmware file on a device.
1293 *
Richard Hughes4ad41f02018-05-08 14:35:36 +01001294 * By this point all the requirements and tests should have been done in
1295 * fu_engine_check_requirements() so this should not fail before running
1296 * the plugin loader.
1297 *
Richard Hughes9945edb2017-06-19 10:03:55 +01001298 * Returns: %TRUE for success
1299 **/
1300gboolean
1301fu_engine_install (FuEngine *self,
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001302 FuInstallTask *task,
Richard Hughes9945edb2017-06-19 10:03:55 +01001303 GBytes *blob_cab,
1304 FwupdInstallFlags flags,
1305 GError **error)
1306{
Richard Hughes481aa2a2018-09-18 20:51:46 +01001307 XbNode *component = fu_install_task_get_component (task);
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01001308 FuDevice *device = fu_install_task_get_device (task);
Richard Hughes9945edb2017-06-19 10:03:55 +01001309 GBytes *blob_fw;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001310 const gchar *tmp = NULL;
1311 g_autofree gchar *release_key = NULL;
1312 g_autofree gchar *version_rel = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001313 g_autoptr(GBytes) blob_fw2 = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001314 g_autoptr(GError) error_local = NULL;
1315 g_autoptr(XbNode) rel = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001316
1317 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
Richard Hughes4ad41f02018-05-08 14:35:36 +01001318 g_return_val_if_fail (FU_IS_DEVICE (device), FALSE);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001319 g_return_val_if_fail (XB_IS_NODE (component), FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01001320 g_return_val_if_fail (blob_cab != NULL, FALSE);
1321 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1322
Richard Hughes9945edb2017-06-19 10:03:55 +01001323 /* not in bootloader mode */
Richard Hughes0a7e7832017-11-22 11:01:13 +00001324 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01001325 const gchar *caption = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001326 caption = xb_node_query_text (component,
1327 "screenshots/screenshot/caption",
1328 NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01001329 if (caption != NULL) {
1330 g_set_error (error,
1331 FWUPD_ERROR,
1332 FWUPD_ERROR_INTERNAL,
1333 "Device %s needs to manually be put in update mode: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00001334 fu_device_get_name (device), caption);
Richard Hughes9945edb2017-06-19 10:03:55 +01001335 } else {
1336 g_set_error (error,
1337 FWUPD_ERROR,
1338 FWUPD_ERROR_INTERNAL,
1339 "Device %s needs to manually be put in update mode",
Richard Hughes0a7e7832017-11-22 11:01:13 +00001340 fu_device_get_name (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01001341 }
1342 return FALSE;
1343 }
1344
Richard Hughes9945edb2017-06-19 10:03:55 +01001345 /* parse the DriverVer */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001346 rel = xb_node_query_first (component, "releases/release", &error_local);
Richard Hughes9945edb2017-06-19 10:03:55 +01001347 if (rel == NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001348 g_set_error (error,
1349 FWUPD_ERROR,
1350 FWUPD_ERROR_INVALID_FILE,
1351 "No releases in the firmware component: %s",
1352 error_local->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01001353 return FALSE;
1354 }
1355
1356 /* get the blob */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001357 tmp = xb_node_query_attr (rel, "checksum[@target='content']", "filename", NULL);
1358 if (tmp == NULL)
1359 tmp = "firmware.bin";
Richard Hughes9945edb2017-06-19 10:03:55 +01001360
1361 /* not all devices have to use the same blob */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001362 release_key = g_strdup_printf ("fwupd::ReleaseBlob(%s)", tmp);
1363 blob_fw = xb_node_get_data (rel, release_key);
Richard Hughes9945edb2017-06-19 10:03:55 +01001364 if (blob_fw == NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001365 g_set_error (error,
1366 FWUPD_ERROR,
1367 FWUPD_ERROR_READ,
1368 "Failed to get firmware blob using %s", tmp);
Richard Hughes9945edb2017-06-19 10:03:55 +01001369 return FALSE;
1370 }
1371
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001372 /* use a bubblewrap helper script to build the firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001373 tmp = g_object_get_data (G_OBJECT (component), "fwupd::BuilderScript");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001374 if (tmp != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01001375 const gchar *tmp2 = g_object_get_data (G_OBJECT (component), "fwupd::BuilderOutput");
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001376 if (tmp2 == NULL)
1377 tmp2 = "firmware.bin";
1378 blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error);
1379 if (blob_fw2 == NULL)
1380 return FALSE;
1381 } else {
1382 blob_fw2 = g_bytes_ref (blob_fw);
1383 }
1384
Richard Hughes6e7419d2018-05-18 10:11:36 +01001385 /* install firmware blob */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001386 version_rel = fu_engine_get_release_version (self, component, rel);
Richard Hughes6e7419d2018-05-18 10:11:36 +01001387 return fu_engine_install_blob (self, device, blob_cab, blob_fw2,
Richard Hughes481aa2a2018-09-18 20:51:46 +01001388 version_rel, flags, error);
Richard Hughes6e7419d2018-05-18 10:11:36 +01001389}
1390
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001391/**
1392 * fu_engine_get_plugins:
1393 * @self: A #FuPluginList
1394 *
1395 * Gets all the plugins that have been added.
1396 *
1397 * Returns: (transfer none) (element-type FuPlugin): the plugins
1398 *
1399 * Since: 1.0.8
1400 **/
1401GPtrArray *
1402fu_engine_get_plugins (FuEngine *self)
1403{
1404 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1405 return fu_plugin_list_get_all (self->plugin_list);
1406}
1407
Richard Hughes6e7419d2018-05-18 10:11:36 +01001408gboolean
1409fu_engine_install_blob (FuEngine *self,
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001410 FuDevice *device_orig,
Richard Hughes6e7419d2018-05-18 10:11:36 +01001411 GBytes *blob_cab,
1412 GBytes *blob_fw2,
1413 const gchar *version,
1414 FwupdInstallFlags flags,
1415 GError **error)
1416{
1417 FuPlugin *plugin;
1418 GPtrArray *plugins;
1419 g_autofree gchar *device_id_orig = NULL;
1420 g_autofree gchar *version_orig = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001421 g_autoptr(FuDevice) device = g_object_ref (device_orig);
Richard Hughes6e7419d2018-05-18 10:11:36 +01001422 g_autoptr(FwupdRelease) release_history = fwupd_release_new ();
1423 g_autoptr(GError) error_local = NULL;
1424 g_autoptr(GHashTable) metadata_hash = NULL;
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05001425 g_autoptr(GTimer) timer = g_timer_new ();
Richard Hughes6e7419d2018-05-18 10:11:36 +01001426
Richard Hughesadcc16a2017-08-21 12:26:46 +01001427 /* test the firmware is not an empty blob */
1428 if (g_bytes_get_size (blob_fw2) == 0) {
1429 g_set_error (error,
1430 FWUPD_ERROR,
1431 FWUPD_ERROR_INVALID_FILE,
1432 "Firmware is invalid as has zero size");
1433 return FALSE;
1434 }
1435
Richard Hughesc2de52a2018-05-18 10:14:29 +01001436 /* we can only write history if we're providing a version number */
1437 if (version == NULL && (flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) {
1438 g_set_error (error,
1439 FWUPD_ERROR,
1440 FWUPD_ERROR_INVALID_FILE,
1441 "Version required if writing history");
1442 return FALSE;
1443 }
1444
Richard Hughes34834102017-11-21 21:55:00 +00001445 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00001446 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00001447 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00001448 error);
Richard Hughes34834102017-11-21 21:55:00 +00001449 if (plugin == NULL)
1450 return FALSE;
1451
Richard Hughes9945edb2017-06-19 10:03:55 +01001452 /* compare the versions of what we have installed */
Richard Hughesc0cd0232018-01-31 15:02:00 +00001453 version_orig = g_strdup (fu_device_get_version (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01001454
1455 /* signal to all the plugins the update is about to happen */
Richard Hughese7e95452017-11-22 09:05:53 +00001456 plugins = fu_plugin_list_get_all (self->plugin_list);
1457 for (guint j = 0; j < plugins->len; j++) {
1458 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
Mario Limoncielloe3b1a3f2018-08-21 13:01:45 -05001459 if (!fu_plugin_runner_update_prepare (plugin_tmp, flags, device, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01001460 return FALSE;
1461 }
1462
Richard Hughes0a7e7832017-11-22 11:01:13 +00001463 /* save the chosen device ID in case the device goes away */
1464 device_id_orig = g_strdup (fu_device_get_id (device));
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001465
Mario Limonciello9cecbe12018-09-24 15:26:40 -05001466 /* in case another device caused us to go into replug before starting */
Mario Limonciello78599ed2018-10-10 10:41:46 -05001467 g_clear_object (&device);
Mario Limonciello9cecbe12018-09-24 15:26:40 -05001468 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
1469 if (device == NULL) {
1470 g_prefix_error (error, "failed to get device ID after detach: ");
1471 return FALSE;
1472 }
1473 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
1474 g_prefix_error (error, "failed to wait for detach replug: ");
1475 return FALSE;
1476 }
Mario Limonciello78599ed2018-10-10 10:41:46 -05001477 g_clear_object (&device);
Mario Limonciello9cecbe12018-09-24 15:26:40 -05001478 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
1479 if (device == NULL) {
1480 g_prefix_error (error, "failed to get device ID after detach replug: ");
1481 return FALSE;
1482 }
1483
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001484 /* mark this as modified even if we actually fail to do the update */
1485 fu_device_set_modified (device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
1486
Richard Hughes473c5202018-01-11 21:06:16 +00001487 /* build the version metadata */
1488 metadata_hash = fu_engine_get_report_metadata (self);
1489 fwupd_release_add_metadata (release_history, metadata_hash);
Richard Hughes80b79bb2018-01-11 21:11:06 +00001490 fwupd_release_add_metadata (release_history,
1491 fu_plugin_get_report_metadata (plugin));
Richard Hughes473c5202018-01-11 21:06:16 +00001492
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001493 /* add device to database */
Richard Hughes76e0f942018-05-14 16:24:00 +01001494 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) {
1495 g_autofree gchar *checksum = NULL;
1496 checksum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob_cab);
1497 fwupd_release_set_version (release_history, version);
1498 fwupd_release_add_checksum (release_history, checksum);
1499 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_FAILED);
1500 if (!fu_history_add_device (self->history, device, release_history, error))
1501 return FALSE;
1502 }
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001503
Richard Hughes9945edb2017-06-19 10:03:55 +01001504 /* do the update */
Richard Hughesc0cd0232018-01-31 15:02:00 +00001505 if (!fu_plugin_runner_update_detach (plugin, device, &error_local)) {
1506 fu_device_set_update_error (device, error_local->message);
Richard Hughes76e0f942018-05-14 16:24:00 +01001507 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1508 !fu_history_modify_device (self->history, device,
Richard Hughesc0cd0232018-01-31 15:02:00 +00001509 FU_HISTORY_FLAGS_MATCH_OLD_VERSION,
1510 error)) {
1511 return FALSE;
1512 }
1513 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001514 return FALSE;
Richard Hughesc0cd0232018-01-31 15:02:00 +00001515 }
Mario Limonciello78599ed2018-10-10 10:41:46 -05001516 g_clear_object (&device);
Richard Hughes40127542018-01-12 20:25:55 +00001517 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001518 if (device == NULL) {
1519 g_prefix_error (error, "failed to get device ID after detach: ");
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001520 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001521 }
1522 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
1523 g_prefix_error (error, "failed to wait for detach replug: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001524 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001525 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001526 g_clear_object (&device);
Richard Hughes3a8d5322018-08-30 11:51:10 +01001527 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001528 if (device == NULL) {
1529 g_prefix_error (error, "failed to get device ID after detach replug: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001530 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001531 }
Richard Hughes34834102017-11-21 21:55:00 +00001532 if (!fu_plugin_runner_update (plugin,
Richard Hughes0a7e7832017-11-22 11:01:13 +00001533 device,
Richard Hughes9945edb2017-06-19 10:03:55 +01001534 blob_cab,
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001535 blob_fw2,
Richard Hughes9945edb2017-06-19 10:03:55 +01001536 flags,
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001537 &error_local)) {
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001538 g_autoptr(GError) error_attach = NULL;
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001539
1540 /* save to database */
Richard Hughesc0cd0232018-01-31 15:02:00 +00001541 fu_device_set_update_error (device, error_local->message);
Richard Hughes76e0f942018-05-14 16:24:00 +01001542 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1543 !fu_history_modify_device (self->history, device,
Richard Hughesc0cd0232018-01-31 15:02:00 +00001544 FU_HISTORY_FLAGS_MATCH_OLD_VERSION,
1545 error)) {
Richard Hughes0b9d9962018-01-12 16:31:28 +00001546 return FALSE;
Richard Hughesc0cd0232018-01-31 15:02:00 +00001547 }
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001548 g_propagate_error (error, g_steal_pointer (&error_local));
1549
1550 /* attack back into runtime */
Richard Hughes34834102017-11-21 21:55:00 +00001551 if (!fu_plugin_runner_update_attach (plugin,
Richard Hughes0a7e7832017-11-22 11:01:13 +00001552 device,
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001553 &error_attach)) {
1554 g_warning ("failed to attach device after failed update: %s",
1555 error_attach->message);
1556 }
Richard Hughese7e95452017-11-22 09:05:53 +00001557 for (guint j = 0; j < plugins->len; j++) {
1558 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
Richard Hugheseafba482018-01-08 16:21:22 +00001559 g_autoptr(GError) error_cleanup = NULL;
Richard Hughes34834102017-11-21 21:55:00 +00001560 if (!fu_plugin_runner_update_cleanup (plugin_tmp,
Mario Limoncielloe3b1a3f2018-08-21 13:01:45 -05001561 flags,
Richard Hughes0a7e7832017-11-22 11:01:13 +00001562 device,
Richard Hugheseafba482018-01-08 16:21:22 +00001563 &error_cleanup)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01001564 g_warning ("failed to update-cleanup "
1565 "after failed update: %s",
Richard Hugheseafba482018-01-08 16:21:22 +00001566 error_cleanup->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01001567 }
1568 }
Richard Hughes4a036012017-11-30 16:33:24 +00001569 fu_device_set_status (device, FWUPD_STATUS_IDLE);
Richard Hughes9945edb2017-06-19 10:03:55 +01001570 return FALSE;
1571 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001572 g_clear_object (&device);
Richard Hughes40127542018-01-12 20:25:55 +00001573 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001574 if (device == NULL) {
1575 g_prefix_error (error, "failed to get device ID after update: ");
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001576 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001577 }
1578 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
1579 g_prefix_error (error, "failed to wait for replug after update: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001580 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001581 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001582 g_clear_object (&device);
Richard Hughes3a8d5322018-08-30 11:51:10 +01001583 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001584 if (device == NULL) {
1585 g_prefix_error (error, "failed to get device ID after post-update restart: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001586 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001587 }
Richard Hughesc0cd0232018-01-31 15:02:00 +00001588 if (!fu_plugin_runner_update_attach (plugin, device, &error_local)) {
1589 fu_device_set_update_error (device, error_local->message);
Richard Hughes76e0f942018-05-14 16:24:00 +01001590 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1591 !fu_history_modify_device (self->history, device,
Richard Hughesc0cd0232018-01-31 15:02:00 +00001592 FU_HISTORY_FLAGS_MATCH_OLD_VERSION |
1593 FU_HISTORY_FLAGS_MATCH_NEW_VERSION,
1594 error)) {
1595 return FALSE;
1596 }
1597 g_propagate_error (error, g_steal_pointer (&error_local));
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001598 return FALSE;
Richard Hughesc0cd0232018-01-31 15:02:00 +00001599 }
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001600 g_clear_object (&device);
Richard Hughes3a8d5322018-08-30 11:51:10 +01001601 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001602 if (device == NULL) {
1603 g_prefix_error (error, "failed to get device ID after attach: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001604 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001605 }
1606 if (!fu_device_list_wait_for_replug (self->device_list, device, error)) {
1607 g_prefix_error (error, "failed to wait for replug after attach: ");
Richard Hughes3a8d5322018-08-30 11:51:10 +01001608 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001609 }
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001610
1611 /* get the new version number */
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01001612 g_clear_object (&device);
Richard Hughes40127542018-01-12 20:25:55 +00001613 device = fu_device_list_get_by_id (self->device_list, device_id_orig, error);
Richard Hughes037ce372018-09-05 18:07:35 +01001614 if (device == NULL) {
1615 g_prefix_error (error, "failed to get device ID after attach replug: ");
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001616 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001617 }
1618 if (!fu_plugin_runner_update_reload (plugin, device, error)) {
1619 g_prefix_error (error, "failed to reload device: ");
Richard Hughes0d7fdb32017-11-11 20:23:14 +00001620 return FALSE;
Richard Hughes037ce372018-09-05 18:07:35 +01001621 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001622
1623 /* signal to all the plugins the update has happened */
Richard Hughese7e95452017-11-22 09:05:53 +00001624 for (guint j = 0; j < plugins->len; j++) {
1625 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
Richard Hugheseafba482018-01-08 16:21:22 +00001626 g_autoptr(GError) error_cleanup = NULL;
Mario Limoncielloe3b1a3f2018-08-21 13:01:45 -05001627 if (!fu_plugin_runner_update_cleanup (plugin_tmp, flags, device, &error_cleanup)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01001628 g_warning ("failed to update-cleanup: %s",
Richard Hugheseafba482018-01-08 16:21:22 +00001629 error_cleanup->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01001630 }
1631 }
1632
1633 /* make the UI update */
Richard Hughes7772dcf2018-09-10 15:49:59 +01001634 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes9945edb2017-06-19 10:03:55 +01001635 fu_engine_emit_changed (self);
Mario Limonciello4afe7fb2018-08-05 23:14:39 -05001636 g_debug ("Updating %s took %f seconds", fu_device_get_name (device),
1637 g_timer_elapsed (timer, NULL));
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001638
1639 /* update database */
1640 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) {
Richard Hughesc0cd0232018-01-31 15:02:00 +00001641 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_NEEDS_REBOOT);
Richard Hughes76e0f942018-05-14 16:24:00 +01001642 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1643 !fu_history_modify_device (self->history, device,
1644 FU_HISTORY_FLAGS_MATCH_OLD_VERSION,
1645 error))
1646 return FALSE;
1647 /* success */
1648 return TRUE;
Richard Hughesc0cd0232018-01-31 15:02:00 +00001649 }
1650
1651 /* for online updates, verify the version changed if not a re-install */
Richard Hughes76e0f942018-05-14 16:24:00 +01001652 if (version != NULL &&
1653 g_strcmp0 (version_orig, version) != 0 &&
Richard Hughesc0cd0232018-01-31 15:02:00 +00001654 g_strcmp0 (version_orig, fu_device_get_version (device)) == 0) {
1655 fu_device_set_update_error (device, "device version not updated on success");
Richard Hughes76e0f942018-05-14 16:24:00 +01001656 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1657 !fu_history_modify_device (self->history, device,
1658 FU_HISTORY_FLAGS_MATCH_OLD_VERSION,
1659 error))
1660 return FALSE;
1661 /* success */
1662 return TRUE;
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001663 }
1664
Richard Hughes4082d332018-02-09 15:50:18 +00001665 /* ensure the new version matched what we expected */
Richard Hughes76e0f942018-05-14 16:24:00 +01001666 if (version != NULL &&
1667 g_strcmp0 (fu_device_get_version (device), version) != 0) {
Richard Hughes4082d332018-02-09 15:50:18 +00001668 g_warning ("new device version '%s' was is not '%s', fixing up",
1669 fu_device_get_version (device), version);
1670 fu_device_set_version (device, version);
1671 }
1672
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001673 /* success */
Richard Hughes76e0f942018-05-14 16:24:00 +01001674 if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0 &&
1675 !fu_history_modify_device (self->history, device,
1676 FU_HISTORY_FLAGS_MATCH_NEW_VERSION,
1677 error))
1678 return FALSE;
Richard Hughesc0cd0232018-01-31 15:02:00 +00001679 fu_device_set_update_state (device, FWUPD_UPDATE_STATE_SUCCESS);
Richard Hughes76e0f942018-05-14 16:24:00 +01001680 return TRUE;
Richard Hughes9945edb2017-06-19 10:03:55 +01001681}
1682
Richard Hughes0a7e7832017-11-22 11:01:13 +00001683static FuDevice *
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001684fu_engine_get_item_by_id_fallback_history (FuEngine *self, const gchar *id, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01001685{
Richard Hughes9945edb2017-06-19 10:03:55 +01001686 g_autoptr(GPtrArray) devices = NULL;
1687
1688 /* not a wildcard */
Richard Hughes65e44ca2018-01-30 17:26:30 +00001689 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) {
1690 g_autoptr(FuDevice) dev = NULL;
1691 g_autoptr(GError) error_local = NULL;
1692
1693 /* get this one device */
1694 dev = fu_history_get_device_by_id (self->history, id, &error_local);
1695 if (dev == NULL) {
1696 g_set_error (error,
1697 FWUPD_ERROR,
1698 FWUPD_ERROR_NOTHING_TO_DO,
1699 "Failed to find %s in history database: %s",
1700 id, error_local->message);
1701 return NULL;
1702 }
1703
1704 /* only useful */
1705 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
1706 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
1707 return g_steal_pointer (&dev);
1708 }
1709
1710 /* nothing in database */
1711 g_set_error (error,
1712 FWUPD_ERROR,
1713 FWUPD_ERROR_NOTHING_TO_DO,
1714 "Device %s has no results to report",
1715 fu_device_get_id (dev));
1716 return NULL;
1717 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001718
1719 /* allow '*' for any */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00001720 devices = fu_history_get_devices (self->history, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01001721 if (devices == NULL)
1722 return NULL;
1723 for (guint i = 0; i < devices->len; i++) {
Richard Hughes65e44ca2018-01-30 17:26:30 +00001724 FuDevice *dev = g_ptr_array_index (devices, i);
1725 if (fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS ||
1726 fu_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED)
1727 return g_object_ref (dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01001728 }
Richard Hughes65e44ca2018-01-30 17:26:30 +00001729 g_set_error_literal (error,
1730 FWUPD_ERROR,
1731 FWUPD_ERROR_NOTHING_TO_DO,
1732 "Failed to find any useful results to report");
1733 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001734}
1735
Richard Hughes481aa2a2018-09-18 20:51:46 +01001736/* for the self tests */
1737void
1738fu_engine_set_silo (FuEngine *self, XbSilo *silo)
Richard Hughesbd4d2852017-09-13 14:05:14 +01001739{
Richard Hughes481aa2a2018-09-18 20:51:46 +01001740 g_return_if_fail (FU_IS_ENGINE (self));
1741 g_return_if_fail (XB_IS_SILO (silo));
1742 g_set_object (&self->silo, silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01001743}
1744
1745static gboolean
Richard Hughesa8997132018-01-12 14:25:39 +00001746fu_engine_is_device_supported (FuEngine *self, FuDevice *device)
1747{
Richard Hughes481aa2a2018-09-18 20:51:46 +01001748 g_autoptr(XbNode) component = NULL;
1749
1750 /* sanity check */
1751 if (self->silo == NULL) {
1752 g_critical ("FuEngine silo not set up");
1753 return FALSE;
1754 }
Richard Hughesa8997132018-01-12 14:25:39 +00001755
1756 /* no device version */
1757 if (fu_device_get_version (device) == NULL)
1758 return FALSE;
1759
1760 /* match the GUIDs in the XML */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001761 component = fu_engine_store_get_app_by_guids (self->silo, device);
1762 if (component == NULL)
Richard Hughesa8997132018-01-12 14:25:39 +00001763 return FALSE;
1764
1765 /* success */
1766 return TRUE;
1767}
1768
1769static gboolean
Richard Hughes9945edb2017-06-19 10:03:55 +01001770fu_engine_load_metadata_store (FuEngine *self, GError **error)
1771{
Richard Hughes9945edb2017-06-19 10:03:55 +01001772 GPtrArray *remotes;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001773 g_autofree gchar *cachedirpkg = NULL;
Richard Hughes1354ea92017-09-19 15:58:31 +01001774 g_autofree gchar *guids_str = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001775 g_autofree gchar *xmlbfn = NULL;
1776 g_autoptr(GFile) xmlb = NULL;
Richard Hughes0a7e7832017-11-22 11:01:13 +00001777 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001778 g_autoptr(GPtrArray) components = NULL;
1779 g_autoptr(XbBuilder) builder = xb_builder_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01001780
Richard Hughes481aa2a2018-09-18 20:51:46 +01001781 /* clear existing silo */
1782 g_clear_object (&self->silo);
1783
1784 /* verbose profiling */
1785 if (g_getenv ("FWUPD_VERBOSE") != NULL) {
1786 xb_builder_set_profile_flags (builder,
1787 XB_SILO_PROFILE_FLAG_XPATH |
1788 XB_SILO_PROFILE_FLAG_DEBUG);
1789 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001790
1791 /* load each enabled metadata file */
1792 remotes = fu_config_get_remotes (self->config);
1793 for (guint i = 0; i < remotes->len; i++) {
1794 const gchar *path = NULL;
Richard Hughes5f733f22017-11-26 16:14:20 +00001795 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001796 g_autoptr(GFile) file = NULL;
1797 g_autoptr(XbBuilderNode) custom = NULL;
1798 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1799
Richard Hughes9945edb2017-06-19 10:03:55 +01001800 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1801 if (!fwupd_remote_get_enabled (remote)) {
1802 g_debug ("remote %s not enabled, so skipping",
1803 fwupd_remote_get_id (remote));
1804 continue;
1805 }
1806 path = fwupd_remote_get_filename_cache (remote);
1807 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1808 g_debug ("no %s, so skipping", path);
1809 continue;
1810 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001811
1812 /* save the remote-id in the custom metadata space */
1813 custom = xb_builder_node_new ("custom");
1814 xb_builder_node_insert_text (custom,
1815 "fwupd::FilenameCache", path,
1816 NULL);
1817 xb_builder_node_insert_text (custom,
1818 "fwupd::RemoteId", fwupd_remote_get_id (remote),
1819 NULL);
1820 file = g_file_new_for_path (path);
1821 if (!xb_builder_source_load_file (source, file,
1822 XB_BUILDER_SOURCE_FLAG_NONE,
1823 NULL, &error_local)) {
Richard Hughes068d3432017-09-16 08:26:46 +01001824 g_warning ("failed to load remote %s: %s",
1825 fwupd_remote_get_id (remote),
1826 error_local->message);
1827 continue;
1828 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001829
1830 /* we need to watch for changes? */
1831 xb_builder_import_source (builder, source);
Richard Hughes9945edb2017-06-19 10:03:55 +01001832 }
1833
Richard Hughes481aa2a2018-09-18 20:51:46 +01001834 /* ensure silo is up to date */
1835 cachedirpkg = fu_common_get_path (FU_PATH_KIND_CACHEDIR_PKG);
1836 xmlbfn = g_build_filename (cachedirpkg, "metadata.xmlb", NULL);
1837 xmlb = g_file_new_for_path (xmlbfn);
1838 self->silo = xb_builder_ensure (builder, xmlb,
1839 XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID,
1840 NULL, error);
1841 if (self->silo == NULL)
1842 return FALSE;
1843
Richard Hughes9945edb2017-06-19 10:03:55 +01001844 /* print what we've got */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001845 components = xb_silo_query (self->silo, "components/component", 0, NULL);
1846 if (components != NULL)
1847 g_debug ("%u components now in silo", components->len);
Richard Hughes9945edb2017-06-19 10:03:55 +01001848
Richard Hughes1354ea92017-09-19 15:58:31 +01001849 /* update the list of supported GUIDs */
1850 g_ptr_array_set_size (self->supported_guids, 0);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001851 guids_str = fu_engine_get_guids_from_store (self->silo);
Richard Hughes1354ea92017-09-19 15:58:31 +01001852 if (guids_str != NULL) {
1853 g_auto(GStrv) guids = g_strsplit (guids_str, ",", -1);
1854 for (guint i = 0; guids[i] != NULL; i++) {
1855 g_ptr_array_add (self->supported_guids,
1856 g_steal_pointer (&guids[i]));
1857 }
1858 }
1859
Richard Hughesa8997132018-01-12 14:25:39 +00001860 /* did any devices SUPPORTED state change? */
Richard Hughes0a7e7832017-11-22 11:01:13 +00001861 devices = fu_device_list_get_all (self->device_list);
1862 for (guint i = 0; i < devices->len; i++) {
1863 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesa8997132018-01-12 14:25:39 +00001864 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) {
1865 if (!fu_engine_is_device_supported (self, device)) {
1866 /* was supported, now unsupported */
1867 fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
1868 fu_engine_emit_device_changed (self, device);
1869 }
1870 } else {
1871 /* was unsupported, now supported */
1872 if (fu_engine_is_device_supported (self, device)) {
1873 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
1874 fu_engine_emit_device_changed (self, device);
1875 }
1876 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001877 }
1878
1879 return TRUE;
1880}
1881
Richard Hughesf69a4812017-08-16 12:27:51 +01001882static FuKeyringResult *
1883fu_engine_get_existing_keyring_result (FuEngine *self,
1884 FuKeyring *kr,
1885 FwupdRemote *remote,
1886 GError **error)
1887{
1888 g_autoptr(GBytes) blob = NULL;
1889 g_autoptr(GBytes) blob_sig = NULL;
1890 blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
1891 if (blob == NULL)
1892 return NULL;
1893 blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
1894 if (blob_sig == NULL)
1895 return NULL;
1896 return fu_keyring_verify_data (kr, blob, blob_sig, error);
1897}
1898
Richard Hughes9945edb2017-06-19 10:03:55 +01001899/**
1900 * fu_engine_update_metadata:
1901 * @self: A #FuEngine
Richard Hughes4eada342017-10-03 21:20:32 +01001902 * @remote_id: A remote ID, e.g. `lvfs`
Richard Hughes9945edb2017-06-19 10:03:55 +01001903 * @fd: file descriptor of the metadata
1904 * @fd_sig: file descriptor of the metadata signature
1905 * @error: A #GError, or %NULL
1906 *
1907 * Updates the metadata for a specific remote.
1908 *
1909 * Note: this will close the fds when done
1910 *
1911 * Returns: %TRUE for success
1912 **/
1913gboolean
1914fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
1915 gint fd, gint fd_sig, GError **error)
1916{
Richard Hughes7403dc52017-08-10 15:34:10 +01001917 FwupdKeyringKind keyring_kind;
Richard Hughes9945edb2017-06-19 10:03:55 +01001918 FwupdRemote *remote;
1919 g_autoptr(GBytes) bytes_raw = NULL;
1920 g_autoptr(GBytes) bytes_sig = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001921 g_autoptr(GInputStream) stream_fd = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001922 g_autoptr(GInputStream) stream_sig = NULL;
Mario Limonciellocf63aec2018-06-11 12:10:54 -05001923 g_autofree gchar *pki_dir = NULL;
1924 g_autofree gchar *sysconfdir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001925
1926 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
1927 g_return_val_if_fail (remote_id != NULL, FALSE);
1928 g_return_val_if_fail (fd > 0, FALSE);
1929 g_return_val_if_fail (fd_sig > 0, FALSE);
1930 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1931
1932 /* ensures the fd's are closed on error */
1933 stream_fd = g_unix_input_stream_new (fd, TRUE);
1934 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1935
1936 /* check remote is valid */
1937 remote = fu_config_get_remote_by_id (self->config, remote_id);
1938 if (remote == NULL) {
1939 g_set_error (error,
1940 FWUPD_ERROR,
1941 FWUPD_ERROR_NOT_FOUND,
1942 "remote %s not found", remote_id);
1943 return FALSE;
1944 }
1945 if (!fwupd_remote_get_enabled (remote)) {
1946 g_set_error (error,
1947 FWUPD_ERROR,
1948 FWUPD_ERROR_NOT_SUPPORTED,
1949 "remote %s not enabled", remote_id);
1950 return FALSE;
1951 }
1952
1953 /* read the entire file into memory */
1954 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1955 if (bytes_raw == NULL)
1956 return FALSE;
1957
1958 /* read signature */
1959 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1960 if (bytes_sig == NULL)
1961 return FALSE;
1962
1963 /* verify file */
Richard Hughes7403dc52017-08-10 15:34:10 +01001964 keyring_kind = fwupd_remote_get_keyring_kind (remote);
1965 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
1966 g_autoptr(FuKeyring) kr = NULL;
Richard Hughesf69a4812017-08-16 12:27:51 +01001967 g_autoptr(FuKeyringResult) kr_result = NULL;
1968 g_autoptr(FuKeyringResult) kr_result_old = NULL;
1969 g_autoptr(GError) error_local = NULL;
Richard Hughes7383ce22018-05-08 14:14:35 +01001970 kr = fu_keyring_create_for_kind (keyring_kind, error);
Richard Hughes7403dc52017-08-10 15:34:10 +01001971 if (kr == NULL)
1972 return FALSE;
Richard Hughes14047d72017-08-18 10:58:47 +01001973 if (!fu_keyring_setup (kr, error))
1974 return FALSE;
Mario Limonciellocf63aec2018-06-11 12:10:54 -05001975 sysconfdir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1976 pki_dir = g_build_filename (sysconfdir, "pki", "fwupd-metadata", NULL);
1977 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
Richard Hughes7403dc52017-08-10 15:34:10 +01001978 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01001979 kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error);
1980 if (kr_result == NULL)
Richard Hughes7403dc52017-08-10 15:34:10 +01001981 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01001982
1983 /* verify the metadata was signed later than the existing
1984 * metadata for this remote to mitigate a rollback attack */
1985 kr_result_old = fu_engine_get_existing_keyring_result (self, kr,
1986 remote,
1987 &error_local);
1988 if (kr_result_old == NULL) {
1989 if (g_error_matches (error_local,
1990 G_FILE_ERROR,
1991 G_FILE_ERROR_NOENT)) {
1992 g_debug ("no existing valid keyrings: %s",
1993 error_local->message);
1994 } else {
1995 g_warning ("could not get existing keyring result: %s",
1996 error_local->message);
1997 }
1998 } else {
1999 gint64 delta = 0;
2000 if (fu_keyring_result_get_timestamp (kr_result) > 0 &&
2001 fu_keyring_result_get_timestamp (kr_result_old) > 0) {
2002 delta = fu_keyring_result_get_timestamp (kr_result) -
2003 fu_keyring_result_get_timestamp (kr_result_old);
2004 }
2005 if (delta < 0) {
2006 g_set_error (error,
2007 FWUPD_ERROR,
2008 FWUPD_ERROR_INVALID_FILE,
2009 "new signing timestamp was %"
2010 G_GINT64_FORMAT " seconds older",
2011 -delta);
2012 return FALSE;
2013 } else if (delta > 0) {
2014 g_debug ("timestamp increased, so no rollback");
2015 }
2016 }
Richard Hughes7403dc52017-08-10 15:34:10 +01002017 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002018
Richard Hughes99e621d2017-08-16 12:24:18 +01002019 /* save XML and signature to remotes.d */
Richard Hughes943d2c92017-06-21 09:04:39 +01002020 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache (remote),
2021 bytes_raw, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01002022 return FALSE;
Richard Hughes99e621d2017-08-16 12:24:18 +01002023 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
2024 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache_sig (remote),
2025 bytes_sig, error))
2026 return FALSE;
2027 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002028 return fu_engine_load_metadata_store (self, error);
2029}
2030
Richard Hughes9945edb2017-06-19 10:03:55 +01002031/**
Richard Hughes481aa2a2018-09-18 20:51:46 +01002032 * fu_engine_get_silo_from_blob:
Richard Hughes9945edb2017-06-19 10:03:55 +01002033 * @self: A #FuEngine
2034 * @blob_cab: A #GBytes
2035 * @error: A #GError, or %NULL
2036 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01002037 * Creates a silo from a .cab file blob.
Richard Hughes9945edb2017-06-19 10:03:55 +01002038 *
Richard Hughes481aa2a2018-09-18 20:51:46 +01002039 * Returns: (transfer container): a #XbSilo, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01002040 **/
Richard Hughes481aa2a2018-09-18 20:51:46 +01002041XbSilo *
2042fu_engine_get_silo_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002043{
Richard Hughes481aa2a2018-09-18 20:51:46 +01002044 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002045
2046 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2047 g_return_val_if_fail (blob_cab != NULL, NULL);
2048 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2049
2050 /* load file */
Richard Hughes9945edb2017-06-19 10:03:55 +01002051 fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002052 silo = fu_common_cab_build_silo (blob_cab,
2053 fu_engine_get_archive_size_max (self),
2054 error);
2055 if (silo == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01002056 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002057
Mario Limonciello5735fd62017-07-13 15:47:52 -05002058 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002059 return g_steal_pointer (&silo);
Richard Hughes9945edb2017-06-19 10:03:55 +01002060}
2061
Richard Hughes93b15762017-09-15 11:05:23 +01002062static FwupdDevice *
Richard Hughes481aa2a2018-09-18 20:51:46 +01002063fu_engine_get_result_from_app (FuEngine *self, XbNode *component, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002064{
2065 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01002066 g_autoptr(FuInstallTask) task = NULL;
Richard Hughes93b15762017-09-15 11:05:23 +01002067 g_autoptr(FwupdDevice) dev = NULL;
2068 g_autoptr(FwupdRelease) rel = NULL;
Salud Lemus980f2d92018-08-28 09:25:40 -07002069 g_autoptr(GError) error_local = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002070 g_autoptr(GPtrArray) provides = NULL;
2071 g_autoptr(XbNode) description = NULL;
2072 g_autoptr(XbNode) release = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002073
Richard Hughes93b15762017-09-15 11:05:23 +01002074 dev = fwupd_device_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01002075 provides = xb_node_query (component,
2076 "provides/firmware[@type='flashed']",
2077 0, &error_local);
2078 if (provides == NULL) {
2079 g_set_error (error,
2080 FWUPD_ERROR,
2081 FWUPD_ERROR_INTERNAL,
2082 "failed to get release: %s",
2083 error_local->message);
2084 return NULL;
2085 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002086 for (guint i = 0; i < provides->len; i++) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01002087 XbNode *prov = XB_NODE (g_ptr_array_index (provides, i));
Richard Hughes9945edb2017-06-19 10:03:55 +01002088 const gchar *guid;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01002089 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002090
Richard Hughes9945edb2017-06-19 10:03:55 +01002091 /* is a online or offline update appropriate */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002092 guid = xb_node_get_text (prov);
Richard Hughes9945edb2017-06-19 10:03:55 +01002093 if (guid == NULL)
2094 continue;
Richard Hughes40127542018-01-12 20:25:55 +00002095 device = fu_device_list_get_by_guid (self->device_list, guid, NULL);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002096 if (device != NULL) {
2097 fwupd_device_set_name (dev, fu_device_get_name (device));
2098 fwupd_device_set_flags (dev, fu_device_get_flags (device));
2099 fwupd_device_set_id (dev, fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01002100 }
2101
2102 /* add GUID */
2103 fwupd_device_add_guid (dev, guid);
2104 }
2105 if (fwupd_device_get_guids(dev)->len == 0) {
2106 g_set_error_literal (error,
2107 FWUPD_ERROR,
2108 FWUPD_ERROR_INTERNAL,
2109 "component has no GUIDs");
2110 return NULL;
2111 }
2112
2113 /* check we can install it */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002114 task = fu_install_task_new (NULL, component);
Richard Hughes1d1f5cf2018-05-19 23:06:03 +01002115 if (!fu_engine_check_requirements (self, task,
2116 FWUPD_INSTALL_FLAG_NONE,
2117 error))
Richard Hughes9945edb2017-06-19 10:03:55 +01002118 return NULL;
2119
2120 /* verify trust */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002121 release = xb_node_query_first (component,
2122 "releases/release",
2123 &error_local);
2124 if (release == NULL) {
2125 g_set_error (error,
2126 FWUPD_ERROR,
2127 FWUPD_ERROR_INTERNAL,
2128 "failed to get release: %s",
2129 error_local->message);
2130 return NULL;
2131 }
Salud Lemus980f2d92018-08-28 09:25:40 -07002132 if (!fu_keyring_get_release_trust_flags (release,
2133 &trust_flags,
2134 &error_local)) {
2135 if (g_error_matches (error_local,
2136 FWUPD_ERROR,
2137 FWUPD_ERROR_NOT_SUPPORTED)) {
2138 g_warning ("Ignoring verification: %s",
2139 error_local->message);
2140 } else {
2141 g_propagate_error (error, g_steal_pointer (&error_local));
2142 return NULL;
2143 }
2144 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002145
Richard Hughes9945edb2017-06-19 10:03:55 +01002146 /* create a result with all the metadata in */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002147 description = xb_node_query_first (component, "description", NULL);
2148 if (description != NULL) {
2149 g_autofree gchar *xml = NULL;
2150 xml = xb_node_export (description,
2151 XB_NODE_EXPORT_FLAG_ONLY_CHILDREN,
2152 NULL);
2153 if (xml != NULL)
2154 fwupd_device_set_description (dev, xml);
2155 }
Richard Hughes93b15762017-09-15 11:05:23 +01002156 rel = fwupd_release_new ();
Richard Hughesb3ca2452017-09-15 11:24:45 +01002157 fwupd_release_set_trust_flags (rel, trust_flags);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002158 fu_engine_set_release_from_appstream (self, rel, component, release);
Richard Hughes93b15762017-09-15 11:05:23 +01002159 fwupd_device_add_release (dev, rel);
2160 return g_steal_pointer (&dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01002161}
2162
2163/**
Richard Hughes07f963a2017-09-15 14:28:47 +01002164 * fu_engine_get_details:
Richard Hughes9945edb2017-06-19 10:03:55 +01002165 * @self: A #FuEngine
2166 * @fd: A file descriptor
2167 * @error: A #GError, or %NULL
2168 *
2169 * Gets the details about a local file.
2170 *
2171 * Note: this will close the fd when done
2172 *
Richard Hughes93b15762017-09-15 11:05:23 +01002173 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01002174 **/
2175GPtrArray *
Richard Hughes07f963a2017-09-15 14:28:47 +01002176fu_engine_get_details (FuEngine *self, gint fd, GError **error)
Richard Hughes9945edb2017-06-19 10:03:55 +01002177{
Richard Hughes534255c2018-01-28 19:51:56 +00002178 const gchar *remote_id;
2179 g_autofree gchar *csum = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002180 g_autoptr(GBytes) blob = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002181 g_autoptr(GError) error_local = NULL;
2182 g_autoptr(GPtrArray) components = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002183 g_autoptr(GPtrArray) details = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002184 g_autoptr(XbSilo) silo = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002185
2186 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2187 g_return_val_if_fail (fd > 0, NULL);
2188 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2189
Richard Hughes481aa2a2018-09-18 20:51:46 +01002190 /* get all components */
Richard Hughesc7bbbc22018-01-02 22:22:25 +00002191 blob = fu_common_get_contents_fd (fd,
2192 fu_engine_get_archive_size_max (self),
2193 error);
Richard Hughes9945edb2017-06-19 10:03:55 +01002194 if (blob == NULL)
2195 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002196 silo = fu_engine_get_silo_from_blob (self, blob, error);
2197 if (silo == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01002198 return NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002199 components = xb_silo_query (silo, "component", 0, &error_local);
2200 if (components == NULL) {
2201 g_set_error (error,
2202 FWUPD_ERROR,
2203 FWUPD_ERROR_INVALID_FILE,
2204 "no components: %s",
2205 error_local->message);
Richard Hughes9945edb2017-06-19 10:03:55 +01002206 return NULL;
2207 }
2208
Richard Hughes534255c2018-01-28 19:51:56 +00002209 /* does this exist in any enabled remote */
2210 csum = g_compute_checksum_for_bytes (G_CHECKSUM_SHA1, blob);
2211 remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
2212
Richard Hughes9945edb2017-06-19 10:03:55 +01002213 /* create results with all the metadata in */
2214 details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002215 for (guint i = 0; i < components->len; i++) {
2216 XbNode *component = g_ptr_array_index (components, i);
Richard Hughes534255c2018-01-28 19:51:56 +00002217 FwupdDevice *dev;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002218 dev = fu_engine_get_result_from_app (self, component, error);
Richard Hughes534255c2018-01-28 19:51:56 +00002219 if (dev == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01002220 return NULL;
Richard Hughes534255c2018-01-28 19:51:56 +00002221 if (remote_id != NULL) {
2222 FwupdRelease *rel = fwupd_device_get_release_default (dev);
2223 fwupd_release_set_remote_id (rel, remote_id);
2224 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
2225 }
2226 g_ptr_array_add (details, dev);
Richard Hughes9945edb2017-06-19 10:03:55 +01002227 }
2228 return g_steal_pointer (&details);
2229}
2230
Mario Limonciello343095d2018-10-23 17:21:13 -05002231static gint
2232fu_engine_sort_devices_by_priority (gconstpointer a, gconstpointer b)
2233{
2234 FuDevice *dev_a = *((FuDevice **) a);
2235 FuDevice *dev_b = *((FuDevice **) b);
2236 gint prio_a = fu_device_get_priority (dev_a);
2237 gint prio_b = fu_device_get_priority (dev_b);
2238
2239 if (prio_a > prio_b)
2240 return -1;
2241 if (prio_a < prio_b)
2242 return 1;
2243 return 0;
2244}
2245
Richard Hughes9945edb2017-06-19 10:03:55 +01002246/**
2247 * fu_engine_get_devices:
2248 * @self: A #FuEngine
2249 * @error: A #GError, or %NULL
2250 *
2251 * Gets the list of devices.
2252 *
2253 * Returns: (transfer container) (element-type FwupdDevice): results
2254 **/
2255GPtrArray *
2256fu_engine_get_devices (FuEngine *self, GError **error)
2257{
Richard Hughesf2eccde2017-09-20 11:18:03 +01002258 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002259
2260 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2261 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2262
Richard Hughes70425fe2017-11-22 12:33:27 +00002263 devices = fu_device_list_get_active (self->device_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01002264 if (devices->len == 0) {
2265 g_set_error_literal (error,
2266 FWUPD_ERROR,
2267 FWUPD_ERROR_NOTHING_TO_DO,
2268 "No detected devices");
2269 return NULL;
2270 }
Mario Limonciello343095d2018-10-23 17:21:13 -05002271 g_ptr_array_sort (devices, fu_engine_sort_devices_by_priority);
Richard Hughesf2eccde2017-09-20 11:18:03 +01002272 return g_steal_pointer (&devices);
Richard Hughes9945edb2017-06-19 10:03:55 +01002273}
2274
Richard Hughes12040b52018-05-14 13:32:10 +01002275/**
2276 * fu_engine_get_device:
2277 * @self: A #FuEngine
2278 * @device_id: A device ID
2279 * @error: A #GError, or %NULL
2280 *
2281 * Gets a specific device.
2282 *
2283 * Returns: (transfer full): a device, or %NULL if not found
2284 **/
Richard Hughes4ad41f02018-05-08 14:35:36 +01002285FuDevice *
2286fu_engine_get_device (FuEngine *self, const gchar *device_id, GError **error)
2287{
Richard Hughes12040b52018-05-14 13:32:10 +01002288 FuDevice *device;
Richard Hughes37d09432018-09-09 10:39:45 +01002289
Richard Hughes12040b52018-05-14 13:32:10 +01002290 device = fu_device_list_get_by_id (self->device_list, device_id, error);
2291 if (device == NULL)
2292 return NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01002293 return device;
Richard Hughes4ad41f02018-05-08 14:35:36 +01002294}
2295
Richard Hughes9945edb2017-06-19 10:03:55 +01002296/**
Richard Hughes476363a2018-01-11 10:08:58 +00002297 * fu_engine_get_history:
2298 * @self: A #FuEngine
2299 * @error: A #GError, or %NULL
2300 *
2301 * Gets the list of history.
2302 *
2303 * Returns: (transfer container) (element-type FwupdDevice): results
2304 **/
2305GPtrArray *
2306fu_engine_get_history (FuEngine *self, GError **error)
2307{
2308 g_autoptr(GPtrArray) devices = NULL;
2309
2310 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2311 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2312
2313 devices = fu_history_get_devices (self->history, error);
2314 if (devices == NULL)
2315 return NULL;
2316 if (devices->len == 0) {
2317 g_set_error_literal (error,
2318 FWUPD_ERROR,
2319 FWUPD_ERROR_NOTHING_TO_DO,
2320 "No history");
2321 return NULL;
2322 }
Richard Hughes611f1a92018-01-11 11:54:28 +00002323
2324 /* try to set the remote ID for each device */
2325 for (guint i = 0; i < devices->len; i++) {
2326 FuDevice *dev = g_ptr_array_index (devices, i);
2327 FwupdRelease *rel;
2328 GPtrArray *csums;
2329
2330 /* get the checksums */
2331 rel = fu_device_get_release_default (dev);
2332 if (rel == NULL)
2333 continue;
2334
2335 /* find the checksum that matches */
2336 csums = fwupd_release_get_checksums (rel);
2337 for (guint j = 0; j < csums->len; j++) {
2338 const gchar *csum = g_ptr_array_index (csums, j);
2339 const gchar *remote_id = fu_engine_get_remote_id_for_checksum (self, csum);
2340 if (remote_id != NULL) {
2341 fu_device_add_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED);
2342 fwupd_release_set_remote_id (rel, remote_id);
2343 break;
2344 }
2345 }
2346 }
2347
Richard Hughes476363a2018-01-11 10:08:58 +00002348 return g_steal_pointer (&devices);
2349}
2350
2351/**
Richard Hughes9945edb2017-06-19 10:03:55 +01002352 * fu_engine_get_remotes:
2353 * @self: A #FuEngine
Richard Hughes9945edb2017-06-19 10:03:55 +01002354 * @error: A #GError, or %NULL
2355 *
2356 * Gets the list of remotes in use by the engine.
2357 *
2358 * Returns: (transfer container) (element-type FwupdRemote): results
2359 **/
2360GPtrArray *
2361fu_engine_get_remotes (FuEngine *self, GError **error)
2362{
2363 GPtrArray *remotes;
2364
2365 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2366 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2367
2368 remotes = fu_config_get_remotes (self->config);
2369 if (remotes->len == 0) {
2370 g_set_error (error,
2371 FWUPD_ERROR,
2372 FWUPD_ERROR_INTERNAL,
2373 "No remotes configured");
2374 return NULL;
2375 }
2376 return g_ptr_array_ref (remotes);
2377}
2378
Richard Hughes225f3a92017-09-13 19:38:51 +01002379static gint
2380fu_engine_sort_releases_cb (gconstpointer a, gconstpointer b)
2381{
2382 FwupdRelease *rel_a = FWUPD_RELEASE (*((FwupdRelease **) a));
2383 FwupdRelease *rel_b = FWUPD_RELEASE (*((FwupdRelease **) b));
Richard Hughes481aa2a2018-09-18 20:51:46 +01002384 return fu_common_vercmp (fwupd_release_get_version (rel_b),
Richard Hughes43f9dd82017-11-16 14:27:45 +00002385 fwupd_release_get_version (rel_a));
Richard Hughes225f3a92017-09-13 19:38:51 +01002386}
2387
Richard Hughes481aa2a2018-09-18 20:51:46 +01002388static gboolean
2389fu_engine_add_releases_for_device_component (FuEngine *self,
2390 FuDevice *device,
2391 XbNode *component,
2392 GPtrArray *releases,
2393 GError **error)
Richard Hughes650dade2017-12-14 14:43:11 +00002394{
Richard Hughes481aa2a2018-09-18 20:51:46 +01002395 g_autoptr(GError) error_local = NULL;
2396 g_autoptr(FuInstallTask) task = fu_install_task_new (device, component);
2397 g_autoptr(GPtrArray) releases_tmp = NULL;
Richard Hughes650dade2017-12-14 14:43:11 +00002398
Richard Hughes481aa2a2018-09-18 20:51:46 +01002399 if (!fu_engine_check_requirements (self, task,
2400 FWUPD_INSTALL_FLAG_ALLOW_REINSTALL |
2401 FWUPD_INSTALL_FLAG_ALLOW_OLDER,
2402 error))
2403 return FALSE;
2404
2405 /* get all releases */
2406 releases_tmp = xb_node_query (component, "releases/release", 0, &error_local);
2407 if (releases_tmp == NULL) {
2408 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
2409 return TRUE;
2410 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT))
2411 return TRUE;
2412 g_propagate_error (error, g_steal_pointer (&error_local));
2413 return FALSE;
2414 }
2415 for (guint i = 0; i < releases_tmp->len; i++) {
2416 XbNode *release = g_ptr_array_index (releases_tmp, i);
2417 GPtrArray *checksums;
2418 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
2419
2420 /* create new FwupdRelease for the XbNode */
2421 fu_engine_set_release_from_appstream (self, rel, component, release);
2422
2423 /* invalid */
2424 if (fwupd_release_get_uri (rel) == NULL)
Richard Hughes650dade2017-12-14 14:43:11 +00002425 continue;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002426 checksums = fwupd_release_get_checksums (rel);
2427 if (checksums->len == 0)
2428 continue;
2429
2430 /* success */
2431 g_ptr_array_add (releases, g_steal_pointer (&rel));
Richard Hughes650dade2017-12-14 14:43:11 +00002432 }
2433
Richard Hughes481aa2a2018-09-18 20:51:46 +01002434 /* success */
2435 return TRUE;
Richard Hughes650dade2017-12-14 14:43:11 +00002436}
2437
Richard Hughes97284b12017-09-13 17:07:58 +01002438static GPtrArray *
2439fu_engine_get_releases_for_device (FuEngine *self, FuDevice *device, GError **error)
2440{
2441 GPtrArray *device_guids;
2442 GPtrArray *releases;
2443 const gchar *version;
Richard Hughes481aa2a2018-09-18 20:51:46 +01002444 g_autoptr(GError) error_all = NULL;
2445 g_autoptr(GError) error_local = NULL;
2446 g_autoptr(GPtrArray) components = NULL;
2447 g_autoptr(GString) xpath = g_string_new (NULL);
Richard Hughes97284b12017-09-13 17:07:58 +01002448
2449 /* get device version */
2450 version = fu_device_get_version (device);
2451 if (version == NULL) {
2452 g_set_error (error,
2453 FWUPD_ERROR,
2454 FWUPD_ERROR_NOT_SUPPORTED,
2455 "no version set");
2456 return NULL;
2457 }
2458
2459 /* only show devices that can be updated */
2460 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) {
2461 g_set_error (error,
2462 FWUPD_ERROR,
2463 FWUPD_ERROR_NOT_SUPPORTED,
2464 "ignoring %s [%s] as not updatable",
Richard Hughes7e77bf32018-05-08 14:56:46 +01002465 fu_device_get_name (device),
2466 fu_device_get_id (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002467 return NULL;
2468 }
2469
Richard Hughes481aa2a2018-09-18 20:51:46 +01002470 /* get all the components that provide any of these GUIDs */
Richard Hughes97284b12017-09-13 17:07:58 +01002471 device_guids = fu_device_get_guids (device);
2472 for (guint i = 0; i < device_guids->len; i++) {
Richard Hughes97284b12017-09-13 17:07:58 +01002473 const gchar *guid = g_ptr_array_index (device_guids, i);
Richard Hughes481aa2a2018-09-18 20:51:46 +01002474 xb_string_append_union (xpath,
2475 "components/component/"
2476 "provides/firmware[@type='flashed'][text()='%s']/"
2477 "../..", guid);
2478 }
2479 components = xb_silo_query (self->silo, xpath->str, 0, &error_local);
2480 if (components == NULL) {
2481 if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
2482 g_set_error (error,
2483 FWUPD_ERROR,
2484 FWUPD_ERROR_NOTHING_TO_DO,
2485 "No releases found for device");
2486 return FALSE;
Richard Hughes97284b12017-09-13 17:07:58 +01002487 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01002488 g_propagate_error (error, g_steal_pointer (&error_local));
2489 return FALSE;
2490 }
2491
2492 /* find all the releases that pass all the requirements */
2493 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2494 for (guint i = 0; i < components->len; i++) {
2495 XbNode *component = XB_NODE (g_ptr_array_index (components, i));
2496 g_autoptr(GError) error_tmp = NULL;
2497 if (!fu_engine_add_releases_for_device_component (self,
2498 device,
2499 component,
2500 releases,
2501 &error_tmp)) {
2502 if (error_all == NULL) {
2503 error_all = g_steal_pointer (&error_tmp);
2504 continue;
2505 }
2506
2507 /* assume the domain and code is the same */
2508 g_prefix_error (&error_all, "%s, ", error_tmp->message);
2509 }
2510 }
2511
2512 /* return the compound error */
2513 if (releases->len == 0) {
2514 g_propagate_error (error, g_steal_pointer (&error_all));
2515 return NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01002516 }
2517 return releases;
2518}
2519
Richard Hughes9945edb2017-06-19 10:03:55 +01002520/**
2521 * fu_engine_get_releases:
2522 * @self: A #FuEngine
2523 * @device_id: A device ID
2524 * @error: A #GError, or %NULL
2525 *
2526 * Gets the releases available for a specific device.
2527 *
Richard Hughes93b15762017-09-15 11:05:23 +01002528 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes9945edb2017-06-19 10:03:55 +01002529 **/
2530GPtrArray *
2531fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error)
2532{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01002533 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002534 g_autoptr(GPtrArray) releases = NULL;
2535
2536 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2537 g_return_val_if_fail (device_id != NULL, NULL);
2538 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2539
2540 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00002541 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002542 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01002543 return NULL;
2544
2545 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00002546 releases = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01002547 if (releases == NULL)
2548 return NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002549 if (releases->len == 0) {
2550 g_set_error_literal (error,
2551 FWUPD_ERROR,
2552 FWUPD_ERROR_NOTHING_TO_DO,
2553 "No releases for device");
2554 return NULL;
2555 }
Richard Hughesa96413a2017-09-13 17:19:59 +01002556 g_ptr_array_sort (releases, fu_engine_sort_releases_cb);
Richard Hughes97284b12017-09-13 17:07:58 +01002557 return g_steal_pointer (&releases);
2558}
2559
2560/**
2561 * fu_engine_get_downgrades:
2562 * @self: A #FuEngine
2563 * @device_id: A device ID
2564 * @error: A #GError, or %NULL
2565 *
2566 * Gets the downgrades available for a specific device.
2567 *
Richard Hughes93b15762017-09-15 11:05:23 +01002568 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughes97284b12017-09-13 17:07:58 +01002569 **/
2570GPtrArray *
2571fu_engine_get_downgrades (FuEngine *self, const gchar *device_id, GError **error)
2572{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01002573 g_autoptr(FuDevice) device = NULL;
Richard Hughes97284b12017-09-13 17:07:58 +01002574 g_autoptr(GPtrArray) releases = NULL;
2575 g_autoptr(GPtrArray) releases_tmp = NULL;
2576 g_autoptr(GString) error_str = g_string_new (NULL);
2577
2578 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2579 g_return_val_if_fail (device_id != NULL, NULL);
2580 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2581
2582 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00002583 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002584 if (device == NULL)
Richard Hughes97284b12017-09-13 17:07:58 +01002585 return NULL;
2586
2587 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00002588 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughes97284b12017-09-13 17:07:58 +01002589 if (releases_tmp == NULL)
2590 return NULL;
2591 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2592 for (guint i = 0; i < releases_tmp->len; i++) {
2593 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
2594 gint vercmp;
2595
2596 /* only include older firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002597 vercmp = fu_common_vercmp (fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002598 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002599 if (vercmp == 0) {
2600 g_string_append_printf (error_str, "%s=same, ",
2601 fwupd_release_get_version (rel_tmp));
2602 g_debug ("ignoring %s as the same as %s",
2603 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002604 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002605 continue;
2606 }
2607 if (vercmp > 0) {
2608 g_string_append_printf (error_str, "%s=newer, ",
2609 fwupd_release_get_version (rel_tmp));
2610 g_debug ("ignoring %s as newer than %s",
2611 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002612 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002613 continue;
2614 }
2615
2616 /* don't show releases we are not allowed to dowgrade to */
Richard Hughes0a7e7832017-11-22 11:01:13 +00002617 if (fu_device_get_version_lowest (device) != NULL) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01002618 if (fu_common_vercmp (fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002619 fu_device_get_version_lowest (device)) <= 0) {
Richard Hughes97284b12017-09-13 17:07:58 +01002620 g_string_append_printf (error_str, "%s=lowest, ",
2621 fwupd_release_get_version (rel_tmp));
2622 g_debug ("ignoring %s as older than lowest %s",
2623 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002624 fu_device_get_version_lowest (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002625 continue;
2626 }
2627 }
2628 g_ptr_array_add (releases, g_object_ref (rel_tmp));
2629 }
2630 if (error_str->len > 2)
2631 g_string_truncate (error_str, error_str->len - 2);
2632 if (releases->len == 0) {
2633 if (error_str->len > 0) {
2634 g_set_error (error,
2635 FWUPD_ERROR,
2636 FWUPD_ERROR_NOTHING_TO_DO,
2637 "No downgrades for device, current is %s: %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00002638 fu_device_get_version (device),
Richard Hughes97284b12017-09-13 17:07:58 +01002639 error_str->str);
2640 } else {
2641 g_set_error (error,
2642 FWUPD_ERROR,
2643 FWUPD_ERROR_NOTHING_TO_DO,
2644 "No downgrades for device, current is %s",
Richard Hughes0a7e7832017-11-22 11:01:13 +00002645 fu_device_get_version (device));
Richard Hughes97284b12017-09-13 17:07:58 +01002646 }
2647 return NULL;
2648 }
Richard Hughes225f3a92017-09-13 19:38:51 +01002649 g_ptr_array_sort (releases, fu_engine_sort_releases_cb);
Richard Hughes9945edb2017-06-19 10:03:55 +01002650 return g_steal_pointer (&releases);
2651}
2652
2653/**
Richard Hughesa96413a2017-09-13 17:19:59 +01002654 * fu_engine_get_upgrades:
2655 * @self: A #FuEngine
2656 * @device_id: A device ID
2657 * @error: A #GError, or %NULL
2658 *
2659 * Gets the upgrades available for a specific device.
2660 *
Richard Hughes93b15762017-09-15 11:05:23 +01002661 * Returns: (transfer container) (element-type FwupdDevice): results
Richard Hughesa96413a2017-09-13 17:19:59 +01002662 **/
2663GPtrArray *
2664fu_engine_get_upgrades (FuEngine *self, const gchar *device_id, GError **error)
2665{
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01002666 g_autoptr(FuDevice) device = NULL;
Richard Hughesa96413a2017-09-13 17:19:59 +01002667 g_autoptr(GPtrArray) releases = NULL;
2668 g_autoptr(GPtrArray) releases_tmp = NULL;
2669 g_autoptr(GString) error_str = g_string_new (NULL);
2670
2671 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2672 g_return_val_if_fail (device_id != NULL, NULL);
2673 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2674
2675 /* find the device */
Richard Hughes40127542018-01-12 20:25:55 +00002676 device = fu_device_list_get_by_id (self->device_list, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002677 if (device == NULL)
Richard Hughesa96413a2017-09-13 17:19:59 +01002678 return NULL;
2679
Mario Limonciellofc139352018-09-13 10:06:39 -05002680 /* don't show upgrades again until we reboot */
2681 if (fu_device_get_update_state (device) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
2682 g_set_error (error,
2683 FWUPD_ERROR,
2684 FWUPD_ERROR_NOTHING_TO_DO,
2685 "No upgrades for %s: A reboot is pending",
2686 fu_device_get_name (device));
2687 return NULL;
2688 }
2689
Richard Hughesa96413a2017-09-13 17:19:59 +01002690 /* get all the releases for the device */
Richard Hughes0a7e7832017-11-22 11:01:13 +00002691 releases_tmp = fu_engine_get_releases_for_device (self, device, error);
Richard Hughesa96413a2017-09-13 17:19:59 +01002692 if (releases_tmp == NULL)
2693 return NULL;
2694 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2695 for (guint i = 0; i < releases_tmp->len; i++) {
2696 FwupdRelease *rel_tmp = g_ptr_array_index (releases_tmp, i);
2697 gint vercmp;
2698
2699 /* only include older firmware */
Richard Hughes481aa2a2018-09-18 20:51:46 +01002700 vercmp = fu_common_vercmp (fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002701 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01002702 if (vercmp == 0) {
2703 g_string_append_printf (error_str, "%s=same, ",
2704 fwupd_release_get_version (rel_tmp));
2705 g_debug ("ignoring %s as the same as %s",
2706 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002707 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01002708 continue;
2709 }
2710 if (vercmp < 0) {
2711 g_string_append_printf (error_str, "%s=older, ",
2712 fwupd_release_get_version (rel_tmp));
2713 g_debug ("ignoring %s as older than %s",
2714 fwupd_release_get_version (rel_tmp),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002715 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01002716 continue;
2717 }
2718 g_ptr_array_add (releases, g_object_ref (rel_tmp));
2719 }
2720 if (error_str->len > 2)
2721 g_string_truncate (error_str, error_str->len - 2);
2722 if (releases->len == 0) {
2723 if (error_str->len > 0) {
2724 g_set_error (error,
2725 FWUPD_ERROR,
2726 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello44ebdb82018-02-12 16:17:19 -05002727 "No upgrades for %s, current is %s: %s",
2728 fu_device_get_name (device),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002729 fu_device_get_version (device),
Richard Hughesa96413a2017-09-13 17:19:59 +01002730 error_str->str);
2731 } else {
2732 g_set_error (error,
2733 FWUPD_ERROR,
2734 FWUPD_ERROR_NOTHING_TO_DO,
Mario Limonciello44ebdb82018-02-12 16:17:19 -05002735 "No upgrades for %s, current is %s",
2736 fu_device_get_name (device),
Richard Hughes0a7e7832017-11-22 11:01:13 +00002737 fu_device_get_version (device));
Richard Hughesa96413a2017-09-13 17:19:59 +01002738 }
2739 return NULL;
2740 }
2741 g_ptr_array_sort (releases, fu_engine_sort_releases_cb);
2742 return g_steal_pointer (&releases);
2743}
2744
2745/**
Richard Hughes9945edb2017-06-19 10:03:55 +01002746 * fu_engine_clear_results:
2747 * @self: A #FuEngine
2748 * @device_id: A device ID
2749 * @error: A #GError, or %NULL
2750 *
2751 * Clear the historical state of a specific device operation.
2752 *
2753 * Returns: %TRUE for success
2754 **/
2755gboolean
2756fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error)
2757{
Richard Hughes65e44ca2018-01-30 17:26:30 +00002758 g_autoptr(FuDevice) device = NULL;
Richard Hughes34834102017-11-21 21:55:00 +00002759 FuPlugin *plugin;
Richard Hughes9945edb2017-06-19 10:03:55 +01002760
2761 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2762 g_return_val_if_fail (device_id != NULL, FALSE);
2763 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2764
2765 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002766 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002767 if (device == NULL)
Richard Hughes9945edb2017-06-19 10:03:55 +01002768 return FALSE;
2769
Richard Hughes65e44ca2018-01-30 17:26:30 +00002770 /* already set on the database */
2771 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
2772 g_set_error_literal (error,
2773 FWUPD_ERROR,
2774 FWUPD_ERROR_NOT_SUPPORTED,
2775 "device already has notified flag");
2776 return FALSE;
2777 }
2778
2779 /* call into the plugin if it still exists */
Richard Hughese7e95452017-11-22 09:05:53 +00002780 plugin = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00002781 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00002782 error);
Richard Hughes65e44ca2018-01-30 17:26:30 +00002783 if (plugin != NULL) {
2784 if (!fu_plugin_runner_clear_results (plugin, device, error))
2785 return FALSE;
2786 }
Richard Hughes34834102017-11-21 21:55:00 +00002787
Richard Hughes65e44ca2018-01-30 17:26:30 +00002788 /* override */
Richard Hughesc0cd0232018-01-31 15:02:00 +00002789 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED);
2790 return fu_history_modify_device (self->history, device,
2791 FU_HISTORY_FLAGS_MATCH_OLD_VERSION |
2792 FU_HISTORY_FLAGS_MATCH_NEW_VERSION,
2793 error);
Richard Hughes9945edb2017-06-19 10:03:55 +01002794}
2795
2796/**
2797 * fu_engine_get_results:
2798 * @self: A #FuEngine
2799 * @device_id: A device ID
2800 * @error: A #GError, or %NULL
2801 *
2802 * Gets the historical state of a specific device operation.
2803 *
Richard Hughes93b15762017-09-15 11:05:23 +01002804 * Returns: (transfer container): a #FwupdDevice, or %NULL
Richard Hughes9945edb2017-06-19 10:03:55 +01002805 **/
Richard Hughes93b15762017-09-15 11:05:23 +01002806FwupdDevice *
Richard Hughes9945edb2017-06-19 10:03:55 +01002807fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error)
2808{
Richard Hughes65e44ca2018-01-30 17:26:30 +00002809 g_autoptr(FuDevice) device = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01002810
2811 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2812 g_return_val_if_fail (device_id != NULL, NULL);
2813 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2814
2815 /* find the device */
Richard Hughesbc3a4e12018-01-06 22:41:47 +00002816 device = fu_engine_get_item_by_id_fallback_history (self, device_id, error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00002817 if (device == NULL)
Richard Hughesfe221dc2018-05-29 09:33:44 +01002818 return NULL;
Richard Hughes34834102017-11-21 21:55:00 +00002819
Richard Hughes65e44ca2018-01-30 17:26:30 +00002820 /* the notification has already been shown to the user */
2821 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NOTIFIED)) {
2822 g_set_error (error,
2823 FWUPD_ERROR,
2824 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes7e77bf32018-05-08 14:56:46 +01002825 "User has already been notified about %s [%s]",
2826 fu_device_get_name (device),
Richard Hughes65e44ca2018-01-30 17:26:30 +00002827 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01002828 return NULL;
Richard Hughes65e44ca2018-01-30 17:26:30 +00002829 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002830
Richard Hughes65e44ca2018-01-30 17:26:30 +00002831 /* success */
Richard Hugheseec8a3c2018-01-02 20:37:31 +00002832 return g_object_ref (FWUPD_DEVICE (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01002833}
2834
2835static void
2836fu_engine_plugins_setup (FuEngine *self)
2837{
Richard Hughese671c052018-09-18 10:17:15 +01002838 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughese7e95452017-11-22 09:05:53 +00002839 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002840 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00002841 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01002842 if (!fu_plugin_runner_startup (plugin, &error)) {
2843 fu_plugin_set_enabled (plugin, FALSE);
2844 g_message ("disabling plugin because: %s", error->message);
2845 }
2846 }
2847}
2848
2849static void
Richard Hughes2de8f132018-01-17 09:12:02 +00002850fu_engine_plugins_coldplug (FuEngine *self, gboolean is_recoldplug)
Richard Hughes9945edb2017-06-19 10:03:55 +01002851{
Richard Hughese7e95452017-11-22 09:05:53 +00002852 GPtrArray *plugins;
Richard Hughes535664c2017-07-24 10:30:09 +01002853 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01002854
2855 /* don't allow coldplug to be scheduled when in coldplug */
2856 self->coldplug_running = TRUE;
2857
2858 /* prepare */
Richard Hughese7e95452017-11-22 09:05:53 +00002859 plugins = fu_plugin_list_get_all (self->plugin_list);
2860 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002861 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00002862 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01002863 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
2864 g_warning ("failed to prepare coldplug: %s", error->message);
2865 }
2866
2867 /* do this in one place */
2868 if (self->coldplug_delay > 0) {
2869 g_debug ("sleeping for %ums", self->coldplug_delay);
2870 g_usleep (self->coldplug_delay * 1000);
2871 }
2872
2873 /* exec */
Richard Hughese7e95452017-11-22 09:05:53 +00002874 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002875 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00002876 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes2de8f132018-01-17 09:12:02 +00002877 if (is_recoldplug) {
2878 if (!fu_plugin_runner_recoldplug (plugin, &error))
2879 g_message ("failed recoldplug: %s", error->message);
2880 } else {
2881 if (!fu_plugin_runner_coldplug (plugin, &error)) {
2882 fu_plugin_set_enabled (plugin, FALSE);
2883 g_message ("disabling plugin because: %s",
2884 error->message);
2885 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002886 }
2887 }
2888
2889 /* cleanup */
Richard Hughese7e95452017-11-22 09:05:53 +00002890 for (guint i = 0; i < plugins->len; i++) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002891 g_autoptr(GError) error = NULL;
Richard Hughese7e95452017-11-22 09:05:53 +00002892 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes9945edb2017-06-19 10:03:55 +01002893 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
2894 g_warning ("failed to cleanup coldplug: %s", error->message);
2895 }
2896
Richard Hughes535664c2017-07-24 10:30:09 +01002897 /* print what we do have */
Richard Hughese7e95452017-11-22 09:05:53 +00002898 for (guint i = 0; i < plugins->len; i++) {
2899 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes535664c2017-07-24 10:30:09 +01002900 if (!fu_plugin_get_enabled (plugin))
2901 continue;
2902 g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin));
2903 }
2904 if (str->len > 2) {
2905 g_string_truncate (str, str->len - 2);
Mario Limonciello59cd4702018-08-09 12:43:47 -05002906 g_debug ("using plugins: %s", str->str);
Richard Hughes535664c2017-07-24 10:30:09 +01002907 }
2908
Richard Hughes9945edb2017-06-19 10:03:55 +01002909 /* we can recoldplug from this point on */
2910 self->coldplug_running = FALSE;
2911}
2912
2913static void
Richard Hughese1fd34d2017-08-24 14:19:51 +01002914fu_engine_plugin_device_register (FuEngine *self, FuDevice *device)
2915{
Richard Hughese7e95452017-11-22 09:05:53 +00002916 GPtrArray *plugins;
Richard Hughese1fd34d2017-08-24 14:19:51 +01002917 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) {
2918 g_warning ("already registered %s, ignoring",
2919 fu_device_get_id (device));
2920 return;
2921 }
Richard Hughese7e95452017-11-22 09:05:53 +00002922 plugins = fu_plugin_list_get_all (self->plugin_list);
2923 for (guint i = 0; i < plugins->len; i++) {
2924 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughese1fd34d2017-08-24 14:19:51 +01002925 fu_plugin_runner_device_register (plugin, device);
2926 }
2927 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REGISTERED);
2928}
2929
2930static void
2931fu_engine_plugin_device_register_cb (FuPlugin *plugin,
2932 FuDevice *device,
2933 gpointer user_data)
2934{
2935 FuEngine *self = FU_ENGINE (user_data);
2936 fu_engine_plugin_device_register (self, device);
2937}
2938
2939static void
Richard Hughes9945edb2017-06-19 10:03:55 +01002940fu_engine_plugin_device_added_cb (FuPlugin *plugin,
2941 FuDevice *device,
2942 gpointer user_data)
2943{
2944 FuEngine *self = (FuEngine *) user_data;
Richard Hughes81c427c2018-08-06 15:20:17 +01002945 fu_device_set_priority (device, fu_plugin_get_priority (plugin));
Richard Hughes0a7e7832017-11-22 11:01:13 +00002946 fu_engine_add_device (self, device);
2947}
2948
Richard Hughes5e447292018-04-27 14:25:54 +01002949static void
2950fu_engine_adopt_children (FuEngine *self, FuDevice *device)
2951{
2952 GPtrArray *guids;
2953 g_autoptr(GPtrArray) devices = fu_device_list_get_active (self->device_list);
2954
2955 /* find the parent GUID in any existing device */
2956 guids = fu_device_get_parent_guids (device);
2957 for (guint j = 0; j < guids->len; j++) {
2958 const gchar *guid = g_ptr_array_index (guids, j);
2959 for (guint i = 0; i < devices->len; i++) {
2960 FuDevice *device_tmp = g_ptr_array_index (devices, i);
2961 if (fu_device_get_parent (device) != NULL)
2962 continue;
2963 if (fu_device_has_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01002964 g_debug ("setting parent of %s [%s] to be %s [%s]",
2965 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01002966 fu_device_get_id (device),
Richard Hughes7e77bf32018-05-08 14:56:46 +01002967 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01002968 fu_device_get_id (device_tmp));
2969 fu_device_add_child (device_tmp, device);
2970 break;
2971 }
2972 }
2973 }
2974
2975 /* the new device is the parent to an existing child */
2976 guids = fu_device_get_guids (device);
2977 for (guint j = 0; j < guids->len; j++) {
2978 const gchar *guid = g_ptr_array_index (guids, j);
2979 for (guint i = 0; i < devices->len; i++) {
2980 FuDevice *device_tmp = g_ptr_array_index (devices, i);
2981 if (fu_device_get_parent (device_tmp) != NULL)
2982 continue;
2983 if (fu_device_has_parent_guid (device_tmp, guid)) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01002984 g_debug ("setting parent of %s [%s] to be %s [%s]",
2985 fu_device_get_name (device_tmp),
Richard Hughes5e447292018-04-27 14:25:54 +01002986 fu_device_get_id (device_tmp),
Richard Hughes7e77bf32018-05-08 14:56:46 +01002987 fu_device_get_name (device),
Richard Hughes5e447292018-04-27 14:25:54 +01002988 fu_device_get_id (device));
2989 fu_device_add_child (device, device_tmp);
2990 }
2991 }
2992 }
2993}
2994
Richard Hughes0a7e7832017-11-22 11:01:13 +00002995void
2996fu_engine_add_device (FuEngine *self, FuDevice *device)
2997{
Richard Hughes9945edb2017-06-19 10:03:55 +01002998 GPtrArray *blacklisted_devices;
Richard Hughes32684f22017-07-13 09:32:21 +01002999 GPtrArray *device_guids;
Richard Hughes9945edb2017-06-19 10:03:55 +01003000
3001 /* device has no GUIDs set! */
Richard Hughes32684f22017-07-13 09:32:21 +01003002 device_guids = fu_device_get_guids (device);
3003 if (device_guids->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003004 g_warning ("no GUIDs for device %s [%s]",
Richard Hughes7e77bf32018-05-08 14:56:46 +01003005 fu_device_get_name (device),
3006 fu_device_get_id (device));
Richard Hughes9945edb2017-06-19 10:03:55 +01003007 return;
3008 }
3009
3010 /* is this GUID blacklisted */
3011 blacklisted_devices = fu_config_get_blacklist_devices (self->config);
3012 for (guint i = 0; i < blacklisted_devices->len; i++) {
Richard Hughes32684f22017-07-13 09:32:21 +01003013 const gchar *blacklisted_guid = g_ptr_array_index (blacklisted_devices, i);
3014 for (guint j = 0; j < device_guids->len; j++) {
3015 const gchar *device_guid = g_ptr_array_index (device_guids, j);
3016 if (g_strcmp0 (blacklisted_guid, device_guid) == 0) {
Richard Hughes7e77bf32018-05-08 14:56:46 +01003017 g_debug ("%s [%s] is blacklisted [%s], ignoring from %s",
3018 fu_device_get_name (device),
3019 fu_device_get_id (device),
3020 device_guid,
Richard Hughes0a7e7832017-11-22 11:01:13 +00003021 fu_device_get_plugin (device));
Richard Hughes32684f22017-07-13 09:32:21 +01003022 return;
3023 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003024 }
3025 }
3026
Richard Hughesf3077752018-06-27 12:45:06 +01003027 /* if this device is locked get some metadata from AppStream */
3028 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) {
Richard Hughes481aa2a2018-09-18 20:51:46 +01003029 g_autoptr(XbNode) component = fu_engine_store_get_app_by_guids (self->silo, device);
3030 if (component != NULL) {
3031 g_autoptr(XbNode) release = NULL;
3032 release = xb_node_query_first (component,
3033 "releases/release",
3034 NULL);
Richard Hughesf3077752018-06-27 12:45:06 +01003035 if (release != NULL) {
3036 g_autoptr(FwupdRelease) rel = fwupd_release_new ();
Richard Hughes481aa2a2018-09-18 20:51:46 +01003037 fu_engine_set_release_from_appstream (self, rel, component, release);
Richard Hughesf3077752018-06-27 12:45:06 +01003038 fu_device_add_release (device, rel);
3039 }
3040 }
3041 }
3042
Richard Hughes5e447292018-04-27 14:25:54 +01003043 /* adopt any required children, which may or may not already exist */
3044 fu_engine_adopt_children (self, device);
3045
Richard Hughese48351e2018-06-22 12:32:39 +01003046 /* set any alternate objects on the device from the ID */
3047 if (fu_device_get_alternate_id (device) != NULL) {
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003048 g_autoptr(FuDevice) device_alt = NULL;
Richard Hughese48351e2018-06-22 12:32:39 +01003049 device_alt = fu_device_list_get_by_id (self->device_list,
3050 fu_device_get_alternate_id (device),
3051 NULL);
3052 if (device_alt != NULL)
3053 fu_device_set_alternate (device, device_alt);
3054 }
3055
Richard Hughese1fd34d2017-08-24 14:19:51 +01003056 /* notify all plugins about this new device */
3057 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED))
3058 fu_engine_plugin_device_register (self, device);
3059
Richard Hughes9945edb2017-06-19 10:03:55 +01003060 /* match the metadata at this point so clients can tell if the
3061 * device is worthy */
Richard Hughes1cf88d62017-11-07 20:02:15 +00003062 if (fu_engine_is_device_supported (self, device))
3063 fu_device_add_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED);
Richard Hughes9945edb2017-06-19 10:03:55 +01003064
Richard Hughes170c0c12017-11-22 11:26:24 +00003065 /* create new device */
3066 fu_device_list_add (self->device_list, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01003067}
3068
3069static void
3070fu_engine_plugin_device_removed_cb (FuPlugin *plugin,
3071 FuDevice *device,
3072 gpointer user_data)
3073{
3074 FuEngine *self = (FuEngine *) user_data;
Richard Hughes34834102017-11-21 21:55:00 +00003075 FuPlugin *plugin_old;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003076 g_autoptr(FuDevice) device_tmp = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003077 g_autoptr(GError) error = NULL;
3078
Richard Hughes40127542018-01-12 20:25:55 +00003079 device_tmp = fu_device_list_get_by_id (self->device_list,
3080 fu_device_get_id (device),
3081 &error);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003082 if (device_tmp == NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003083 g_debug ("%s", error->message);
3084 return;
3085 }
3086
Richard Hughes34834102017-11-21 21:55:00 +00003087 /* get the plugin */
Richard Hughese7e95452017-11-22 09:05:53 +00003088 plugin_old = fu_plugin_list_find_by_name (self->plugin_list,
Richard Hughes0a7e7832017-11-22 11:01:13 +00003089 fu_device_get_plugin (device),
Richard Hughese7e95452017-11-22 09:05:53 +00003090 &error);
Richard Hughes34834102017-11-21 21:55:00 +00003091 if (plugin_old == NULL) {
3092 g_debug ("%s", error->message);
3093 return;
3094 }
3095
Richard Hughes9945edb2017-06-19 10:03:55 +01003096 /* check this came from the same plugin */
3097 if (g_strcmp0 (fu_plugin_get_name (plugin),
Richard Hughes34834102017-11-21 21:55:00 +00003098 fu_plugin_get_name (plugin_old)) != 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003099 g_debug ("ignoring duplicate removal from %s",
3100 fu_plugin_get_name (plugin));
3101 return;
3102 }
3103
3104 /* make the UI update */
Richard Hughes0a7e7832017-11-22 11:01:13 +00003105 fu_device_list_remove (self->device_list, device);
Richard Hughes9945edb2017-06-19 10:03:55 +01003106 fu_engine_emit_changed (self);
3107}
3108
Richard Hughes9945edb2017-06-19 10:03:55 +01003109static gboolean
3110fu_engine_recoldplug_delay_cb (gpointer user_data)
3111{
3112 FuEngine *self = (FuEngine *) user_data;
3113 g_debug ("performing a recoldplug");
Richard Hughes2de8f132018-01-17 09:12:02 +00003114 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003115 self->coldplug_id = 0;
3116 return FALSE;
3117}
3118
3119static void
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003120fu_engine_udev_device_add (FuEngine *self, GUdevDevice *udev_device)
3121{
3122 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes23170a82018-08-29 09:11:52 +01003123 const gchar *plugin_name;
Richard Hughesff704412018-09-04 11:28:32 +01003124 g_autoptr(FuUdevDevice) device = fu_udev_device_new (udev_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01003125 g_autoptr(GError) error_local = NULL;
3126
3127 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01003128 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
3129 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01003130 g_warning ("failed to probe device %s: %s",
3131 g_udev_device_get_sysfs_path (udev_device),
3132 error_local->message);
3133 return;
3134 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003135
Richard Hughes23170a82018-08-29 09:11:52 +01003136 /* can be specified using a quirk */
Richard Hughesff704412018-09-04 11:28:32 +01003137 plugin_name = fu_device_get_plugin (FU_DEVICE (device));
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003138 if (plugin_name != NULL) {
3139 g_autoptr(GError) error = NULL;
3140 FuPlugin *plugin = fu_plugin_list_find_by_name (self->plugin_list,
3141 plugin_name, &error);
3142 if (plugin == NULL) {
3143 g_warning ("failed to find specified plugin %s: %s",
3144 plugin_name, error->message);
3145 return;
3146 }
Richard Hughesff704412018-09-04 11:28:32 +01003147 if (!fu_plugin_runner_udev_device_added (plugin, device, &error)) {
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003148 g_warning ("failed to add udev device %s: %s",
3149 g_udev_device_get_sysfs_path (udev_device),
3150 error->message);
3151 }
3152 return;
3153 }
3154
3155 /* call into each plugin */
3156 g_debug ("no plugin specified for udev device %s",
3157 g_udev_device_get_sysfs_path (udev_device));
3158 for (guint j = 0; j < plugins->len; j++) {
3159 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
3160 g_autoptr(GError) error = NULL;
3161
3162 /* skipping plugin as requires quirk */
3163 if (fu_plugin_has_rule (plugin_tmp,
3164 FU_PLUGIN_RULE_REQUIRES_QUIRK,
3165 FU_QUIRKS_PLUGIN)) {
3166 continue;
3167 }
3168
3169 /* run all plugins */
Richard Hughesff704412018-09-04 11:28:32 +01003170 if (!fu_plugin_runner_udev_device_added (plugin_tmp, device, &error)) {
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003171 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
3172 g_debug ("%s ignoring: %s",
3173 fu_plugin_get_name (plugin_tmp),
3174 error->message);
3175 continue;
3176 }
3177 g_warning ("%s failed to add udev device %s: %s",
3178 fu_plugin_get_name (plugin_tmp),
3179 g_udev_device_get_sysfs_path (udev_device),
3180 error->message);
3181 }
3182 }
3183}
3184
3185static void
3186fu_engine_udev_device_remove (FuEngine *self, GUdevDevice *udev_device)
3187{
3188 g_autoptr(GPtrArray) devices = NULL;
3189
3190 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01003191 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003192 for (guint i = 0; i < devices->len; i++) {
3193 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01003194 if (!FU_IS_UDEV_DEVICE (device))
3195 continue;
3196 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
3197 g_udev_device_get_sysfs_path (udev_device)) == 0) {
3198 g_debug ("auto-removing GUdevDevice");
3199 fu_device_list_remove (self->device_list, device);
3200 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003201 }
3202}
3203
3204static void
3205fu_engine_udev_device_changed (FuEngine *self, GUdevDevice *udev_device)
3206{
3207 g_autoptr(GPtrArray) devices = NULL;
3208
3209 /* emit changed on any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01003210 devices = fu_device_list_get_all (self->device_list);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003211 for (guint i = 0; i < devices->len; i++) {
3212 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01003213 if (!FU_IS_UDEV_DEVICE (device))
3214 continue;
3215 if (g_strcmp0 (fu_udev_device_get_sysfs_path (FU_UDEV_DEVICE (device)),
3216 g_udev_device_get_sysfs_path (udev_device)) == 0) {
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003217 fu_udev_device_emit_changed (FU_UDEV_DEVICE (device));
Richard Hughesc125ec02018-09-05 19:35:17 +01003218 }
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003219 }
3220}
3221
3222static void
3223fu_engine_enumerate_udev (FuEngine *self)
3224{
3225 /* get all devices of class */
3226 for (guint i = 0; i < self->udev_subsystems->len; i++) {
3227 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
3228 GList *devices = g_udev_client_query_by_subsystem (self->gudev_client,
3229 subsystem);
3230 g_debug ("%u devices with subsystem %s",
3231 g_list_length (devices), subsystem);
3232 for (GList *l = devices; l != NULL; l = l->next) {
3233 GUdevDevice *udev_device = l->data;
3234 fu_engine_udev_device_add (self, udev_device);
3235 }
3236 g_list_foreach (devices, (GFunc) g_object_unref, NULL);
3237 g_list_free (devices);
3238 }
3239}
3240
3241static void
Richard Hughes9945edb2017-06-19 10:03:55 +01003242fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self)
3243{
3244 if (self->coldplug_running) {
3245 g_warning ("coldplug already running, cannot recoldplug");
3246 return;
3247 }
Richard Hughes5b5f6552018-05-18 10:22:39 +01003248 if (self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) {
3249 g_debug ("doing direct recoldplug");
3250 fu_engine_plugins_coldplug (self, TRUE);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003251 fu_engine_enumerate_udev (self);
Richard Hughes5b5f6552018-05-18 10:22:39 +01003252 return;
3253 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003254 g_debug ("scheduling a recoldplug");
3255 if (self->coldplug_id != 0)
3256 g_source_remove (self->coldplug_id);
3257 self->coldplug_id = g_timeout_add (1500, fu_engine_recoldplug_delay_cb, self);
3258}
3259
3260static void
3261fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngine *self)
3262{
3263 self->coldplug_delay = MAX (self->coldplug_delay, duration);
3264 g_debug ("got coldplug delay of %ums, global maximum is now %ums",
3265 duration, self->coldplug_delay);
3266}
3267
Richard Hughese7e95452017-11-22 09:05:53 +00003268/* for the self tests to use */
Richard Hughes34834102017-11-21 21:55:00 +00003269void
3270fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin)
3271{
Richard Hughese7e95452017-11-22 09:05:53 +00003272 fu_plugin_list_add (self->plugin_list, plugin);
Richard Hughes34834102017-11-21 21:55:00 +00003273}
3274
Richard Hughes9945edb2017-06-19 10:03:55 +01003275static gboolean
Richard Hughes1e456bc2018-05-10 20:16:16 +01003276fu_engine_is_plugin_name_blacklisted (FuEngine *self, const gchar *name)
3277{
3278 GPtrArray *blacklist = fu_config_get_blacklist_plugins (self->config);
3279 for (guint i = 0; i < blacklist->len; i++) {
3280 const gchar *name_tmp = g_ptr_array_index (blacklist, i);
3281 if (g_strcmp0 (name_tmp, name) == 0)
3282 return TRUE;
3283 }
3284 return FALSE;
3285}
3286
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003287static gboolean
3288fu_engine_is_plugin_name_whitelisted (FuEngine *self, const gchar *name)
3289{
3290 if (self->plugin_filter->len == 0)
3291 return TRUE;
3292 for (guint i = 0; i < self->plugin_filter->len; i++) {
3293 const gchar *name_tmp = g_ptr_array_index (self->plugin_filter, i);
3294 if (fnmatch (name_tmp, name, 0) == 0)
3295 return TRUE;
3296 }
3297 return FALSE;
3298}
3299
3300void
3301fu_engine_add_plugin_filter (FuEngine *self, const gchar *plugin_glob)
3302{
3303 g_return_if_fail (FU_IS_ENGINE (self));
3304 g_return_if_fail (plugin_glob != NULL);
3305 g_ptr_array_add (self->plugin_filter, g_strdup (plugin_glob));
3306}
3307
Richard Hughes8c71a3f2018-05-22 19:19:52 +01003308gboolean
Richard Hughes9945edb2017-06-19 10:03:55 +01003309fu_engine_load_plugins (FuEngine *self, GError **error)
3310{
3311 const gchar *fn;
3312 g_autoptr(GDir) dir = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +01003313 g_autofree gchar *plugin_path = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00003314
Richard Hughes9945edb2017-06-19 10:03:55 +01003315 /* search */
Richard Hughes4be17d12018-05-30 20:36:29 +01003316 plugin_path = fu_common_get_path (FU_PATH_KIND_PLUGINDIR_PKG);
3317 dir = g_dir_open (plugin_path, 0, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01003318 if (dir == NULL)
3319 return FALSE;
3320 while ((fn = g_dir_read_name (dir)) != NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +01003321 g_autofree gchar *filename = NULL;
Richard Hughes1e456bc2018-05-10 20:16:16 +01003322 g_autofree gchar *name = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01003323 g_autoptr(FuPlugin) plugin = NULL;
3324 g_autoptr(GError) error_local = NULL;
3325
3326 /* ignore non-plugins */
3327 if (!g_str_has_suffix (fn, ".so"))
3328 continue;
3329
Richard Hughes1e456bc2018-05-10 20:16:16 +01003330 /* is blacklisted */
3331 name = fu_plugin_guess_name_from_fn (fn);
3332 if (name == NULL)
3333 continue;
3334 if (fu_engine_is_plugin_name_blacklisted (self, name)) {
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003335 g_debug ("plugin %s is blacklisted", name);
3336 continue;
3337 }
3338 if (!fu_engine_is_plugin_name_whitelisted (self, name)) {
3339 g_debug ("plugin %s is not whitelisted", name);
Richard Hughes1e456bc2018-05-10 20:16:16 +01003340 continue;
3341 }
3342
Richard Hughes9945edb2017-06-19 10:03:55 +01003343 /* open module */
Richard Hughes4be17d12018-05-30 20:36:29 +01003344 filename = g_build_filename (plugin_path, fn, NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01003345 plugin = fu_plugin_new ();
Richard Hughes1e456bc2018-05-10 20:16:16 +01003346 fu_plugin_set_name (plugin, name);
Richard Hughes9945edb2017-06-19 10:03:55 +01003347 fu_plugin_set_usb_context (plugin, self->usb_ctx);
3348 fu_plugin_set_hwids (plugin, self->hwids);
Richard Hughes49e5e052017-09-03 12:15:41 +01003349 fu_plugin_set_smbios (plugin, self->smbios);
Richard Hughes1354ea92017-09-19 15:58:31 +01003350 fu_plugin_set_supported (plugin, self->supported_guids);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003351 fu_plugin_set_udev_subsystems (plugin, self->udev_subsystems);
Richard Hughes9c028f02017-10-28 21:14:28 +01003352 fu_plugin_set_quirks (plugin, self->quirks);
Richard Hughes0eb123b2018-04-19 12:00:04 +01003353 fu_plugin_set_runtime_versions (plugin, self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01003354 fu_plugin_set_compile_versions (plugin, self->compile_versions);
Richard Hughes9945edb2017-06-19 10:03:55 +01003355 g_debug ("adding plugin %s", filename);
Richard Hughes8c71a3f2018-05-22 19:19:52 +01003356
3357 /* if loaded from fu_engine_load() open the plugin */
3358 if (self->usb_ctx != NULL) {
3359 if (!fu_plugin_open (plugin, filename, &error_local)) {
3360 g_warning ("failed to open plugin %s: %s",
3361 filename, error_local->message);
3362 continue;
3363 }
Richard Hughes9945edb2017-06-19 10:03:55 +01003364 }
3365
Richard Hughes1e456bc2018-05-10 20:16:16 +01003366 /* self disabled */
Richard Hughes9945edb2017-06-19 10:03:55 +01003367 if (!fu_plugin_get_enabled (plugin)) {
Richard Hughes1e456bc2018-05-10 20:16:16 +01003368 g_debug ("%s self disabled",
Richard Hughes9945edb2017-06-19 10:03:55 +01003369 fu_plugin_get_name (plugin));
3370 continue;
3371 }
3372
3373 /* watch for changes */
3374 g_signal_connect (plugin, "device-added",
3375 G_CALLBACK (fu_engine_plugin_device_added_cb),
3376 self);
3377 g_signal_connect (plugin, "device-removed",
3378 G_CALLBACK (fu_engine_plugin_device_removed_cb),
3379 self);
Richard Hughese1fd34d2017-08-24 14:19:51 +01003380 g_signal_connect (plugin, "device-register",
3381 G_CALLBACK (fu_engine_plugin_device_register_cb),
3382 self);
Richard Hughes9945edb2017-06-19 10:03:55 +01003383 g_signal_connect (plugin, "recoldplug",
3384 G_CALLBACK (fu_engine_plugin_recoldplug_cb),
3385 self);
3386 g_signal_connect (plugin, "set-coldplug-delay",
3387 G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb),
3388 self);
3389
3390 /* add */
Richard Hughese7e95452017-11-22 09:05:53 +00003391 fu_plugin_list_add (self->plugin_list, plugin);
Richard Hughes9945edb2017-06-19 10:03:55 +01003392 }
3393
Richard Hughese7e95452017-11-22 09:05:53 +00003394 /* depsolve into the correct order */
3395 if (!fu_plugin_list_depsolve (self->plugin_list, error))
3396 return FALSE;
Richard Hughes08a37992017-09-12 12:57:43 +01003397
Richard Hughese7e95452017-11-22 09:05:53 +00003398 /* success */
Richard Hughes9945edb2017-06-19 10:03:55 +01003399 return TRUE;
3400}
3401
Richard Hughes9945edb2017-06-19 10:03:55 +01003402static gboolean
3403fu_engine_cleanup_state (GError **error)
3404{
3405 const gchar *filenames[] = {
3406 "/var/cache/app-info/xmls/fwupd-verify.xml",
3407 "/var/cache/app-info/xmls/fwupd.xml",
3408 NULL };
3409 for (guint i = 0; filenames[i] != NULL; i++) {
3410 g_autoptr(GFile) file = g_file_new_for_path (filenames[i]);
3411 if (g_file_query_exists (file, NULL)) {
3412 if (!g_file_delete (file, NULL, error))
3413 return FALSE;
3414 }
3415 }
3416 return TRUE;
3417}
3418
Richard Hughesc7bbbc22018-01-02 22:22:25 +00003419guint64
3420fu_engine_get_archive_size_max (FuEngine *self)
3421{
3422 return fu_config_get_archive_size_max (self->config);
3423}
3424
Richard Hughes104f6512017-11-24 11:44:57 +00003425static void
3426fu_engine_usb_device_removed_cb (GUsbContext *ctx,
3427 GUsbDevice *usb_device,
3428 FuEngine *self)
3429{
3430 g_autoptr(GPtrArray) devices = NULL;
3431
3432 /* go through each device and remove any that match */
Richard Hughesc125ec02018-09-05 19:35:17 +01003433 devices = fu_device_list_get_all (self->device_list);
Richard Hughes104f6512017-11-24 11:44:57 +00003434 for (guint i = 0; i < devices->len; i++) {
3435 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughesc125ec02018-09-05 19:35:17 +01003436 if (!FU_IS_USB_DEVICE (device))
3437 continue;
3438 if (g_strcmp0 (fu_usb_device_get_platform_id (FU_USB_DEVICE (device)),
3439 g_usb_device_get_platform_id (usb_device)) == 0) {
3440 g_debug ("auto-removing GUsbDevice");
3441 fu_device_list_remove (self->device_list, device);
3442 }
Richard Hughes104f6512017-11-24 11:44:57 +00003443 }
3444}
3445
3446static void
3447fu_engine_usb_device_added_cb (GUsbContext *ctx,
3448 GUsbDevice *usb_device,
3449 FuEngine *self)
3450{
3451 GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
Richard Hughes23170a82018-08-29 09:11:52 +01003452 const gchar *plugin_name;
Richard Hughesff704412018-09-04 11:28:32 +01003453 g_autoptr(FuUsbDevice) device = fu_usb_device_new (usb_device);
Richard Hughes6dec4012018-08-27 19:13:00 +01003454 g_autoptr(GError) error_local = NULL;
3455
3456 /* add any extra quirks */
Richard Hughesff704412018-09-04 11:28:32 +01003457 fu_device_set_quirks (FU_DEVICE (device), self->quirks);
3458 if (!fu_device_probe (FU_DEVICE (device), &error_local)) {
Richard Hughes6dec4012018-08-27 19:13:00 +01003459 g_warning ("failed to probe device %s: %s",
Richard Hughesc125ec02018-09-05 19:35:17 +01003460 fu_device_get_physical_id (FU_DEVICE (device)),
Richard Hughes6dec4012018-08-27 19:13:00 +01003461 error_local->message);
3462 return;
3463 }
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01003464
Richard Hughes23170a82018-08-29 09:11:52 +01003465 /* can be specified using a quirk */
3466 plugin_name = fu_device_get_plugin (device);
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01003467 if (plugin_name != NULL) {
3468 g_autoptr(GError) error = NULL;
3469 FuPlugin *plugin = fu_plugin_list_find_by_name (self->plugin_list,
3470 plugin_name, &error);
3471 if (plugin == NULL) {
3472 g_warning ("failed to find specified plugin %s: %s",
3473 plugin_name, error->message);
3474 return;
3475 }
Richard Hughesff704412018-09-04 11:28:32 +01003476 if (!fu_plugin_runner_usb_device_added (plugin, device, &error)) {
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01003477 g_warning ("failed to add USB device %04x:%04x: %s",
3478 g_usb_device_get_vid (usb_device),
3479 g_usb_device_get_pid (usb_device),
3480 error->message);
3481 }
3482 return;
3483 }
Richard Hughes104f6512017-11-24 11:44:57 +00003484
3485 /* call into each plugin */
Richard Hughes6dbe8fe2018-06-28 12:16:26 +01003486 g_debug ("no plugin specified for USB device %04x:%04x",
3487 g_usb_device_get_vid (usb_device),
3488 g_usb_device_get_pid (usb_device));
Richard Hughes104f6512017-11-24 11:44:57 +00003489 for (guint j = 0; j < plugins->len; j++) {
3490 FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
3491 g_autoptr(GError) error = NULL;
Richard Hughesa39d6a52018-06-28 12:17:15 +01003492
3493 /* skipping plugin as requires quirk */
3494 if (fu_plugin_has_rule (plugin_tmp,
3495 FU_PLUGIN_RULE_REQUIRES_QUIRK,
3496 FU_QUIRKS_PLUGIN)) {
3497 continue;
3498 }
3499
3500 /* create a device, then probe */
Richard Hughesff704412018-09-04 11:28:32 +01003501 if (!fu_plugin_runner_usb_device_added (plugin_tmp, device, &error)) {
Richard Hughesc3476d52017-11-30 09:32:35 +00003502 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
Richard Hughesa39d6a52018-06-28 12:17:15 +01003503 g_debug ("%s ignoring: %s",
3504 fu_plugin_get_name (plugin_tmp),
3505 error->message);
Richard Hughesc3476d52017-11-30 09:32:35 +00003506 continue;
3507 }
Richard Hughesa39d6a52018-06-28 12:17:15 +01003508 g_warning ("%s failed to add USB device %04x:%04x: %s",
3509 fu_plugin_get_name (plugin_tmp),
Richard Hughese598fc32018-04-19 20:11:46 +01003510 g_usb_device_get_vid (usb_device),
3511 g_usb_device_get_pid (usb_device),
3512 error->message);
Richard Hughesc3476d52017-11-30 09:32:35 +00003513 }
Richard Hughes104f6512017-11-24 11:44:57 +00003514 }
3515}
3516
Richard Hughesf77d7062017-11-27 12:06:36 +00003517
3518static void
3519fu_engine_load_quirks (FuEngine *self)
3520{
Richard Hughesf77d7062017-11-27 12:06:36 +00003521 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00003522 if (!fu_quirks_load (self->quirks, &error))
3523 g_warning ("Failed to load quirks: %s", error->message);
3524}
3525
3526static void
3527fu_engine_load_smbios (FuEngine *self)
3528{
Richard Hughesf77d7062017-11-27 12:06:36 +00003529 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00003530 if (!fu_smbios_setup (self->smbios, &error))
3531 g_warning ("Failed to load SMBIOS: %s", error->message);
3532}
3533
3534static void
3535fu_engine_load_hwids (FuEngine *self)
3536{
Richard Hughesf77d7062017-11-27 12:06:36 +00003537 g_autoptr(GError) error = NULL;
Richard Hughesf77d7062017-11-27 12:06:36 +00003538 if (!fu_hwids_setup (self->hwids, self->smbios, &error))
3539 g_warning ("Failed to load HWIDs: %s", error->message);
3540}
3541
Richard Hughesa2f8e452018-01-11 10:11:17 +00003542static gboolean
3543fu_engine_update_history_device (FuEngine *self, FuDevice *dev_history, GError **error)
3544{
Richard Hughesa2f8e452018-01-11 10:11:17 +00003545 FuPlugin *plugin;
3546 FwupdRelease *rel_history;
Richard Hughesf2711422018-01-12 09:49:05 +00003547 g_autofree gchar *btime = NULL;
Richard Hughes5a9a6bd2018-09-10 10:02:20 +01003548 g_autoptr(FuDevice) dev = NULL;
Richard Hughesa2f8e452018-01-11 10:11:17 +00003549
3550 /* is in the device list */
Richard Hughes40127542018-01-12 20:25:55 +00003551 dev = fu_device_list_get_by_id (self->device_list,
3552 fu_device_get_id (dev_history),
3553 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00003554 if (dev == NULL)
3555 return FALSE;
3556
3557 /* does the installed version match what we tried to install
3558 * before fwupd was restarted */
3559 rel_history = fu_device_get_release_default (dev_history);
3560 if (rel_history == NULL) {
3561 g_set_error_literal (error,
3562 FWUPD_ERROR,
3563 FWUPD_ERROR_INTERNAL,
3564 "no release for history FuDevice");
3565 return FALSE;
3566 }
3567
Richard Hughesf2711422018-01-12 09:49:05 +00003568 /* is this the same boot time as when we scheduled the update,
3569 * i.e. has fwupd been restarted before we rebooted */
3570 btime = fu_engine_get_boot_time ();
3571 if (g_strcmp0 (fwupd_release_get_metadata_item (rel_history, "BootTime"),
3572 btime) == 0) {
3573 g_debug ("service restarted, but no reboot has taken place");
3574 return TRUE;
3575 }
3576
Richard Hughesa2f8e452018-01-11 10:11:17 +00003577 /* the system is running with the new firmware version */
3578 if (g_strcmp0 (fu_device_get_version (dev),
Richard Hughes3eb2a002018-01-11 20:18:15 +00003579 fwupd_release_get_version (rel_history)) == 0) {
Richard Hughesa2f8e452018-01-11 10:11:17 +00003580 g_debug ("installed version %s matching history %s",
3581 fu_device_get_version (dev),
3582 fwupd_release_get_version (rel_history));
Richard Hughesc0cd0232018-01-31 15:02:00 +00003583 fu_device_set_update_state (dev_history, FWUPD_UPDATE_STATE_SUCCESS);
3584 return fu_history_modify_device (self->history, dev_history,
3585 FU_HISTORY_FLAGS_MATCH_NEW_VERSION,
3586 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00003587 }
3588
Richard Hughes7e070c92018-01-12 16:50:05 +00003589 /* does the plugin know the update failure */
Richard Hughesa2f8e452018-01-11 10:11:17 +00003590 plugin = fu_plugin_list_find_by_name (self->plugin_list,
3591 fu_device_get_plugin (dev),
3592 error);
3593 if (plugin == NULL)
3594 return FALSE;
Richard Hughesa2f8e452018-01-11 10:11:17 +00003595 if (!fu_plugin_runner_get_results (plugin, dev, error))
3596 return FALSE;
Richard Hughes7e070c92018-01-12 16:50:05 +00003597
3598 /* the plugin either can't tell us the error, or doesn't know itself */
3599 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED) {
3600 g_debug ("falling back to generic failure");
Richard Hughesc0cd0232018-01-31 15:02:00 +00003601 fu_device_set_update_error (dev_history, "failed to run update on reboot");
Richard Hughesa2f8e452018-01-11 10:11:17 +00003602 }
Richard Hughes7e070c92018-01-12 16:50:05 +00003603
3604 /* update the state in the database */
Richard Hughesc0cd0232018-01-31 15:02:00 +00003605 fu_device_set_update_error (dev_history, fu_device_get_update_error (dev));
3606 return fu_history_modify_device (self->history, dev_history,
3607 FU_HISTORY_FLAGS_MATCH_OLD_VERSION,
3608 error);
Richard Hughesa2f8e452018-01-11 10:11:17 +00003609}
3610
3611static gboolean
3612fu_engine_update_history_database (FuEngine *self, GError **error)
3613{
3614 g_autoptr(GPtrArray) devices = NULL;
3615
3616 /* get any devices */
3617 devices = fu_history_get_devices (self->history, error);
3618 if (devices == NULL)
3619 return FALSE;
3620 for (guint i = 0; i < devices->len; i++) {
3621 FuDevice *dev = g_ptr_array_index (devices, i);
3622 g_autoptr(GError) error_local = NULL;
3623
3624 /* not in the required state */
3625 if (fu_device_get_update_state (dev) != FWUPD_UPDATE_STATE_NEEDS_REBOOT)
3626 continue;
3627
3628 /* try to save the new update-state, but ignoring any error */
3629 if (!fu_engine_update_history_device (self, dev, &error_local))
3630 g_warning ("%s", error_local->message);
3631 }
3632 return TRUE;
3633}
3634
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003635static void
3636fu_engine_udev_uevent_cb (GUdevClient *gudev_client,
3637 const gchar *action,
3638 GUdevDevice *udev_device,
3639 FuEngine *self)
3640{
3641 if (g_strcmp0 (action, "add") == 0) {
3642 fu_engine_udev_device_add (self, udev_device);
3643 return;
3644 }
3645 if (g_strcmp0 (action, "remove") == 0) {
3646 fu_engine_udev_device_remove (self, udev_device);
3647 return;
3648 }
3649 if (g_strcmp0 (action, "change") == 0) {
3650 fu_engine_udev_device_changed (self, udev_device);
3651 return;
3652 }
3653}
3654
Richard Hughes9945edb2017-06-19 10:03:55 +01003655/**
3656 * fu_engine_load:
3657 * @self: A #FuEngine
3658 * @error: A #GError, or %NULL
3659 *
3660 * Load the firmware update engine so it is ready for use.
3661 *
3662 * Returns: %TRUE for success
3663 **/
3664gboolean
3665fu_engine_load (FuEngine *self, GError **error)
3666{
3667 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
3668 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
3669
3670 /* read config file */
3671 if (!fu_config_load (self->config, error)) {
3672 g_prefix_error (error, "Failed to load config: ");
3673 return FALSE;
3674 }
3675
Richard Hughesf77d7062017-11-27 12:06:36 +00003676 /* load quirks, SMBIOS and the hwids */
3677 fu_engine_load_smbios (self);
3678 fu_engine_load_hwids (self);
3679 fu_engine_load_quirks (self);
Richard Hughes9c028f02017-10-28 21:14:28 +01003680
Richard Hughes9945edb2017-06-19 10:03:55 +01003681 /* load AppStream metadata */
Richard Hughes9945edb2017-06-19 10:03:55 +01003682 if (!fu_engine_load_metadata_store (self, error)) {
3683 g_prefix_error (error, "Failed to load AppStream data: ");
3684 return FALSE;
3685 }
3686
3687 /* set shared USB context */
3688 self->usb_ctx = g_usb_context_new (error);
3689 if (self->usb_ctx == NULL) {
3690 g_prefix_error (error, "Failed to get USB context: ");
3691 return FALSE;
3692 }
3693
Richard Hughes9945edb2017-06-19 10:03:55 +01003694 /* delete old data files */
3695 if (!fu_engine_cleanup_state (error)) {
3696 g_prefix_error (error, "Failed to clean up: ");
3697 return FALSE;
3698 }
3699
3700 /* load plugin */
3701 if (!fu_engine_load_plugins (self, error)) {
3702 g_prefix_error (error, "Failed to load plugins: ");
3703 return FALSE;
3704 }
3705
Richard Hughes170c0c12017-11-22 11:26:24 +00003706 /* watch the device list for updates and proxy */
3707 g_signal_connect (self->device_list, "added",
3708 G_CALLBACK (fu_engine_device_added_cb),
3709 self);
3710 g_signal_connect (self->device_list, "removed",
3711 G_CALLBACK (fu_engine_device_removed_cb),
3712 self);
3713 g_signal_connect (self->device_list, "changed",
3714 G_CALLBACK (fu_engine_device_changed_cb),
3715 self);
3716
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003717 /* udev watches can only be set up in _init() so set up client now */
3718 if (self->udev_subsystems->len > 0) {
Richard Hugheseffcc7a2018-08-31 14:32:07 +01003719 g_auto(GStrv) udev_subsystems = g_new0 (gchar *, self->udev_subsystems->len + 1);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003720 for (guint i = 0; i < self->udev_subsystems->len; i++) {
3721 const gchar *subsystem = g_ptr_array_index (self->udev_subsystems, i);
3722 udev_subsystems[i] = g_strdup (subsystem);
3723 }
3724 self->gudev_client = g_udev_client_new ((const gchar * const *) udev_subsystems);
3725 g_signal_connect (self->gudev_client, "uevent",
3726 G_CALLBACK (fu_engine_udev_uevent_cb), self);
3727 }
3728
Mario Limonciello6463f312018-08-09 13:28:46 -05003729 fu_engine_set_status (self, FWUPD_STATUS_LOADING);
3730
Richard Hughes9945edb2017-06-19 10:03:55 +01003731 /* add devices */
3732 fu_engine_plugins_setup (self);
Richard Hughes2de8f132018-01-17 09:12:02 +00003733 fu_engine_plugins_coldplug (self, FALSE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003734
Richard Hughes634e9222017-11-29 15:50:02 +00003735 /* coldplug USB devices */
Richard Hughes104f6512017-11-24 11:44:57 +00003736 g_signal_connect (self->usb_ctx, "device-added",
3737 G_CALLBACK (fu_engine_usb_device_added_cb),
3738 self);
3739 g_signal_connect (self->usb_ctx, "device-removed",
3740 G_CALLBACK (fu_engine_usb_device_removed_cb),
3741 self);
Richard Hughes634e9222017-11-29 15:50:02 +00003742 g_usb_context_enumerate (self->usb_ctx);
Richard Hughes104f6512017-11-24 11:44:57 +00003743
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003744 /* coldplug udev devices */
3745 fu_engine_enumerate_udev (self);
3746
Richard Hughesa2f8e452018-01-11 10:11:17 +00003747 /* update the db for devices that were updated during the reboot */
3748 if (!fu_engine_update_history_database (self, error))
3749 return FALSE;
3750
Mario Limonciello6463f312018-08-09 13:28:46 -05003751 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
3752
Richard Hughes9945edb2017-06-19 10:03:55 +01003753 /* success */
3754 return TRUE;
3755}
3756
3757static void
3758fu_engine_class_init (FuEngineClass *klass)
3759{
3760 GObjectClass *object_class = G_OBJECT_CLASS (klass);
3761 object_class->finalize = fu_engine_finalize;
3762
3763 signals[SIGNAL_CHANGED] =
3764 g_signal_new ("changed",
3765 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3766 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
3767 G_TYPE_NONE, 0);
3768 signals[SIGNAL_DEVICE_ADDED] =
3769 g_signal_new ("device-added",
3770 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3771 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
3772 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
3773 signals[SIGNAL_DEVICE_REMOVED] =
3774 g_signal_new ("device-removed",
3775 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3776 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
3777 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughesa5bb4d82017-06-19 20:22:25 +01003778 signals[SIGNAL_DEVICE_CHANGED] =
3779 g_signal_new ("device-changed",
3780 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3781 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
3782 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughes9945edb2017-06-19 10:03:55 +01003783 signals[SIGNAL_STATUS_CHANGED] =
3784 g_signal_new ("status-changed",
3785 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3786 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
3787 G_TYPE_NONE, 1, G_TYPE_UINT);
3788 signals[SIGNAL_PERCENTAGE_CHANGED] =
3789 g_signal_new ("percentage-changed",
3790 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
3791 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
3792 G_TYPE_NONE, 1, G_TYPE_UINT);
3793}
3794
Richard Hughes0eb123b2018-04-19 12:00:04 +01003795void
3796fu_engine_add_runtime_version (FuEngine *self,
3797 const gchar *component_id,
3798 const gchar *version)
3799{
3800 g_hash_table_insert (self->runtime_versions,
3801 g_strdup (component_id),
3802 g_strdup (version));
3803}
3804
Richard Hughes9945edb2017-06-19 10:03:55 +01003805static void
3806fu_engine_init (FuEngine *self)
3807{
3808 self->percentage = 0;
3809 self->status = FWUPD_STATUS_IDLE;
3810 self->config = fu_config_new ();
Richard Hughes0a7e7832017-11-22 11:01:13 +00003811 self->device_list = fu_device_list_new ();
Richard Hughes49e5e052017-09-03 12:15:41 +01003812 self->smbios = fu_smbios_new ();
Richard Hughesd7704d42017-08-08 20:29:09 +01003813 self->hwids = fu_hwids_new ();
Richard Hughes9c028f02017-10-28 21:14:28 +01003814 self->quirks = fu_quirks_new ();
Richard Hughesbc3a4e12018-01-06 22:41:47 +00003815 self->history = fu_history_new ();
Richard Hughese7e95452017-11-22 09:05:53 +00003816 self->plugin_list = fu_plugin_list_new ();
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003817 self->plugin_filter = g_ptr_array_new_with_free_func (g_free);
Richard Hughes1354ea92017-09-19 15:58:31 +01003818 self->supported_guids = g_ptr_array_new_with_free_func (g_free);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003819 self->udev_subsystems = g_ptr_array_new_with_free_func (g_free);
Richard Hughes0eb123b2018-04-19 12:00:04 +01003820 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 +01003821 self->compile_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
Richard Hughes0eb123b2018-04-19 12:00:04 +01003822
3823 /* add some runtime versions of things the daemon depends on */
3824 fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION);
Richard Hughes2d37c3f2018-08-04 15:18:25 +01003825 fu_engine_add_runtime_version (self, "com.redhat.fwupdate", "12");
Richard Hughes416ade72018-10-10 20:36:53 +01003826 fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", "0.7.14");
Richard Hughes0eb123b2018-04-19 12:00:04 +01003827#if G_USB_CHECK_VERSION(0,3,1)
3828 fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ());
3829#endif
Richard Hughes34e0dab2018-04-20 16:43:00 +01003830
3831 g_hash_table_insert (self->compile_versions,
Richard Hughes2d37c3f2018-08-04 15:18:25 +01003832 g_strdup ("com.redhat.fwupdate"),
3833 g_strdup ("12"));
3834 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01003835 g_strdup ("org.freedesktop.fwupd"),
3836 g_strdup (VERSION));
3837 g_hash_table_insert (self->compile_versions,
Richard Hughes34e0dab2018-04-20 16:43:00 +01003838 g_strdup ("org.freedesktop.gusb"),
3839 g_strdup_printf ("%i.%i.%i",
3840 G_USB_MAJOR_VERSION,
3841 G_USB_MINOR_VERSION,
3842 G_USB_MICRO_VERSION));
3843
Richard Hughes9945edb2017-06-19 10:03:55 +01003844}
3845
3846static void
3847fu_engine_finalize (GObject *obj)
3848{
3849 FuEngine *self = FU_ENGINE (obj);
3850
3851 if (self->usb_ctx != NULL)
3852 g_object_unref (self->usb_ctx);
Richard Hughes481aa2a2018-09-18 20:51:46 +01003853 if (self->silo != NULL)
3854 g_object_unref (self->silo);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003855 if (self->gudev_client != NULL)
3856 g_object_unref (self->gudev_client);
Richard Hughes9945edb2017-06-19 10:03:55 +01003857 if (self->coldplug_id != 0)
3858 g_source_remove (self->coldplug_id);
3859
Richard Hughes9945edb2017-06-19 10:03:55 +01003860 g_object_unref (self->config);
Richard Hughes49e5e052017-09-03 12:15:41 +01003861 g_object_unref (self->smbios);
Richard Hughes9c028f02017-10-28 21:14:28 +01003862 g_object_unref (self->quirks);
Richard Hughesd7704d42017-08-08 20:29:09 +01003863 g_object_unref (self->hwids);
Richard Hughesbc3a4e12018-01-06 22:41:47 +00003864 g_object_unref (self->history);
Richard Hughes0a7e7832017-11-22 11:01:13 +00003865 g_object_unref (self->device_list);
Richard Hughes1354ea92017-09-19 15:58:31 +01003866 g_ptr_array_unref (self->supported_guids);
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003867 g_ptr_array_unref (self->plugin_filter);
Richard Hughes9d6e0e72018-08-24 20:20:17 +01003868 g_ptr_array_unref (self->udev_subsystems);
Richard Hughes0eb123b2018-04-19 12:00:04 +01003869 g_hash_table_unref (self->runtime_versions);
Richard Hughes34e0dab2018-04-20 16:43:00 +01003870 g_hash_table_unref (self->compile_versions);
Mario Limonciello3f9a1c12018-06-06 14:06:40 -05003871 g_object_unref (self->plugin_list);
Richard Hughes9945edb2017-06-19 10:03:55 +01003872
3873 G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj);
3874}
3875
3876FuEngine *
Richard Hughes5b5f6552018-05-18 10:22:39 +01003877fu_engine_new (FuAppFlags app_flags)
Richard Hughes9945edb2017-06-19 10:03:55 +01003878{
3879 FuEngine *self;
3880 self = g_object_new (FU_TYPE_ENGINE, NULL);
Richard Hughes5b5f6552018-05-18 10:22:39 +01003881 self->app_flags = app_flags;
Richard Hughes9945edb2017-06-19 10:03:55 +01003882 return FU_ENGINE (self);
3883}