Coverage Report

Created: 2025-08-03 06:57

/src/fwupd/libfwupdplugin/fu-context.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2021 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuContext"
8
9
#include "config.h"
10
11
#include "fu-bios-settings-private.h"
12
#include "fu-common-private.h"
13
#include "fu-config-private.h"
14
#include "fu-context-helper.h"
15
#include "fu-context-private.h"
16
#include "fu-dummy-efivars.h"
17
#include "fu-efi-device-path-list.h"
18
#include "fu-efi-file-path-device-path.h"
19
#include "fu-efi-hard-drive-device-path.h"
20
#include "fu-fdt-firmware.h"
21
#include "fu-hwids-private.h"
22
#include "fu-path.h"
23
#include "fu-pefile-firmware.h"
24
#include "fu-volume-private.h"
25
26
/**
27
 * FuContext:
28
 *
29
 * A context that represents the shared system state. This object is shared
30
 * between the engine, the plugins and the devices.
31
 */
32
33
typedef struct {
34
  FuContextFlags flags;
35
  FuHwids *hwids;
36
  FuConfig *config;
37
  FuSmbios *smbios;
38
  FuSmbiosChassisKind chassis_kind;
39
  FuQuirks *quirks;
40
  FuEfivars *efivars;
41
  GPtrArray *backends;
42
  GHashTable *runtime_versions;
43
  GHashTable *compile_versions;
44
  GHashTable *udev_subsystems; /* utf8:GPtrArray */
45
  GPtrArray *esp_volumes;
46
  GHashTable *firmware_gtypes; /* utf8:GType */
47
  GHashTable *hwid_flags;      /* str: */
48
  FuPowerState power_state;
49
  FuLidState lid_state;
50
  FuDisplayState display_state;
51
  guint battery_level;
52
  guint battery_threshold;
53
  FuBiosSettings *host_bios_settings;
54
  FuFirmware *fdt; /* optional */
55
  gchar *esp_location;
56
} FuContextPrivate;
57
58
enum { SIGNAL_SECURITY_CHANGED, SIGNAL_HOUSEKEEPING, SIGNAL_LAST };
59
60
enum {
61
  PROP_0,
62
  PROP_POWER_STATE,
63
  PROP_LID_STATE,
64
  PROP_DISPLAY_STATE,
65
  PROP_BATTERY_LEVEL,
66
  PROP_BATTERY_THRESHOLD,
67
  PROP_FLAGS,
68
  PROP_LAST
69
};
70
71
static guint signals[SIGNAL_LAST] = {0};
72
73
G_DEFINE_TYPE_WITH_PRIVATE(FuContext, fu_context, G_TYPE_OBJECT)
74
75
0
#define GET_PRIVATE(o) (fu_context_get_instance_private(o))
76
77
static GFile *
78
fu_context_get_fdt_file(GError **error)
79
0
{
80
0
  g_autofree gchar *fdtfn_local = NULL;
81
0
  g_autofree gchar *fdtfn_sys = NULL;
82
0
  g_autofree gchar *localstatedir_pkg = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG);
83
0
  g_autofree gchar *sysfsdir = NULL;
84
85
  /* look for override first, fall back to system value */
86
0
  fdtfn_local = g_build_filename(localstatedir_pkg, "system.dtb", NULL);
87
0
  if (g_file_test(fdtfn_local, G_FILE_TEST_EXISTS))
88
0
    return g_file_new_for_path(fdtfn_local);
89
90
  /* actual hardware value */
91
0
  sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW);
92
0
  fdtfn_sys = g_build_filename(sysfsdir, "fdt", NULL);
93
0
  if (g_file_test(fdtfn_sys, G_FILE_TEST_EXISTS))
94
0
    return g_file_new_for_path(fdtfn_sys);
95
96
  /* failed */
97
0
  g_set_error(error,
98
0
        FWUPD_ERROR,
99
0
        FWUPD_ERROR_NOT_SUPPORTED,
100
0
        "cannot find %s or override %s",
101
0
        fdtfn_sys,
102
0
        fdtfn_local);
103
0
  return NULL;
104
0
}
105
106
/**
107
 * fu_context_get_fdt:
108
 * @self: a #FuContext
109
 * @error: (nullable): optional return location for an error
110
 *
111
 * Gets and parses the system FDT, aka. the Flat Device Tree.
112
 *
113
 * The results are cached internally to the context, and subsequent calls to this function
114
 * returns the pre-parsed object.
115
 *
116
 * Returns: (transfer full): a #FuFdtFirmware, or %NULL
117
 *
118
 * Since: 1.8.10
119
 **/
120
FuFirmware *
121
fu_context_get_fdt(FuContext *self, GError **error)
122
0
{
123
0
  FuContextPrivate *priv = GET_PRIVATE(self);
124
125
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
126
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
127
128
  /* load if not already parsed */
129
0
  if (priv->fdt == NULL) {
130
0
    g_autoptr(FuFirmware) fdt_tmp = fu_fdt_firmware_new();
131
0
    g_autoptr(GFile) file = fu_context_get_fdt_file(error);
132
0
    if (file == NULL)
133
0
      return NULL;
134
0
    if (!fu_firmware_parse_file(fdt_tmp,
135
0
              file,
136
0
              FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
137
0
              error)) {
138
0
      g_prefix_error(error, "failed to parse FDT: ");
139
0
      return NULL;
140
0
    }
141
0
    priv->fdt = g_steal_pointer(&fdt_tmp);
142
0
  }
143
144
  /* success */
145
0
  return g_object_ref(priv->fdt);
146
0
}
147
148
/**
149
 * fu_context_get_efivars:
150
 * @self: a #FuContext
151
 *
152
 * Gets the EFI variable store.
153
 *
154
 * Returns: (transfer none): a #FuEfivars
155
 *
156
 * Since: 2.0.0
157
 **/
158
FuEfivars *
159
fu_context_get_efivars(FuContext *self)
160
0
{
161
0
  FuContextPrivate *priv = GET_PRIVATE(self);
162
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
163
0
  return priv->efivars;
164
0
}
165
166
/**
167
 * fu_context_efivars_check_free_space:
168
 * @self: a #FuContext
169
 * @count: size in bytes
170
 * @error: (nullable): optional return location for an error
171
 *
172
 * Checks for a given amount of free space in the EFI NVRAM variable store.
173
 *
174
 * Returns: %TRUE for success
175
 *
176
 * Since: 2.0.12
177
 **/
178
gboolean
179
fu_context_efivars_check_free_space(FuContext *self, gsize count, GError **error)
180
0
{
181
0
  FuContextPrivate *priv = GET_PRIVATE(self);
182
0
  guint64 total;
183
184
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
185
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
186
187
  /* escape hatch */
188
0
  if (fu_context_has_flag(self, FU_CONTEXT_FLAG_IGNORE_EFIVARS_FREE_SPACE))
189
0
    return TRUE;
190
191
0
  total = fu_efivars_space_free(priv->efivars, error);
192
0
  if (total == G_MAXUINT64)
193
0
    return FALSE;
194
0
  if (total < count) {
195
0
    g_autofree gchar *countstr = g_format_size(count);
196
0
    g_autofree gchar *totalstr = g_format_size(total);
197
0
    g_set_error(error,
198
0
          FWUPD_ERROR,
199
0
          FWUPD_ERROR_BROKEN_SYSTEM,
200
0
          "Not enough efivarfs space, requested %s and got %s",
201
0
          countstr,
202
0
          totalstr);
203
0
    return FALSE;
204
0
  }
205
0
  return TRUE;
206
0
}
207
208
/**
209
 * fu_context_get_smbios:
210
 * @self: a #FuContext
211
 *
212
 * Gets the SMBIOS store.
213
 *
214
 * Returns: (transfer none): a #FuSmbios
215
 *
216
 * Since: 1.8.10
217
 **/
218
FuSmbios *
219
fu_context_get_smbios(FuContext *self)
220
0
{
221
0
  FuContextPrivate *priv = GET_PRIVATE(self);
222
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
223
0
  return priv->smbios;
224
0
}
225
226
/**
227
 * fu_context_get_hwids:
228
 * @self: a #FuContext
229
 *
230
 * Gets the HWIDs store.
231
 *
232
 * Returns: (transfer none): a #FuHwids
233
 *
234
 * Since: 1.8.10
235
 **/
236
FuHwids *
237
fu_context_get_hwids(FuContext *self)
238
0
{
239
0
  FuContextPrivate *priv = GET_PRIVATE(self);
240
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
241
0
  return priv->hwids;
242
0
}
243
244
/**
245
 * fu_context_get_config:
246
 * @self: a #FuContext
247
 *
248
 * Gets the system config.
249
 *
250
 * Returns: (transfer none): a #FuHwids
251
 *
252
 * Since: 1.9.1
253
 **/
254
FuConfig *
255
fu_context_get_config(FuContext *self)
256
0
{
257
0
  FuContextPrivate *priv = GET_PRIVATE(self);
258
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
259
0
  return priv->config;
260
0
}
261
262
/**
263
 * fu_context_get_smbios_string:
264
 * @self: a #FuContext
265
 * @type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
266
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
267
 * @offset: a SMBIOS offset
268
 * @error: (nullable): optional return location for an error
269
 *
270
 * Gets a hardware SMBIOS string.
271
 *
272
 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
273
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
274
 *
275
 * Returns: a string, or %NULL
276
 *
277
 * Since: 2.0.7
278
 **/
279
const gchar *
280
fu_context_get_smbios_string(FuContext *self,
281
           guint8 type,
282
           guint8 length,
283
           guint8 offset,
284
           GError **error)
285
0
{
286
0
  FuContextPrivate *priv = GET_PRIVATE(self);
287
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
288
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
289
0
    g_critical("cannot use SMBIOS before calling ->load_hwinfo()");
290
0
    return NULL;
291
0
  }
292
0
  return fu_smbios_get_string(priv->smbios, type, length, offset, error);
293
0
}
294
295
/**
296
 * fu_context_get_smbios_data:
297
 * @self: a #FuContext
298
 * @type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
299
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
300
 * @error: (nullable): optional return location for an error
301
 *
302
 * Gets all hardware SMBIOS data for a specific type.
303
 *
304
 * Returns: (transfer container) (element-type GBytes): a #GBytes, or %NULL if not found
305
 *
306
 * Since: 2.0.7
307
 **/
308
GPtrArray *
309
fu_context_get_smbios_data(FuContext *self, guint8 type, guint8 length, GError **error)
310
0
{
311
0
  FuContextPrivate *priv = GET_PRIVATE(self);
312
313
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
314
315
  /* must be valid and non-zero length */
316
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
317
0
    g_critical("cannot use SMBIOS before calling ->load_hwinfo()");
318
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no data");
319
0
    return NULL;
320
0
  }
321
0
  return fu_smbios_get_data(priv->smbios, type, length, error);
322
0
}
323
324
/**
325
 * fu_context_get_smbios_integer:
326
 * @self: a #FuContext
327
 * @type: a structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
328
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
329
 * @offset: a structure offset
330
 * @error: (nullable): optional return location for an error
331
 *
332
 * Reads an integer value from the SMBIOS string table of a specific structure.
333
 *
334
 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
335
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
336
 *
337
 * Returns: an integer, or %G_MAXUINT if invalid or not found
338
 *
339
 * Since: 2.0.7
340
 **/
341
guint
342
fu_context_get_smbios_integer(FuContext *self,
343
            guint8 type,
344
            guint8 length,
345
            guint8 offset,
346
            GError **error)
347
0
{
348
0
  FuContextPrivate *priv = GET_PRIVATE(self);
349
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT);
350
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
351
0
    g_critical("cannot use SMBIOS before calling ->load_hwinfo()");
352
0
    return G_MAXUINT;
353
0
  }
354
0
  return fu_smbios_get_integer(priv->smbios, type, length, offset, error);
355
0
}
356
357
/**
358
 * fu_context_reload_bios_settings:
359
 * @self: a #FuContext
360
 * @error: (nullable): optional return location for an error
361
 *
362
 * Refreshes the list of firmware attributes on the system.
363
 *
364
 * Since: 1.8.4
365
 **/
366
gboolean
367
fu_context_reload_bios_settings(FuContext *self, GError **error)
368
0
{
369
0
  FuContextPrivate *priv = GET_PRIVATE(self);
370
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
371
0
  return fu_bios_settings_setup(priv->host_bios_settings, error);
372
0
}
373
374
/**
375
 * fu_context_get_bios_settings:
376
 * @self: a #FuContext
377
 *
378
 * Returns all the firmware attributes defined in the system.
379
 *
380
 * Returns: (transfer full): A #FuBiosSettings
381
 *
382
 * Since: 1.8.4
383
 **/
384
FuBiosSettings *
385
fu_context_get_bios_settings(FuContext *self)
386
0
{
387
0
  FuContextPrivate *priv = GET_PRIVATE(self);
388
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
389
0
  return g_object_ref(priv->host_bios_settings);
390
0
}
391
392
/**
393
 * fu_context_get_bios_setting:
394
 * @self: a #FuContext
395
 * @name: a BIOS setting title, e.g. `BootOrderLock`
396
 *
397
 * Finds out if a system supports a given BIOS setting.
398
 *
399
 * Returns: (transfer none): #FwupdBiosSetting if the attr exists.
400
 *
401
 * Since: 1.8.4
402
 **/
403
FwupdBiosSetting *
404
fu_context_get_bios_setting(FuContext *self, const gchar *name)
405
0
{
406
0
  FuContextPrivate *priv = GET_PRIVATE(self);
407
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
408
0
  g_return_val_if_fail(name != NULL, NULL);
409
0
  return fu_bios_settings_get_attr(priv->host_bios_settings, name);
410
0
}
411
412
/**
413
 * fu_context_get_bios_setting_pending_reboot:
414
 * @self: a #FuContext
415
 *
416
 * Determine if updates to BIOS settings are pending until next boot.
417
 *
418
 * Returns: %TRUE if updates are pending.
419
 *
420
 * Since: 1.8.4
421
 **/
422
gboolean
423
fu_context_get_bios_setting_pending_reboot(FuContext *self)
424
0
{
425
0
  FuContextPrivate *priv = GET_PRIVATE(self);
426
0
  gboolean ret;
427
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
428
0
  fu_bios_settings_get_pending_reboot(priv->host_bios_settings, &ret, NULL);
429
0
  return ret;
430
0
}
431
432
/**
433
 * fu_context_get_chassis_kind:
434
 * @self: a #FuContext
435
 *
436
 * Gets the chassis kind, if known.
437
 *
438
 * Returns: a #FuSmbiosChassisKind, e.g. %FU_SMBIOS_CHASSIS_KIND_LAPTOP
439
 *
440
 * Since: 1.8.10
441
 **/
442
FuSmbiosChassisKind
443
fu_context_get_chassis_kind(FuContext *self)
444
0
{
445
0
  FuContextPrivate *priv = GET_PRIVATE(self);
446
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
447
0
  return priv->chassis_kind;
448
0
}
449
450
/**
451
 * fu_context_set_chassis_kind:
452
 * @self: a #FuContext
453
 * @chassis_kind: a #FuSmbiosChassisKind, e.g. %FU_SMBIOS_CHASSIS_KIND_TABLET
454
 *
455
 * Sets the chassis kind.
456
 *
457
 * Since: 1.8.10
458
 **/
459
void
460
fu_context_set_chassis_kind(FuContext *self, FuSmbiosChassisKind chassis_kind)
461
0
{
462
0
  FuContextPrivate *priv = GET_PRIVATE(self);
463
0
  g_return_if_fail(FU_IS_CONTEXT(self));
464
0
  priv->chassis_kind = chassis_kind;
465
0
}
466
467
/**
468
 * fu_context_has_hwid_guid:
469
 * @self: a #FuContext
470
 * @guid: a GUID, e.g. `059eb22d-6dc7-59af-abd3-94bbe017f67c`
471
 *
472
 * Finds out if a hardware GUID exists.
473
 *
474
 * Returns: %TRUE if the GUID exists
475
 *
476
 * Since: 1.6.0
477
 **/
478
gboolean
479
fu_context_has_hwid_guid(FuContext *self, const gchar *guid)
480
0
{
481
0
  FuContextPrivate *priv = GET_PRIVATE(self);
482
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
483
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
484
0
    g_critical("cannot use HWIDs before calling ->load_hwinfo()");
485
0
    return FALSE;
486
0
  }
487
0
  return fu_hwids_has_guid(priv->hwids, guid);
488
0
}
489
490
/**
491
 * fu_context_get_hwid_guids:
492
 * @self: a #FuContext
493
 *
494
 * Returns all the HWIDs defined in the system. All hardware IDs on a
495
 * specific system can be shown using the `fwupdmgr hwids` command.
496
 *
497
 * Returns: (transfer none) (element-type utf8): an array of GUIDs
498
 *
499
 * Since: 1.6.0
500
 **/
501
GPtrArray *
502
fu_context_get_hwid_guids(FuContext *self)
503
0
{
504
0
  FuContextPrivate *priv = GET_PRIVATE(self);
505
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
506
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
507
0
    g_critical("cannot use HWIDs before calling ->load_hwinfo()");
508
0
    return NULL;
509
0
  }
510
0
  return fu_hwids_get_guids(priv->hwids);
511
0
}
512
513
/**
514
 * fu_context_get_hwid_value:
515
 * @self: a #FuContext
516
 * @key: a DMI ID, e.g. `BiosVersion`
517
 *
518
 * Gets the cached value for one specific key that is valid ASCII and suitable
519
 * for display.
520
 *
521
 * Returns: the string, e.g. `1.2.3`, or %NULL if not found
522
 *
523
 * Since: 1.6.0
524
 **/
525
const gchar *
526
fu_context_get_hwid_value(FuContext *self, const gchar *key)
527
0
{
528
0
  FuContextPrivate *priv = GET_PRIVATE(self);
529
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
530
0
  g_return_val_if_fail(key != NULL, NULL);
531
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
532
0
    g_critical("cannot use HWIDs before calling ->load_hwinfo()");
533
0
    return NULL;
534
0
  }
535
0
  return fu_hwids_get_value(priv->hwids, key);
536
0
}
537
538
/**
539
 * fu_context_get_hwid_replace_value:
540
 * @self: a #FuContext
541
 * @keys: a key, e.g. `HardwareID-3` or %FU_HWIDS_KEY_PRODUCT_SKU
542
 * @error: (nullable): optional return location for an error
543
 *
544
 * Gets the replacement value for a specific key. All hardware IDs on a
545
 * specific system can be shown using the `fwupdmgr hwids` command.
546
 *
547
 * Returns: (transfer full): a string, or %NULL for error.
548
 *
549
 * Since: 1.6.0
550
 **/
551
gchar *
552
fu_context_get_hwid_replace_value(FuContext *self, const gchar *keys, GError **error)
553
0
{
554
0
  FuContextPrivate *priv = GET_PRIVATE(self);
555
556
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
557
0
  g_return_val_if_fail(keys != NULL, NULL);
558
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
559
560
0
  if (!fu_context_has_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO)) {
561
0
    g_critical("cannot use HWIDs before calling ->load_hwinfo()");
562
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no data");
563
0
    return NULL;
564
0
  }
565
0
  return fu_hwids_get_replace_values(priv->hwids, keys, error);
566
0
}
567
568
/**
569
 * fu_context_add_runtime_version:
570
 * @self: a #FuContext
571
 * @component_id: an AppStream component id, e.g. `org.gnome.Software`
572
 * @version: a version string, e.g. `1.2.3`
573
 *
574
 * Sets a runtime version of a specific dependency.
575
 *
576
 * Since: 1.6.0
577
 **/
578
void
579
fu_context_add_runtime_version(FuContext *self, const gchar *component_id, const gchar *version)
580
0
{
581
0
  FuContextPrivate *priv = GET_PRIVATE(self);
582
583
0
  g_return_if_fail(FU_IS_CONTEXT(self));
584
0
  g_return_if_fail(component_id != NULL);
585
0
  g_return_if_fail(version != NULL);
586
587
0
  if (priv->runtime_versions == NULL)
588
0
    return;
589
0
  g_hash_table_insert(priv->runtime_versions, g_strdup(component_id), g_strdup(version));
590
0
}
591
592
/**
593
 * fu_context_get_runtime_version:
594
 * @self: a #FuContext
595
 * @component_id: an AppStream component id, e.g. `org.gnome.Software`
596
 *
597
 * Sets a runtime version of a specific dependency.
598
 *
599
 * Returns: a version string, e.g. `1.2.3`, or %NULL
600
 *
601
 * Since: 1.9.10
602
 **/
603
const gchar *
604
fu_context_get_runtime_version(FuContext *self, const gchar *component_id)
605
0
{
606
0
  FuContextPrivate *priv = GET_PRIVATE(self);
607
608
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
609
0
  g_return_val_if_fail(component_id != NULL, NULL);
610
611
0
  if (priv->runtime_versions == NULL)
612
0
    return NULL;
613
0
  return g_hash_table_lookup(priv->runtime_versions, component_id);
614
0
}
615
616
/**
617
 * fu_context_get_runtime_versions:
618
 * @self: a #FuContext
619
 *
620
 * Gets the runtime versions for the context.
621
 *
622
 * Returns: (transfer none) (element-type utf8 utf8): dictionary of versions
623
 *
624
 * Since: 1.9.10
625
 **/
626
GHashTable *
627
fu_context_get_runtime_versions(FuContext *self)
628
0
{
629
0
  FuContextPrivate *priv = GET_PRIVATE(self);
630
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
631
0
  return priv->runtime_versions;
632
0
}
633
634
/**
635
 * fu_context_add_compile_version:
636
 * @self: a #FuContext
637
 * @component_id: an AppStream component id, e.g. `org.gnome.Software`
638
 * @version: a version string, e.g. `1.2.3`
639
 *
640
 * Sets a compile-time version of a specific dependency.
641
 *
642
 * Since: 1.6.0
643
 **/
644
void
645
fu_context_add_compile_version(FuContext *self, const gchar *component_id, const gchar *version)
646
0
{
647
0
  FuContextPrivate *priv = GET_PRIVATE(self);
648
649
0
  g_return_if_fail(FU_IS_CONTEXT(self));
650
0
  g_return_if_fail(component_id != NULL);
651
0
  g_return_if_fail(version != NULL);
652
653
0
  if (priv->compile_versions == NULL)
654
0
    return;
655
0
  g_hash_table_insert(priv->compile_versions, g_strdup(component_id), g_strdup(version));
656
0
}
657
658
/**
659
 * fu_context_get_compile_versions:
660
 * @self: a #FuContext
661
 *
662
 * Gets the compile time versions for the context.
663
 *
664
 * Returns: (transfer none) (element-type utf8 utf8): dictionary of versions
665
 *
666
 * Since: 1.9.10
667
 **/
668
GHashTable *
669
fu_context_get_compile_versions(FuContext *self)
670
0
{
671
0
  FuContextPrivate *priv = GET_PRIVATE(self);
672
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
673
0
  return priv->compile_versions;
674
0
}
675
676
static gint
677
fu_context_udev_plugin_names_sort_cb(gconstpointer a, gconstpointer b)
678
0
{
679
0
  const gchar *str_a = *((const gchar **)a);
680
0
  const gchar *str_b = *((const gchar **)b);
681
0
  return g_strcmp0(str_a, str_b);
682
0
}
683
684
/**
685
 * fu_context_add_udev_subsystem:
686
 * @self: a #FuContext
687
 * @subsystem: a subsystem name, e.g. `pciport`, or `block:partition`
688
 * @plugin_name: (nullable): a plugin name, e.g. `iommu`
689
 *
690
 * Registers the udev subsystem to be watched by the daemon.
691
 *
692
 * Plugins can use this method only in fu_plugin_init()
693
 *
694
 * Since: 1.6.0
695
 **/
696
void
697
fu_context_add_udev_subsystem(FuContext *self, const gchar *subsystem, const gchar *plugin_name)
698
0
{
699
0
  FuContextPrivate *priv = GET_PRIVATE(self);
700
0
  GPtrArray *plugin_names;
701
0
  g_auto(GStrv) subsystem_devtype = NULL;
702
703
0
  g_return_if_fail(FU_IS_CONTEXT(self));
704
0
  g_return_if_fail(subsystem != NULL);
705
706
  /* add the base subsystem watch if passed a subsystem:devtype */
707
0
  subsystem_devtype = g_strsplit(subsystem, ":", 2);
708
0
  if (g_strv_length(subsystem_devtype) > 1)
709
0
    fu_context_add_udev_subsystem(self, subsystem_devtype[0], NULL);
710
711
  /* already exists */
712
0
  plugin_names = g_hash_table_lookup(priv->udev_subsystems, subsystem);
713
0
  if (plugin_names != NULL) {
714
0
    if (plugin_name != NULL) {
715
0
      for (guint i = 0; i < plugin_names->len; i++) {
716
0
        const gchar *tmp = g_ptr_array_index(plugin_names, i);
717
0
        if (g_strcmp0(tmp, plugin_name) == 0)
718
0
          return;
719
0
      }
720
0
      g_ptr_array_add(plugin_names, g_strdup(plugin_name));
721
0
      g_ptr_array_sort(plugin_names, fu_context_udev_plugin_names_sort_cb);
722
0
    }
723
0
    return;
724
0
  }
725
726
  /* add */
727
0
  plugin_names = g_ptr_array_new_with_free_func(g_free);
728
0
  if (plugin_name != NULL)
729
0
    g_ptr_array_add(plugin_names, g_strdup(plugin_name));
730
0
  g_hash_table_insert(priv->udev_subsystems,
731
0
          g_strdup(subsystem),
732
0
          g_steal_pointer(&plugin_names));
733
0
  if (plugin_name != NULL)
734
0
    g_info("added udev subsystem watch of %s for plugin %s", subsystem, plugin_name);
735
0
  else
736
0
    g_info("added udev subsystem watch of %s", subsystem);
737
0
}
738
739
/**
740
 * fu_context_get_plugin_names_for_udev_subsystem:
741
 * @self: a #FuContext
742
 * @subsystem: a subsystem name, e.g. `pciport`, or `block:partition`
743
 * @error: (nullable): optional return location for an error
744
 *
745
 * Gets the plugins which registered for a specific subsystem.
746
 *
747
 * Returns: (transfer container) (element-type utf8): List of plugin names
748
 *
749
 * Since: 1.9.3
750
 **/
751
GPtrArray *
752
fu_context_get_plugin_names_for_udev_subsystem(FuContext *self,
753
                 const gchar *subsystem,
754
                 GError **error)
755
0
{
756
0
  FuContextPrivate *priv = GET_PRIVATE(self);
757
0
  GPtrArray *plugin_names_tmp;
758
0
  g_auto(GStrv) subsystem_devtype = NULL;
759
0
  g_autoptr(GPtrArray) plugin_names = g_ptr_array_new_with_free_func(g_free);
760
761
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
762
0
  g_return_val_if_fail(subsystem != NULL, NULL);
763
764
  /* add the base subsystem first */
765
0
  subsystem_devtype = g_strsplit(subsystem, ":", 2);
766
0
  if (g_strv_length(subsystem_devtype) > 1) {
767
0
    plugin_names_tmp = g_hash_table_lookup(priv->udev_subsystems, subsystem_devtype[0]);
768
0
    if (plugin_names_tmp != NULL)
769
0
      g_ptr_array_extend(plugin_names,
770
0
             plugin_names_tmp,
771
0
             (GCopyFunc)g_strdup,
772
0
             NULL);
773
0
  }
774
775
  /* add the exact match */
776
0
  plugin_names_tmp = g_hash_table_lookup(priv->udev_subsystems, subsystem);
777
0
  if (plugin_names_tmp != NULL)
778
0
    g_ptr_array_extend(plugin_names, plugin_names_tmp, (GCopyFunc)g_strdup, NULL);
779
780
  /* no matches */
781
0
  if (plugin_names->len == 0) {
782
0
    g_set_error(error,
783
0
          FWUPD_ERROR,
784
0
          FWUPD_ERROR_NOT_FOUND,
785
0
          "no plugins registered for %s",
786
0
          subsystem);
787
0
    return NULL;
788
0
  }
789
790
  /* success */
791
0
  return g_steal_pointer(&plugin_names);
792
0
}
793
794
/**
795
 * fu_context_get_udev_subsystems:
796
 * @self: a #FuContext
797
 *
798
 * Gets the udev subsystems required by all plugins.
799
 *
800
 * Returns: (transfer container) (element-type utf8): List of subsystems
801
 *
802
 * Since: 1.6.0
803
 **/
804
GPtrArray *
805
fu_context_get_udev_subsystems(FuContext *self)
806
0
{
807
0
  FuContextPrivate *priv = GET_PRIVATE(self);
808
0
  g_autoptr(GList) keys = g_hash_table_get_keys(priv->udev_subsystems);
809
0
  g_autoptr(GPtrArray) subsystems = g_ptr_array_new_with_free_func(g_free);
810
811
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
812
813
0
  for (GList *l = keys; l != NULL; l = l->next) {
814
0
    const gchar *subsystem = (const gchar *)l->data;
815
0
    g_ptr_array_add(subsystems, g_strdup(subsystem));
816
0
  }
817
0
  return g_steal_pointer(&subsystems);
818
0
}
819
820
/**
821
 * fu_context_add_firmware_gtype:
822
 * @self: a #FuContext
823
 * @id: (nullable): an optional string describing the type, e.g. `ihex`
824
 * @gtype: a #GType e.g. `FU_TYPE_FOO_FIRMWARE`
825
 *
826
 * Adds a firmware #GType which is used when creating devices. If @id is not
827
 * specified then it is guessed using the #GType name.
828
 *
829
 * Plugins can use this method only in fu_plugin_init()
830
 *
831
 * Since: 1.6.0
832
 **/
833
void
834
fu_context_add_firmware_gtype(FuContext *self, const gchar *id, GType gtype)
835
0
{
836
0
  FuContextPrivate *priv = GET_PRIVATE(self);
837
0
  g_return_if_fail(FU_IS_CONTEXT(self));
838
0
  g_return_if_fail(id != NULL);
839
0
  g_return_if_fail(gtype != G_TYPE_INVALID);
840
0
  g_type_ensure(gtype);
841
0
  g_hash_table_insert(priv->firmware_gtypes, g_strdup(id), GSIZE_TO_POINTER(gtype));
842
0
}
843
844
/**
845
 * fu_context_get_firmware_gtype_by_id:
846
 * @self: a #FuContext
847
 * @id: an string describing the type, e.g. `ihex`
848
 *
849
 * Returns the #GType using the firmware @id.
850
 *
851
 * Returns: a #GType, or %G_TYPE_INVALID
852
 *
853
 * Since: 1.6.0
854
 **/
855
GType
856
fu_context_get_firmware_gtype_by_id(FuContext *self, const gchar *id)
857
0
{
858
0
  FuContextPrivate *priv = GET_PRIVATE(self);
859
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), G_TYPE_INVALID);
860
0
  g_return_val_if_fail(id != NULL, G_TYPE_INVALID);
861
0
  return GPOINTER_TO_SIZE(g_hash_table_lookup(priv->firmware_gtypes, id));
862
0
}
863
864
static gint
865
fu_context_gtypes_sort_cb(gconstpointer a, gconstpointer b)
866
0
{
867
0
  const gchar *stra = *((const gchar **)a);
868
0
  const gchar *strb = *((const gchar **)b);
869
0
  return g_strcmp0(stra, strb);
870
0
}
871
872
/**
873
 * fu_context_get_firmware_gtype_ids:
874
 * @self: a #FuContext
875
 *
876
 * Returns all the firmware #GType IDs.
877
 *
878
 * Returns: (transfer container) (element-type utf8): firmware IDs
879
 *
880
 * Since: 1.6.0
881
 **/
882
GPtrArray *
883
fu_context_get_firmware_gtype_ids(FuContext *self)
884
0
{
885
0
  FuContextPrivate *priv = GET_PRIVATE(self);
886
0
  GPtrArray *firmware_gtypes = g_ptr_array_new_with_free_func(g_free);
887
0
  g_autoptr(GList) keys = g_hash_table_get_keys(priv->firmware_gtypes);
888
889
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
890
891
0
  for (GList *l = keys; l != NULL; l = l->next) {
892
0
    const gchar *id = l->data;
893
0
    g_ptr_array_add(firmware_gtypes, g_strdup(id));
894
0
  }
895
0
  g_ptr_array_sort(firmware_gtypes, fu_context_gtypes_sort_cb);
896
0
  return firmware_gtypes;
897
0
}
898
899
/**
900
 * fu_context_get_firmware_gtypes:
901
 * @self: a #FuContext
902
 *
903
 * Returns all the firmware #GType's.
904
 *
905
 * Returns: (transfer container) (element-type GType): Firmware types
906
 *
907
 * Since: 1.9.1
908
 **/
909
GArray *
910
fu_context_get_firmware_gtypes(FuContext *self)
911
0
{
912
0
  FuContextPrivate *priv = GET_PRIVATE(self);
913
0
  GArray *firmware_gtypes = g_array_new(FALSE, FALSE, sizeof(GType));
914
0
  g_autoptr(GList) values = g_hash_table_get_values(priv->firmware_gtypes);
915
916
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
917
918
0
  for (GList *l = values; l != NULL; l = l->next) {
919
0
    GType gtype = GPOINTER_TO_SIZE(l->data);
920
0
    g_array_append_val(firmware_gtypes, gtype);
921
0
  }
922
0
  return firmware_gtypes;
923
0
}
924
925
/**
926
 * fu_context_add_quirk_key:
927
 * @self: a #FuContext
928
 * @key: a quirk string, e.g. `DfuVersion`
929
 *
930
 * Adds a possible quirk key. If added by a plugin it should be namespaced
931
 * using the plugin name, where possible.
932
 *
933
 * Plugins can use this method only in fu_plugin_init()
934
 *
935
 * Since: 1.6.0
936
 **/
937
void
938
fu_context_add_quirk_key(FuContext *self, const gchar *key)
939
0
{
940
0
  FuContextPrivate *priv = GET_PRIVATE(self);
941
0
  g_return_if_fail(FU_IS_CONTEXT(self));
942
0
  g_return_if_fail(key != NULL);
943
0
  if (priv->quirks == NULL)
944
0
    return;
945
0
  fu_quirks_add_possible_key(priv->quirks, key);
946
0
}
947
948
/**
949
 * fu_context_lookup_quirk_by_id:
950
 * @self: a #FuContext
951
 * @guid: GUID to lookup
952
 * @key: an ID to match the entry, e.g. `Summary`
953
 *
954
 * Looks up an entry in the hardware database using a string value.
955
 *
956
 * Returns: (transfer none): values from the database, or %NULL if not found
957
 *
958
 * Since: 1.6.0
959
 **/
960
const gchar *
961
fu_context_lookup_quirk_by_id(FuContext *self, const gchar *guid, const gchar *key)
962
0
{
963
0
  FuContextPrivate *priv = GET_PRIVATE(self);
964
965
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
966
0
  g_return_val_if_fail(guid != NULL, NULL);
967
0
  g_return_val_if_fail(key != NULL, NULL);
968
969
  /* exact ID */
970
0
  return fu_quirks_lookup_by_id(priv->quirks, guid, key);
971
0
}
972
973
typedef struct {
974
  FuContext *self; /* noref */
975
  FuContextLookupIter iter_cb;
976
  gpointer user_data;
977
} FuContextQuirkLookupHelper;
978
979
static void
980
fu_context_lookup_quirk_by_id_iter_cb(FuQuirks *self,
981
              const gchar *key,
982
              const gchar *value,
983
              FuContextQuirkSource source,
984
              gpointer user_data)
985
0
{
986
0
  FuContextQuirkLookupHelper *helper = (FuContextQuirkLookupHelper *)user_data;
987
0
  helper->iter_cb(helper->self, key, value, source, helper->user_data);
988
0
}
989
990
/**
991
 * fu_context_lookup_quirk_by_id_iter:
992
 * @self: a #FuContext
993
 * @guid: GUID to lookup
994
 * @key: (nullable): an ID to match the entry, e.g. `Name`, or %NULL for all keys
995
 * @iter_cb: (scope call) (closure user_data): a function to call for each result
996
 * @user_data: user data passed to @iter_cb
997
 *
998
 * Looks up all entries in the hardware database using a GUID value.
999
 *
1000
 * Returns: %TRUE if the ID was found, and @iter was called
1001
 *
1002
 * Since: 1.6.0
1003
 **/
1004
gboolean
1005
fu_context_lookup_quirk_by_id_iter(FuContext *self,
1006
           const gchar *guid,
1007
           const gchar *key,
1008
           FuContextLookupIter iter_cb,
1009
           gpointer user_data)
1010
0
{
1011
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1012
0
  FuContextQuirkLookupHelper helper = {
1013
0
      .self = self,
1014
0
      .iter_cb = iter_cb,
1015
0
      .user_data = user_data,
1016
0
  };
1017
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1018
0
  g_return_val_if_fail(guid != NULL, FALSE);
1019
0
  g_return_val_if_fail(iter_cb != NULL, FALSE);
1020
0
  return fu_quirks_lookup_by_id_iter(priv->quirks,
1021
0
             guid,
1022
0
             key,
1023
0
             fu_context_lookup_quirk_by_id_iter_cb,
1024
0
             &helper);
1025
0
}
1026
1027
/**
1028
 * fu_context_security_changed:
1029
 * @self: a #FuContext
1030
 *
1031
 * Informs the daemon that the HSI state may have changed.
1032
 *
1033
 * Since: 1.6.0
1034
 **/
1035
void
1036
fu_context_security_changed(FuContext *self)
1037
0
{
1038
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1039
0
  g_signal_emit(self, signals[SIGNAL_SECURITY_CHANGED], 0);
1040
0
}
1041
1042
/**
1043
 * fu_context_housekeeping:
1044
 * @self: a #FuContext
1045
 *
1046
 * Performs any housekeeping maintenance when the daemon is idle.
1047
 *
1048
 * Since: 2.0.0
1049
 **/
1050
void
1051
fu_context_housekeeping(FuContext *self)
1052
0
{
1053
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1054
0
  g_signal_emit(self, signals[SIGNAL_HOUSEKEEPING], 0);
1055
0
}
1056
1057
typedef gboolean (*FuContextHwidsSetupFunc)(FuContext *self, FuHwids *hwids, GError **error);
1058
1059
static void
1060
fu_context_detect_full_disk_encryption(FuContext *self)
1061
0
{
1062
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1063
0
  g_autoptr(GPtrArray) devices = NULL;
1064
0
  g_autoptr(GError) error_local = NULL;
1065
1066
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1067
1068
0
  devices = fu_common_get_block_devices(&error_local);
1069
0
  if (devices == NULL) {
1070
0
    g_info("Failed to get block devices: %s", error_local->message);
1071
0
    return;
1072
0
  }
1073
1074
0
  for (guint i = 0; i < devices->len; i++) {
1075
0
    GDBusProxy *proxy = g_ptr_array_index(devices, i);
1076
0
    g_autoptr(GVariant) id_type = g_dbus_proxy_get_cached_property(proxy, "IdType");
1077
0
    g_autoptr(GVariant) device = g_dbus_proxy_get_cached_property(proxy, "Device");
1078
0
    g_autoptr(GVariant) id_label = g_dbus_proxy_get_cached_property(proxy, "IdLabel");
1079
0
    if (id_type != NULL && device != NULL &&
1080
0
        g_strcmp0(g_variant_get_string(id_type, NULL), "BitLocker") == 0)
1081
0
      priv->flags |= FU_CONTEXT_FLAG_FDE_BITLOCKER;
1082
1083
0
    if (id_type != NULL && id_label != NULL &&
1084
0
        g_strcmp0(g_variant_get_string(id_label, NULL), "ubuntu-data-enc") == 0 &&
1085
0
        g_strcmp0(g_variant_get_string(id_type, NULL), "crypto_LUKS") == 0) {
1086
0
      priv->flags |= FU_CONTEXT_FLAG_FDE_SNAPD;
1087
0
    }
1088
0
  }
1089
0
}
1090
1091
static void
1092
fu_context_hwid_quirk_cb(FuContext *self,
1093
       const gchar *key,
1094
       const gchar *value,
1095
       FuContextQuirkSource source,
1096
       gpointer user_data)
1097
0
{
1098
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1099
0
  if (value != NULL) {
1100
0
    g_auto(GStrv) values = g_strsplit(value, ",", -1);
1101
0
    for (guint j = 0; values[j] != NULL; j++)
1102
0
      g_hash_table_add(priv->hwid_flags, g_strdup(values[j]));
1103
0
  }
1104
0
}
1105
1106
/**
1107
 * fu_context_load_hwinfo:
1108
 * @self: a #FuContext
1109
 * @progress: a #FuProgress
1110
 * @flags: a #FuContextHwidFlags, e.g. %FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS
1111
 * @error: (nullable): optional return location for an error
1112
 *
1113
 * Loads all hardware information parts of the context.
1114
 *
1115
 * Returns: %TRUE for success
1116
 *
1117
 * Since: 1.8.10
1118
 **/
1119
gboolean
1120
fu_context_load_hwinfo(FuContext *self,
1121
           FuProgress *progress,
1122
           FuContextHwidFlags flags,
1123
           GError **error)
1124
0
{
1125
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1126
0
  GPtrArray *guids;
1127
0
  g_autoptr(GError) error_hwids = NULL;
1128
0
  g_autoptr(GError) error_bios_settings = NULL;
1129
0
  struct {
1130
0
    const gchar *name;
1131
0
    FuContextHwidFlags flag;
1132
0
    FuContextHwidsSetupFunc func;
1133
0
  } hwids_setup_map[] = {{"config", FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, fu_hwids_config_setup},
1134
0
             {"smbios", FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS, fu_hwids_smbios_setup},
1135
0
             {"fdt", FU_CONTEXT_HWID_FLAG_LOAD_FDT, fu_hwids_fdt_setup},
1136
0
             {"kenv", FU_CONTEXT_HWID_FLAG_LOAD_KENV, fu_hwids_kenv_setup},
1137
0
             {"dmi", FU_CONTEXT_HWID_FLAG_LOAD_DMI, fu_hwids_dmi_setup},
1138
0
             {"darwin", FU_CONTEXT_HWID_FLAG_LOAD_DARWIN, fu_hwids_darwin_setup},
1139
0
             {NULL, FU_CONTEXT_HWID_FLAG_NONE, NULL}};
1140
1141
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1142
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1143
1144
  /* progress */
1145
0
  fu_progress_set_id(progress, G_STRLOC);
1146
0
  fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "hwids-setup-funcs");
1147
0
  fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "hwids-setup");
1148
0
  fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 3, "set-flags");
1149
0
  fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "detect-fde");
1150
0
  fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 94, "reload-bios-settings");
1151
1152
  /* required always */
1153
0
  if (!fu_config_load(priv->config, error))
1154
0
    return FALSE;
1155
1156
  /* run all the HWID setup funcs */
1157
0
  for (guint i = 0; hwids_setup_map[i].name != NULL; i++) {
1158
0
    if ((flags & hwids_setup_map[i].flag) > 0) {
1159
0
      g_autoptr(GError) error_local = NULL;
1160
0
      if (!hwids_setup_map[i].func(self, priv->hwids, &error_local)) {
1161
0
        g_info("failed to load %s: %s",
1162
0
               hwids_setup_map[i].name,
1163
0
               error_local->message);
1164
0
        continue;
1165
0
      }
1166
0
    }
1167
0
  }
1168
0
  fu_context_add_flag(self, FU_CONTEXT_FLAG_LOADED_HWINFO);
1169
0
  fu_progress_step_done(progress);
1170
1171
0
  if (!fu_hwids_setup(priv->hwids, &error_hwids))
1172
0
    g_warning("Failed to load HWIDs: %s", error_hwids->message);
1173
0
  fu_progress_step_done(progress);
1174
1175
  /* set the hwid flags */
1176
0
  guids = fu_context_get_hwid_guids(self);
1177
0
  for (guint i = 0; i < guids->len; i++) {
1178
0
    const gchar *guid = g_ptr_array_index(guids, i);
1179
0
    fu_context_lookup_quirk_by_id_iter(self,
1180
0
               guid,
1181
0
               FU_QUIRKS_FLAGS,
1182
0
               fu_context_hwid_quirk_cb,
1183
0
               NULL);
1184
0
  }
1185
0
  fu_progress_step_done(progress);
1186
1187
0
  fu_context_detect_full_disk_encryption(self);
1188
0
  fu_progress_step_done(progress);
1189
1190
0
  fu_context_add_udev_subsystem(self, "firmware-attributes", NULL);
1191
0
  if (!fu_context_reload_bios_settings(self, &error_bios_settings))
1192
0
    g_debug("%s", error_bios_settings->message);
1193
0
  fu_progress_step_done(progress);
1194
1195
  /* always */
1196
0
  return TRUE;
1197
0
}
1198
1199
/**
1200
 * fu_context_has_hwid_flag:
1201
 * @self: a #FuContext
1202
 * @flag: flag, e.g. `use-legacy-bootmgr-desc`
1203
 *
1204
 * Returns if a HwId custom flag exists, typically added from a DMI quirk.
1205
 *
1206
 * Returns: %TRUE if the flag exists
1207
 *
1208
 * Since: 1.7.2
1209
 **/
1210
gboolean
1211
fu_context_has_hwid_flag(FuContext *self, const gchar *flag)
1212
0
{
1213
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1214
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1215
0
  g_return_val_if_fail(flag != NULL, FALSE);
1216
0
  return g_hash_table_lookup(priv->hwid_flags, flag) != NULL;
1217
0
}
1218
1219
/**
1220
 * fu_context_load_quirks:
1221
 * @self: a #FuContext
1222
 * @flags: quirks load flags, e.g. %FU_QUIRKS_LOAD_FLAG_READONLY_FS
1223
 * @error: (nullable): optional return location for an error
1224
 *
1225
 * Loads all quirks into the context.
1226
 *
1227
 * Returns: %TRUE for success
1228
 *
1229
 * Since: 1.6.0
1230
 **/
1231
gboolean
1232
fu_context_load_quirks(FuContext *self, FuQuirksLoadFlags flags, GError **error)
1233
0
{
1234
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1235
0
  g_autoptr(GError) error_local = NULL;
1236
1237
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1238
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1239
1240
  /* rebuild silo if required */
1241
0
  if (!fu_quirks_load(priv->quirks, flags, &error_local))
1242
0
    g_warning("Failed to load quirks: %s", error_local->message);
1243
1244
  /* always */
1245
0
  return TRUE;
1246
0
}
1247
1248
/**
1249
 * fu_context_get_power_state:
1250
 * @self: a #FuContext
1251
 *
1252
 * Gets if the system is on battery power, e.g. UPS or laptop battery.
1253
 *
1254
 * Returns: a power state, e.g. %FU_POWER_STATE_BATTERY_DISCHARGING
1255
 *
1256
 * Since: 1.8.11
1257
 **/
1258
FuPowerState
1259
fu_context_get_power_state(FuContext *self)
1260
0
{
1261
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1262
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1263
0
  return priv->power_state;
1264
0
}
1265
1266
/**
1267
 * fu_context_set_power_state:
1268
 * @self: a #FuContext
1269
 * @power_state: a power state, e.g. %FU_POWER_STATE_BATTERY_DISCHARGING
1270
 *
1271
 * Sets if the system is on battery power, e.g. UPS or laptop battery.
1272
 *
1273
 * Since: 1.8.11
1274
 **/
1275
void
1276
fu_context_set_power_state(FuContext *self, FuPowerState power_state)
1277
0
{
1278
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1279
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1280
0
  if (priv->power_state == power_state)
1281
0
    return;
1282
0
  priv->power_state = power_state;
1283
0
  g_info("power state now %s", fu_power_state_to_string(power_state));
1284
0
  g_object_notify(G_OBJECT(self), "power-state");
1285
0
}
1286
1287
/**
1288
 * fu_context_get_lid_state:
1289
 * @self: a #FuContext
1290
 *
1291
 * Gets the laptop lid state, if applicable.
1292
 *
1293
 * Returns: a lid state, e.g. %FU_LID_STATE_CLOSED
1294
 *
1295
 * Since: 1.7.4
1296
 **/
1297
FuLidState
1298
fu_context_get_lid_state(FuContext *self)
1299
0
{
1300
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1301
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1302
0
  return priv->lid_state;
1303
0
}
1304
1305
/**
1306
 * fu_context_set_lid_state:
1307
 * @self: a #FuContext
1308
 * @lid_state: a lid state, e.g. %FU_LID_STATE_CLOSED
1309
 *
1310
 * Sets the laptop lid state, if applicable.
1311
 *
1312
 * Since: 1.7.4
1313
 **/
1314
void
1315
fu_context_set_lid_state(FuContext *self, FuLidState lid_state)
1316
0
{
1317
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1318
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1319
0
  if (priv->lid_state == lid_state)
1320
0
    return;
1321
0
  priv->lid_state = lid_state;
1322
0
  g_info("lid state now %s", fu_lid_state_to_string(lid_state));
1323
0
  g_object_notify(G_OBJECT(self), "lid-state");
1324
0
}
1325
1326
/**
1327
 * fu_context_get_display_state:
1328
 * @self: a #FuContext
1329
 *
1330
 * Gets the display state, if applicable.
1331
 *
1332
 * Returns: a display state, e.g. %FU_DISPLAY_STATE_CONNECTED
1333
 *
1334
 * Since: 1.9.6
1335
 **/
1336
FuDisplayState
1337
fu_context_get_display_state(FuContext *self)
1338
0
{
1339
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1340
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
1341
0
  return priv->display_state;
1342
0
}
1343
1344
/**
1345
 * fu_context_set_display_state:
1346
 * @self: a #FuContext
1347
 * @display_state: a display state, e.g. %FU_DISPLAY_STATE_CONNECTED
1348
 *
1349
 * Sets the display state, if applicable.
1350
 *
1351
 * Since: 1.9.6
1352
 **/
1353
void
1354
fu_context_set_display_state(FuContext *self, FuDisplayState display_state)
1355
0
{
1356
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1357
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1358
0
  if (priv->display_state == display_state)
1359
0
    return;
1360
0
  priv->display_state = display_state;
1361
0
  g_info("display-state now %s", fu_display_state_to_string(display_state));
1362
0
  g_object_notify(G_OBJECT(self), "display-state");
1363
0
}
1364
1365
/**
1366
 * fu_context_get_battery_level:
1367
 * @self: a #FuContext
1368
 *
1369
 * Gets the system battery level in percent.
1370
 *
1371
 * Returns: percentage value, or %FWUPD_BATTERY_LEVEL_INVALID for unknown
1372
 *
1373
 * Since: 1.6.0
1374
 **/
1375
guint
1376
fu_context_get_battery_level(FuContext *self)
1377
0
{
1378
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1379
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT);
1380
0
  return priv->battery_level;
1381
0
}
1382
1383
/**
1384
 * fu_context_set_battery_level:
1385
 * @self: a #FuContext
1386
 * @battery_level: value
1387
 *
1388
 * Sets the system battery level in percent.
1389
 *
1390
 * Since: 1.6.0
1391
 **/
1392
void
1393
fu_context_set_battery_level(FuContext *self, guint battery_level)
1394
0
{
1395
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1396
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1397
0
  g_return_if_fail(battery_level <= FWUPD_BATTERY_LEVEL_INVALID);
1398
0
  if (priv->battery_level == battery_level)
1399
0
    return;
1400
0
  priv->battery_level = battery_level;
1401
0
  g_info("battery level now %u", battery_level);
1402
0
  g_object_notify(G_OBJECT(self), "battery-level");
1403
0
}
1404
1405
/**
1406
 * fu_context_get_battery_threshold:
1407
 * @self: a #FuContext
1408
 *
1409
 * Gets the system battery threshold in percent.
1410
 *
1411
 * Returns: percentage value, or %FWUPD_BATTERY_LEVEL_INVALID for unknown
1412
 *
1413
 * Since: 1.6.0
1414
 **/
1415
guint
1416
fu_context_get_battery_threshold(FuContext *self)
1417
0
{
1418
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1419
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), G_MAXUINT);
1420
0
  return priv->battery_threshold;
1421
0
}
1422
1423
/**
1424
 * fu_context_set_battery_threshold:
1425
 * @self: a #FuContext
1426
 * @battery_threshold: value
1427
 *
1428
 * Sets the system battery threshold in percent.
1429
 *
1430
 * Since: 1.6.0
1431
 **/
1432
void
1433
fu_context_set_battery_threshold(FuContext *self, guint battery_threshold)
1434
0
{
1435
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1436
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1437
0
  g_return_if_fail(battery_threshold <= FWUPD_BATTERY_LEVEL_INVALID);
1438
0
  if (priv->battery_threshold == battery_threshold)
1439
0
    return;
1440
0
  priv->battery_threshold = battery_threshold;
1441
0
  g_info("battery threshold now %u", battery_threshold);
1442
0
  g_object_notify(G_OBJECT(self), "battery-threshold");
1443
0
}
1444
1445
/**
1446
 * fu_context_add_flag:
1447
 * @context: a #FuContext
1448
 * @flag: the context flag
1449
 *
1450
 * Adds a specific flag to the context.
1451
 *
1452
 * Since: 1.8.5
1453
 **/
1454
void
1455
fu_context_add_flag(FuContext *context, FuContextFlags flag)
1456
0
{
1457
0
  FuContextPrivate *priv = GET_PRIVATE(context);
1458
0
  g_return_if_fail(FU_IS_CONTEXT(context));
1459
0
  if (priv->flags & flag)
1460
0
    return;
1461
0
  priv->flags |= flag;
1462
0
  g_object_notify(G_OBJECT(context), "flags");
1463
0
}
1464
1465
/**
1466
 * fu_context_remove_flag:
1467
 * @context: a #FuContext
1468
 * @flag: the context flag
1469
 *
1470
 * Removes a specific flag from the context.
1471
 *
1472
 * Since: 1.8.10
1473
 **/
1474
void
1475
fu_context_remove_flag(FuContext *context, FuContextFlags flag)
1476
0
{
1477
0
  FuContextPrivate *priv = GET_PRIVATE(context);
1478
0
  g_return_if_fail(FU_IS_CONTEXT(context));
1479
0
  if ((priv->flags & flag) == 0)
1480
0
    return;
1481
0
  priv->flags &= ~flag;
1482
0
  g_object_notify(G_OBJECT(context), "flags");
1483
0
}
1484
1485
/**
1486
 * fu_context_has_flag:
1487
 * @context: a #FuContext
1488
 * @flag: the context flag
1489
 *
1490
 * Finds if the context has a specific flag.
1491
 *
1492
 * Returns: %TRUE if the flag is set
1493
 *
1494
 * Since: 1.8.5
1495
 **/
1496
gboolean
1497
fu_context_has_flag(FuContext *context, FuContextFlags flag)
1498
0
{
1499
0
  FuContextPrivate *priv = GET_PRIVATE(context);
1500
0
  g_return_val_if_fail(FU_IS_CONTEXT(context), FALSE);
1501
0
  return (priv->flags & flag) > 0;
1502
0
}
1503
1504
/**
1505
 * fu_context_add_esp_volume:
1506
 * @self: a #FuContext
1507
 * @volume: a #FuVolume
1508
 *
1509
 * Adds an ESP volume location.
1510
 *
1511
 * Since: 1.8.5
1512
 **/
1513
void
1514
fu_context_add_esp_volume(FuContext *self, FuVolume *volume)
1515
0
{
1516
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1517
1518
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1519
0
  g_return_if_fail(FU_IS_VOLUME(volume));
1520
1521
  /* check for dupes */
1522
0
  for (guint i = 0; i < priv->esp_volumes->len; i++) {
1523
0
    FuVolume *volume_tmp = g_ptr_array_index(priv->esp_volumes, i);
1524
0
    if (g_strcmp0(fu_volume_get_id(volume_tmp), fu_volume_get_id(volume)) == 0) {
1525
0
      g_debug("not adding duplicate volume %s", fu_volume_get_id(volume));
1526
0
      return;
1527
0
    }
1528
0
  }
1529
1530
  /* add */
1531
0
  g_ptr_array_add(priv->esp_volumes, g_object_ref(volume));
1532
0
}
1533
1534
/**
1535
 * fu_context_set_esp_location:
1536
 * @self: A #FuContext object.
1537
 * @location: The path to the preferred ESP.
1538
 *
1539
 * Sets the user's desired ESP (EFI System Partition) location for the given #FuContext.
1540
 */
1541
void
1542
fu_context_set_esp_location(FuContext *self, const gchar *location)
1543
0
{
1544
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1545
0
  g_return_if_fail(FU_IS_CONTEXT(self));
1546
0
  g_return_if_fail(location != NULL);
1547
0
  g_free(priv->esp_location);
1548
0
  priv->esp_location = g_strdup(location);
1549
0
}
1550
1551
/**
1552
 * fu_context_get_esp_location:
1553
 * @self: The FuContext object.
1554
 *
1555
 * Retrieves the user's desired ESP (EFI System Partition) location for the given #FuContext
1556
 *
1557
 * Return: The preferred ESP location as a string.
1558
 */
1559
const gchar *
1560
fu_context_get_esp_location(FuContext *self)
1561
0
{
1562
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1563
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
1564
0
  return priv->esp_location;
1565
0
}
1566
1567
/**
1568
 * fu_context_get_esp_volumes:
1569
 * @self: a #FuContext
1570
 * @error: (nullable): optional return location for an error
1571
 *
1572
 * Finds all volumes that could be an ESP.
1573
 *
1574
 * The volumes are cached and so subsequent calls to this function will be much faster.
1575
 *
1576
 * Returns: (transfer container) (element-type FuVolume): a #GPtrArray, or %NULL if no ESP was found
1577
 *
1578
 * Since: 1.8.5
1579
 **/
1580
GPtrArray *
1581
fu_context_get_esp_volumes(FuContext *self, GError **error)
1582
0
{
1583
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1584
0
  const gchar *path_tmp;
1585
0
  g_autoptr(GError) error_bdp = NULL;
1586
0
  g_autoptr(GError) error_esp = NULL;
1587
0
  g_autoptr(GPtrArray) volumes_bdp = NULL;
1588
0
  g_autoptr(GPtrArray) volumes_esp = NULL;
1589
1590
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
1591
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1592
1593
  /* cached result */
1594
0
  if (priv->esp_volumes->len > 0)
1595
0
    return g_ptr_array_ref(priv->esp_volumes);
1596
1597
  /* for the test suite use local directory for ESP */
1598
0
  path_tmp = g_getenv("FWUPD_UEFI_ESP_PATH");
1599
0
  if (path_tmp != NULL) {
1600
0
    g_autoptr(FuVolume) vol = fu_volume_new_from_mount_path(path_tmp);
1601
0
    fu_volume_set_partition_kind(vol, FU_VOLUME_KIND_ESP);
1602
0
    fu_volume_set_partition_uuid(vol, "00000000-0000-0000-0000-000000000000");
1603
0
    fu_context_add_esp_volume(self, vol);
1604
0
    return g_ptr_array_ref(priv->esp_volumes);
1605
0
  }
1606
1607
  /* ESP */
1608
0
  volumes_esp = fu_volume_new_by_kind(FU_VOLUME_KIND_ESP, &error_esp);
1609
0
  if (volumes_esp == NULL) {
1610
0
    g_debug("%s", error_esp->message);
1611
0
  } else {
1612
0
    for (guint i = 0; i < volumes_esp->len; i++) {
1613
0
      FuVolume *vol = g_ptr_array_index(volumes_esp, i);
1614
0
      g_autofree gchar *type = fu_volume_get_id_type(vol);
1615
0
      if (g_strcmp0(type, "vfat") != 0)
1616
0
        continue;
1617
0
      fu_context_add_esp_volume(self, vol);
1618
0
    }
1619
0
  }
1620
1621
  /* BDP */
1622
0
  volumes_bdp = fu_volume_new_by_kind(FU_VOLUME_KIND_BDP, &error_bdp);
1623
0
  if (volumes_bdp == NULL) {
1624
0
    g_debug("%s", error_bdp->message);
1625
0
  } else {
1626
0
    for (guint i = 0; i < volumes_bdp->len; i++) {
1627
0
      FuVolume *vol = g_ptr_array_index(volumes_bdp, i);
1628
0
      g_autofree gchar *type = fu_volume_get_id_type(vol);
1629
0
      if (g_strcmp0(type, "vfat") != 0)
1630
0
        continue;
1631
0
      if (!fu_volume_is_internal(vol))
1632
0
        continue;
1633
0
      fu_context_add_esp_volume(self, vol);
1634
0
    }
1635
0
  }
1636
1637
  /* nothing found */
1638
0
  if (priv->esp_volumes->len == 0) {
1639
0
    g_autoptr(GPtrArray) devices = NULL;
1640
1641
    /* check if udisks2 is working */
1642
0
    devices = fu_common_get_block_devices(error);
1643
0
    if (devices == NULL)
1644
0
      return NULL;
1645
0
    g_set_error_literal(error,
1646
0
            FWUPD_ERROR,
1647
0
            FWUPD_ERROR_NOT_FOUND,
1648
0
            "No ESP or BDP found");
1649
0
    return NULL;
1650
0
  }
1651
1652
  /* success */
1653
0
  return g_ptr_array_ref(priv->esp_volumes);
1654
0
}
1655
1656
static gboolean
1657
fu_context_is_esp(FuVolume *esp)
1658
0
{
1659
0
  g_autofree gchar *mount_point = fu_volume_get_mount_point(esp);
1660
0
  g_autofree gchar *fn = NULL;
1661
0
  g_autofree gchar *fn2 = NULL;
1662
1663
0
  if (mount_point == NULL)
1664
0
    return FALSE;
1665
1666
0
  fn = g_build_filename(mount_point, "EFI", NULL);
1667
0
  fn2 = g_build_filename(mount_point, "efi", NULL);
1668
1669
0
  return g_file_test(fn, G_FILE_TEST_IS_DIR) || g_file_test(fn2, G_FILE_TEST_IS_DIR);
1670
0
}
1671
1672
static gboolean
1673
fu_context_is_esp_linux(FuVolume *esp, GError **error)
1674
0
{
1675
0
  const gchar *prefixes[] = {"grub", "shim", "systemd-boot", "zfsbootmenu", NULL};
1676
0
  g_autofree gchar *prefixes_str = NULL;
1677
0
  g_autofree gchar *mount_point = fu_volume_get_mount_point(esp);
1678
0
  g_autoptr(GPtrArray) files = NULL;
1679
1680
  /* look for any likely basenames */
1681
0
  if (mount_point == NULL) {
1682
0
    g_set_error_literal(error,
1683
0
            FWUPD_ERROR,
1684
0
            FWUPD_ERROR_NOT_SUPPORTED,
1685
0
            "no mountpoint for ESP");
1686
0
    return FALSE;
1687
0
  }
1688
0
  files = fu_path_get_files(mount_point, error);
1689
0
  if (files == NULL)
1690
0
    return FALSE;
1691
0
  for (guint i = 0; i < files->len; i++) {
1692
0
    const gchar *fn = g_ptr_array_index(files, i);
1693
0
    g_autofree gchar *basename = g_path_get_basename(fn);
1694
0
    g_autofree gchar *basename_lower = g_utf8_strdown(basename, -1);
1695
1696
0
    for (guint j = 0; prefixes[j] != NULL; j++) {
1697
0
      if (!g_str_has_prefix(basename_lower, prefixes[j]))
1698
0
        continue;
1699
0
      if (!g_str_has_suffix(basename_lower, ".efi"))
1700
0
        continue;
1701
0
      g_info("found %s which indicates a Linux ESP, using %s", fn, mount_point);
1702
0
      return TRUE;
1703
0
    }
1704
0
  }
1705
1706
  /* failed */
1707
0
  prefixes_str = g_strjoinv("|", (gchar **)prefixes);
1708
0
  g_set_error(error,
1709
0
        FWUPD_ERROR,
1710
0
        FWUPD_ERROR_NOT_FOUND,
1711
0
        "did not any files with prefix %s in %s",
1712
0
        prefixes_str,
1713
0
        mount_point);
1714
0
  return FALSE;
1715
0
}
1716
1717
static gint
1718
fu_context_sort_esp_score_cb(gconstpointer a, gconstpointer b, gpointer user_data)
1719
0
{
1720
0
  GHashTable *esp_scores = (GHashTable *)user_data;
1721
0
  guint esp1_score = GPOINTER_TO_UINT(g_hash_table_lookup(esp_scores, *((FuVolume **)a)));
1722
0
  guint esp2_score = GPOINTER_TO_UINT(g_hash_table_lookup(esp_scores, *((FuVolume **)b)));
1723
0
  if (esp1_score < esp2_score)
1724
0
    return 1;
1725
0
  if (esp1_score > esp2_score)
1726
0
    return -1;
1727
0
  return 0;
1728
0
}
1729
1730
/**
1731
 * fu_context_get_default_esp:
1732
 * @self: a #FuContext
1733
 * @error: (nullable): optional return location for an error
1734
 *
1735
 * Finds the volume that represents the ESP that plugins should nominally
1736
 * use for accessing storing data.
1737
 *
1738
 * Returns: (transfer full): a volume, or %NULL if no ESP was found
1739
 *
1740
 * Since: 2.0.0
1741
 **/
1742
FuVolume *
1743
fu_context_get_default_esp(FuContext *self, GError **error)
1744
0
{
1745
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1746
0
  g_autoptr(GPtrArray) esp_volumes = NULL;
1747
0
  const gchar *user_esp_location = fu_context_get_esp_location(self);
1748
1749
  /* show which volumes we're choosing from */
1750
0
  esp_volumes = fu_context_get_esp_volumes(self, error);
1751
0
  if (esp_volumes == NULL)
1752
0
    return NULL;
1753
1754
  /* no mounting */
1755
0
  if (priv->flags & FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT) {
1756
0
    g_set_error_literal(error,
1757
0
            FWUPD_ERROR,
1758
0
            FWUPD_ERROR_NOT_SUPPORTED,
1759
0
            "cannot mount volume by policy");
1760
0
    return NULL;
1761
0
  }
1762
1763
  /* we found more than one: lets look for the best one */
1764
0
  if (esp_volumes->len > 1) {
1765
0
    g_autoptr(GString) str = g_string_new("more than one ESP possible:");
1766
0
    g_autoptr(GHashTable) esp_scores = g_hash_table_new(g_direct_hash, g_direct_equal);
1767
0
    for (guint i = 0; i < esp_volumes->len; i++) {
1768
0
      FuVolume *esp = g_ptr_array_index(esp_volumes, i);
1769
0
      guint score = 0;
1770
0
      g_autofree gchar *kind = NULL;
1771
0
      g_autoptr(FuDeviceLocker) locker = NULL;
1772
0
      g_autoptr(GError) error_local = NULL;
1773
1774
      /* ignore the volume completely if we cannot mount it */
1775
0
      locker = fu_volume_locker(esp, &error_local);
1776
0
      if (locker == NULL) {
1777
0
        g_warning("failed to mount ESP: %s", error_local->message);
1778
0
        continue;
1779
0
      }
1780
1781
      /* if user specified, make sure that it matches */
1782
0
      if (user_esp_location != NULL) {
1783
0
        g_autofree gchar *mount = fu_volume_get_mount_point(esp);
1784
0
        if (g_strcmp0(mount, user_esp_location) != 0) {
1785
0
          g_debug("skipping %s as it's not the user "
1786
0
            "specified ESP",
1787
0
            mount);
1788
0
          continue;
1789
0
        }
1790
0
      }
1791
1792
0
      if (!fu_context_is_esp(esp)) {
1793
0
        g_debug("not an ESP: %s", fu_volume_get_id(esp));
1794
0
        continue;
1795
0
      }
1796
1797
      /* big partitions are better than small partitions */
1798
0
      score += fu_volume_get_size(esp) / (1024 * 1024);
1799
1800
      /* prefer partitions with the ESP flag set over msftdata */
1801
0
      kind = fu_volume_get_partition_kind(esp);
1802
0
      if (g_strcmp0(kind, FU_VOLUME_KIND_ESP) == 0)
1803
0
        score += 0x20000;
1804
1805
      /* prefer linux ESP */
1806
0
      if (!fu_context_is_esp_linux(esp, &error_local)) {
1807
0
        g_debug("not a Linux ESP: %s", error_local->message);
1808
0
      } else {
1809
0
        score += 0x10000;
1810
0
      }
1811
0
      g_hash_table_insert(esp_scores, (gpointer)esp, GUINT_TO_POINTER(score));
1812
0
    }
1813
1814
0
    if (g_hash_table_size(esp_scores) == 0) {
1815
0
      g_set_error(error,
1816
0
            FWUPD_ERROR,
1817
0
            FWUPD_ERROR_NOT_SUPPORTED,
1818
0
            "no EFI system partition found");
1819
0
      return NULL;
1820
0
    }
1821
1822
0
    g_ptr_array_sort_with_data(esp_volumes, fu_context_sort_esp_score_cb, esp_scores);
1823
0
    for (guint i = 0; i < esp_volumes->len; i++) {
1824
0
      FuVolume *esp = g_ptr_array_index(esp_volumes, i);
1825
0
      guint score = GPOINTER_TO_UINT(g_hash_table_lookup(esp_scores, esp));
1826
0
      g_string_append_printf(str, "\n - 0x%x:\t%s", score, fu_volume_get_id(esp));
1827
0
    }
1828
0
    g_debug("%s", str->str);
1829
0
  }
1830
1831
0
  if (esp_volumes->len == 1) {
1832
0
    FuVolume *esp = g_ptr_array_index(esp_volumes, 0);
1833
0
    g_autoptr(FuDeviceLocker) locker = NULL;
1834
1835
    /* ensure it can be mounted */
1836
0
    locker = fu_volume_locker(esp, error);
1837
0
    if (locker == NULL)
1838
0
      return NULL;
1839
1840
    /* if user specified, does it match mountpoints ? */
1841
0
    if (user_esp_location != NULL) {
1842
0
      g_autofree gchar *mount = fu_volume_get_mount_point(esp);
1843
1844
0
      if (g_strcmp0(mount, user_esp_location) != 0) {
1845
0
        g_set_error(error,
1846
0
              FWUPD_ERROR,
1847
0
              FWUPD_ERROR_NOT_SUPPORTED,
1848
0
              "user specified ESP %s not found",
1849
0
              user_esp_location);
1850
0
        return NULL;
1851
0
      }
1852
0
    }
1853
0
  }
1854
1855
  /* "success" */
1856
0
  return g_object_ref(g_ptr_array_index(esp_volumes, 0));
1857
0
}
1858
1859
/**
1860
 * fu_context_get_esp_volume_by_hard_drive_device_path:
1861
 * @self: a #FuContext
1862
 * @dp: a #FuEfiHardDriveDevicePath
1863
 * @error: (nullable): optional return location for an error
1864
 *
1865
 * Gets a volume that matches the EFI device path
1866
 *
1867
 * Returns: (transfer full): a volume, or %NULL if it was not found
1868
 *
1869
 * Since: 2.0.0
1870
 **/
1871
FuVolume *
1872
fu_context_get_esp_volume_by_hard_drive_device_path(FuContext *self,
1873
                FuEfiHardDriveDevicePath *dp,
1874
                GError **error)
1875
0
{
1876
0
  g_autoptr(GPtrArray) volumes = NULL;
1877
1878
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
1879
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(dp), NULL);
1880
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1881
1882
0
  volumes = fu_context_get_esp_volumes(self, error);
1883
0
  if (volumes == NULL)
1884
0
    return NULL;
1885
0
  for (guint i = 0; i < volumes->len; i++) {
1886
0
    FuVolume *volume = g_ptr_array_index(volumes, i);
1887
0
    g_autoptr(GError) error_local = NULL;
1888
0
    g_autoptr(FuEfiHardDriveDevicePath) dp_tmp = NULL;
1889
1890
0
    dp_tmp = fu_efi_hard_drive_device_path_new_from_volume(volume, &error_local);
1891
0
    if (dp_tmp == NULL) {
1892
0
      g_debug("%s", error_local->message);
1893
0
      continue;
1894
0
    }
1895
0
    if (!fu_efi_hard_drive_device_path_compare(dp, dp_tmp))
1896
0
      continue;
1897
0
    return g_object_ref(volume);
1898
0
  }
1899
1900
  /* failed */
1901
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "could not find EFI DP");
1902
0
  return NULL;
1903
0
}
1904
1905
static FuFirmware *
1906
fu_context_esp_load_pe_file(const gchar *filename, GError **error)
1907
0
{
1908
0
  g_autoptr(FuFirmware) firmware = fu_pefile_firmware_new();
1909
0
  g_autoptr(GFile) file = g_file_new_for_path(filename);
1910
0
  fu_firmware_set_filename(firmware, filename);
1911
0
  if (!fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) {
1912
0
    g_prefix_error(error, "failed to load %s: ", filename);
1913
0
    return NULL;
1914
0
  }
1915
0
  return g_steal_pointer(&firmware);
1916
0
}
1917
1918
static gchar *
1919
fu_context_build_uefi_basename_for_arch(const gchar *app_name)
1920
0
{
1921
0
#if defined(__x86_64__)
1922
0
  return g_strdup_printf("%sx64.efi", app_name);
1923
0
#endif
1924
#if defined(__aarch64__)
1925
  return g_strdup_printf("%saa64.efi", app_name);
1926
#endif
1927
#if defined(__loongarch_lp64)
1928
  return g_strdup_printf("%sloongarch64.efi", app_name);
1929
#endif
1930
#if (defined(__riscv) && __riscv_xlen == 64)
1931
  return g_strdup_printf("%sriscv64.efi", app_name);
1932
#endif
1933
#if defined(__i386__) || defined(__i686__)
1934
  return g_strdup_printf("%sia32.efi", app_name);
1935
#endif
1936
#if defined(__arm__)
1937
  return g_strdup_printf("%sarm.efi", app_name);
1938
#endif
1939
0
  return NULL;
1940
0
}
1941
1942
static gboolean
1943
fu_context_get_esp_files_for_entry(FuContext *self,
1944
           FuEfiLoadOption *entry,
1945
           GPtrArray *files,
1946
           FuContextEspFileFlags flags,
1947
           GError **error)
1948
0
{
1949
0
  FuContextPrivate *priv = GET_PRIVATE(self);
1950
0
  g_autofree gchar *dp_filename = NULL;
1951
0
  g_autofree gchar *filename = NULL;
1952
0
  g_autofree gchar *mount_point = NULL;
1953
0
  g_autofree gchar *shim_name = fu_context_build_uefi_basename_for_arch("shim");
1954
0
  g_autoptr(FuDeviceLocker) volume_locker = NULL;
1955
0
  g_autoptr(FuEfiFilePathDevicePath) dp_path = NULL;
1956
0
  g_autoptr(FuEfiHardDriveDevicePath) dp_hdd = NULL;
1957
0
  g_autoptr(FuFirmware) dp_list = NULL;
1958
0
  g_autoptr(FuVolume) volume = NULL;
1959
1960
  /* all entries should have a list */
1961
0
  dp_list =
1962
0
      fu_firmware_get_image_by_gtype(FU_FIRMWARE(entry), FU_TYPE_EFI_DEVICE_PATH_LIST, NULL);
1963
0
  if (dp_list == NULL)
1964
0
    return TRUE;
1965
1966
  /* HDD */
1967
0
  dp_hdd = FU_EFI_HARD_DRIVE_DEVICE_PATH(
1968
0
      fu_firmware_get_image_by_gtype(FU_FIRMWARE(dp_list),
1969
0
             FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH,
1970
0
             NULL));
1971
0
  if (dp_hdd == NULL)
1972
0
    return TRUE;
1973
1974
  /* FILE */
1975
0
  dp_path = FU_EFI_FILE_PATH_DEVICE_PATH(
1976
0
      fu_firmware_get_image_by_gtype(FU_FIRMWARE(dp_list),
1977
0
             FU_TYPE_EFI_FILE_PATH_DEVICE_PATH,
1978
0
             NULL));
1979
0
  if (dp_path == NULL)
1980
0
    return TRUE;
1981
1982
  /* can we match the volume? */
1983
0
  volume = fu_context_get_esp_volume_by_hard_drive_device_path(self, dp_hdd, error);
1984
0
  if (volume == NULL)
1985
0
    return FALSE;
1986
0
  if (priv->flags & FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT) {
1987
0
    g_set_error_literal(error,
1988
0
            FWUPD_ERROR,
1989
0
            FWUPD_ERROR_NOT_SUPPORTED,
1990
0
            "cannot mount volume by policy");
1991
0
    return FALSE;
1992
0
  }
1993
0
  volume_locker = fu_volume_locker(volume, error);
1994
0
  if (volume_locker == NULL)
1995
0
    return FALSE;
1996
0
  dp_filename = fu_efi_file_path_device_path_get_name(dp_path, error);
1997
0
  if (dp_filename == NULL)
1998
0
    return FALSE;
1999
2000
  /* the file itself */
2001
0
  mount_point = fu_volume_get_mount_point(volume);
2002
0
  filename = g_build_filename(mount_point, dp_filename, NULL);
2003
0
  g_debug("check for 1st stage bootloader: %s", filename);
2004
0
  if (flags & FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_FIRST_STAGE) {
2005
0
    g_autoptr(FuFirmware) firmware = NULL;
2006
0
    g_autoptr(GError) error_local = NULL;
2007
2008
    /* ignore if the file cannot be loaded as a PE file */
2009
0
    firmware = fu_context_esp_load_pe_file(filename, &error_local);
2010
0
    if (firmware == NULL) {
2011
0
      if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) ||
2012
0
          g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
2013
0
        g_debug("ignoring: %s", error_local->message);
2014
0
      } else {
2015
0
        g_propagate_error(error, g_steal_pointer(&error_local));
2016
0
        return FALSE;
2017
0
      }
2018
0
    } else {
2019
0
      fu_firmware_set_idx(firmware, fu_firmware_get_idx(FU_FIRMWARE(entry)));
2020
0
      g_ptr_array_add(files, g_steal_pointer(&firmware));
2021
0
    }
2022
0
  }
2023
2024
  /* the 2nd stage bootloader, typically grub */
2025
0
  if (flags & FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_SECOND_STAGE &&
2026
0
      g_str_has_suffix(filename, shim_name)) {
2027
0
    g_autoptr(FuFirmware) firmware = NULL;
2028
0
    g_autoptr(GError) error_local = NULL;
2029
0
    g_autoptr(GString) filename2 = g_string_new(filename);
2030
0
    const gchar *path;
2031
2032
0
    path =
2033
0
        fu_efi_load_option_get_metadata(entry, FU_EFI_LOAD_OPTION_METADATA_PATH, NULL);
2034
0
    if (path != NULL) {
2035
0
      g_string_replace(filename2, shim_name, path, 1);
2036
0
    } else {
2037
0
      g_autofree gchar *grub_name =
2038
0
          fu_context_build_uefi_basename_for_arch("grub");
2039
0
      g_string_replace(filename2, shim_name, grub_name, 1);
2040
0
    }
2041
0
    g_debug("check for 2nd stage bootloader: %s", filename2->str);
2042
2043
    /* ignore if the file cannot be loaded as a PE file */
2044
0
    firmware = fu_context_esp_load_pe_file(filename2->str, &error_local);
2045
0
    if (firmware == NULL) {
2046
0
      if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) ||
2047
0
          g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
2048
0
        g_debug("ignoring: %s", error_local->message);
2049
0
      } else {
2050
0
        g_propagate_error(error, g_steal_pointer(&error_local));
2051
0
        return FALSE;
2052
0
      }
2053
0
    } else {
2054
0
      fu_firmware_set_idx(firmware, fu_firmware_get_idx(FU_FIRMWARE(entry)));
2055
0
      g_ptr_array_add(files, g_steal_pointer(&firmware));
2056
0
    }
2057
0
  }
2058
2059
  /* revocations, typically for SBAT */
2060
0
  if (flags & FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_REVOCATIONS &&
2061
0
      g_str_has_suffix(filename, shim_name)) {
2062
0
    g_autoptr(GString) filename2 = g_string_new(filename);
2063
0
    g_autoptr(FuFirmware) firmware = NULL;
2064
0
    g_autoptr(GError) error_local = NULL;
2065
2066
0
    g_string_replace(filename2, shim_name, "revocations.efi", 1);
2067
0
    g_debug("check for revocation: %s", filename2->str);
2068
2069
    /* ignore if the file cannot be loaded as a PE file */
2070
0
    firmware = fu_context_esp_load_pe_file(filename2->str, &error_local);
2071
0
    if (firmware == NULL) {
2072
0
      if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) ||
2073
0
          g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
2074
0
        g_debug("ignoring: %s", error_local->message);
2075
0
      } else {
2076
0
        g_propagate_error(error, g_steal_pointer(&error_local));
2077
0
        return FALSE;
2078
0
      }
2079
0
    } else {
2080
0
      fu_firmware_set_idx(firmware, fu_firmware_get_idx(FU_FIRMWARE(entry)));
2081
0
      g_ptr_array_add(files, g_steal_pointer(&firmware));
2082
0
    }
2083
0
  }
2084
2085
  /* success */
2086
0
  return TRUE;
2087
0
}
2088
2089
/**
2090
 * fu_context_get_esp_files:
2091
 * @self: a #FuContext
2092
 * @flags: some #FuContextEspFileFlags, e.g. #FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_FIRST_STAGE
2093
 * @error: #GError
2094
 *
2095
 * Gets the PE files for all the entries listed in `BootOrder`.
2096
 *
2097
 * Returns: (transfer full) (element-type FuPefileFirmware): PE firmware data
2098
 *
2099
 * Since: 2.0.0
2100
 **/
2101
GPtrArray *
2102
fu_context_get_esp_files(FuContext *self, FuContextEspFileFlags flags, GError **error)
2103
0
{
2104
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2105
0
  g_autoptr(GPtrArray) entries = NULL;
2106
0
  g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
2107
2108
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
2109
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2110
2111
0
  entries = fu_efivars_get_boot_entries(priv->efivars, error);
2112
0
  if (entries == NULL)
2113
0
    return NULL;
2114
0
  for (guint i = 0; i < entries->len; i++) {
2115
0
    FuEfiLoadOption *entry = g_ptr_array_index(entries, i);
2116
0
    g_autoptr(GError) error_local = NULL;
2117
0
    if (!fu_context_get_esp_files_for_entry(self, entry, files, flags, &error_local)) {
2118
0
      if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) ||
2119
0
          g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) {
2120
0
        g_debug("ignoring %s: %s",
2121
0
          fu_firmware_get_id(FU_FIRMWARE(entry)),
2122
0
          error_local->message);
2123
0
        continue;
2124
0
      }
2125
0
      g_propagate_error(error, g_steal_pointer(&error_local));
2126
0
      return NULL;
2127
0
    }
2128
0
  }
2129
2130
  /* success */
2131
0
  return g_steal_pointer(&files);
2132
0
}
2133
2134
/**
2135
 * fu_context_get_backends:
2136
 * @self: a #FuContext
2137
 *
2138
 * Gets all the possible backends used by all plugins.
2139
 *
2140
 * Returns: (transfer none) (element-type FuBackend): List of backends
2141
 *
2142
 * Since: 2.0.0
2143
 **/
2144
GPtrArray *
2145
fu_context_get_backends(FuContext *self)
2146
0
{
2147
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2148
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
2149
0
  return priv->backends;
2150
0
}
2151
2152
/**
2153
 * fu_context_add_backend:
2154
 * @self: a #FuContext
2155
 * @backend: a #FuBackend
2156
 *
2157
 * Adds a backend to the context.
2158
 *
2159
 * Returns: (transfer full): a #FuBackend, or %NULL on error
2160
 *
2161
 * Since: 2.0.0
2162
 **/
2163
void
2164
fu_context_add_backend(FuContext *self, FuBackend *backend)
2165
0
{
2166
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2167
0
  g_return_if_fail(FU_IS_CONTEXT(self));
2168
0
  g_return_if_fail(FU_IS_BACKEND(backend));
2169
0
  g_ptr_array_add(priv->backends, g_object_ref(backend));
2170
0
}
2171
2172
/**
2173
 * fu_context_get_backend_by_name:
2174
 * @self: a #FuContext
2175
 * @name: backend name, e.g. `udev` or `bluez`
2176
 * @error: (nullable): optional return location for an error
2177
 *
2178
 * Gets a specific backend added to the context.
2179
 *
2180
 * Returns: (transfer full): a #FuBackend, or %NULL on error
2181
 *
2182
 * Since: 2.0.0
2183
 **/
2184
FuBackend *
2185
fu_context_get_backend_by_name(FuContext *self, const gchar *name, GError **error)
2186
0
{
2187
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2188
2189
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
2190
0
  g_return_val_if_fail(name != NULL, NULL);
2191
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2192
2193
0
  for (guint i = 0; i < priv->backends->len; i++) {
2194
0
    FuBackend *backend = g_ptr_array_index(priv->backends, i);
2195
0
    if (g_strcmp0(fu_backend_get_name(backend), name) == 0)
2196
0
      return g_object_ref(backend);
2197
0
  }
2198
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no backend with name %s", name);
2199
0
  return NULL;
2200
0
}
2201
2202
/* private */
2203
gboolean
2204
fu_context_has_backend(FuContext *self, const gchar *name)
2205
0
{
2206
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2207
2208
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE);
2209
0
  g_return_val_if_fail(name != NULL, FALSE);
2210
2211
0
  for (guint i = 0; i < priv->backends->len; i++) {
2212
0
    FuBackend *backend = g_ptr_array_index(priv->backends, i);
2213
0
    if (g_strcmp0(fu_backend_get_name(backend), name) == 0)
2214
0
      return TRUE;
2215
0
  }
2216
0
  return FALSE;
2217
0
}
2218
2219
/* private */
2220
gpointer
2221
fu_context_get_data(FuContext *self, const gchar *key)
2222
0
{
2223
0
  g_return_val_if_fail(FU_IS_CONTEXT(self), NULL);
2224
0
  g_return_val_if_fail(key != NULL, NULL);
2225
0
  return g_object_get_data(G_OBJECT(self), key);
2226
0
}
2227
2228
/* private */
2229
void
2230
fu_context_set_data(FuContext *self, const gchar *key, gpointer data)
2231
0
{
2232
0
  g_return_if_fail(FU_IS_CONTEXT(self));
2233
0
  g_return_if_fail(key != NULL);
2234
0
  g_object_set_data(G_OBJECT(self), key, data);
2235
0
}
2236
2237
static void
2238
fu_context_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
2239
0
{
2240
0
  FuContext *self = FU_CONTEXT(object);
2241
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2242
0
  switch (prop_id) {
2243
0
  case PROP_POWER_STATE:
2244
0
    g_value_set_uint(value, priv->power_state);
2245
0
    break;
2246
0
  case PROP_LID_STATE:
2247
0
    g_value_set_uint(value, priv->lid_state);
2248
0
    break;
2249
0
  case PROP_DISPLAY_STATE:
2250
0
    g_value_set_uint(value, priv->display_state);
2251
0
    break;
2252
0
  case PROP_BATTERY_LEVEL:
2253
0
    g_value_set_uint(value, priv->battery_level);
2254
0
    break;
2255
0
  case PROP_BATTERY_THRESHOLD:
2256
0
    g_value_set_uint(value, priv->battery_threshold);
2257
0
    break;
2258
0
  case PROP_FLAGS:
2259
0
    g_value_set_uint64(value, priv->flags);
2260
0
    break;
2261
0
  default:
2262
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2263
0
    break;
2264
0
  }
2265
0
}
2266
2267
static void
2268
fu_context_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
2269
0
{
2270
0
  FuContext *self = FU_CONTEXT(object);
2271
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2272
0
  switch (prop_id) {
2273
0
  case PROP_POWER_STATE:
2274
0
    fu_context_set_power_state(self, g_value_get_uint(value));
2275
0
    break;
2276
0
  case PROP_LID_STATE:
2277
0
    fu_context_set_lid_state(self, g_value_get_uint(value));
2278
0
    break;
2279
0
  case PROP_DISPLAY_STATE:
2280
0
    fu_context_set_display_state(self, g_value_get_uint(value));
2281
0
    break;
2282
0
  case PROP_BATTERY_LEVEL:
2283
0
    fu_context_set_battery_level(self, g_value_get_uint(value));
2284
0
    break;
2285
0
  case PROP_BATTERY_THRESHOLD:
2286
0
    fu_context_set_battery_threshold(self, g_value_get_uint(value));
2287
0
    break;
2288
0
  case PROP_FLAGS:
2289
0
    priv->flags = g_value_get_uint64(value);
2290
0
    break;
2291
0
  default:
2292
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2293
0
    break;
2294
0
  }
2295
0
}
2296
2297
static void
2298
fu_context_dispose(GObject *object)
2299
0
{
2300
0
  FuContext *self = FU_CONTEXT(object);
2301
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2302
0
  g_ptr_array_set_size(priv->backends, 0);
2303
0
  G_OBJECT_CLASS(fu_context_parent_class)->dispose(object);
2304
0
}
2305
2306
static void
2307
fu_context_finalize(GObject *object)
2308
0
{
2309
0
  FuContext *self = FU_CONTEXT(object);
2310
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2311
2312
0
  if (priv->fdt != NULL)
2313
0
    g_object_unref(priv->fdt);
2314
0
  if (priv->efivars != NULL)
2315
0
    g_object_unref(priv->efivars);
2316
0
  g_free(priv->esp_location);
2317
0
  g_hash_table_unref(priv->runtime_versions);
2318
0
  g_hash_table_unref(priv->compile_versions);
2319
0
  g_object_unref(priv->hwids);
2320
0
  g_object_unref(priv->config);
2321
0
  g_hash_table_unref(priv->hwid_flags);
2322
0
  g_object_unref(priv->quirks);
2323
0
  g_object_unref(priv->smbios);
2324
0
  g_object_unref(priv->host_bios_settings);
2325
0
  g_hash_table_unref(priv->firmware_gtypes);
2326
0
  g_hash_table_unref(priv->udev_subsystems);
2327
0
  g_ptr_array_unref(priv->esp_volumes);
2328
0
  g_ptr_array_unref(priv->backends);
2329
2330
0
  G_OBJECT_CLASS(fu_context_parent_class)->finalize(object);
2331
0
}
2332
2333
static void
2334
fu_context_class_init(FuContextClass *klass)
2335
0
{
2336
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
2337
0
  GParamSpec *pspec;
2338
2339
0
  object_class->dispose = fu_context_dispose;
2340
0
  object_class->get_property = fu_context_get_property;
2341
0
  object_class->set_property = fu_context_set_property;
2342
2343
  /**
2344
   * FuContext:power-state:
2345
   *
2346
   * The system power state.
2347
   *
2348
   * Since: 1.8.11
2349
   */
2350
0
  pspec = g_param_spec_uint("power-state",
2351
0
          NULL,
2352
0
          NULL,
2353
0
          FU_POWER_STATE_UNKNOWN,
2354
0
          FU_POWER_STATE_LAST,
2355
0
          FU_POWER_STATE_UNKNOWN,
2356
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2357
0
  g_object_class_install_property(object_class, PROP_POWER_STATE, pspec);
2358
2359
  /**
2360
   * FuContext:lid-state:
2361
   *
2362
   * The system lid state.
2363
   *
2364
   * Since: 1.7.4
2365
   */
2366
0
  pspec = g_param_spec_uint("lid-state",
2367
0
          NULL,
2368
0
          NULL,
2369
0
          FU_LID_STATE_UNKNOWN,
2370
0
          FU_LID_STATE_LAST,
2371
0
          FU_LID_STATE_UNKNOWN,
2372
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2373
0
  g_object_class_install_property(object_class, PROP_LID_STATE, pspec);
2374
2375
  /**
2376
   * FuContext:display-state:
2377
   *
2378
   * The display state.
2379
   *
2380
   * Since: 1.9.6
2381
   */
2382
0
  pspec = g_param_spec_uint("display-state",
2383
0
          NULL,
2384
0
          NULL,
2385
0
          FU_DISPLAY_STATE_UNKNOWN,
2386
0
          FU_DISPLAY_STATE_LAST,
2387
0
          FU_DISPLAY_STATE_UNKNOWN,
2388
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2389
0
  g_object_class_install_property(object_class, PROP_DISPLAY_STATE, pspec);
2390
2391
  /**
2392
   * FuContext:battery-level:
2393
   *
2394
   * The system battery level in percent.
2395
   *
2396
   * Since: 1.6.0
2397
   */
2398
0
  pspec = g_param_spec_uint("battery-level",
2399
0
          NULL,
2400
0
          NULL,
2401
0
          0,
2402
0
          FWUPD_BATTERY_LEVEL_INVALID,
2403
0
          FWUPD_BATTERY_LEVEL_INVALID,
2404
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2405
0
  g_object_class_install_property(object_class, PROP_BATTERY_LEVEL, pspec);
2406
2407
  /**
2408
   * FuContext:battery-threshold:
2409
   *
2410
   * The system battery threshold in percent.
2411
   *
2412
   * Since: 1.6.0
2413
   */
2414
0
  pspec = g_param_spec_uint("battery-threshold",
2415
0
          NULL,
2416
0
          NULL,
2417
0
          0,
2418
0
          FWUPD_BATTERY_LEVEL_INVALID,
2419
0
          FWUPD_BATTERY_LEVEL_INVALID,
2420
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2421
0
  g_object_class_install_property(object_class, PROP_BATTERY_THRESHOLD, pspec);
2422
2423
  /**
2424
   * FuContext:flags:
2425
   *
2426
   * The context flags.
2427
   *
2428
   * Since: 1.8.10
2429
   */
2430
0
  pspec = g_param_spec_uint64("flags",
2431
0
            NULL,
2432
0
            NULL,
2433
0
            FU_CONTEXT_FLAG_NONE,
2434
0
            G_MAXUINT64,
2435
0
            FU_CONTEXT_FLAG_NONE,
2436
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2437
0
  g_object_class_install_property(object_class, PROP_FLAGS, pspec);
2438
2439
  /**
2440
   * FuContext::security-changed:
2441
   * @self: the #FuContext instance that emitted the signal
2442
   *
2443
   * The ::security-changed signal is emitted when some system state has changed that could
2444
   * have affected the security level.
2445
   *
2446
   * Since: 1.6.0
2447
   **/
2448
0
  signals[SIGNAL_SECURITY_CHANGED] =
2449
0
      g_signal_new("security-changed",
2450
0
       G_TYPE_FROM_CLASS(object_class),
2451
0
       G_SIGNAL_RUN_LAST,
2452
0
       G_STRUCT_OFFSET(FuContextClass, security_changed),
2453
0
       NULL,
2454
0
       NULL,
2455
0
       g_cclosure_marshal_VOID__VOID,
2456
0
       G_TYPE_NONE,
2457
0
       0);
2458
  /**
2459
   * FuContext::housekeeping:
2460
   * @self: the #FuContext instance that emitted the signal
2461
   *
2462
   * The ::housekeeping signal is emitted when helper objects should do house-keeping actions
2463
   * when the daemon is idle.
2464
   *
2465
   * Since: 2.0.0
2466
   **/
2467
0
  signals[SIGNAL_HOUSEKEEPING] = g_signal_new("housekeeping",
2468
0
                G_TYPE_FROM_CLASS(object_class),
2469
0
                G_SIGNAL_RUN_LAST,
2470
0
                G_STRUCT_OFFSET(FuContextClass, housekeeping),
2471
0
                NULL,
2472
0
                NULL,
2473
0
                g_cclosure_marshal_VOID__VOID,
2474
0
                G_TYPE_NONE,
2475
0
                0);
2476
2477
0
  object_class->finalize = fu_context_finalize;
2478
0
}
2479
2480
static void
2481
fu_context_init(FuContext *self)
2482
0
{
2483
0
  FuContextPrivate *priv = GET_PRIVATE(self);
2484
0
  priv->chassis_kind = FU_SMBIOS_CHASSIS_KIND_UNKNOWN;
2485
0
  priv->battery_level = FWUPD_BATTERY_LEVEL_INVALID;
2486
0
  priv->battery_threshold = FWUPD_BATTERY_LEVEL_INVALID;
2487
0
  priv->smbios = fu_smbios_new();
2488
0
  priv->hwids = fu_hwids_new();
2489
0
  priv->config = fu_config_new();
2490
0
  priv->efivars = g_strcmp0(g_getenv("FWUPD_EFIVARS"), "dummy") == 0 ? fu_dummy_efivars_new()
2491
0
                     : fu_efivars_new();
2492
0
  priv->hwid_flags = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2493
0
  priv->udev_subsystems = g_hash_table_new_full(g_str_hash,
2494
0
                  g_str_equal,
2495
0
                  g_free,
2496
0
                  (GDestroyNotify)g_ptr_array_unref);
2497
0
  priv->firmware_gtypes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
2498
0
  priv->quirks = fu_quirks_new(self);
2499
0
  priv->host_bios_settings = fu_bios_settings_new();
2500
0
  priv->esp_volumes = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
2501
0
  priv->runtime_versions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
2502
0
  priv->compile_versions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
2503
0
  priv->backends = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
2504
0
}
2505
2506
/**
2507
 * fu_context_new:
2508
 *
2509
 * Creates a new #FuContext
2510
 *
2511
 * Returns: (transfer full): the object
2512
 *
2513
 * Since: 1.6.0
2514
 **/
2515
FuContext *
2516
fu_context_new(void)
2517
0
{
2518
0
  return FU_CONTEXT(g_object_new(FU_TYPE_CONTEXT, NULL));
2519
0
}