blob: 1704195a0a68617289e8351575d505cdc6e4c096 [file] [log] [blame]
Richard Hughes9945edb2017-06-19 10:03:55 +01001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2015-2017 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU Lesser General Public License Version 2.1
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "config.h"
23
24#include <appstream-glib.h>
25#include <gio/gio.h>
26#include <gio/gunixinputstream.h>
27#include <glib-object.h>
28
29#include "fwupd-common-private.h"
30#include "fwupd-enums-private.h"
31#include "fwupd-error.h"
32#include "fwupd-release-private.h"
33#include "fwupd-remote-private.h"
34#include "fwupd-resources.h"
35
Richard Hughes943d2c92017-06-21 09:04:39 +010036#include "fu-common.h"
Richard Hughes9945edb2017-06-19 10:03:55 +010037#include "fu-config.h"
38#include "fu-debug.h"
39#include "fu-device.h"
40#include "fu-engine.h"
41#include "fu-hwids.h"
42#include "fu-keyring.h"
43#include "fu-pending.h"
44#include "fu-plugin.h"
45#include "fu-plugin-private.h"
46#include "fu-quirks.h"
47
Richard Hughes7403dc52017-08-10 15:34:10 +010048#ifdef ENABLE_GPG
49#include "fu-keyring-gpg.h"
50#endif
51#ifdef ENABLE_PKCS7
52#include "fu-keyring-pkcs7.h"
53#endif
54
Richard Hughes9945edb2017-06-19 10:03:55 +010055static void fu_engine_finalize (GObject *obj);
56
57struct _FuEngine
58{
59 GObject parent_instance;
60 GUsbContext *usb_ctx;
61 FuConfig *config;
62 GPtrArray *devices; /* of FuDeviceItem */
63 FwupdStatus status;
64 guint percentage;
65 FuPending *pending;
66 AsProfile *profile;
67 AsStore *store;
68 gboolean coldplug_running;
69 guint coldplug_id;
70 guint coldplug_delay;
71 GPtrArray *plugins; /* of FuPlugin */
72 GHashTable *plugins_hash; /* of name : FuPlugin */
Richard Hughesd7704d42017-08-08 20:29:09 +010073 FuHwids *hwids;
Richard Hughes9945edb2017-06-19 10:03:55 +010074};
75
76enum {
77 SIGNAL_CHANGED,
78 SIGNAL_DEVICE_ADDED,
79 SIGNAL_DEVICE_REMOVED,
80 SIGNAL_DEVICE_CHANGED,
81 SIGNAL_STATUS_CHANGED,
82 SIGNAL_PERCENTAGE_CHANGED,
83 SIGNAL_LAST
84};
85
86static guint signals[SIGNAL_LAST] = { 0 };
87
88G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT)
89
90typedef struct {
91 FuDevice *device;
92 FuPlugin *plugin;
93} FuDeviceItem;
94
95static gboolean fu_engine_get_updates_item_update (FuEngine *self, FuDeviceItem *item);
96
97static void
98fu_engine_emit_changed (FuEngine *self)
99{
100 g_signal_emit (self, signals[SIGNAL_CHANGED], 0);
101}
102
103static void
104fu_engine_emit_device_added (FuEngine *self, FuDevice *device)
105{
106 g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device);
107}
108
109static void
110fu_engine_emit_device_removed (FuEngine *self, FuDevice *device)
111{
112 g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device);
113}
114
115static void
116fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
117{
118 g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
119}
120
121/**
122 * fu_engine_get_status:
123 * @self: A #FuEngine
124 * @device_id: A device ID
125 * @error: A #GError, or %NULL
126 *
127 * Gets the current engine status.
128 *
129 * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING
130 **/
131FwupdStatus
132fu_engine_get_status (FuEngine *self)
133{
134 g_return_val_if_fail (FU_IS_ENGINE (self), 0);
135 return self->status;
136}
137
138/**
139 * fu_engine_profile_dump:
140 * @self: A #FuEngine
141 *
142 * Dumps the engine profiling state to the console.
143 **/
144void
145fu_engine_profile_dump (FuEngine *self)
146{
147 g_return_if_fail (FU_IS_ENGINE (self));
148 as_profile_dump (self->profile);
149}
150
151static void
152fu_engine_set_status (FuEngine *self, FwupdStatus status)
153{
154 if (self->status == status)
155 return;
156 self->status = status;
157
158 /* emit changed */
159 g_debug ("Emitting PropertyChanged('Status'='%s')",
160 fwupd_status_to_string (status));
161 g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status);
162}
163
164static void
165fu_engine_set_percentage (FuEngine *self, guint percentage)
166{
167 if (self->percentage == percentage)
168 return;
169 self->percentage = percentage;
170
171 /* emit changed */
172 g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage);
173}
174
175static void
176fu_engine_item_free (FuDeviceItem *item)
177{
178 g_object_unref (item->device);
179 g_object_unref (item->plugin);
180 g_free (item);
181}
182
183static FuDeviceItem *
184fu_engine_get_item_by_id (FuEngine *self, const gchar *device_id, GError **error)
185{
186 for (guint i = 0; i < self->devices->len; i++) {
187 FuDeviceItem *item = g_ptr_array_index (self->devices, i);
188 if (g_strcmp0 (fu_device_get_id (item->device), device_id) == 0)
189 return item;
190 if (g_strcmp0 (fu_device_get_equivalent_id (item->device), device_id) == 0)
191 return item;
192 }
193 g_set_error (error,
194 FWUPD_ERROR,
195 FWUPD_ERROR_INVALID_FILE,
196 "Device %s was not found",
197 device_id);
198 return NULL;
199}
200
201static FuDeviceItem *
202fu_engine_get_item_by_guid (FuEngine *self, const gchar *guid)
203{
204 for (guint i = 0; i < self->devices->len; i++) {
205 FuDeviceItem *item = g_ptr_array_index (self->devices, i);
206 if (fu_device_has_guid (item->device, guid))
207 return item;
208 }
209 return NULL;
210}
211
212static FuPlugin *
213fu_engine_get_plugin_by_name (FuEngine *self, const gchar *name)
214{
215 for (guint i = 0; i < self->plugins->len; i++) {
216 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
217 if (g_strcmp0 (fu_plugin_get_name (plugin), name) == 0)
218 return plugin;
219 }
220 return NULL;
221}
222
223static const gchar *
224fu_engine_get_sysconfig_dir (void)
225{
226 if (g_file_test (SYSCONFDIR, G_FILE_TEST_EXISTS))
227 return SYSCONFDIR;
228 return "/etc";
229}
230
231static void
Richard Hughesc6afb512017-08-22 10:22:20 +0100232fu_engine_set_release_from_appstream (FuEngine *self, FwupdRelease *rel,
233 AsApp *app, AsRelease *release)
Richard Hughes9945edb2017-06-19 10:03:55 +0100234{
235 AsChecksum *csum;
Richard Hughesc6afb512017-08-22 10:22:20 +0100236 FwupdRemote *remote = NULL;
237 const gchar *remote_id;
Richard Hughes9945edb2017-06-19 10:03:55 +0100238 const gchar *tmp;
239
Richard Hughesc6afb512017-08-22 10:22:20 +0100240 /* find the remote */
241 remote_id = as_app_get_metadata_item (app, "fwupd::RemoteID");
242 if (remote_id != NULL) {
243 remote = fu_config_get_remote_by_id (self->config, remote_id);
244 if (remote == NULL)
245 g_warning ("failed to find remote %s", remote_id);
246 } else {
247 g_warning ("no fwupd::RemoteID set on %s",
248 as_app_get_unique_id (app));
249 }
250
Richard Hughes9945edb2017-06-19 10:03:55 +0100251 tmp = as_release_get_version (release);
252 if (tmp != NULL)
253 fwupd_release_set_version (rel, tmp);
254 tmp = as_release_get_description (release, NULL);
255 if (tmp != NULL)
256 fwupd_release_set_description (rel, tmp);
257 tmp = as_release_get_location_default (release);
Richard Hughesc6afb512017-08-22 10:22:20 +0100258 if (tmp != NULL) {
259 g_autofree gchar *uri = NULL;
260 if (remote != NULL)
261 uri = fwupd_remote_build_firmware_uri (remote, tmp, NULL);
262 if (uri == NULL)
263 uri = g_strdup (tmp);
264 fwupd_release_set_uri (rel, uri);
265 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100266 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
267 if (csum != NULL) {
268 tmp = as_checksum_get_filename (csum);
269 if (tmp != NULL)
270 fwupd_release_set_filename (rel, tmp);
271 }
272 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTAINER);
273 if (csum != NULL) {
274 tmp = as_checksum_get_value (csum);
275 if (tmp != NULL)
276 fwupd_release_add_checksum (rel, tmp);
277 }
278 fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED));
279}
280
Richard Hughes7403dc52017-08-10 15:34:10 +0100281static FuKeyring *
282fu_engine_get_keyring_for_kind (FwupdKeyringKind kind, GError **error)
283{
284 if (kind == FWUPD_KEYRING_KIND_GPG) {
285#ifdef ENABLE_GPG
286 return fu_keyring_gpg_new ();
287#else
288 g_set_error_literal (error,
289 FWUPD_ERROR,
290 FWUPD_ERROR_NOT_SUPPORTED,
291 "Not compiled with GPG support");
292 return NULL;
293#endif
294 }
Richard Hughes556ec352017-08-16 13:36:38 +0100295 if (kind == FWUPD_KEYRING_KIND_PKCS7) {
Richard Hughes7403dc52017-08-10 15:34:10 +0100296#ifdef ENABLE_PKCS7
297 return fu_keyring_pkcs7_new ();
298#else
299 g_set_error_literal (error,
300 FWUPD_ERROR,
301 FWUPD_ERROR_NOT_SUPPORTED,
302 "Not compiled with PKCS7 support");
303 return NULL;
304#endif
305 }
306 g_set_error (error,
307 FWUPD_ERROR,
308 FWUPD_ERROR_NOT_SUPPORTED,
309 "Keyring kind %s not supported",
310 fwupd_keyring_kind_to_string (kind));
311 return NULL;
312}
313
Richard Hughes9945edb2017-06-19 10:03:55 +0100314static gboolean
315fu_engine_get_release_trust_flags (AsRelease *release,
316 FwupdTrustFlags *trust_flags,
317 GError **error)
318{
319 AsChecksum *csum_tmp;
Richard Hughes7403dc52017-08-10 15:34:10 +0100320 FwupdKeyringKind keyring_kind = FWUPD_KEYRING_KIND_UNKNOWN;
Richard Hughes9945edb2017-06-19 10:03:55 +0100321 GBytes *blob_payload;
322 GBytes *blob_signature;
323 const gchar *fn;
324 g_autofree gchar *pki_dir = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100325 g_autoptr(GError) error_local = NULL;
326 g_autoptr(FuKeyring) kr = NULL;
Richard Hughesf69a4812017-08-16 12:27:51 +0100327 g_autoptr(FuKeyringResult) kr_result = NULL;
Richard Hughes7403dc52017-08-10 15:34:10 +0100328 struct {
329 FwupdKeyringKind kind;
330 const gchar *ext;
331 } keyrings[] = {
332 { FWUPD_KEYRING_KIND_GPG, "asc" },
333 { FWUPD_KEYRING_KIND_PKCS7, "p7b" },
334 { FWUPD_KEYRING_KIND_PKCS7, "p7c" },
335 { FWUPD_KEYRING_KIND_NONE, NULL }
336 };
Richard Hughes9945edb2017-06-19 10:03:55 +0100337
338 /* no filename? */
339 csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
340 fn = as_checksum_get_filename (csum_tmp);
341 if (fn == NULL) {
342 g_set_error_literal (error,
343 FWUPD_ERROR,
344 FWUPD_ERROR_INVALID_FILE,
345 "no filename");
346 return FALSE;
347 }
348
349 /* no signature == no trust */
Richard Hughes7403dc52017-08-10 15:34:10 +0100350 for (guint i = 0; keyrings[i].ext != NULL; i++) {
351 g_autofree gchar *fn_tmp = g_strdup_printf ("%s.%s", fn, keyrings[i].ext);
Richard Hughesb9ad09c2017-08-15 15:02:17 +0100352 blob_signature = as_release_get_blob (release, fn_tmp);
Richard Hughes7403dc52017-08-10 15:34:10 +0100353 if (blob_signature != NULL) {
354 keyring_kind = keyrings[i].kind;
355 break;
356 }
357 }
358 if (keyring_kind == FWUPD_KEYRING_KIND_UNKNOWN) {
359 g_debug ("firmware archive contained no signature");
Richard Hughes9945edb2017-06-19 10:03:55 +0100360 return TRUE;
361 }
362
363 /* get payload */
364 blob_payload = as_release_get_blob (release, fn);
365 if (blob_payload == NULL) {
366 g_set_error_literal (error,
367 FWUPD_ERROR,
368 FWUPD_ERROR_INVALID_FILE,
369 "no payload");
370 return FALSE;
371 }
372
373 /* check we were installed correctly */
374 pki_dir = g_build_filename (fu_engine_get_sysconfig_dir (), "pki", "fwupd", NULL);
375 if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) {
376 g_set_error (error,
377 FWUPD_ERROR,
378 FWUPD_ERROR_NOT_FOUND,
379 "PKI directory %s not found", pki_dir);
380 return FALSE;
381 }
382
383 /* verify against the system trusted keys */
Richard Hughes7403dc52017-08-10 15:34:10 +0100384 kr = fu_engine_get_keyring_for_kind (keyring_kind, error);
385 if (kr == NULL)
386 return FALSE;
Richard Hughes14047d72017-08-18 10:58:47 +0100387 if (!fu_keyring_setup (kr, error))
388 return FALSE;
389 if (!fu_keyring_add_public_keys (kr, pki_dir, error))
Richard Hughes9945edb2017-06-19 10:03:55 +0100390 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +0100391 kr_result = fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local);
392 if (kr_result == NULL) {
Richard Hughes9945edb2017-06-19 10:03:55 +0100393 g_warning ("untrusted as failed to verify: %s",
394 error_local->message);
395 return TRUE;
396 }
397
398 /* awesome! */
399 g_debug ("marking payload as trusted");
400 *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD;
401 return TRUE;
402}
403
404/**
405 * fu_engine_unlock:
406 * @self: A #FuEngine
407 * @device_id: A device ID
408 * @error: A #GError, or %NULL
409 *
410 * Unlocks a device.
411 *
412 * Returns: %TRUE for success
413 **/
414gboolean
415fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error)
416{
417 FuDeviceItem *item;
418
419 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
420 g_return_val_if_fail (device_id != NULL, FALSE);
421 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
422
423 /* check the device exists */
424 item = fu_engine_get_item_by_id (self, device_id, error);
425 if (item == NULL)
426 return FALSE;
427
428 /* run the correct plugin that added this */
429 if (!fu_plugin_runner_unlock (item->plugin,
430 item->device,
431 error))
432 return FALSE;
433
434 /* make the UI update */
435 fu_engine_emit_device_changed (self, item->device);
436 fu_engine_emit_changed (self);
437 return TRUE;
438}
439
440static AsApp *
441fu_engine_verify_update_device_to_app (FuDevice *device)
442{
443 AsApp *app = NULL;
444 GPtrArray *checksums;
445 g_autofree gchar *id = NULL;
Richard Hughes08f12de2017-06-22 10:33:29 +0100446 g_autoptr(AsFormat) format = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100447 g_autoptr(AsProvide) prov = NULL;
448 g_autoptr(AsRelease) rel = NULL;
449
450 /* make a plausible ID */
451 id = g_strdup_printf ("%s.firmware", fu_device_get_guid_default (device));
452
453 /* add app to store */
454 app = as_app_new ();
455 as_app_set_id (app, id);
456 as_app_set_kind (app, AS_APP_KIND_FIRMWARE);
457 rel = as_release_new ();
458 as_release_set_version (rel, fu_device_get_version (device));
459 checksums = fu_device_get_checksums (device);
460 for (guint j = 0; j < checksums->len; j++) {
461 const gchar *checksum = g_ptr_array_index (checksums, j);
462 g_autoptr(AsChecksum) csum = as_checksum_new ();
463 as_checksum_set_kind (csum, fwupd_checksum_guess_kind (checksum));
464 as_checksum_set_value (csum, checksum);
465 as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT);
466 as_release_add_checksum (rel, csum);
467 }
468 as_app_add_release (app, rel);
469 prov = as_provide_new ();
470 as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED);
471 as_provide_set_value (prov, fu_device_get_guid_default (device));
472 as_app_add_provide (app, prov);
Richard Hughes08f12de2017-06-22 10:33:29 +0100473 format = as_format_new ();
474 as_format_set_kind (format, AS_FORMAT_KIND_UNKNOWN);
475 as_app_add_format (app, format);
Richard Hughes9945edb2017-06-19 10:03:55 +0100476 return app;
477}
478
479static AsStore *
480fu_engine_load_verify_store (GError **error)
481{
482 const gchar *fn = "/var/lib/fwupd/verify.xml";
483 g_autoptr(AsStore) store = NULL;
484 g_autoptr(GFile) file = NULL;
485
486 /* load existing store */
487 store = as_store_new ();
488 as_store_set_api_version (store, 0.9);
489 file = g_file_new_for_path (fn);
490 if (g_file_query_exists (file, NULL)) {
491 if (!as_store_from_file (store, file, NULL, NULL, error))
492 return NULL;
493 }
494 return g_steal_pointer (&store);
495}
496
497/**
498 * fu_engine_verify_update:
499 * @self: A #FuEngine
500 * @device_id: A device ID
501 * @error: A #GError, or %NULL
502 *
503 * Updates the verification store entry for a specific device.
504 *
505 * Returns: %TRUE for success
506 **/
507gboolean
508fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error)
509{
510 FuDeviceItem *item;
511 GPtrArray *checksums;
512 const gchar *fn = "/var/lib/fwupd/verify.xml";
513 g_autoptr(AsApp) app = NULL;
514 g_autoptr(AsStore) store = NULL;
515 g_autoptr(GFile) file = NULL;
516
517 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
518 g_return_val_if_fail (device_id != NULL, FALSE);
519 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
520
521 /* check the devices still exists */
522 item = fu_engine_get_item_by_id (self, device_id, error);
523 if (item == NULL)
524 return FALSE;
525
Richard Hughes9945edb2017-06-19 10:03:55 +0100526 /* get the checksum */
527 checksums = fu_device_get_checksums (item->device);
528 if (checksums->len == 0) {
529 if (!fu_plugin_runner_verify (item->plugin,
530 item->device,
531 FU_PLUGIN_VERIFY_FLAG_NONE,
532 error))
533 return FALSE;
534 fu_engine_emit_device_changed (self, item->device);
535 }
536
537 /* we got nothing */
538 if (checksums->len == 0) {
539 g_set_error_literal (error,
540 FWUPD_ERROR,
541 FWUPD_ERROR_NOT_SUPPORTED,
542 "device verification not supported");
543 return FALSE;
544 }
545
546 /* load existing store */
547 store = fu_engine_load_verify_store (error);
548 if (store == NULL)
549 return FALSE;
550
551 /* add to store */
552 app = fu_engine_verify_update_device_to_app (item->device);
Richard Hughesb9bddfd2017-06-22 10:34:15 +0100553 as_store_remove_app_by_id (store, as_app_get_id (app));
Richard Hughes9945edb2017-06-19 10:03:55 +0100554 as_store_add_app (store, app);
555
556 /* write */
557 g_debug ("writing %s", fn);
558 file = g_file_new_for_path (fn);
559 return as_store_to_file (store, file,
560 AS_NODE_TO_XML_FLAG_ADD_HEADER |
561 AS_NODE_TO_XML_FLAG_FORMAT_INDENT |
562 AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE,
563 NULL, error);
564}
565
566static AsApp *
567fu_engine_store_get_app_by_guids (AsStore *store, FuDevice *device)
568{
569 GPtrArray *guids = fu_device_get_guids (device);
570 for (guint i = 0; i < guids->len; i++) {
571 AsApp *app = NULL;
572 app = as_store_get_app_by_provide (store,
573 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
574 g_ptr_array_index (guids, i));
575 if (app != NULL)
576 return app;
577 }
578 return NULL;
579}
580
581/**
582 * fu_engine_verify:
583 * @self: A #FuEngine
584 * @device_id: A device ID
585 * @error: A #GError, or %NULL
586 *
587 * Verifies a device firmware checksum using the verification store entry.
588 *
589 * Returns: %TRUE for success
590 **/
591gboolean
592fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error)
593{
594 AsApp *app;
595 AsChecksum *csum;
596 AsRelease *release;
597 FuDeviceItem *item = NULL;
598 GPtrArray *checksums;
599 const gchar *hash = NULL;
600 const gchar *version = NULL;
601 g_autoptr(AsStore) store = NULL;
602
603 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
604 g_return_val_if_fail (device_id != NULL, FALSE);
605 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
606
607 /* check the id exists */
608 item = fu_engine_get_item_by_id (self, device_id, error);
609 if (item == NULL)
610 return FALSE;
611
612 /* set the device firmware hash */
613 if (!fu_plugin_runner_verify (item->plugin, item->device,
614 FU_PLUGIN_VERIFY_FLAG_NONE, error))
615 return FALSE;
616
617 /* find component in metadata */
618 store = fu_engine_load_verify_store (error);
619 if (store == NULL)
620 return FALSE;
621 app = fu_engine_store_get_app_by_guids (store, item->device);
622 if (app == NULL) {
623 g_set_error_literal (error,
624 FWUPD_ERROR,
625 FWUPD_ERROR_NOT_FOUND,
626 "No metadata");
627 return FALSE;
628 }
629
630 /* find version in metadata */
631 version = fu_device_get_version (item->device);
632 release = as_app_get_release (app, version);
633 if (release == NULL) {
634 /* try again with the system metadata */
635 app = fu_engine_store_get_app_by_guids (self->store, item->device);
636 if (app == NULL) {
637 g_set_error_literal (error,
638 FWUPD_ERROR,
639 FWUPD_ERROR_NOT_FOUND,
640 "No system metadata");
641 return FALSE;
642 }
643 release = as_app_get_release (app, version);
644 }
645 if (release == NULL) {
646 g_set_error (error,
647 FWUPD_ERROR,
648 FWUPD_ERROR_NOT_FOUND,
649 "No version %s", version);
650 return FALSE;
651 }
652
653 /* find checksum */
654 csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT);
655 if (csum == NULL) {
656 g_set_error (error,
657 FWUPD_ERROR,
658 FWUPD_ERROR_NOT_FOUND,
659 "No content checksum for %s", version);
660 return FALSE;
661 }
662
663 /* get the matching checksum */
664 checksums = fu_device_get_checksums (item->device);
665 if (checksums->len == 0) {
666 g_set_error (error,
667 FWUPD_ERROR,
668 FWUPD_ERROR_NOT_FOUND,
669 "No device checksums for %s", version);
670 return FALSE;
671 }
672 for (guint j = 0; j < checksums->len; j++) {
673 const gchar *hash_tmp = g_ptr_array_index (checksums, j);
674 GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp);
675 if (as_checksum_get_kind (csum) == hash_kind) {
676 hash = hash_tmp;
677 break;
678 }
679 }
680 if (hash == NULL) {
681 g_set_error (error,
682 FWUPD_ERROR,
683 FWUPD_ERROR_NOT_FOUND,
684 "No matching hash kind for %s", version);
685 return FALSE;
686 }
687 if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) {
688 g_set_error (error,
689 FWUPD_ERROR,
690 FWUPD_ERROR_NOT_FOUND,
691 "For v%s expected %s, got %s",
692 version,
693 as_checksum_get_value (csum),
694 hash);
695 return FALSE;
696 }
697
698 /* success */
699 return TRUE;
700}
701
702static AsScreenshot *
703_as_app_get_screenshot_default (AsApp *app)
704{
705 GPtrArray *array = as_app_get_screenshots (app);
706 if (array->len == 0)
707 return NULL;
708 return g_ptr_array_index (array, 0);
709}
710
Richard Hughes9945edb2017-06-19 10:03:55 +0100711static gboolean
712fu_engine_check_version_requirement (AsApp *app,
713 AsRequireKind kind,
714 const gchar *id,
715 const gchar *version,
716 GError **error)
717{
718 AsRequire *req;
719
720 /* check args */
721 if (version == NULL) {
Mario Limoncielloc29b3982017-07-17 13:05:34 -0500722 g_debug ("no parameter given for %s{%s}",
Richard Hughes9945edb2017-06-19 10:03:55 +0100723 as_require_kind_to_string (kind), id);
724 return TRUE;
725 }
726
727 /* does requirement exist */
728 req = as_app_get_require_by_value (app, kind, id);
729 if (req == NULL) {
730 g_debug ("no requirement on %s{%s}",
731 as_require_kind_to_string (kind), id);
732 return TRUE;
733 }
734
735 /* check version */
736 if (!as_require_version_compare (req, version, error)) {
737 g_prefix_error (error, "Value of %s incorrect: ", id);
738 return FALSE;
739 }
740
741 /* success */
742 g_debug ("requirement %s %s %s on %s passed",
743 as_require_get_version (req),
744 as_require_compare_to_string (as_require_get_compare (req)),
745 version, id);
746 return TRUE;
747}
Richard Hughes9945edb2017-06-19 10:03:55 +0100748
749static gboolean
750fu_engine_check_requirements (AsApp *app, FuDevice *device, GError **error)
751{
Richard Hughes9945edb2017-06-19 10:03:55 +0100752 /* make sure requirements are satisfied */
753 if (!fu_engine_check_version_requirement (app,
754 AS_REQUIRE_KIND_ID,
755 "org.freedesktop.fwupd",
756 VERSION,
757 error)) {
758 return FALSE;
759 }
760
761 if (device != NULL) {
762 if (!fu_engine_check_version_requirement (app,
763 AS_REQUIRE_KIND_FIRMWARE,
764 NULL,
765 fu_device_get_version (device),
766 error)) {
767 return FALSE;
768 }
769 if (!fu_engine_check_version_requirement (app,
770 AS_REQUIRE_KIND_FIRMWARE,
771 "bootloader",
772 fu_device_get_version_bootloader (device),
773 error)) {
774 return FALSE;
775 }
776 if (!fu_engine_check_version_requirement (app,
777 AS_REQUIRE_KIND_FIRMWARE,
778 "vendor-id",
779 fu_device_get_vendor_id (device),
780 error)) {
781 return FALSE;
782 }
783 }
Richard Hughes9945edb2017-06-19 10:03:55 +0100784
785 /* success */
786 return TRUE;
787}
788
789static void
790fu_engine_vendor_fixup_provide_value (AsApp *app)
791{
792 GPtrArray *provides;
793
794 /* no quirk required */
795 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
796 return;
797
798 /* fix each provide to be a GUID */
799 provides = as_app_get_provides (app);
800 for (guint i = 0; i < provides->len; i++) {
801 AsProvide *prov = g_ptr_array_index (provides, i);
802 const gchar *value = as_provide_get_value (prov);
803 g_autofree gchar *guid = NULL;
804 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
805 continue;
806 if (as_utils_guid_is_valid (value))
807 continue;
808 guid = as_utils_guid_from_string (value);
809 as_provide_set_value (prov, guid);
810 }
811}
812
813static void
814fu_engine_vendor_quirk_release_version (AsApp *app)
815{
816 AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET;
817 GPtrArray *releases;
818
819 /* no quirk required */
820 if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE)
821 return;
822
823 for (guint i = 0; quirk_table[i].identifier != NULL; i++) {
824 if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier))
825 flags = quirk_table[i].flags;
826 }
827
828 /* fix each release */
829 releases = as_app_get_releases (app);
830 for (guint i = 0; i < releases->len; i++) {
831 AsRelease *rel;
832 const gchar *version;
833 guint64 ver_uint32;
834 g_autofree gchar *version_new = NULL;
835
836 rel = g_ptr_array_index (releases, i);
837 version = as_release_get_version (rel);
838 if (version == NULL)
839 continue;
840 if (g_strstr_len (version, -1, ".") != NULL)
841 continue;
842
843 /* metainfo files use hex and the LVFS uses decimal */
844 if (g_str_has_prefix (version, "0x")) {
845 ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16);
846 } else {
847 ver_uint32 = g_ascii_strtoull (version, NULL, 10);
848 }
849 if (ver_uint32 == 0)
850 continue;
851
852 /* convert to dotted decimal */
853 version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags);
854 as_release_set_version (rel, version_new);
855 }
856}
857
858static gchar *
859fu_engine_get_guids_from_store (AsStore *store)
860{
861 AsProvide *prov;
862 GPtrArray *provides;
863 GPtrArray *apps;
864 GString *str = g_string_new ("");
865
866 /* return a string with all the firmware apps in the store */
867 apps = as_store_get_apps (store);
868 for (guint i = 0; i < apps->len; i++) {
869 AsApp *app = AS_APP (g_ptr_array_index (apps, i));
870 provides = as_app_get_provides (app);
871 for (guint j = 0; j < provides->len; j++) {
872 prov = AS_PROVIDE (g_ptr_array_index (provides, j));
873 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
874 continue;
875 g_string_append_printf (str, "%s,", as_provide_get_value (prov));
876 }
877 }
878 if (str->len == 0)
879 return NULL;
880 g_string_truncate (str, str->len - 1);
881 return g_string_free (str, FALSE);
882}
883
884static FuDeviceItem *
885fu_engine_get_item_by_wildcard (FuEngine *self, AsStore *store, GError **error)
886{
887 g_autofree gchar *guids = NULL;
888 for (guint i = 0; i < self->devices->len; i++) {
889 FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i);
890 if (fu_engine_store_get_app_by_guids (store, item_tmp->device) != NULL)
891 return item_tmp;
892 }
893 guids = fu_engine_get_guids_from_store (store);
894 g_set_error (error,
895 FWUPD_ERROR,
896 FWUPD_ERROR_INVALID_FILE,
897 "Firmware is not for any attached hardware: got %s",
898 guids);
899 return NULL;
900}
901
902/**
903 * fu_engine_install:
904 * @self: A #FuEngine
905 * @device_id: A device ID
906 * @store: The #AsStore with the firmware metadata
907 * @blob_cab: The #GBytes of the .cab file
908 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_ALLOW_ONLINE
909 * @error: A #GError, or %NULL
910 *
911 * Installs a specfic firmware file on a device.
912 *
913 * Returns: %TRUE for success
914 **/
915gboolean
916fu_engine_install (FuEngine *self,
917 const gchar *device_id,
918 AsStore *store,
919 GBytes *blob_cab,
920 FwupdInstallFlags flags,
921 GError **error)
922{
923 AsApp *app;
924 AsChecksum *csum_tmp;
925 AsRelease *rel;
926 FuDeviceItem *item;
927 GBytes *blob_fw;
928 const gchar *tmp;
929 const gchar *version;
930 gboolean is_downgrade;
931 gint vercmp;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100932 g_autoptr(GBytes) blob_fw2 = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +0100933
934 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
935 g_return_val_if_fail (device_id != NULL, FALSE);
936 g_return_val_if_fail (AS_IS_STORE (store), FALSE);
937 g_return_val_if_fail (blob_cab != NULL, FALSE);
938 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
939
940 /* wildcard */
941 if (g_strcmp0 (device_id, FWUPD_DEVICE_ID_ANY) == 0) {
942 item = fu_engine_get_item_by_wildcard (self, store, error);
943 if (item == NULL)
944 return FALSE;
945 } else {
946 /* find the specific device */
947 item = fu_engine_get_item_by_id (self, device_id, error);
948 if (item == NULL)
949 return FALSE;
950 }
951
952 /* check the device is not locked */
953 if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) {
954 g_set_error (error,
955 FWUPD_ERROR,
956 FWUPD_ERROR_INTERNAL,
957 "Device %s is locked",
958 device_id);
959 return FALSE;
960 }
961
962 /* no update abilities */
963 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
964 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
965 g_set_error (error,
966 FWUPD_ERROR,
967 FWUPD_ERROR_INTERNAL,
968 "Device %s does not currently allow updates",
969 device_id);
970 return FALSE;
971 }
972
973 /* Called with online update, test if device is supposed to allow this */
974 if (!(flags & FWUPD_INSTALL_FLAG_OFFLINE) &&
975 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
976 g_set_error(error,
977 FWUPD_ERROR,
978 FWUPD_ERROR_NOT_SUPPORTED,
979 "Device %s does not allow online updates",
980 device_id);
981 return FALSE;
982 }
983
984 /* Called with offline update, test if device is supposed to allow this */
985 if (flags & FWUPD_INSTALL_FLAG_OFFLINE &&
986 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE)) {
987 g_set_error(error,
988 FWUPD_ERROR,
989 FWUPD_ERROR_NOT_SUPPORTED,
990 "Device %s does not allow offline updates",
991 device_id);
992 return FALSE;
993 }
994
995 /* find from guid */
996 app = fu_engine_store_get_app_by_guids (store, item->device);
997 if (app == NULL) {
998 g_autofree gchar *guid = NULL;
999 guid = fu_engine_get_guids_from_store (store);
1000 g_set_error (error,
1001 FWUPD_ERROR,
1002 FWUPD_ERROR_INVALID_FILE,
1003 "Firmware is not for this hw: required %s got %s",
1004 fu_device_get_guid_default (item->device), guid);
1005 return FALSE;
1006 }
1007
1008 /* not in bootloader mode */
1009 if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) {
1010 const gchar *caption = NULL;
1011 AsScreenshot *ss = _as_app_get_screenshot_default (app);
1012 if (ss != NULL)
1013 caption = as_screenshot_get_caption (ss, NULL);
1014 if (caption != NULL) {
1015 g_set_error (error,
1016 FWUPD_ERROR,
1017 FWUPD_ERROR_INTERNAL,
1018 "Device %s needs to manually be put in update mode: %s",
1019 fu_device_get_name (item->device), caption);
1020 } else {
1021 g_set_error (error,
1022 FWUPD_ERROR,
1023 FWUPD_ERROR_INTERNAL,
1024 "Device %s needs to manually be put in update mode",
1025 fu_device_get_name (item->device));
1026 }
1027 return FALSE;
1028 }
1029
1030 /* possibly convert the version from 0x to dotted */
1031 fu_engine_vendor_quirk_release_version (app);
1032
1033 /* possibly convert the flashed provide to a GUID */
1034 fu_engine_vendor_fixup_provide_value (app);
1035
1036 /* check we can install it */
1037 if (!fu_engine_check_requirements (app, item->device, error))
1038 return FALSE;
1039
1040 /* parse the DriverVer */
1041 rel = as_app_get_release_default (app);
1042 if (rel == NULL) {
1043 g_set_error_literal (error,
1044 FWUPD_ERROR,
1045 FWUPD_ERROR_INVALID_FILE,
1046 "No releases in the firmware component");
1047 return FALSE;
1048 }
1049
1050 /* get the blob */
1051 csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT);
1052 tmp = as_checksum_get_filename (csum_tmp);
1053 if (tmp == NULL) {
1054 g_set_error_literal (error,
1055 FWUPD_ERROR,
1056 FWUPD_ERROR_INVALID_FILE,
1057 "No checksum filename");
1058 return FALSE;
1059 }
1060
1061 /* not all devices have to use the same blob */
1062 blob_fw = as_release_get_blob (rel, tmp);
1063 if (blob_fw == NULL) {
1064 g_set_error_literal (error,
1065 FWUPD_ERROR,
1066 FWUPD_ERROR_READ,
1067 "Failed to get firmware blob");
1068 return FALSE;
1069 }
1070
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001071 /* use a bubblewrap helper script to build the firmware */
1072 tmp = as_app_get_metadata_item (app, "fwupd::BuilderScript");
1073 if (tmp != NULL) {
1074 const gchar *tmp2 = as_app_get_metadata_item (app, "fwupd::BuilderOutput");
1075 if (tmp2 == NULL)
1076 tmp2 = "firmware.bin";
1077 blob_fw2 = fu_common_firmware_builder (blob_fw, tmp, tmp2, error);
1078 if (blob_fw2 == NULL)
1079 return FALSE;
1080 } else {
1081 blob_fw2 = g_bytes_ref (blob_fw);
1082 }
1083
Richard Hughesadcc16a2017-08-21 12:26:46 +01001084 /* test the firmware is not an empty blob */
1085 if (g_bytes_get_size (blob_fw2) == 0) {
1086 g_set_error (error,
1087 FWUPD_ERROR,
1088 FWUPD_ERROR_INVALID_FILE,
1089 "Firmware is invalid as has zero size");
1090 return FALSE;
1091 }
1092
Richard Hughes9945edb2017-06-19 10:03:55 +01001093 version = as_release_get_version (rel);
1094 fu_device_set_update_version (item->device, version);
1095
1096 /* compare to the lowest supported version, if it exists */
1097 tmp = fu_device_get_version_lowest (item->device);
1098 if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) {
1099 g_set_error (error,
1100 FWUPD_ERROR,
1101 FWUPD_ERROR_VERSION_NEWER,
1102 "Specified firmware is older than the minimum "
1103 "required version '%s < %s'", tmp, version);
1104 return FALSE;
1105 }
1106
1107 /* compare the versions of what we have installed */
1108 tmp = fu_device_get_version (item->device);
1109 if (tmp == NULL) {
1110 g_set_error (error,
1111 FWUPD_ERROR,
1112 FWUPD_ERROR_INTERNAL,
1113 "Device %s does not yet have a current version",
1114 device_id);
1115 return FALSE;
1116 }
1117 vercmp = as_utils_vercmp (tmp, version);
1118 if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
1119 g_set_error (error,
1120 FWUPD_ERROR,
1121 FWUPD_ERROR_VERSION_SAME,
1122 "Specified firmware is already installed '%s'",
1123 tmp);
1124 return FALSE;
1125 }
1126 is_downgrade = vercmp > 0;
1127 if (is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
1128 g_set_error (error,
1129 FWUPD_ERROR,
1130 FWUPD_ERROR_VERSION_NEWER,
1131 "Specified firmware is older than installed '%s < %s'",
1132 tmp, version);
1133 return FALSE;
1134 }
1135
1136 /* signal to all the plugins the update is about to happen */
1137 for (guint j = 0; j < self->plugins->len; j++) {
1138 FuPlugin *plugin = g_ptr_array_index (self->plugins, j);
1139 if (!fu_plugin_runner_update_prepare (plugin, item->device, error))
1140 return FALSE;
1141 }
1142
1143 /* do the update */
1144 if (!fu_plugin_runner_update (item->plugin,
1145 item->device,
1146 blob_cab,
Richard Hughes41cbe2a2017-08-08 14:13:35 +01001147 blob_fw2,
Richard Hughes9945edb2017-06-19 10:03:55 +01001148 flags,
1149 error)) {
1150 for (guint j = 0; j < self->plugins->len; j++) {
1151 FuPlugin *plugin = g_ptr_array_index (self->plugins, j);
1152 g_autoptr(GError) error_local = NULL;
1153 if (!fu_plugin_runner_update_cleanup (plugin,
1154 item->device,
1155 &error_local)) {
1156 g_warning ("failed to update-cleanup "
1157 "after failed update: %s",
1158 error_local->message);
1159 }
1160 }
1161 return FALSE;
1162 }
1163
1164 /* signal to all the plugins the update has happened */
1165 for (guint j = 0; j < self->plugins->len; j++) {
1166 FuPlugin *plugin = g_ptr_array_index (self->plugins, j);
1167 g_autoptr(GError) error_local = NULL;
1168 if (!fu_plugin_runner_update_cleanup (plugin, item->device, &error_local)) {
1169 g_warning ("failed to update-cleanup: %s",
1170 error_local->message);
1171 }
1172 }
1173
1174 /* make the UI update */
Mario Limonciello5735fd62017-07-13 15:47:52 -05001175 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes9945edb2017-06-19 10:03:55 +01001176 fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC);
1177 fu_engine_emit_device_changed (self, item->device);
1178 fu_engine_emit_changed (self);
1179 return TRUE;
1180}
1181
1182static FuDeviceItem *
1183fu_engine_get_item_by_id_fallback_pending (FuEngine *self, const gchar *id, GError **error)
1184{
1185 FuDevice *dev;
1186 FuPlugin *plugin;
1187 FuDeviceItem *item = NULL;
1188 FwupdUpdateState update_state;
1189 const gchar *tmp;
1190 g_autoptr(GPtrArray) devices = NULL;
1191
1192 /* not a wildcard */
1193 if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0)
1194 return fu_engine_get_item_by_id (self, id, error);
1195
1196 /* allow '*' for any */
1197 devices = fu_pending_get_devices (self->pending, error);
1198 if (devices == NULL)
1199 return NULL;
1200 for (guint i = 0; i < devices->len; i++) {
1201 dev = g_ptr_array_index (devices, i);
1202 update_state = fu_device_get_update_state (dev);
1203 if (update_state == FWUPD_UPDATE_STATE_UNKNOWN)
1204 continue;
1205 if (update_state == FWUPD_UPDATE_STATE_PENDING)
1206 continue;
1207
1208 /* if the device is not still connected, fake a FuDeviceItem */
1209 item = fu_engine_get_item_by_id (self, fu_device_get_id (dev), NULL);
1210 if (item == NULL) {
1211 tmp = fu_device_get_plugin (dev);
1212 plugin = fu_engine_get_plugin_by_name (self, tmp);
1213 if (plugin == NULL) {
1214 g_set_error (error,
1215 FWUPD_ERROR,
1216 FWUPD_ERROR_NOT_FOUND,
1217 "no plugin %s found", tmp);
1218 return NULL;
1219 }
1220 item = g_new0 (FuDeviceItem, 1);
1221 item->device = g_object_ref (dev);
1222 item->plugin = g_object_ref (plugin);
1223 g_ptr_array_add (self->devices, item);
1224
1225 /* FIXME: just a boolean on FuDeviceItem? */
1226 fu_device_set_metadata (dev, "FakeDevice", "TRUE");
1227 }
1228 break;
1229 }
1230
1231 /* no device found */
1232 if (item == NULL) {
1233 g_set_error_literal (error,
1234 FWUPD_ERROR,
1235 FWUPD_ERROR_NOT_FOUND,
1236 "no suitable devices found");
1237 }
1238 return item;
1239}
1240
1241/**
1242 * fu_engine_get_action_id_for_device:
1243 * @self: A #FuEngine
1244 * @device_id: A device ID
1245 * @store: The #AsStore with the firmware metadata
1246 * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_ALLOW_ONLINE
1247 * @error: A #GError, or %NULL
1248 *
1249 * Gets the PolicyKit action ID to use for the install operation.
1250 *
1251 * Returns: string, e.g. "org.freedesktop.fwupd.update-internal-trusted"
1252 **/
1253const gchar *
1254fu_engine_get_action_id_for_device (FuEngine *self,
1255 const gchar *device_id,
1256 AsStore *store,
1257 FwupdInstallFlags flags,
1258 GError **error)
1259{
1260 AsApp *app;
1261 AsRelease *release;
1262 FuDeviceItem *item;
1263 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
1264 const gchar *version;
1265 const gchar *version_release;
1266 gboolean is_downgrade;
1267 gboolean is_trusted;
1268 gint vercmp;
1269
1270 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1271 g_return_val_if_fail (AS_IS_STORE (store), NULL);
1272 g_return_val_if_fail (device_id != NULL, NULL);
1273 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1274
1275 /* wildcard */
1276 if (g_strcmp0 (device_id, FWUPD_DEVICE_ID_ANY) == 0) {
1277 item = fu_engine_get_item_by_wildcard (self, store, error);
1278 if (item == NULL)
1279 return NULL;
1280 } else {
1281 /* find the specific device */
1282 item = fu_engine_get_item_by_id (self, device_id, error);
1283 if (item == NULL)
1284 return NULL;
1285 }
1286
1287 /* get device */
1288 version = fu_device_get_version (item->device);
1289 if (version == NULL) {
1290 g_set_error (error,
1291 FWUPD_ERROR,
1292 FWUPD_ERROR_INTERNAL,
1293 "Device with ID %s has no firmware version",
1294 device_id);
1295 return NULL;
1296 }
1297
1298 /* match the GUIDs in the XML */
1299 app = fu_engine_store_get_app_by_guids (store, item->device);
1300 if (app == NULL) {
1301 g_autofree gchar *guid = NULL;
1302 guid = fu_engine_get_guids_from_store (store);
1303 g_set_error (error,
1304 FWUPD_ERROR,
1305 FWUPD_ERROR_INVALID_FILE,
1306 "firmware is not for this hw: required %s got %s",
1307 fu_device_get_guid_default (item->device), guid);
1308 return NULL;
1309 }
1310
1311 /* possibly convert the version from 0x to dotted */
1312 fu_engine_vendor_quirk_release_version (app);
1313
1314 /* possibly convert the flashed provide to a GUID */
1315 fu_engine_vendor_fixup_provide_value (app);
1316
1317 /* get latest release */
1318 release = as_app_get_release_default (app);
1319 if (release == NULL) {
1320 g_set_error (error,
1321 FWUPD_ERROR,
1322 FWUPD_ERROR_INVALID_FILE,
1323 "%s [%s] has no firmware update metadata",
1324 fu_device_get_id (item->device),
1325 fu_device_get_name (item->device));
1326 return NULL;
1327 }
1328
1329 /* is this a downgrade or re-install */
1330 version_release = as_release_get_version (release);
1331 if (version_release == NULL) {
1332 g_set_error_literal (error,
1333 FWUPD_ERROR,
1334 FWUPD_ERROR_INTERNAL,
1335 "Release has no firmware version");
1336 return NULL;
1337 }
1338 vercmp = as_utils_vercmp (version, version_release);
1339 if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) {
1340 g_set_error (error,
1341 FWUPD_ERROR,
1342 FWUPD_ERROR_VERSION_SAME,
1343 "Specified firmware is already installed '%s'",
1344 version_release);
1345 return NULL;
1346 }
1347 is_downgrade = vercmp > 0;
1348 if (is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) {
1349 g_set_error (error,
1350 FWUPD_ERROR,
1351 FWUPD_ERROR_VERSION_NEWER,
1352 "Specified firmware is older than installed '%s < %s'",
1353 version_release, version);
1354 return NULL;
1355 }
1356
1357 /* verify */
1358 if (!fu_engine_get_release_trust_flags (release, &trust_flags, error))
1359 return NULL;
1360 is_trusted = (trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0;
1361
1362 /* relax authentication checks for removable devices */
1363 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_INTERNAL)) {
1364 if (is_downgrade)
1365 return "org.freedesktop.fwupd.downgrade-hotplug";
1366 if (is_trusted)
1367 return "org.freedesktop.fwupd.update-hotplug-trusted";
1368 return "org.freedesktop.fwupd.update-hotplug";
1369 }
1370
1371 /* internal device */
1372 if (is_downgrade)
1373 return "org.freedesktop.fwupd.downgrade-internal";
1374 if (is_trusted)
1375 return "org.freedesktop.fwupd.update-internal-trusted";
1376 return "org.freedesktop.fwupd.update-internal";
1377}
1378
1379static gboolean
1380fu_engine_load_metadata_from_file (FuEngine *self,
1381 const gchar *path,
1382 const gchar *remote_id,
1383 GError **error)
1384{
1385 GPtrArray *apps;
1386 g_autoptr(AsStore) store = NULL;
1387 g_autoptr(GFile) file = NULL;
1388
1389 /* load the store locally until we know it is valid */
1390 store = as_store_new ();
1391 file = g_file_new_for_path (path);
1392 if (!as_store_from_file (store, file, NULL, NULL, error))
1393 return FALSE;
1394
1395 /* add the new application from the store */
1396 apps = as_store_get_apps (store);
1397 for (guint i = 0; i < apps->len; i++) {
1398 AsApp *app = g_ptr_array_index (apps, i);
1399
1400 /* does this app already exist */
1401 if (as_store_get_app_by_id (self->store, as_app_get_id (app)) != NULL) {
1402 g_debug ("%s exists in remote %s, skipping",
1403 as_app_get_unique_id (app),
1404 as_app_get_metadata_item (app, "fwupd::RemoteID"));
1405 continue;
1406 }
1407 if (remote_id != NULL && remote_id[0] != '\0')
1408 as_app_add_metadata (app, "fwupd::RemoteID", remote_id);
1409 as_store_add_app (self->store, app);
1410 }
1411 return TRUE;
1412}
1413
1414static gboolean
1415fu_engine_load_metadata_store (FuEngine *self, GError **error)
1416{
1417 GPtrArray *apps;
1418 GPtrArray *remotes;
1419
1420 /* clear existing store */
1421 as_store_remove_all (self->store);
1422
1423 /* load each enabled metadata file */
1424 remotes = fu_config_get_remotes (self->config);
1425 for (guint i = 0; i < remotes->len; i++) {
1426 const gchar *path = NULL;
1427 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1428 if (!fwupd_remote_get_enabled (remote)) {
1429 g_debug ("remote %s not enabled, so skipping",
1430 fwupd_remote_get_id (remote));
1431 continue;
1432 }
1433 path = fwupd_remote_get_filename_cache (remote);
1434 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
1435 g_debug ("no %s, so skipping", path);
1436 continue;
1437 }
1438 if (!fu_engine_load_metadata_from_file (self, path,
1439 fwupd_remote_get_id (remote),
1440 error))
1441 return FALSE;
1442 }
1443
1444 /* print what we've got */
1445 apps = as_store_get_apps (self->store);
1446 if (apps->len == 0) {
1447 g_debug ("no devices in store");
1448 } else {
1449 g_debug ("devices now in store:");
1450 for (guint i = 0; i < apps->len; i++) {
1451 AsApp *app = g_ptr_array_index (apps, i);
1452 g_debug ("%u\t%s\t%s", i + 1,
1453 as_app_get_id (app),
1454 as_app_get_name (app, NULL));
1455 }
1456 }
1457
1458 /* are any devices now supported? */
1459 for (guint i = 0; i < self->devices->len; i++) {
1460 FuDeviceItem *item = g_ptr_array_index (self->devices, i);
1461 if (fu_engine_get_updates_item_update (self, item))
1462 fu_engine_emit_device_changed (self, item->device);
1463 }
1464
1465 return TRUE;
1466}
1467
Richard Hughesf69a4812017-08-16 12:27:51 +01001468static FuKeyringResult *
1469fu_engine_get_existing_keyring_result (FuEngine *self,
1470 FuKeyring *kr,
1471 FwupdRemote *remote,
1472 GError **error)
1473{
1474 g_autoptr(GBytes) blob = NULL;
1475 g_autoptr(GBytes) blob_sig = NULL;
1476 blob = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache (remote), error);
1477 if (blob == NULL)
1478 return NULL;
1479 blob_sig = fu_common_get_contents_bytes (fwupd_remote_get_filename_cache_sig (remote), error);
1480 if (blob_sig == NULL)
1481 return NULL;
1482 return fu_keyring_verify_data (kr, blob, blob_sig, error);
1483}
1484
Richard Hughes9945edb2017-06-19 10:03:55 +01001485/**
1486 * fu_engine_update_metadata:
1487 * @self: A #FuEngine
1488 * @remote_id: A remote ID, e.g. "lvfs"
1489 * @fd: file descriptor of the metadata
1490 * @fd_sig: file descriptor of the metadata signature
1491 * @error: A #GError, or %NULL
1492 *
1493 * Updates the metadata for a specific remote.
1494 *
1495 * Note: this will close the fds when done
1496 *
1497 * Returns: %TRUE for success
1498 **/
1499gboolean
1500fu_engine_update_metadata (FuEngine *self, const gchar *remote_id,
1501 gint fd, gint fd_sig, GError **error)
1502{
Richard Hughes7403dc52017-08-10 15:34:10 +01001503 FwupdKeyringKind keyring_kind;
Richard Hughes9945edb2017-06-19 10:03:55 +01001504 FwupdRemote *remote;
1505 g_autoptr(GBytes) bytes_raw = NULL;
1506 g_autoptr(GBytes) bytes_sig = NULL;
Richard Hughes9945edb2017-06-19 10:03:55 +01001507 g_autoptr(GInputStream) stream_fd = NULL;
1508 g_autoptr(GInputStream) stream = NULL;
1509 g_autoptr(GInputStream) stream_sig = NULL;
1510
1511 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
1512 g_return_val_if_fail (remote_id != NULL, FALSE);
1513 g_return_val_if_fail (fd > 0, FALSE);
1514 g_return_val_if_fail (fd_sig > 0, FALSE);
1515 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1516
1517 /* ensures the fd's are closed on error */
1518 stream_fd = g_unix_input_stream_new (fd, TRUE);
1519 stream_sig = g_unix_input_stream_new (fd_sig, TRUE);
1520
1521 /* check remote is valid */
1522 remote = fu_config_get_remote_by_id (self->config, remote_id);
1523 if (remote == NULL) {
1524 g_set_error (error,
1525 FWUPD_ERROR,
1526 FWUPD_ERROR_NOT_FOUND,
1527 "remote %s not found", remote_id);
1528 return FALSE;
1529 }
1530 if (!fwupd_remote_get_enabled (remote)) {
1531 g_set_error (error,
1532 FWUPD_ERROR,
1533 FWUPD_ERROR_NOT_SUPPORTED,
1534 "remote %s not enabled", remote_id);
1535 return FALSE;
1536 }
1537
1538 /* read the entire file into memory */
1539 bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error);
1540 if (bytes_raw == NULL)
1541 return FALSE;
1542
1543 /* read signature */
1544 bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error);
1545 if (bytes_sig == NULL)
1546 return FALSE;
1547
1548 /* verify file */
Richard Hughes7403dc52017-08-10 15:34:10 +01001549 keyring_kind = fwupd_remote_get_keyring_kind (remote);
1550 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
1551 g_autoptr(FuKeyring) kr = NULL;
Richard Hughesf69a4812017-08-16 12:27:51 +01001552 g_autoptr(FuKeyringResult) kr_result = NULL;
1553 g_autoptr(FuKeyringResult) kr_result_old = NULL;
1554 g_autoptr(GError) error_local = NULL;
Richard Hughes7403dc52017-08-10 15:34:10 +01001555 kr = fu_engine_get_keyring_for_kind (keyring_kind, error);
1556 if (kr == NULL)
1557 return FALSE;
Richard Hughes14047d72017-08-18 10:58:47 +01001558 if (!fu_keyring_setup (kr, error))
1559 return FALSE;
1560 if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error))
Richard Hughes7403dc52017-08-10 15:34:10 +01001561 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01001562 kr_result = fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error);
1563 if (kr_result == NULL)
Richard Hughes7403dc52017-08-10 15:34:10 +01001564 return FALSE;
Richard Hughesf69a4812017-08-16 12:27:51 +01001565
1566 /* verify the metadata was signed later than the existing
1567 * metadata for this remote to mitigate a rollback attack */
1568 kr_result_old = fu_engine_get_existing_keyring_result (self, kr,
1569 remote,
1570 &error_local);
1571 if (kr_result_old == NULL) {
1572 if (g_error_matches (error_local,
1573 G_FILE_ERROR,
1574 G_FILE_ERROR_NOENT)) {
1575 g_debug ("no existing valid keyrings: %s",
1576 error_local->message);
1577 } else {
1578 g_warning ("could not get existing keyring result: %s",
1579 error_local->message);
1580 }
1581 } else {
1582 gint64 delta = 0;
1583 if (fu_keyring_result_get_timestamp (kr_result) > 0 &&
1584 fu_keyring_result_get_timestamp (kr_result_old) > 0) {
1585 delta = fu_keyring_result_get_timestamp (kr_result) -
1586 fu_keyring_result_get_timestamp (kr_result_old);
1587 }
1588 if (delta < 0) {
1589 g_set_error (error,
1590 FWUPD_ERROR,
1591 FWUPD_ERROR_INVALID_FILE,
1592 "new signing timestamp was %"
1593 G_GINT64_FORMAT " seconds older",
1594 -delta);
1595 return FALSE;
1596 } else if (delta > 0) {
1597 g_debug ("timestamp increased, so no rollback");
1598 }
1599 }
Richard Hughes7403dc52017-08-10 15:34:10 +01001600 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001601
Richard Hughes99e621d2017-08-16 12:24:18 +01001602 /* save XML and signature to remotes.d */
Richard Hughes943d2c92017-06-21 09:04:39 +01001603 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache (remote),
1604 bytes_raw, error))
Richard Hughes9945edb2017-06-19 10:03:55 +01001605 return FALSE;
Richard Hughes99e621d2017-08-16 12:24:18 +01001606 if (keyring_kind != FWUPD_KEYRING_KIND_NONE) {
1607 if (!fu_common_set_contents_bytes (fwupd_remote_get_filename_cache_sig (remote),
1608 bytes_sig, error))
1609 return FALSE;
1610 }
Richard Hughes9945edb2017-06-19 10:03:55 +01001611 return fu_engine_load_metadata_store (self, error);
1612}
1613
1614static gboolean
1615fu_engine_get_updates_item_update (FuEngine *self, FuDeviceItem *item)
1616{
1617 AsApp *app;
1618 AsRelease *release;
1619 GPtrArray *releases;
1620 const gchar *tmp;
1621 const gchar *version;
1622 g_autoptr(GError) error = NULL;
1623 g_autoptr(GPtrArray) updates_list = NULL;
1624
1625 /* get device version */
1626 version = fu_device_get_version (item->device);
1627 if (version == NULL)
1628 return FALSE;
1629
1630 /* match the GUIDs in the XML */
1631 app = fu_engine_store_get_app_by_guids (self->store, item->device);
1632 if (app == NULL)
1633 return FALSE;
1634
1635 /* possibly convert the version from 0x to dotted */
1636 fu_engine_vendor_quirk_release_version (app);
1637
1638 /* possibly convert the flashed provide to a GUID */
1639 fu_engine_vendor_fixup_provide_value (app);
1640
1641 /* get latest release */
1642 release = as_app_get_release_default (app);
1643 if (release == NULL) {
1644 g_debug ("%s [%s] has no firmware update metadata",
1645 fu_device_get_id (item->device),
1646 fu_device_get_name (item->device));
1647 return FALSE;
1648 }
1649
1650 /* supported in metadata */
1651 fwupd_device_add_flag (fwupd_result_get_device (FWUPD_RESULT (item->device)),
1652 FWUPD_DEVICE_FLAG_SUPPORTED);
1653
1654 /* check if actually newer than what we have installed */
1655 if (as_utils_vercmp (as_release_get_version (release), version) <= 0) {
1656 g_debug ("%s has no firmware updates",
1657 fu_device_get_id (item->device));
1658 return FALSE;
1659 }
1660
1661 /* check we can install it */
1662 if (!fu_engine_check_requirements (app, item->device, &error)) {
1663 g_debug ("can not be installed: %s", error->message);
1664 return FALSE;
1665 }
1666
1667 /* only show devices that can be updated */
1668 if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) &&
1669 !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) {
1670 g_debug ("ignoring %s [%s] as not updatable live or offline",
1671 fu_device_get_id (item->device),
1672 fu_device_get_name (item->device));
1673 return FALSE;
1674 }
1675
1676 /* add application metadata */
1677 fu_device_set_update_id (item->device, as_app_get_id (app));
1678 tmp = as_app_get_developer_name (app, NULL);
1679 if (tmp != NULL)
1680 fu_device_set_update_vendor (item->device, tmp);
1681 tmp = as_app_get_name (app, NULL);
1682 if (tmp != NULL)
1683 fu_device_set_update_name (item->device, tmp);
1684 tmp = as_app_get_comment (app, NULL);
1685 if (tmp != NULL)
1686 fu_device_set_update_summary (item->device, tmp);
1687 tmp = as_app_get_description (app, NULL);
1688 if (tmp != NULL)
1689 fu_device_set_description (item->device, tmp);
1690 tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE);
1691 if (tmp != NULL)
1692 fu_device_set_update_homepage (item->device, tmp);
1693 tmp = as_app_get_project_license (app);
1694 if (tmp != NULL)
1695 fu_device_set_update_license (item->device, tmp);
1696 tmp = as_app_get_metadata_item (app, "fwupd::RemoteID");
1697 if (tmp != NULL)
1698 fu_device_set_update_remote_id (item->device, tmp);
Richard Hughes9945edb2017-06-19 10:03:55 +01001699 tmp = as_app_get_unique_id (app);
1700 if (tmp != NULL)
1701 fu_device_set_unique_id (item->device, tmp);
Richard Hughes9945edb2017-06-19 10:03:55 +01001702
1703 /* add release information */
Richard Hughesc6afb512017-08-22 10:22:20 +01001704 fu_engine_set_release_from_appstream (self,
1705 fwupd_result_get_release (FWUPD_RESULT (item->device)),
1706 app, release);
Richard Hughes9945edb2017-06-19 10:03:55 +01001707
1708 /* get the list of releases newer than the one installed */
1709 updates_list = g_ptr_array_new ();
1710 releases = as_app_get_releases (app);
1711 for (guint i = 0; i < releases->len; i++) {
1712 release = g_ptr_array_index (releases, i);
1713 if (as_utils_vercmp (as_release_get_version (release), version) <= 0)
1714 continue;
1715 tmp = as_release_get_description (release, NULL);
1716 if (tmp == NULL)
1717 continue;
1718 g_ptr_array_add (updates_list, release);
1719 }
1720
1721 /* no prefix on each release */
1722 if (updates_list->len == 1) {
1723 release = g_ptr_array_index (updates_list, 0);
1724 fu_device_set_update_description (item->device,
1725 as_release_get_description (release, NULL));
1726 } else {
1727 g_autoptr(GString) update_desc = NULL;
1728 update_desc = g_string_new ("");
1729
1730 /* get the descriptions with a version prefix */
1731 for (guint i = 0; i < updates_list->len; i++) {
1732 release = g_ptr_array_index (updates_list, i);
1733 g_string_append_printf (update_desc,
1734 "<p>%s:</p>%s",
1735 as_release_get_version (release),
1736 as_release_get_description (release, NULL));
1737 }
1738 if (update_desc->len > 0)
1739 fu_device_set_update_description (item->device, update_desc->str);
1740 }
1741
1742 /* success */
1743 return TRUE;
1744}
1745
1746/**
Richard Hughes9945edb2017-06-19 10:03:55 +01001747 * fu_engine_get_store_from_blob:
1748 * @self: A #FuEngine
1749 * @blob_cab: A #GBytes
1750 * @error: A #GError, or %NULL
1751 *
1752 * Creates an AppStream store from a .cab file blob.
1753 *
1754 * Returns: (transfer container): a #AsStore, or %NULL
1755 **/
1756AsStore *
1757fu_engine_get_store_from_blob (FuEngine *self, GBytes *blob_cab, GError **error)
1758{
1759 g_autofree gchar *checksum = NULL;
1760 g_autoptr(AsStore) store = NULL;
1761 g_autoptr(GError) error_local = NULL;
1762
1763 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1764 g_return_val_if_fail (blob_cab != NULL, NULL);
1765 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1766
1767 /* load file */
1768 store = as_store_new ();
1769 fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING);
1770 if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) {
1771 g_set_error_literal (error,
1772 FWUPD_ERROR,
1773 FWUPD_ERROR_INVALID_FILE,
1774 error_local->message);
1775 return NULL;
1776 }
1777
1778 /* get a checksum of the file and use it as the origin */
1779 checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256,
1780 g_bytes_get_data (blob_cab, NULL),
1781 g_bytes_get_size (blob_cab));
1782 as_store_set_origin (store, checksum);
1783
Mario Limonciello5735fd62017-07-13 15:47:52 -05001784 fu_engine_set_status (self, FWUPD_STATUS_IDLE);
Richard Hughes9945edb2017-06-19 10:03:55 +01001785 return g_steal_pointer (&store);
1786}
1787
1788static FwupdResult *
1789fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error)
1790{
1791 FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE;
1792 AsRelease *release;
1793 FwupdDevice *dev;
1794 FwupdRelease *rel;
1795 GPtrArray *provides;
1796 g_autoptr(FwupdResult) res = NULL;
1797
1798 res = fwupd_result_new ();
1799 dev = fwupd_result_get_device (res);
1800 provides = as_app_get_provides (app);
1801 for (guint i = 0; i < provides->len; i++) {
1802 AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i));
1803 FuDeviceItem *item;
1804 const gchar *guid;
1805
1806 /* not firmware */
1807 if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED)
1808 continue;
1809
1810 /* is a online or offline update appropriate */
1811 guid = as_provide_get_value (prov);
1812 if (guid == NULL)
1813 continue;
1814 item = fu_engine_get_item_by_guid (self, guid);
1815 if (item != NULL) {
Mario Limonciello790701f2017-07-13 13:55:55 -05001816 fwupd_device_set_name (dev, fu_device_get_name (item->device));
Richard Hughes9945edb2017-06-19 10:03:55 +01001817 fwupd_device_set_flags (dev, fu_device_get_flags (item->device));
1818 fwupd_device_set_id (dev, fu_device_get_id (item->device));
1819 }
1820
1821 /* add GUID */
1822 fwupd_device_add_guid (dev, guid);
1823 }
1824 if (fwupd_device_get_guids(dev)->len == 0) {
1825 g_set_error_literal (error,
1826 FWUPD_ERROR,
1827 FWUPD_ERROR_INTERNAL,
1828 "component has no GUIDs");
1829 return NULL;
1830 }
1831
1832 /* check we can install it */
1833 if (!fu_engine_check_requirements (app, NULL, error))
1834 return NULL;
1835
1836 /* verify trust */
1837 release = as_app_get_release_default (app);
1838 if (!fu_engine_get_release_trust_flags (release, &trust_flags, error))
1839 return NULL;
1840 fwupd_result_set_update_trust_flags (res, trust_flags);
1841
1842 /* possibly convert the version from 0x to dotted */
1843 fu_engine_vendor_quirk_release_version (app);
1844
1845 /* possibly convert the flashed provide to a GUID */
1846 fu_engine_vendor_fixup_provide_value (app);
1847
1848 /* create a result with all the metadata in */
1849 fwupd_device_set_description (dev, as_app_get_description (app, NULL));
1850 rel = fwupd_result_get_release (res);
1851 fwupd_release_set_homepage (rel, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE));
1852 fwupd_release_set_license (rel, as_app_get_project_license (app));
1853 fwupd_release_set_name (rel, as_app_get_name (app, NULL));
1854 fwupd_release_set_summary (rel, as_app_get_comment (app, NULL));
1855 fwupd_release_set_vendor (rel, as_app_get_developer_name (app, NULL));
Richard Hughes9945edb2017-06-19 10:03:55 +01001856 fwupd_result_set_unique_id (res, as_app_get_unique_id (app));
Richard Hughes9945edb2017-06-19 10:03:55 +01001857 fwupd_release_set_appstream_id (rel, as_app_get_id (app));
Richard Hughesc6afb512017-08-22 10:22:20 +01001858 fu_engine_set_release_from_appstream (self, rel, app, release);
Richard Hughes9945edb2017-06-19 10:03:55 +01001859 return g_steal_pointer (&res);
1860}
1861
1862/**
1863 * fu_engine_get_details_local:
1864 * @self: A #FuEngine
1865 * @fd: A file descriptor
1866 * @error: A #GError, or %NULL
1867 *
1868 * Gets the details about a local file.
1869 *
1870 * Note: this will close the fd when done
1871 *
1872 * Returns: (transfer container) (element-type FwupdResult): results
1873 **/
1874GPtrArray *
1875fu_engine_get_details_local (FuEngine *self, gint fd, GError **error)
1876{
1877 GPtrArray *apps;
1878 g_autoptr(AsStore) store = NULL;
1879 g_autoptr(GBytes) blob = NULL;
1880 g_autoptr(GPtrArray) details = NULL;
1881
1882 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1883 g_return_val_if_fail (fd > 0, NULL);
1884 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1885
1886 /* get all apps */
Richard Hughes943d2c92017-06-21 09:04:39 +01001887 blob = fu_common_get_contents_fd (fd, FU_ENGINE_FIRMWARE_SIZE_MAX, error);
Richard Hughes9945edb2017-06-19 10:03:55 +01001888 if (blob == NULL)
1889 return NULL;
1890 store = fu_engine_get_store_from_blob (self, blob, error);
1891 if (store == NULL)
1892 return NULL;
1893 apps = as_store_get_apps (store);
1894 if (apps->len == 0) {
1895 g_set_error_literal (error,
1896 FWUPD_ERROR,
1897 FWUPD_ERROR_INVALID_FILE,
1898 "no components");
1899 return NULL;
1900 }
1901
1902 /* create results with all the metadata in */
1903 details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1904 for (guint i = 0; i < apps->len; i++) {
1905 AsApp *app = g_ptr_array_index (apps, i);
1906 FwupdResult *res = NULL;
1907
1908 /* check we can install it */
1909 if (!fu_engine_check_requirements (app, NULL, error))
1910 return NULL;
1911
1912 as_app_set_origin (app, as_store_get_origin (store));
1913 res = fu_engine_get_result_from_app (self, app, error);
1914 if (res == NULL)
1915 return NULL;
1916 g_ptr_array_add (details, res);
1917 }
1918 return g_steal_pointer (&details);
1919}
1920
1921/**
1922 * fu_engine_get_devices:
1923 * @self: A #FuEngine
1924 * @error: A #GError, or %NULL
1925 *
1926 * Gets the list of devices.
1927 *
1928 * Returns: (transfer container) (element-type FwupdDevice): results
1929 **/
1930GPtrArray *
1931fu_engine_get_devices (FuEngine *self, GError **error)
1932{
1933 GPtrArray *devices;
1934
1935 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1936 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1937
1938 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1939 for (guint i = 0; i < self->devices->len; i++) {
1940 FuDeviceItem *item;
1941 item = g_ptr_array_index (self->devices, i);
1942 g_ptr_array_add (devices, g_object_ref (item->device));
1943 }
1944 if (devices->len == 0) {
1945 g_set_error_literal (error,
1946 FWUPD_ERROR,
1947 FWUPD_ERROR_NOTHING_TO_DO,
1948 "No detected devices");
1949 return NULL;
1950 }
1951 return devices;
1952}
1953
1954/**
1955 * fu_engine_get_updates:
1956 * @self: A #FuEngine
1957 * @error: A #GError, or %NULL
1958 *
1959 * Gets the list of updates.
1960 *
1961 * Returns: (transfer container) (element-type FwupdDevice): results
1962 **/
1963GPtrArray *
1964fu_engine_get_updates (FuEngine *self, GError **error)
1965{
1966 GPtrArray *updates;
1967
1968 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
1969 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1970
1971 updates = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1972 for (guint i = 0; i < self->devices->len; i++) {
1973 FuDeviceItem *item = g_ptr_array_index (self->devices, i);
1974 if (fu_engine_get_updates_item_update (self, item))
1975 g_ptr_array_add (updates, g_object_ref (item->device));
1976 }
1977 if (updates->len == 0) {
1978 g_set_error_literal (error,
1979 FWUPD_ERROR,
1980 FWUPD_ERROR_NOTHING_TO_DO,
1981 "No devices can be updated");
1982 return NULL;
1983 }
1984 return updates;
1985}
1986
1987/**
1988 * fu_engine_get_remotes:
1989 * @self: A #FuEngine
1990 * @device_id: A device ID
1991 * @error: A #GError, or %NULL
1992 *
1993 * Gets the list of remotes in use by the engine.
1994 *
1995 * Returns: (transfer container) (element-type FwupdRemote): results
1996 **/
1997GPtrArray *
1998fu_engine_get_remotes (FuEngine *self, GError **error)
1999{
2000 GPtrArray *remotes;
2001
2002 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2003 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2004
2005 remotes = fu_config_get_remotes (self->config);
2006 if (remotes->len == 0) {
2007 g_set_error (error,
2008 FWUPD_ERROR,
2009 FWUPD_ERROR_INTERNAL,
2010 "No remotes configured");
2011 return NULL;
2012 }
2013 return g_ptr_array_ref (remotes);
2014}
2015
2016/**
2017 * fu_engine_get_releases:
2018 * @self: A #FuEngine
2019 * @device_id: A device ID
2020 * @error: A #GError, or %NULL
2021 *
2022 * Gets the releases available for a specific device.
2023 *
2024 * Returns: (transfer container) (element-type FwupdResult): results
2025 **/
2026GPtrArray *
2027fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error)
2028{
2029 FuDeviceItem *item;
2030 GPtrArray *device_guids;
2031 g_autoptr(GPtrArray) releases = NULL;
2032
2033 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2034 g_return_val_if_fail (device_id != NULL, NULL);
2035 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2036
2037 /* find the device */
2038 item = fu_engine_get_item_by_id (self, device_id, error);
2039 if (item == NULL)
2040 return NULL;
2041
2042 /* get all the releases for the device */
2043 releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2044 device_guids = fu_device_get_guids (item->device);
2045 for (guint i = 0; i < device_guids->len; i++) {
2046 GPtrArray *releases_tmp;
2047 const gchar *guid = g_ptr_array_index (device_guids, i);
2048 AsApp *app = as_store_get_app_by_provide (self->store,
2049 AS_PROVIDE_KIND_FIRMWARE_FLASHED,
2050 guid);
2051 if (app == NULL)
2052 continue;
2053 releases_tmp = as_app_get_releases (app);
2054 for (guint j = 0; j < releases_tmp->len; j++) {
2055 AsRelease *release = g_ptr_array_index (releases_tmp, j);
2056 FwupdRelease *rel = fwupd_release_new ();
Richard Hughesc6afb512017-08-22 10:22:20 +01002057 fu_engine_set_release_from_appstream (self, rel, app, release);
Richard Hughes9945edb2017-06-19 10:03:55 +01002058 g_ptr_array_add (releases, g_object_ref (rel));
2059 }
2060 }
2061
2062 /* no devices */
2063 if (releases->len == 0) {
2064 g_set_error_literal (error,
2065 FWUPD_ERROR,
2066 FWUPD_ERROR_NOTHING_TO_DO,
2067 "No releases for device");
2068 return NULL;
2069 }
2070 return g_steal_pointer (&releases);
2071}
2072
2073/**
2074 * fu_engine_clear_results:
2075 * @self: A #FuEngine
2076 * @device_id: A device ID
2077 * @error: A #GError, or %NULL
2078 *
2079 * Clear the historical state of a specific device operation.
2080 *
2081 * Returns: %TRUE for success
2082 **/
2083gboolean
2084fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error)
2085{
2086 FuDeviceItem *item;
2087
2088 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2089 g_return_val_if_fail (device_id != NULL, FALSE);
2090 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2091
2092 /* find the device */
2093 item = fu_engine_get_item_by_id_fallback_pending (self, device_id, error);
2094 if (item == NULL)
2095 return FALSE;
2096
2097 /* call into the plugin */
2098 return fu_plugin_runner_clear_results (item->plugin, item->device, error);
2099}
2100
2101/**
2102 * fu_engine_get_results:
2103 * @self: A #FuEngine
2104 * @device_id: A device ID
2105 * @error: A #GError, or %NULL
2106 *
2107 * Gets the historical state of a specific device operation.
2108 *
2109 * Returns: (transfer container): a #FwupdResult, or %NULL
2110 **/
2111FwupdResult *
2112fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error)
2113{
2114 FuDeviceItem *item;
2115
2116 g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
2117 g_return_val_if_fail (device_id != NULL, NULL);
2118 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
2119
2120 /* find the device */
2121 item = fu_engine_get_item_by_id_fallback_pending (self, device_id, error);
2122 if (item == NULL)
2123 return NULL;
2124
2125 /* call into the plugin */
2126 if (!fu_plugin_runner_get_results (item->plugin, item->device, error))
2127 return NULL;
2128
2129 /* ensure the unique ID is set */
2130 if (fwupd_result_get_unique_id (FWUPD_RESULT (item->device)) == NULL) {
2131 g_autofree gchar *id2 = NULL;
2132 FwupdResult *res = FWUPD_RESULT (item->device);
2133 FwupdDevice *dev = fwupd_result_get_device (res);
Richard Hughes9945edb2017-06-19 10:03:55 +01002134 id2 = as_utils_unique_id_build (AS_APP_SCOPE_SYSTEM,
2135 AS_BUNDLE_KIND_UNKNOWN,
2136 NULL,
2137 AS_APP_KIND_FIRMWARE,
2138 fwupd_device_get_name (dev),
2139 fwupd_device_get_version (dev));
Richard Hughes9945edb2017-06-19 10:03:55 +01002140 fwupd_result_set_unique_id (res, id2);
2141 }
2142 return g_object_ref (item->device);
2143}
2144
2145static void
2146fu_engine_plugins_setup (FuEngine *self)
2147{
2148 g_autoptr(AsProfileTask) ptask = NULL;
2149
2150 ptask = as_profile_start_literal (self->profile, "FuMain:setup");
2151 g_assert (ptask != NULL);
2152 for (guint i = 0; i < self->plugins->len; i++) {
2153 g_autoptr(GError) error = NULL;
2154 g_autoptr(AsProfileTask) ptask2 = NULL;
2155 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2156 ptask2 = as_profile_start (self->profile,
2157 "FuMain:setup{%s}",
2158 fu_plugin_get_name (plugin));
2159 g_assert (ptask2 != NULL);
2160 if (!fu_plugin_runner_startup (plugin, &error)) {
2161 fu_plugin_set_enabled (plugin, FALSE);
2162 g_message ("disabling plugin because: %s", error->message);
2163 }
2164 }
2165}
2166
2167static void
2168fu_engine_plugins_coldplug (FuEngine *self)
2169{
2170 g_autoptr(AsProfileTask) ptask = NULL;
Richard Hughes535664c2017-07-24 10:30:09 +01002171 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes9945edb2017-06-19 10:03:55 +01002172
2173 /* don't allow coldplug to be scheduled when in coldplug */
2174 self->coldplug_running = TRUE;
2175
2176 /* prepare */
2177 for (guint i = 0; i < self->plugins->len; i++) {
2178 g_autoptr(GError) error = NULL;
2179 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2180 if (!fu_plugin_runner_coldplug_prepare (plugin, &error))
2181 g_warning ("failed to prepare coldplug: %s", error->message);
2182 }
2183
2184 /* do this in one place */
2185 if (self->coldplug_delay > 0) {
2186 g_debug ("sleeping for %ums", self->coldplug_delay);
2187 g_usleep (self->coldplug_delay * 1000);
2188 }
2189
2190 /* exec */
2191 ptask = as_profile_start_literal (self->profile, "FuMain:coldplug");
2192 g_assert (ptask != NULL);
2193 for (guint i = 0; i < self->plugins->len; i++) {
2194 g_autoptr(GError) error = NULL;
2195 g_autoptr(AsProfileTask) ptask2 = NULL;
2196 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2197 ptask2 = as_profile_start (self->profile,
2198 "FuMain:coldplug{%s}",
2199 fu_plugin_get_name (plugin));
2200 g_assert (ptask2 != NULL);
2201 if (!fu_plugin_runner_coldplug (plugin, &error)) {
2202 fu_plugin_set_enabled (plugin, FALSE);
2203 g_message ("disabling plugin because: %s", error->message);
2204 }
2205 }
2206
2207 /* cleanup */
2208 for (guint i = 0; i < self->plugins->len; i++) {
2209 g_autoptr(GError) error = NULL;
2210 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2211 if (!fu_plugin_runner_coldplug_cleanup (plugin, &error))
2212 g_warning ("failed to cleanup coldplug: %s", error->message);
2213 }
2214
Richard Hughes535664c2017-07-24 10:30:09 +01002215 /* print what we do have */
2216 for (guint i = 0; i < self->plugins->len; i++) {
2217 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2218 if (!fu_plugin_get_enabled (plugin))
2219 continue;
2220 g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin));
2221 }
2222 if (str->len > 2) {
2223 g_string_truncate (str, str->len - 2);
2224 g_message ("using plugins: %s", str->str);
2225 }
2226
Richard Hughes9945edb2017-06-19 10:03:55 +01002227 /* we can recoldplug from this point on */
2228 self->coldplug_running = FALSE;
2229}
2230
2231static void
2232fu_engine_plugin_device_added_cb (FuPlugin *plugin,
2233 FuDevice *device,
2234 gpointer user_data)
2235{
2236 FuEngine *self = (FuEngine *) user_data;
2237 FuDeviceItem *item;
2238 GPtrArray *blacklisted_devices;
Richard Hughes32684f22017-07-13 09:32:21 +01002239 GPtrArray *device_guids;
Richard Hughes9945edb2017-06-19 10:03:55 +01002240
2241 /* device has no GUIDs set! */
Richard Hughes32684f22017-07-13 09:32:21 +01002242 device_guids = fu_device_get_guids (device);
2243 if (device_guids->len == 0) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002244 g_warning ("no GUIDs for device %s [%s]",
2245 fu_device_get_id (device),
2246 fu_device_get_name (device));
2247 return;
2248 }
2249
2250 /* is this GUID blacklisted */
2251 blacklisted_devices = fu_config_get_blacklist_devices (self->config);
2252 for (guint i = 0; i < blacklisted_devices->len; i++) {
Richard Hughes32684f22017-07-13 09:32:21 +01002253 const gchar *blacklisted_guid = g_ptr_array_index (blacklisted_devices, i);
2254 for (guint j = 0; j < device_guids->len; j++) {
2255 const gchar *device_guid = g_ptr_array_index (device_guids, j);
2256 if (g_strcmp0 (blacklisted_guid, device_guid) == 0) {
2257 g_debug ("%s is blacklisted [%s], ignoring from %s",
2258 fu_device_get_id (device), device_guid,
2259 fu_plugin_get_name (plugin));
2260 return;
2261 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002262 }
2263 }
2264
2265 /* remove any fake device */
2266 item = fu_engine_get_item_by_id (self, fu_device_get_id (device), NULL);
2267 if (item != NULL) {
Richard Hughes22c88de2017-06-21 17:32:07 +01002268 if (g_strcmp0 (fu_device_get_plugin (item->device),
2269 fu_plugin_get_name (plugin)) == 0) {
2270 g_warning ("already added %s by %s",
2271 fu_device_get_id (item->device),
2272 fu_device_get_plugin (item->device));
2273 } else {
2274 g_debug ("already added %s by %s, ignoring device from %s",
2275 fu_device_get_id (item->device),
2276 fu_device_get_plugin (item->device),
2277 fu_plugin_get_name (plugin));
2278 }
Richard Hughes9945edb2017-06-19 10:03:55 +01002279 return;
2280 }
2281
2282 /* create new device */
2283 item = g_new0 (FuDeviceItem, 1);
2284 item->device = g_object_ref (device);
2285 item->plugin = g_object_ref (plugin);
2286 g_ptr_array_add (self->devices, item);
2287
2288 /* match the metadata at this point so clients can tell if the
2289 * device is worthy */
2290 fu_engine_get_updates_item_update (self, item);
2291
2292 /* notify clients */
2293 fu_engine_emit_device_added (self, item->device);
2294 fu_engine_emit_changed (self);
2295}
2296
2297static void
2298fu_engine_plugin_device_removed_cb (FuPlugin *plugin,
2299 FuDevice *device,
2300 gpointer user_data)
2301{
2302 FuEngine *self = (FuEngine *) user_data;
2303 FuDeviceItem *item;
2304 g_autoptr(GError) error = NULL;
2305
2306 item = fu_engine_get_item_by_id (self, fu_device_get_id (device), &error);
2307 if (item == NULL) {
2308 g_debug ("%s", error->message);
2309 return;
2310 }
2311
2312 /* check this came from the same plugin */
2313 if (g_strcmp0 (fu_plugin_get_name (plugin),
2314 fu_plugin_get_name (item->plugin)) != 0) {
2315 g_debug ("ignoring duplicate removal from %s",
2316 fu_plugin_get_name (plugin));
2317 return;
2318 }
2319
2320 /* make the UI update */
2321 fu_engine_emit_device_removed (self, device);
2322 g_ptr_array_remove (self->devices, item);
2323 fu_engine_emit_changed (self);
2324}
2325
2326static void
2327fu_engine_plugin_status_changed_cb (FuPlugin *plugin,
2328 FwupdStatus status,
2329 gpointer user_data)
2330{
2331 FuEngine *self = (FuEngine *) user_data;
2332 fu_engine_set_status (self, status);
2333}
2334
2335static void
2336fu_engine_plugin_percentage_changed_cb (FuPlugin *plugin,
2337 guint percentage,
2338 gpointer user_data)
2339{
2340 FuEngine *self = (FuEngine *) user_data;
2341 fu_engine_set_percentage (self, percentage);
2342}
2343
2344static gboolean
2345fu_engine_recoldplug_delay_cb (gpointer user_data)
2346{
2347 FuEngine *self = (FuEngine *) user_data;
2348 g_debug ("performing a recoldplug");
2349 fu_engine_plugins_coldplug (self);
2350 self->coldplug_id = 0;
2351 return FALSE;
2352}
2353
2354static void
2355fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self)
2356{
2357 if (self->coldplug_running) {
2358 g_warning ("coldplug already running, cannot recoldplug");
2359 return;
2360 }
2361 g_debug ("scheduling a recoldplug");
2362 if (self->coldplug_id != 0)
2363 g_source_remove (self->coldplug_id);
2364 self->coldplug_id = g_timeout_add (1500, fu_engine_recoldplug_delay_cb, self);
2365}
2366
2367static void
2368fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngine *self)
2369{
2370 self->coldplug_delay = MAX (self->coldplug_delay, duration);
2371 g_debug ("got coldplug delay of %ums, global maximum is now %ums",
2372 duration, self->coldplug_delay);
2373}
2374
Richard Hughes535664c2017-07-24 10:30:09 +01002375static gint
2376fu_engine_plugin_sort_cb (gconstpointer a, gconstpointer b)
2377{
2378 FuPlugin *plugin1 = *((FuPlugin **) a);
2379 FuPlugin *plugin2 = *((FuPlugin **) b);
2380 return g_strcmp0 (fu_plugin_get_name (plugin1),
2381 fu_plugin_get_name (plugin2));
2382}
2383
Richard Hughes9945edb2017-06-19 10:03:55 +01002384static gboolean
2385fu_engine_load_plugins (FuEngine *self, GError **error)
2386{
2387 const gchar *fn;
2388 g_autoptr(GDir) dir = NULL;
2389
2390 /* search */
2391 dir = g_dir_open (PLUGINDIR, 0, error);
2392 if (dir == NULL)
2393 return FALSE;
2394 while ((fn = g_dir_read_name (dir)) != NULL) {
2395 GPtrArray *blacklist;
2396 g_autofree gchar *filename = NULL;
2397 g_autoptr(FuPlugin) plugin = NULL;
2398 g_autoptr(GError) error_local = NULL;
2399
2400 /* ignore non-plugins */
2401 if (!g_str_has_suffix (fn, ".so"))
2402 continue;
2403
2404 /* open module */
2405 filename = g_build_filename (PLUGINDIR, fn, NULL);
2406 plugin = fu_plugin_new ();
2407 fu_plugin_set_usb_context (plugin, self->usb_ctx);
2408 fu_plugin_set_hwids (plugin, self->hwids);
2409 g_debug ("adding plugin %s", filename);
2410 if (!fu_plugin_open (plugin, filename, &error_local)) {
2411 g_warning ("failed to open plugin %s: %s",
2412 filename, error_local->message);
2413 continue;
2414 }
2415
2416 /* is blacklisted */
2417 blacklist = fu_config_get_blacklist_plugins (self->config);
2418 for (guint i = 0; i < blacklist->len; i++) {
2419 const gchar *name = g_ptr_array_index (blacklist, i);
2420 if (g_strcmp0 (name, fu_plugin_get_name (plugin)) == 0) {
2421 fu_plugin_set_enabled (plugin, FALSE);
2422 break;
2423 }
2424 }
2425 if (!fu_plugin_get_enabled (plugin)) {
2426 g_debug ("%s blacklisted by config",
2427 fu_plugin_get_name (plugin));
2428 continue;
2429 }
2430
2431 /* watch for changes */
2432 g_signal_connect (plugin, "device-added",
2433 G_CALLBACK (fu_engine_plugin_device_added_cb),
2434 self);
2435 g_signal_connect (plugin, "device-removed",
2436 G_CALLBACK (fu_engine_plugin_device_removed_cb),
2437 self);
2438 g_signal_connect (plugin, "status-changed",
2439 G_CALLBACK (fu_engine_plugin_status_changed_cb),
2440 self);
2441 g_signal_connect (plugin, "percentage-changed",
2442 G_CALLBACK (fu_engine_plugin_percentage_changed_cb),
2443 self);
2444 g_signal_connect (plugin, "recoldplug",
2445 G_CALLBACK (fu_engine_plugin_recoldplug_cb),
2446 self);
2447 g_signal_connect (plugin, "set-coldplug-delay",
2448 G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb),
2449 self);
2450
2451 /* add */
2452 g_ptr_array_add (self->plugins, g_object_ref (plugin));
2453 g_hash_table_insert (self->plugins_hash,
2454 g_strdup (fu_plugin_get_name (plugin)),
2455 g_object_ref (plugin));
2456 }
Richard Hughes535664c2017-07-24 10:30:09 +01002457 g_ptr_array_sort (self->plugins, fu_engine_plugin_sort_cb);
Richard Hughes9945edb2017-06-19 10:03:55 +01002458
2459 return TRUE;
2460}
2461
2462/**
2463 * fu_engine_check_plugins_pending:
2464 * @self: A #FuEngine
2465 * @error: A #GError, or %NULL
2466 *
2467 * Checks if any plugins have pending devices to be added.
2468 *
2469 * Returns: %FALSE if any plugins have pending devices.
2470 **/
2471gboolean
2472fu_engine_check_plugins_pending (FuEngine *self, GError **error)
2473{
2474 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2475 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2476
2477 for (guint i = 0; i < self->plugins->len; i++) {
2478 FuPlugin *plugin = g_ptr_array_index (self->plugins, i);
2479 if (fu_plugin_has_device_delay (plugin)) {
2480 g_set_error (error,
2481 FWUPD_ERROR,
2482 FWUPD_ERROR_INTERNAL,
2483 "%s pending",
2484 fu_plugin_get_name (plugin));
2485 return FALSE;
2486 }
2487 }
2488 return TRUE;
2489}
2490
2491static gboolean
2492fu_engine_cleanup_state (GError **error)
2493{
2494 const gchar *filenames[] = {
2495 "/var/cache/app-info/xmls/fwupd-verify.xml",
2496 "/var/cache/app-info/xmls/fwupd.xml",
2497 NULL };
2498 for (guint i = 0; filenames[i] != NULL; i++) {
2499 g_autoptr(GFile) file = g_file_new_for_path (filenames[i]);
2500 if (g_file_query_exists (file, NULL)) {
2501 if (!g_file_delete (file, NULL, error))
2502 return FALSE;
2503 }
2504 }
2505 return TRUE;
2506}
2507
2508/**
2509 * fu_engine_load:
2510 * @self: A #FuEngine
2511 * @error: A #GError, or %NULL
2512 *
2513 * Load the firmware update engine so it is ready for use.
2514 *
2515 * Returns: %TRUE for success
2516 **/
2517gboolean
2518fu_engine_load (FuEngine *self, GError **error)
2519{
2520 g_return_val_if_fail (FU_IS_ENGINE (self), FALSE);
2521 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
2522
2523 /* read config file */
2524 if (!fu_config_load (self->config, error)) {
2525 g_prefix_error (error, "Failed to load config: ");
2526 return FALSE;
2527 }
2528
2529 /* load AppStream metadata */
2530 as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE);
2531 if (!fu_engine_load_metadata_store (self, error)) {
2532 g_prefix_error (error, "Failed to load AppStream data: ");
2533 return FALSE;
2534 }
2535
2536 /* set shared USB context */
2537 self->usb_ctx = g_usb_context_new (error);
2538 if (self->usb_ctx == NULL) {
2539 g_prefix_error (error, "Failed to get USB context: ");
2540 return FALSE;
2541 }
Richard Hughes1ad45ca2017-07-22 21:07:42 +01002542#if G_USB_CHECK_VERSION(0,2,11)
2543 g_usb_context_set_flags (self->usb_ctx,
2544 G_USB_CONTEXT_FLAGS_AUTO_OPEN_DEVICES);
2545#endif
Richard Hughes9945edb2017-06-19 10:03:55 +01002546
2547 /* load the hwids */
Richard Hughesd7704d42017-08-08 20:29:09 +01002548 if (!fu_hwids_setup (self->hwids, NULL, error)) {
Richard Hughes9945edb2017-06-19 10:03:55 +01002549 g_prefix_error (error, "Failed to load hwids: ");
2550 return FALSE;
2551 }
2552
2553 /* delete old data files */
2554 if (!fu_engine_cleanup_state (error)) {
2555 g_prefix_error (error, "Failed to clean up: ");
2556 return FALSE;
2557 }
2558
2559 /* load plugin */
2560 if (!fu_engine_load_plugins (self, error)) {
2561 g_prefix_error (error, "Failed to load plugins: ");
2562 return FALSE;
2563 }
2564
2565 /* disable udev? */
2566 if (!fu_config_get_enable_option_rom (self->config)) {
2567 FuPlugin *plugin = g_hash_table_lookup (self->plugins_hash, "udev");
2568 if (plugin != NULL)
2569 fu_plugin_set_enabled (plugin, FALSE);
2570 }
2571
2572 /* add devices */
2573 fu_engine_plugins_setup (self);
2574 g_usb_context_enumerate (self->usb_ctx);
2575 fu_engine_plugins_coldplug (self);
2576
2577 /* success */
2578 return TRUE;
2579}
2580
2581static void
2582fu_engine_class_init (FuEngineClass *klass)
2583{
2584 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2585 object_class->finalize = fu_engine_finalize;
2586
2587 signals[SIGNAL_CHANGED] =
2588 g_signal_new ("changed",
2589 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2590 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
2591 G_TYPE_NONE, 0);
2592 signals[SIGNAL_DEVICE_ADDED] =
2593 g_signal_new ("device-added",
2594 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2595 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
2596 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
2597 signals[SIGNAL_DEVICE_REMOVED] =
2598 g_signal_new ("device-removed",
2599 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2600 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
2601 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughesa5bb4d82017-06-19 20:22:25 +01002602 signals[SIGNAL_DEVICE_CHANGED] =
2603 g_signal_new ("device-changed",
2604 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2605 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
2606 G_TYPE_NONE, 1, FU_TYPE_DEVICE);
Richard Hughes9945edb2017-06-19 10:03:55 +01002607 signals[SIGNAL_STATUS_CHANGED] =
2608 g_signal_new ("status-changed",
2609 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2610 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
2611 G_TYPE_NONE, 1, G_TYPE_UINT);
2612 signals[SIGNAL_PERCENTAGE_CHANGED] =
2613 g_signal_new ("percentage-changed",
2614 G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
2615 0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
2616 G_TYPE_NONE, 1, G_TYPE_UINT);
2617}
2618
2619static void
2620fu_engine_init (FuEngine *self)
2621{
2622 self->percentage = 0;
2623 self->status = FWUPD_STATUS_IDLE;
2624 self->config = fu_config_new ();
2625 self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_engine_item_free);
Richard Hughesd7704d42017-08-08 20:29:09 +01002626 self->hwids = fu_hwids_new ();
Richard Hughes9945edb2017-06-19 10:03:55 +01002627 self->pending = fu_pending_new ();
2628 self->profile = as_profile_new ();
2629 self->store = as_store_new ();
2630 self->plugins = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2631 self->plugins_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
2632 g_free, (GDestroyNotify) g_object_unref);
2633}
2634
2635static void
2636fu_engine_finalize (GObject *obj)
2637{
2638 FuEngine *self = FU_ENGINE (obj);
2639
2640 if (self->usb_ctx != NULL)
2641 g_object_unref (self->usb_ctx);
2642 if (self->coldplug_id != 0)
2643 g_source_remove (self->coldplug_id);
2644
Richard Hughes9945edb2017-06-19 10:03:55 +01002645 g_hash_table_unref (self->plugins_hash);
2646 g_object_unref (self->config);
Richard Hughesd7704d42017-08-08 20:29:09 +01002647 g_object_unref (self->hwids);
Richard Hughes9945edb2017-06-19 10:03:55 +01002648 g_object_unref (self->pending);
2649 g_object_unref (self->profile);
2650 g_object_unref (self->store);
2651 g_ptr_array_unref (self->devices);
2652 g_ptr_array_unref (self->plugins);
2653
2654 G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj);
2655}
2656
2657FuEngine *
2658fu_engine_new (void)
2659{
2660 FuEngine *self;
2661 self = g_object_new (FU_TYPE_ENGINE, NULL);
2662 return FU_ENGINE (self);
2663}