Coverage Report

Created: 2026-01-17 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rauc/src/config_file.c
Line
Count
Source
1
#include <glib.h>
2
#include <string.h>
3
4
#include "artifacts.h"
5
#include "bootchooser.h"
6
#include "config_file.h"
7
#include "context.h"
8
#include "event_log.h"
9
#include "install.h"
10
#include "manifest.h"
11
#include "mount.h"
12
#include "slot.h"
13
#include "utils.h"
14
15
G_DEFINE_QUARK(r-config-error-quark, r_config_error)
16
G_DEFINE_QUARK(r-slot-error-quark, r_slot_error)
17
18
#define RAUC_SLOT_PREFIX  "slot"
19
20
static gboolean fix_grandparent_links(GHashTable *slots, GError **error)
21
5.06k
{
22
  /* Every child slot in a group must refer to the same parent.
23
   * Some kind of grandparent relationship makes no sense, but if the
24
   * user has accidentally constructed such a configuration we will fix
25
   * it up for them here. */
26
5.06k
  GHashTableIter iter;
27
5.06k
  gpointer value;
28
29
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
30
31
5.06k
  g_hash_table_iter_init(&iter, slots);
32
5.06k
  while (g_hash_table_iter_next(&iter, NULL, &value)) {
33
0
    RaucSlot *slot = value;
34
0
    RaucSlot *realparent;
35
0
    unsigned int steps = 0;
36
0
    if (slot->parent == NULL)
37
0
      continue; /* not part of a group */
38
0
    realparent = slot->parent;
39
0
    while (realparent->parent) {
40
0
      realparent = realparent->parent;
41
0
      steps++;
42
0
      if (steps > 100) {
43
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_PARENT_LOOP,
44
0
            "Slot '%s' has a parent loop!", slot->name);
45
0
        return FALSE;
46
0
      }
47
0
    }
48
0
    if (realparent != slot->parent) {
49
0
      g_message("Updating slot %s parent link to %s",
50
0
          slot->name,
51
0
          realparent->name);
52
0
      slot->parent = realparent;
53
0
    }
54
0
  }
55
5.06k
  return TRUE;
56
5.06k
}
57
58
gboolean parse_bundle_formats(guint *mask, const gchar *config, GError **error)
59
0
{
60
0
  gboolean res = TRUE;
61
0
  guint imask = 0;
62
0
  g_auto(GStrv) tokens = NULL;
63
0
  guint set = FALSE, modify = FALSE;
64
65
0
  g_return_val_if_fail(mask != NULL, FALSE);
66
0
  g_return_val_if_fail(config != NULL, FALSE);
67
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
68
69
0
  imask = *mask;
70
0
  tokens = g_strsplit(config, " ", -1);
71
72
0
  for (size_t i = 0; tokens[i]; i++) {
73
0
    const gchar *token = tokens[i];
74
0
    gboolean plus = FALSE, minus = FALSE;
75
0
    RManifestBundleFormat format;
76
77
0
    if (!token[0]) /* empty string */
78
0
      continue;
79
0
    if (token[0] == '-') {
80
0
      minus = TRUE;
81
0
      token++;
82
0
    } else if (token[0] == '+') {
83
0
      plus = TRUE;
84
0
      token++;
85
0
    }
86
87
0
    if (g_strcmp0(token, "plain") == 0) {
88
0
      format = R_MANIFEST_FORMAT_PLAIN;
89
0
    } else if (g_strcmp0(token, "verity") == 0) {
90
0
      format = R_MANIFEST_FORMAT_VERITY;
91
0
    } else if (g_strcmp0(token, "crypt") == 0) {
92
0
      format = R_MANIFEST_FORMAT_CRYPT;
93
0
    } else {
94
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
95
0
          "Invalid bundle format '%s'", token);
96
0
      res = FALSE;
97
0
      goto out;
98
0
    }
99
100
0
    if (plus || minus) {
101
0
      modify = TRUE;
102
0
    } else {
103
0
      if (!set)
104
0
        imask = 0;
105
0
      set = TRUE;
106
0
    }
107
108
0
    if (minus)
109
0
      imask &= ~(1 << format);
110
0
    else
111
0
      imask |= 1 << format;
112
0
  }
113
114
0
  if (set && modify) {
115
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
116
0
        "Invalid bundle format configuration '%s', cannot combine fixed value with modification (+/-)", config);
117
0
    res = FALSE;
118
0
    goto out;
119
0
  }
120
121
0
  if (!imask) {
122
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
123
0
        "Invalid bundle format configuration '%s', no remaining formats", config);
124
0
    res = FALSE;
125
0
    goto out;
126
0
  }
127
128
0
out:
129
0
  if (res)
130
0
    *mask = imask;
131
0
  return res;
132
0
}
133
134
0
#define RAUC_LOG_EVENT_CONF_PREFIX "log"
135
136
static gboolean r_event_log_parse_config_sections(GKeyFile *key_file, RaucConfig *config, GError **error)
137
5.06k
{
138
5.06k
  gsize group_count;
139
5.06k
  g_auto(GStrv) groups = NULL;
140
5.06k
  gint tmp_maxfiles;
141
142
5.06k
  g_return_val_if_fail(key_file, FALSE);
143
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
144
145
5.06k
  g_assert_null(config->loggers);
146
147
  /* parse [log.*] sections */
148
5.06k
  groups = g_key_file_get_groups(key_file, &group_count);
149
5.06k
  for (gchar **group = groups; *group != NULL; group++) {
150
0
    GError *ierror = NULL;
151
0
    g_autoptr(REventLogger) logger = NULL;
152
0
    const gchar *logger_name;
153
0
    g_autofree gchar *log_format = NULL;
154
0
    gsize entries;
155
156
0
    if (!g_str_has_prefix(*group, RAUC_LOG_EVENT_CONF_PREFIX "."))
157
0
      continue;
158
159
0
    logger_name = *group + strlen(RAUC_LOG_EVENT_CONF_PREFIX ".");
160
161
0
    logger = g_new0(REventLogger, 1);
162
0
    logger->name = g_strdup(logger_name);
163
164
    /* 'filename' option is currently mandatory for a logger group */
165
0
    logger->filename = key_file_consume_string(key_file, *group, "filename", &ierror);
166
0
    if (!logger->filename) {
167
0
      g_propagate_error(error, ierror);
168
0
      return FALSE;
169
0
    }
170
    /* relative paths are resolved relative to the data-directory */
171
0
    if (!g_path_is_absolute(logger->filename)) {
172
0
      gchar *abspath;
173
0
      if (!config->data_directory) {
174
0
        g_set_error_literal(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
175
0
            "Relative filename requires data-directory to be set");
176
0
        return FALSE;
177
0
      }
178
179
0
      abspath = g_build_filename(config->data_directory, logger->filename, NULL);
180
0
      g_free(logger->filename);
181
0
      logger->filename = abspath;
182
0
    }
183
184
0
    log_format = key_file_consume_string(key_file, *group, "format", &ierror);
185
0
    if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
186
0
      g_clear_pointer(&log_format, g_free);
187
0
      log_format = g_strdup("readable");
188
0
      g_clear_error(&ierror);
189
0
    } else if (ierror) {
190
0
      g_propagate_error(error, ierror);
191
0
      return FALSE;
192
0
    }
193
194
0
    if (g_strcmp0(log_format, "readable") == 0) {
195
0
      logger->format = R_EVENT_LOGFMT_READABLE;
196
0
    } else if (g_strcmp0(log_format, "short") == 0) {
197
0
      logger->format = R_EVENT_LOGFMT_READABLE_SHORT;
198
0
    } else if (g_strcmp0(log_format, "json") == 0) {
199
#if ENABLE_JSON
200
      logger->format = R_EVENT_LOGFMT_JSON;
201
#else
202
0
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
203
0
          "Invalid log format %s. RAUC is compiled without JSON support", log_format);
204
0
      return FALSE;
205
0
#endif
206
0
    } else if (g_strcmp0(log_format, "json-pretty") == 0) {
207
#if ENABLE_JSON
208
      logger->format = R_EVENT_LOGFMT_JSON_PRETTY;
209
#else
210
0
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
211
0
          "Invalid log format %s. RAUC is compiled without JSON support", log_format);
212
0
      return FALSE;
213
0
#endif
214
0
    } else {
215
0
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
216
0
          "Unknown log format '%s'", log_format);
217
0
      return FALSE;
218
0
    }
219
220
0
    logger->events = g_key_file_get_string_list(key_file, *group, "events", &entries, &ierror);
221
0
    if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
222
0
      g_assert_null(logger->events);
223
0
      logger->events = g_malloc(2 * sizeof(gchar *));
224
0
      logger->events[0] = g_strdup("all");
225
0
      logger->events[1] = NULL;
226
0
      g_clear_error(&ierror);
227
0
    } else if (ierror) {
228
0
      g_propagate_error(error, ierror);
229
0
      return FALSE;
230
0
    }
231
232
0
    if (g_strv_length(logger->events) > 1 && g_strv_contains((const gchar * const *) logger->events, "all")) {
233
0
      g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
234
0
          "Event type 'all' cannot be combined");
235
0
      return FALSE;
236
0
    }
237
0
    for (gsize j = 0; j < entries; j++) {
238
0
      if (!r_event_log_is_supported_type(logger->events[j])) {
239
0
        g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
240
0
            "Unsupported event log type '%s'", logger->events[j]);
241
0
        return FALSE;
242
0
      }
243
0
    }
244
0
    g_key_file_remove_key(key_file, *group, "events", NULL);
245
246
0
    logger->maxsize = key_file_consume_binary_suffixed_string(key_file, *group, "max-size", &ierror);
247
0
    if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
248
0
      g_clear_error(&ierror);
249
0
    } else if (ierror) {
250
0
      g_propagate_error(error, ierror);
251
0
      return FALSE;
252
0
    }
253
254
0
    tmp_maxfiles = key_file_consume_integer(key_file, *group, "max-files", &ierror);
255
0
    if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
256
0
      tmp_maxfiles = 10;
257
0
      g_clear_error(&ierror);
258
0
    } else if (ierror) {
259
0
      g_propagate_error(error, ierror);
260
0
      return FALSE;
261
0
    }
262
0
    if (tmp_maxfiles < 1) {
263
0
      g_set_error_literal(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
264
0
          "Value for 'max-files' must be >= 1");
265
0
      return FALSE;
266
0
    } else if (tmp_maxfiles > 1000) {
267
0
      g_set_error_literal(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
268
0
          "Value of %d for 'max-files' looks implausible");
269
0
      return FALSE;
270
0
    }
271
0
    logger->maxfiles = (guint) tmp_maxfiles;
272
273
0
    if (!check_remaining_keys(key_file, *group, &ierror)) {
274
0
      g_propagate_error(error, ierror);
275
0
      return FALSE;
276
0
    }
277
278
0
    g_key_file_remove_group(key_file, *group, NULL);
279
280
    /* insert new logger in list */
281
0
    config->loggers = g_list_append(config->loggers, g_steal_pointer(&logger));
282
0
  }
283
284
5.06k
  return TRUE;
285
5.06k
}
286
287
static gboolean parse_system_section(const gchar *filename, GKeyFile *key_file, RaucConfig *c, GError **error)
288
5.06k
{
289
5.06k
  GError *ierror = NULL;
290
5.06k
  gboolean dtbvariant;
291
5.06k
  g_autofree gchar *variant_data = NULL;
292
5.06k
  g_autofree gchar *version_data = NULL;
293
5.06k
  g_autofree gchar *bundle_formats = NULL;
294
295
5.06k
  g_return_val_if_fail(key_file, FALSE);
296
5.06k
  g_return_val_if_fail(c, FALSE);
297
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
298
299
5.06k
  c->system_compatible = key_file_consume_string(key_file, "system", "compatible", &ierror);
300
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
301
5.06k
    g_clear_pointer(&c->system_compatible, g_free);
302
5.06k
    g_clear_error(&ierror);
303
5.06k
  } else if (ierror) {
304
0
    g_propagate_error(error, ierror);
305
0
    return FALSE;
306
0
  }
307
308
  /* check optional 'min-bundle-version' key for validity */
309
5.06k
  version_data = key_file_consume_string(key_file, "system", "min-bundle-version", &ierror);
310
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
311
5.06k
    g_clear_pointer(&version_data, g_free);
312
5.06k
    g_clear_error(&ierror);
313
5.06k
  } else if (ierror) {
314
0
    g_propagate_error(error, ierror);
315
0
    return FALSE;
316
0
  }
317
5.06k
  if (version_data) {
318
0
    if (!r_semver_less_equal("0", version_data, NULL)) {
319
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
320
0
          "Min version format invalid, expected: Major[.Minor[.Patch]][-pre_release], got: %s",
321
0
          version_data);
322
0
      return FALSE;
323
0
    }
324
0
    c->system_min_bundle_version = g_steal_pointer(&version_data);
325
0
  }
326
327
5.06k
  c->system_bootloader = key_file_consume_string(key_file, "system", "bootloader", NULL);
328
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
329
0
    g_clear_pointer(&c->system_bootloader, g_free);
330
0
    g_clear_error(&ierror);
331
5.06k
  } else if (ierror) {
332
0
    g_propagate_error(error, ierror);
333
0
    return FALSE;
334
0
  }
335
336
5.06k
  if (g_strcmp0(c->system_bootloader, "barebox") == 0) {
337
0
    c->system_bb_statename = key_file_consume_string(key_file, "system", "barebox-statename", NULL);
338
0
    c->system_bb_dtbpath = key_file_consume_string(key_file, "system", "barebox-dtbpath", NULL);
339
5.06k
  } else if (g_strcmp0(c->system_bootloader, "grub") == 0) {
340
0
    c->grubenv_path = resolve_path_take(filename,
341
0
        key_file_consume_string(key_file, "system", "grubenv", NULL));
342
0
    if (!c->grubenv_path) {
343
0
      g_debug("No grubenv path provided, using /boot/grub/grubenv as default");
344
0
      c->grubenv_path = g_strdup("/boot/grub/grubenv");
345
0
    }
346
5.06k
  } else if (g_strcmp0(c->system_bootloader, "efi") == 0) {
347
0
    c->efi_use_bootnext = g_key_file_get_boolean(key_file, "system", "efi-use-bootnext", &ierror);
348
0
    if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
349
0
      c->efi_use_bootnext = TRUE;
350
0
      g_clear_error(&ierror);
351
0
    } else if (ierror) {
352
0
      g_propagate_error(error, ierror);
353
0
      return FALSE;
354
0
    }
355
0
    g_key_file_remove_key(key_file, "system", "efi-use-bootnext", NULL);
356
5.06k
  } else if (g_strcmp0(c->system_bootloader, "custom") == 0) {
357
0
    c->custom_bootloader_backend = resolve_path_take(filename,
358
0
        key_file_consume_string(key_file, "handlers", "bootloader-custom-backend", NULL));
359
0
    if (!c->custom_bootloader_backend) {
360
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
361
0
          "No custom bootloader backend defined");
362
0
      return FALSE;
363
0
    }
364
0
  }
365
366
5.06k
  c->boot_default_attempts = key_file_consume_integer(key_file, "system", "boot-attempts", &ierror);
367
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
368
5.06k
    c->boot_default_attempts = 0; /* to indicate 'unset' */
369
5.06k
    g_clear_error(&ierror);
370
5.06k
  } else if (ierror) {
371
0
    g_propagate_error(error, ierror);
372
0
    return FALSE;
373
0
  }
374
5.06k
  if (c->boot_default_attempts < 0) {
375
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
376
0
        "Value for \"boot-attempts\" must not be negative");
377
0
    return FALSE;
378
0
  }
379
380
5.06k
  c->boot_attempts_primary = key_file_consume_integer(key_file, "system", "boot-attempts-primary", &ierror);
381
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
382
5.06k
    c->boot_attempts_primary = 0; /* to indicate 'unset' */
383
5.06k
    g_clear_error(&ierror);
384
5.06k
  } else if (ierror) {
385
0
    g_propagate_error(error, ierror);
386
0
    return FALSE;
387
0
  }
388
5.06k
  if (c->boot_attempts_primary < 0) {
389
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
390
0
        "Value for \"boot-attempts-primary\" must not be negative");
391
0
    return FALSE;
392
0
  }
393
5.06k
  if (c->boot_default_attempts > 0 || c->boot_attempts_primary > 0) {
394
0
    if ((g_strcmp0(c->system_bootloader, "uboot") != 0) && (g_strcmp0(c->system_bootloader, "barebox") != 0)) {
395
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
396
0
          "Configuring boot attempts is valid for uboot or barebox only (not for %s)", c->system_bootloader);
397
0
      return FALSE;
398
0
    }
399
0
  }
400
401
5.06k
  c->max_bundle_download_size = g_key_file_get_uint64(key_file, "system", "max-bundle-download-size", &ierror);
402
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
403
5.06k
    g_debug("No value for key \"max-bundle-download-size\" in [system] defined "
404
5.06k
        "- using default value of %d bytes.", DEFAULT_MAX_BUNDLE_DOWNLOAD_SIZE);
405
5.06k
    c->max_bundle_download_size = DEFAULT_MAX_BUNDLE_DOWNLOAD_SIZE;
406
5.06k
    g_clear_error(&ierror);
407
5.06k
  } else if (ierror) {
408
0
    g_propagate_error(error, ierror);
409
0
    return FALSE;
410
0
  } else if (ENABLE_STREAMING) {
411
0
    g_message("Using max-bundle-download-size with streaming has no effect.");
412
0
  }
413
5.06k
  if (c->max_bundle_download_size == 0) {
414
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_MAX_BUNDLE_DOWNLOAD_SIZE,
415
0
        "Invalid value (%" G_GUINT64_FORMAT ") for key \"max-bundle-download-size\" in system config", c->max_bundle_download_size);
416
0
    return FALSE;
417
0
  }
418
5.06k
  g_key_file_remove_key(key_file, "system", "max-bundle-download-size", NULL);
419
420
5.06k
  c->max_bundle_signature_size = g_key_file_get_uint64(key_file, "system", "max-bundle-signature-size", &ierror);
421
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
422
5.06k
    g_debug("No value for key \"max-bundle-signature-size\" in [system] defined "
423
5.06k
        "- using default value of %d bytes.", DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE);
424
5.06k
    c->max_bundle_signature_size = DEFAULT_MAX_BUNDLE_SIGNATURE_SIZE;
425
5.06k
    g_clear_error(&ierror);
426
5.06k
  } else if (ierror) {
427
0
    g_propagate_error(error, ierror);
428
0
    return FALSE;
429
0
  }
430
5.06k
  if (c->max_bundle_signature_size == 0) {
431
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_MAX_BUNDLE_SIGNATURE_SIZE,
432
0
        "Invalid value (%" G_GUINT64_FORMAT ") for key \"max-bundle-signature-size\" in system config", c->max_bundle_signature_size);
433
0
    return FALSE;
434
0
  }
435
5.06k
  g_key_file_remove_key(key_file, "system", "max-bundle-signature-size", NULL);
436
437
5.06k
  c->mount_prefix = key_file_consume_string(key_file, "system", "mountprefix", NULL);
438
5.06k
  if (!c->mount_prefix) {
439
5.06k
    g_debug("No mount prefix provided, using /mnt/rauc/ as default");
440
5.06k
    c->mount_prefix = g_strdup("/mnt/rauc/");
441
5.06k
  }
442
443
5.06k
  c->activate_installed = g_key_file_get_boolean(key_file, "system", "activate-installed", &ierror);
444
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
445
5.06k
    c->activate_installed = TRUE;
446
5.06k
    g_clear_error(&ierror);
447
5.06k
  } else if (ierror) {
448
0
    g_propagate_error(error, ierror);
449
0
    return FALSE;
450
0
  }
451
5.06k
  g_key_file_remove_key(key_file, "system", "activate-installed", NULL);
452
453
5.06k
  c->system_variant_type = R_CONFIG_SYS_VARIANT_NONE;
454
455
  /* parse 'variant-dtb' key */
456
5.06k
  dtbvariant = g_key_file_get_boolean(key_file, "system", "variant-dtb", &ierror);
457
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
458
5.06k
    dtbvariant = FALSE;
459
5.06k
    g_clear_error(&ierror);
460
5.06k
  } else if (ierror) {
461
0
    g_propagate_error(error, ierror);
462
0
    return FALSE;
463
0
  }
464
5.06k
  g_key_file_remove_key(key_file, "system", "variant-dtb", NULL);
465
5.06k
  if (dtbvariant)
466
0
    c->system_variant_type = R_CONFIG_SYS_VARIANT_DTB;
467
468
5.06k
  c->prevent_late_fallback = g_key_file_get_boolean(key_file, "system", "prevent-late-fallback", &ierror);
469
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
470
5.06k
    c->prevent_late_fallback = FALSE;
471
5.06k
    g_clear_error(&ierror);
472
5.06k
  } else if (ierror) {
473
0
    g_propagate_error(error, ierror);
474
0
    return FALSE;
475
0
  }
476
5.06k
  g_key_file_remove_key(key_file, "system", "prevent-late-fallback", NULL);
477
478
  /* parse 'variant-file' key */
479
5.06k
  variant_data = key_file_consume_string(key_file, "system", "variant-file", &ierror);
480
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
481
5.06k
    g_clear_pointer(&variant_data, g_free);
482
5.06k
    g_clear_error(&ierror);
483
5.06k
  } else if (ierror) {
484
0
    g_propagate_error(error, ierror);
485
0
    return FALSE;
486
0
  }
487
5.06k
  if (variant_data) {
488
0
    if (c->system_variant_type != R_CONFIG_SYS_VARIANT_NONE) {
489
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
490
0
          "Only one of the keys 'variant-file', variant-dtb','variant-name' is allowed");
491
0
      return FALSE;
492
0
    }
493
494
0
    c->system_variant_type = R_CONFIG_SYS_VARIANT_FILE;
495
0
    c->system_variant = g_steal_pointer(&variant_data);
496
0
  }
497
498
  /* parse 'variant-name' key */
499
5.06k
  variant_data = key_file_consume_string(key_file, "system", "variant-name", &ierror);
500
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
501
5.06k
    g_clear_pointer(&variant_data, g_free);
502
5.06k
    g_clear_error(&ierror);
503
5.06k
  } else if (ierror) {
504
0
    g_propagate_error(error, ierror);
505
0
    return FALSE;
506
0
  }
507
5.06k
  if (variant_data) {
508
0
    if (c->system_variant_type != R_CONFIG_SYS_VARIANT_NONE) {
509
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
510
0
          "Only one of the keys 'variant-file', variant-dtb','variant-name' is allowed");
511
0
      return FALSE;
512
0
    }
513
514
0
    c->system_variant_type = R_CONFIG_SYS_VARIANT_NAME;
515
0
    c->system_variant = g_steal_pointer(&variant_data);
516
0
  }
517
518
  /* parse data/status location
519
   *
520
   * We have multiple levels of backwards compatibility:
521
   * - per-slot status and no shared data directory
522
   *   (by default or explicitly with ``statusfile=per-slot``)
523
   * - central status file and no shared data directory
524
   *   (``statusfile=/data/central.raucs``)
525
   * - central status file and shared data directory
526
   *   (``statusfile=/data/central.raucs`` and ``data-directory=/data/rauc``)
527
   * - central status file in shared data directory
528
   *   (``data-directory=/data/rauc``, implies ``statusfile=/data/rauc/central.raucs``)
529
   */
530
5.06k
  c->data_directory = resolve_path_take(filename,
531
5.06k
      key_file_consume_string(key_file, "system", "data-directory", &ierror));
532
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
533
5.06k
    g_clear_error(&ierror);
534
5.06k
  } else if (ierror) {
535
0
    g_propagate_error(error, ierror);
536
0
    return FALSE;
537
0
  }
538
539
5.06k
  c->statusfile_path = key_file_consume_string(key_file, "system", "statusfile", &ierror);
540
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
541
5.06k
    g_assert_null(c->statusfile_path);
542
5.06k
    if (c->data_directory) {
543
0
      c->statusfile_path = g_build_filename(c->data_directory, "central.raucs", NULL);
544
5.06k
    } else {
545
5.06k
      if (filename)
546
0
        g_message("No data directory or status file set, falling back to per-slot status.\n"
547
5.06k
            "Consider setting 'data-directory=<path>' or 'statusfile=<path>/per-slot' explicitly.");
548
5.06k
      c->statusfile_path = g_strdup("per-slot");
549
5.06k
    }
550
5.06k
    g_clear_error(&ierror);
551
5.06k
  } else if (ierror) {
552
0
    g_propagate_error(error, ierror);
553
0
    return FALSE;
554
0
  }
555
556
5.06k
  if (g_strcmp0(c->statusfile_path, "per-slot") == 0) {
557
5.06k
    if (c->data_directory) {
558
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_DATA_DIRECTORY,
559
0
          "Using data-directory= with statusfile=per-slot is not supported.");
560
0
      return FALSE;
561
0
    }
562
5.06k
    if (filename)
563
0
      g_message("Using per-slot statusfile. System status information not supported!");
564
5.06k
  } else {
565
0
    gchar *resolved = resolve_path(filename, c->statusfile_path);
566
0
    g_free(c->statusfile_path);
567
0
    c->statusfile_path = resolved;
568
0
    g_message("Using central status file %s", c->statusfile_path);
569
0
  }
570
571
  /* parse bundle formats */
572
5.06k
  c->bundle_formats_mask =
573
5.06k
    1 << R_MANIFEST_FORMAT_PLAIN |
574
5.06k
            1 << R_MANIFEST_FORMAT_VERITY |
575
5.06k
            1 << R_MANIFEST_FORMAT_CRYPT;
576
5.06k
  bundle_formats = key_file_consume_string(key_file, "system", "bundle-formats", &ierror);
577
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
578
5.06k
    g_clear_error(&ierror);
579
5.06k
  } else if (ierror) {
580
0
    g_propagate_error(error, ierror);
581
0
    return FALSE;
582
0
  } else {
583
0
    if (!parse_bundle_formats(&c->bundle_formats_mask, bundle_formats, &ierror)) {
584
0
      g_propagate_error(error, ierror);
585
0
      return FALSE;
586
0
    }
587
0
  }
588
589
5.06k
  c->perform_pre_check = g_key_file_get_boolean(key_file, "system", "perform-pre-check", &ierror);
590
5.06k
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
591
5.06k
    c->perform_pre_check = FALSE;
592
5.06k
    g_clear_error(&ierror);
593
5.06k
  } else if (ierror) {
594
0
    g_propagate_error(error, ierror);
595
0
    return FALSE;
596
0
  }
597
5.06k
  g_key_file_remove_key(key_file, "system", "perform-pre-check", NULL);
598
599
5.06k
  if (!check_remaining_keys(key_file, "system", &ierror)) {
600
0
    g_propagate_error(error, ierror);
601
0
    return FALSE;
602
0
  }
603
5.06k
  g_key_file_remove_group(key_file, "system", NULL);
604
605
5.06k
  return TRUE;
606
5.06k
}
607
608
static gboolean parse_keyring_section(const gchar *filename, GKeyFile *key_file, RaucConfig *c, GError **error)
609
5.06k
{
610
5.06k
  GError *ierror = NULL;
611
5.06k
  gsize entries;
612
613
5.06k
  g_return_val_if_fail(key_file, FALSE);
614
5.06k
  g_return_val_if_fail(c, FALSE);
615
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
616
617
5.06k
  if (!g_key_file_has_group(key_file, "keyring"))
618
5.06k
    return TRUE;
619
620
0
  c->keyring_path = resolve_path_take(filename,
621
0
      key_file_consume_string(key_file, "keyring", "path", NULL));
622
0
  c->keyring_directory = resolve_path_take(filename,
623
0
      key_file_consume_string(key_file, "keyring", "directory", NULL));
624
625
0
  c->keyring_check_crl = g_key_file_get_boolean(key_file, "keyring", "check-crl", &ierror);
626
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
627
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
628
0
    c->keyring_check_crl = FALSE;
629
0
    g_clear_error(&ierror);
630
0
  } else if (ierror) {
631
0
    g_propagate_error(error, ierror);
632
0
    return FALSE;
633
0
  }
634
0
  g_key_file_remove_key(key_file, "keyring", "check-crl", NULL);
635
636
0
  c->keyring_allow_partial_chain = g_key_file_get_boolean(key_file, "keyring", "allow-partial-chain", &ierror);
637
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
638
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
639
0
    c->keyring_allow_partial_chain = FALSE;
640
0
    g_clear_error(&ierror);
641
0
  } else if (ierror) {
642
0
    g_propagate_error(error, ierror);
643
0
    return FALSE;
644
0
  }
645
0
  g_key_file_remove_key(key_file, "keyring", "allow-partial-chain", NULL);
646
647
0
  c->use_bundle_signing_time = g_key_file_get_boolean(key_file, "keyring", "use-bundle-signing-time", &ierror);
648
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
649
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
650
0
    c->use_bundle_signing_time = FALSE;
651
0
    g_clear_error(&ierror);
652
0
  } else if (ierror) {
653
0
    g_propagate_error(error, ierror);
654
0
    return FALSE;
655
0
  }
656
0
  g_key_file_remove_key(key_file, "keyring", "use-bundle-signing-time", NULL);
657
658
0
  c->keyring_check_purpose = key_file_consume_string(key_file, "keyring", "check-purpose", &ierror);
659
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
660
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
661
0
    g_assert_null(c->keyring_check_purpose);
662
0
    g_clear_error(&ierror);
663
0
  } else if (ierror) {
664
0
    g_propagate_error(error, ierror);
665
0
    return FALSE;
666
0
  }
667
  /* Rewrite 'codesign' check-purpose to RAUC's internal 'codesign-rauc' check-purpose
668
   * to avoid conflicts with purpose definition from OpenSSL 3.2.0. */
669
0
  if (g_strcmp0(c->keyring_check_purpose, "codesign") == 0) {
670
0
    g_free(c->keyring_check_purpose);
671
0
    c->keyring_check_purpose = g_strdup("codesign-rauc");
672
0
  }
673
674
0
  c->keyring_allowed_signer_cns = g_key_file_get_string_list(key_file, "keyring", "allowed-signer-cns", &entries, &ierror);
675
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
676
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
677
0
    g_clear_error(&ierror);
678
0
  } else if (ierror) {
679
0
    g_propagate_error(error, ierror);
680
0
    return FALSE;
681
0
  }
682
0
  g_key_file_remove_key(key_file, "keyring", "allowed-signer-cns", NULL);
683
684
0
  if (!check_remaining_keys(key_file, "keyring", &ierror)) {
685
0
    g_propagate_error(error, ierror);
686
0
    return FALSE;
687
0
  }
688
0
  g_key_file_remove_group(key_file, "keyring", NULL);
689
690
0
  return TRUE;
691
0
}
692
693
static gboolean parse_casync_section(GKeyFile *key_file, RaucConfig *c, GError **error)
694
5.06k
{
695
5.06k
  GError *ierror = NULL;
696
697
5.06k
  g_return_val_if_fail(key_file, FALSE);
698
5.06k
  g_return_val_if_fail(c, FALSE);
699
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
700
701
5.06k
  if (!g_key_file_has_group(key_file, "casync"))
702
5.06k
    return TRUE;
703
704
0
  c->store_path = key_file_consume_string(key_file, "casync", "storepath", NULL);
705
0
  c->tmp_path = key_file_consume_string(key_file, "casync", "tmppath", NULL);
706
0
  c->casync_install_args = key_file_consume_string(key_file, "casync", "install-args", NULL);
707
0
  c->use_desync = g_key_file_get_boolean(key_file, "casync", "use-desync", &ierror);
708
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
709
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
710
0
    c->use_desync = FALSE;
711
0
    g_clear_error(&ierror);
712
0
  } else if (ierror) {
713
0
    g_propagate_error(error, ierror);
714
0
    return FALSE;
715
0
  }
716
0
  g_key_file_remove_key(key_file, "casync", "use-desync", NULL);
717
0
  if (!check_remaining_keys(key_file, "casync", &ierror)) {
718
0
    g_propagate_error(error, ierror);
719
0
    return FALSE;
720
0
  }
721
0
  g_key_file_remove_group(key_file, "casync", NULL);
722
723
0
  return TRUE;
724
0
}
725
726
static gboolean parse_streaming_section(GKeyFile *key_file, RaucConfig *c, GError **error)
727
5.06k
{
728
5.06k
  GError *ierror = NULL;
729
5.06k
  gsize entries;
730
731
5.06k
  g_return_val_if_fail(key_file, FALSE);
732
5.06k
  g_return_val_if_fail(c, FALSE);
733
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
734
735
5.06k
  if (!g_key_file_has_group(key_file, "streaming"))
736
5.06k
    return TRUE;
737
738
0
  c->streaming_sandbox_user = key_file_consume_string(key_file, "streaming", "sandbox-user", NULL);
739
0
  c->streaming_tls_cert = key_file_consume_string(key_file, "streaming", "tls-cert", NULL);
740
0
  c->streaming_tls_key = key_file_consume_string(key_file, "streaming", "tls-key", NULL);
741
0
  c->streaming_tls_ca = key_file_consume_string(key_file, "streaming", "tls-ca", NULL);
742
0
  c->enabled_headers = g_key_file_get_string_list(key_file, "streaming", "send-headers", &entries, &ierror);
743
0
  if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND) ||
744
0
      g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
745
0
    g_clear_error(&ierror);
746
0
  } else if (ierror) {
747
0
    g_propagate_error(error, ierror);
748
0
    return FALSE;
749
0
  } else {
750
0
    for (gsize j = 0; j < entries; j++) {
751
0
      if (!r_install_is_supported_http_header(c->enabled_headers[j])) {
752
0
        g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE,
753
0
            "Automatic HTTP header '%s' not supported", c->enabled_headers[j]);
754
0
        return FALSE;
755
0
      }
756
0
    }
757
0
  }
758
0
  g_key_file_remove_key(key_file, "streaming", "send-headers", NULL);
759
0
  if (!check_remaining_keys(key_file, "streaming", &ierror)) {
760
0
    g_propagate_error(error, ierror);
761
0
    return FALSE;
762
0
  }
763
0
  g_key_file_remove_group(key_file, "streaming", NULL);
764
765
0
  return TRUE;
766
0
}
767
768
static gboolean parse_encryption_section(const gchar *filename, GKeyFile *key_file, RaucConfig *c, GError **error)
769
5.06k
{
770
5.06k
  GError *ierror = NULL;
771
772
5.06k
  g_return_val_if_fail(key_file, FALSE);
773
5.06k
  g_return_val_if_fail(c, FALSE);
774
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
775
776
5.06k
  if (!g_key_file_has_group(key_file, "encryption"))
777
5.06k
    return TRUE;
778
779
0
  c->encryption_key = resolve_path_take(filename,
780
0
      key_file_consume_string(key_file, "encryption", "key", NULL));
781
0
  c->encryption_cert = resolve_path_take(filename,
782
0
      key_file_consume_string(key_file, "encryption", "cert", NULL));
783
0
  if (!check_remaining_keys(key_file, "encryption", &ierror)) {
784
0
    g_propagate_error(error, ierror);
785
0
    return FALSE;
786
0
  }
787
0
  g_key_file_remove_group(key_file, "encryption", NULL);
788
789
0
  return TRUE;
790
0
}
791
792
static gboolean parse_autoinstall_section(const gchar *filename, GKeyFile *key_file, RaucConfig *c, GError **error)
793
5.06k
{
794
5.06k
  GError *ierror = NULL;
795
796
5.06k
  g_return_val_if_fail(key_file, FALSE);
797
5.06k
  g_return_val_if_fail(c, FALSE);
798
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
799
800
5.06k
  if (!g_key_file_has_group(key_file, "autoinstall"))
801
5.06k
    return TRUE;
802
803
0
  c->autoinstall_path = resolve_path_take(filename,
804
0
      key_file_consume_string(key_file, "autoinstall", "path", NULL));
805
0
  if (!check_remaining_keys(key_file, "autoinstall", &ierror)) {
806
0
    g_propagate_error(error, ierror);
807
0
    return FALSE;
808
0
  }
809
0
  g_key_file_remove_group(key_file, "autoinstall", NULL);
810
811
0
  return TRUE;
812
0
}
813
814
static gboolean parse_handlers_section(const gchar *filename, GKeyFile *key_file, RaucConfig *c, GError **error)
815
5.06k
{
816
5.06k
  GError *ierror = NULL;
817
818
5.06k
  g_return_val_if_fail(key_file, FALSE);
819
5.06k
  g_return_val_if_fail(c, FALSE);
820
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
821
822
5.06k
  if (!g_key_file_has_group(key_file, "handlers"))
823
5.06k
    return TRUE;
824
825
0
  c->systeminfo_handler = resolve_path_take(filename,
826
0
      key_file_consume_string(key_file, "handlers", "system-info", NULL));
827
828
0
  c->preinstall_handler = resolve_path_take(filename,
829
0
      key_file_consume_string(key_file, "handlers", "pre-install", NULL));
830
831
0
  c->postinstall_handler = resolve_path_take(filename,
832
0
      key_file_consume_string(key_file, "handlers", "post-install", NULL));
833
0
  if (!check_remaining_keys(key_file, "handlers", &ierror)) {
834
0
    g_propagate_error(error, ierror);
835
0
    return FALSE;
836
0
  }
837
0
  g_key_file_remove_group(key_file, "handlers", NULL);
838
839
0
  return TRUE;
840
0
}
841
842
static GHashTable *parse_slots(const char *filename, const char *data_directory, GKeyFile *key_file, GError **error)
843
5.06k
{
844
5.06k
  GError *ierror = NULL;
845
5.06k
  g_auto(GStrv) groups = NULL;
846
5.06k
  gsize group_count;
847
5.06k
  g_autoptr(GHashTable) slots = NULL;
848
5.06k
  g_autoptr(GList) slotlist = NULL;
849
5.06k
  g_autoptr(GHashTable) bootnames = NULL;
850
851
5.06k
  slots = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, r_slot_free);
852
5.06k
  bootnames = g_hash_table_new(g_str_hash, g_str_equal);
853
854
5.06k
  groups = g_key_file_get_groups(key_file, &group_count);
855
5.06k
  for (gsize i = 0; i < group_count; i++) {
856
0
    g_auto(GStrv) groupsplit = g_strsplit(groups[i], ".", -1);
857
858
    /* We treat sections starting with "slot." as slots */
859
0
    if (g_str_equal(groupsplit[0], RAUC_SLOT_PREFIX)) {
860
0
      g_autoptr(RaucSlot) slot = g_new0(RaucSlot, 1);
861
0
      gchar* value;
862
863
      /* Assure slot strings consist of 3 parts, delimited by dots */
864
0
      if (g_strv_length(groupsplit) != 3) {
865
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
866
0
            "Invalid slot name format: %s", groups[i]);
867
0
        return NULL;
868
0
      }
869
870
0
      value = g_strconcat(groupsplit[1], ".", groupsplit[2], NULL);
871
0
      slot->name = g_intern_string(value);
872
0
      g_free(value);
873
874
      /* If we have a data_directory, use a slot.<class>.<index>
875
       * subdirectory for per-slot data. */
876
0
      if (data_directory)
877
0
        slot->data_directory = g_build_filename(data_directory, groups[i], NULL);
878
879
0
      slot->description = key_file_consume_string(key_file, groups[i], "description", NULL);
880
881
0
      slot->sclass = g_intern_string(groupsplit[1]);
882
883
0
      value = resolve_path_take(filename,
884
0
          key_file_consume_string(key_file, groups[i], "device", &ierror));
885
0
      if (!value) {
886
0
        g_propagate_error(error, ierror);
887
0
        return NULL;
888
0
      }
889
0
      slot->device = value;
890
891
0
      value = key_file_consume_string(key_file, groups[i], "type", NULL);
892
0
      if (!value)
893
0
        value = g_strdup("raw");
894
0
      slot->type = value;
895
896
0
      if (!r_slot_is_valid_type(slot->type)) {
897
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_SLOT_TYPE,
898
0
            "Unsupported slot type '%s' for slot %s selected in system config", slot->type, slot->name);
899
0
        return NULL;
900
0
      }
901
902
      /* check if the device has an appropriate path */
903
0
      if (g_str_equal(slot->type, "jffs2") && !g_str_has_prefix(slot->device, "/dev/")) {
904
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_DEVICE,
905
0
            "%s: device must be located in /dev/ for jffs2", groups[i]);
906
0
        return NULL;
907
0
      }
908
0
      if (g_str_equal(slot->type, "boot-emmc") &&
909
0
          (g_str_has_suffix(slot->device, "boot0") || g_str_has_suffix(slot->device, "boot1"))) {
910
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_DEVICE,
911
0
            "%s: 'device' must refer to the eMMC base device, not the boot partition", groups[i]);
912
0
        return NULL;
913
0
      }
914
915
0
      value = key_file_consume_string(key_file, groups[i], "extra-mkfs-opts", NULL);
916
0
      if (value != NULL) {
917
0
        if (!g_shell_parse_argv(value, NULL, &(slot->extra_mkfs_opts), &ierror)) {
918
0
          g_free(value);
919
0
          g_propagate_prefixed_error(error, ierror, "Failed to parse extra-mkfs-opts: ");
920
0
          return NULL;
921
0
        }
922
0
        g_free(value);
923
0
      }
924
925
0
      value = key_file_consume_string(key_file, groups[i], "bootname", NULL);
926
927
0
      slot->bootname = value;
928
0
      if (slot->bootname) {
929
        /* Ensure that the bootname does not contain whitespace or tab */
930
0
        if (!value_check_tab_whitespace(value, &ierror)) {
931
0
          g_propagate_prefixed_error(error, ierror,
932
0
              "Invalid bootname for slot %s: ", slot->name);
933
0
          return NULL;
934
0
        }
935
936
        /* check if we have seen this bootname on another slot */
937
0
        if (g_hash_table_contains(bootnames, slot->bootname)) {
938
0
          g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_DUPLICATE_BOOTNAME,
939
0
              "Bootname '%s' is set on more than one slot",
940
0
              slot->bootname);
941
0
          return NULL;
942
0
        }
943
0
        g_hash_table_add(bootnames, slot->bootname);
944
0
      }
945
946
      /* Collect name of parent here for easing remaining key checking.
947
       * Will be resolved to slot->parent pointer after config parsing loop. */
948
0
      slot->parent_name = key_file_consume_string(key_file, groups[i], "parent", NULL);
949
950
0
      slot->allow_mounted = g_key_file_get_boolean(key_file, groups[i], "allow-mounted", &ierror);
951
0
      if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
952
0
        slot->allow_mounted = FALSE;
953
0
        g_clear_error(&ierror);
954
0
      } else if (ierror) {
955
0
        g_propagate_error(error, ierror);
956
0
        return NULL;
957
0
      }
958
0
      g_key_file_remove_key(key_file, groups[i], "allow-mounted", NULL);
959
960
0
      slot->readonly = g_key_file_get_boolean(key_file, groups[i], "readonly", &ierror);
961
0
      if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
962
0
        slot->readonly = FALSE;
963
0
        g_clear_error(&ierror);
964
0
      } else if (ierror) {
965
0
        g_propagate_error(error, ierror);
966
0
        return NULL;
967
0
      }
968
0
      g_key_file_remove_key(key_file, groups[i], "readonly", NULL);
969
970
0
      slot->install_same = g_key_file_get_boolean(key_file, groups[i], "install-same", &ierror);
971
0
      if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
972
0
        g_clear_error(&ierror);
973
        /* try also deprecated flag force-install-same */
974
0
        slot->install_same = g_key_file_get_boolean(key_file, groups[i], "force-install-same", &ierror);
975
0
        if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
976
0
          g_clear_error(&ierror);
977
          /* try also deprecated flag ignore-checksum */
978
0
          slot->install_same = g_key_file_get_boolean(key_file, groups[i], "ignore-checksum", &ierror);
979
0
          if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
980
0
            slot->install_same = TRUE;
981
0
            g_clear_error(&ierror);
982
0
          } else if (ierror) {
983
0
            g_propagate_error(error, ierror);
984
0
            return NULL;
985
0
          }
986
0
        } else if (ierror) {
987
0
          g_propagate_error(error, ierror);
988
0
          return NULL;
989
0
        }
990
0
      } else if (ierror) {
991
0
        g_propagate_error(error, ierror);
992
0
        return NULL;
993
0
      }
994
0
      g_key_file_remove_key(key_file, groups[i], "install-same", NULL);
995
0
      g_key_file_remove_key(key_file, groups[i], "force-install-same", NULL);
996
0
      g_key_file_remove_key(key_file, groups[i], "ignore-checksum", NULL);
997
998
0
      slot->extra_mount_opts = key_file_consume_string(key_file, groups[i], "extra-mount-opts", NULL);
999
1000
0
      slot->resize = g_key_file_get_boolean(key_file, groups[i], "resize", &ierror);
1001
0
      if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
1002
0
        slot->resize = FALSE;
1003
0
        g_clear_error(&ierror);
1004
0
      } else if (ierror) {
1005
0
        g_propagate_error(error, ierror);
1006
0
        return NULL;
1007
0
      }
1008
0
      g_key_file_remove_key(key_file, groups[i], "resize", NULL);
1009
1010
0
      if (g_strcmp0(slot->type, "boot-mbr-switch") == 0 ||
1011
0
          g_strcmp0(slot->type, "boot-gpt-switch") == 0 ||
1012
0
          g_strcmp0(slot->type, "boot-raw-fallback") == 0) {
1013
0
        slot->region_start = key_file_consume_binary_suffixed_string(key_file, groups[i],
1014
0
            "region-start", &ierror);
1015
0
        if (ierror) {
1016
0
          g_propagate_prefixed_error(error, ierror, "mandatory for %s: ", slot->type);
1017
0
          return NULL;
1018
0
        }
1019
1020
0
        slot->region_size = key_file_consume_binary_suffixed_string(key_file, groups[i],
1021
0
            "region-size", &ierror);
1022
0
        if (ierror) {
1023
0
          g_propagate_prefixed_error(error, ierror, "mandatory for %s: ", slot->type);
1024
0
          return NULL;
1025
0
        }
1026
0
      }
1027
1028
0
      if (g_strcmp0(slot->type, "boot-emmc") == 0) {
1029
0
        slot->size_limit = key_file_consume_binary_suffixed_string(key_file, groups[i],
1030
0
            "size-limit", &ierror);
1031
0
        if (g_error_matches(ierror, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
1032
0
          slot->size_limit = 0;
1033
0
          g_clear_error(&ierror);
1034
0
        } else if (ierror) {
1035
0
          g_propagate_error(error, ierror);
1036
0
          return NULL;
1037
0
        }
1038
0
      }
1039
1040
0
      if (!check_remaining_keys(key_file, groups[i], &ierror)) {
1041
0
        g_propagate_error(error, ierror);
1042
0
        return NULL;
1043
0
      }
1044
1045
0
      g_key_file_remove_group(key_file, groups[i], NULL);
1046
1047
0
      g_hash_table_insert(slots, (gchar*)slot->name, slot);
1048
0
      slot = NULL;
1049
0
    }
1050
0
  }
1051
1052
  /* Add parent pointers */
1053
5.06k
  slotlist = g_hash_table_get_keys(slots);
1054
5.06k
  for (GList *l = slotlist; l != NULL; l = l->next) {
1055
0
    RaucSlot *slot;
1056
0
    RaucSlot *parent;
1057
0
    RaucSlot *child;
1058
1059
0
    slot = g_hash_table_lookup(slots, l->data);
1060
0
    if (!slot->parent_name) {
1061
0
      continue;
1062
0
    }
1063
1064
0
    parent = g_hash_table_lookup(slots, slot->parent_name);
1065
0
    if (!parent) {
1066
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_PARENT,
1067
0
          "Parent slot '%s' not found!", slot->parent_name);
1068
0
      return NULL;
1069
0
    }
1070
1071
0
    child = g_hash_table_lookup(slots, l->data);
1072
0
    child->parent = parent;
1073
1074
0
    if (child->bootname) {
1075
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_CHILD_HAS_BOOTNAME,
1076
0
          "Child slot '%s' has bootname set",
1077
0
          child->name);
1078
0
      return NULL;
1079
0
    }
1080
0
  }
1081
1082
5.06k
  if (!fix_grandparent_links(slots, &ierror)) {
1083
0
    g_propagate_error(error, ierror);
1084
0
    return NULL;
1085
0
  }
1086
1087
5.06k
  return g_steal_pointer(&slots);
1088
5.06k
}
1089
1090
static GHashTable *parse_artifact_repos(const char *filename, const char *data_directory, GKeyFile *key_file, GError **error)
1091
5.06k
{
1092
5.06k
  GError *ierror = NULL;
1093
5.06k
  gsize group_count;
1094
1095
5.06k
  g_autoptr(GHashTable) repos = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, r_artifact_repo_free);
1096
1097
5.06k
  g_auto(GStrv) groups = g_key_file_get_groups(key_file, &group_count);
1098
5.06k
  for (gsize i = 0; i < group_count; i++) {
1099
0
    g_auto(GStrv) groupsplit = g_strsplit(groups[i], ".", -1);
1100
1101
    /* We treat sections starting with "artifacts." as artifact repositories. */
1102
0
    if (g_str_equal(groupsplit[0], "artifacts")) {
1103
0
      g_autoptr(RArtifactRepo) repo = g_new0(RArtifactRepo, 1);
1104
1105
      /* Assure artifact repo strings consist of 2 parts, delimited by dots */
1106
0
      if (g_strv_length(groupsplit) != 2) {
1107
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_INVALID_FORMAT,
1108
0
            "Invalid artifacts repo format: %s", groups[i]);
1109
0
        return NULL;
1110
0
      }
1111
0
      repo->name = g_intern_string(groupsplit[1]);
1112
1113
      /* If we have a data_directory, use a artifacts.name subdirectory
1114
       * for per-repo data. */
1115
0
      if (data_directory)
1116
0
        repo->data_directory = g_build_filename(data_directory, groups[i], NULL);
1117
1118
0
      repo->description = key_file_consume_string(key_file, groups[i], "description", NULL);
1119
1120
0
      gchar* value = resolve_path_take(filename, key_file_consume_string(key_file, groups[i], "path", &ierror));
1121
0
      if (!value) {
1122
0
        g_propagate_error(error, ierror);
1123
0
        return NULL;
1124
0
      }
1125
0
      repo->path = value;
1126
1127
0
      value = key_file_consume_string(key_file, groups[i], "type", &ierror);
1128
0
      if (!value) {
1129
0
        g_propagate_error(error, ierror);
1130
0
        return NULL;
1131
0
      }
1132
0
      repo->type = value;
1133
1134
0
      if (!r_artifact_repo_is_valid_type(repo->type)) {
1135
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_ARTIFACT_REPO_TYPE,
1136
0
            "Unsupported artifacts repo type '%s' for repo %s selected in system config", repo->type, repo->name);
1137
0
        return NULL;
1138
0
      }
1139
1140
0
      value = key_file_consume_string(key_file, groups[i], "parent-class", NULL);
1141
0
      repo->parent_class = g_intern_string(value);
1142
0
      g_free(value);
1143
0
      if (repo->parent_class) {
1144
0
        g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_PARENT,
1145
0
            "Parent slot classes are not yet supported for artifact repos");
1146
0
        return NULL;
1147
0
      }
1148
0
      if (!check_remaining_keys(key_file, groups[i], &ierror)) {
1149
0
        g_propagate_error(error, ierror);
1150
0
        return NULL;
1151
0
      }
1152
1153
0
      g_key_file_remove_group(key_file, groups[i], NULL);
1154
1155
0
      g_hash_table_insert(repos, (gchar*)repo->name, repo);
1156
0
      repo = NULL;
1157
0
    }
1158
0
  }
1159
1160
5.06k
  return g_steal_pointer(&repos);
1161
5.06k
}
1162
1163
static gboolean check_unique_slotclasses(RaucConfig *config, GError **error)
1164
0
{
1165
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1166
1167
0
  GHashTableIter iter;
1168
0
  g_hash_table_iter_init(&iter, config->slots);
1169
0
  gpointer value;
1170
0
  while (g_hash_table_iter_next(&iter, NULL, &value)) {
1171
0
    RaucSlot *slot = value;
1172
1173
0
    if (g_hash_table_contains(config->artifact_repos, slot->sclass)) {
1174
0
      g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_DUPLICATE_CLASS,
1175
0
          "Existing slot class '%s' cannot be used as artifact repo name!", slot->sclass);
1176
0
      return FALSE;
1177
0
    }
1178
0
  }
1179
0
  return TRUE;
1180
0
}
1181
1182
void r_config_file_modified_check(void)
1183
0
{
1184
0
  g_autoptr(GError) ierror = NULL;
1185
0
  g_autofree gchar *data = NULL;
1186
0
  gsize length;
1187
0
  g_autofree gchar *new_checksum = NULL;
1188
1189
0
  if (!r_context()->config->file_checksum)
1190
0
    return;
1191
1192
0
  if (!g_file_get_contents(r_context()->configpath, &data, &length, &ierror)) {
1193
0
    g_warning("Failed to compare config: %s", ierror->message);
1194
0
    return;
1195
0
  }
1196
1197
0
  new_checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar*) data, length);
1198
1199
0
  if (g_strcmp0(r_context()->config->file_checksum, new_checksum) != 0) {
1200
0
    g_warning("System configuration file changed on disk! "
1201
0
        "Still using old configuration! "
1202
0
        "Please restart the rauc service.");
1203
0
  }
1204
0
}
1205
1206
/**
1207
 * Parse a configuration, supplied as text in GKeyFile format.
1208
 *
1209
 * @param filename filename to resolve relative path names, or NULL
1210
 * @param data the text to parse
1211
 * @param length the length of data in bytes
1212
 * @param error return location for a GError, or NULL
1213
 *
1214
 * @return a RaucConfig on success, NULL if there were errors
1215
 */
1216
static RaucConfig *parse_config(const gchar *filename, const gchar *data, gsize length, GError **error)
1217
5.06k
{
1218
5.06k
  GError *ierror = NULL;
1219
5.06k
  g_autoptr(RaucConfig) c = g_new0(RaucConfig, 1);
1220
5.06k
  g_autoptr(GKeyFile) key_file = NULL;
1221
1222
5.06k
  g_return_val_if_fail(data, NULL);
1223
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1224
1225
5.06k
  c->file_checksum = g_compute_checksum_for_data(G_CHECKSUM_SHA256, (guchar*) data, length);
1226
1227
5.06k
  key_file = g_key_file_new();
1228
1229
5.06k
  if (!g_key_file_load_from_data(key_file, data, length, G_KEY_FILE_NONE, &ierror)) {
1230
0
    g_propagate_error(error, ierror);
1231
0
    return NULL;
1232
0
  }
1233
1234
  /* process overrides */
1235
5.06k
  for (GList *l = r_context_conf()->configoverride; l != NULL; l = l->next) {
1236
0
    ConfigFileOverride *override = (ConfigFileOverride *)l->data;
1237
0
    g_key_file_set_value(key_file, override->section, override->name, override->value);
1238
0
  }
1239
1240
  /* parse [system] section */
1241
5.06k
  if (!parse_system_section(filename, key_file, c, &ierror)) {
1242
0
    g_propagate_error(error, ierror);
1243
0
    return NULL;
1244
0
  }
1245
1246
  /* parse [keyring] section */
1247
5.06k
  if (!parse_keyring_section(filename, key_file, c, &ierror)) {
1248
0
    g_propagate_error(error, ierror);
1249
0
    return NULL;
1250
0
  }
1251
1252
  /* parse [casync] section */
1253
5.06k
  if (!parse_casync_section(key_file, c, &ierror)) {
1254
0
    g_propagate_error(error, ierror);
1255
0
    return NULL;
1256
0
  }
1257
1258
  /* parse [streaming] section */
1259
5.06k
  if (!parse_streaming_section(key_file, c, &ierror)) {
1260
0
    g_propagate_error(error, ierror);
1261
0
    return NULL;
1262
0
  }
1263
1264
  /* parse [encryption] section */
1265
5.06k
  if (!parse_encryption_section(filename, key_file, c, &ierror)) {
1266
0
    g_propagate_error(error, ierror);
1267
0
    return NULL;
1268
0
  }
1269
1270
  /* parse [autoinstall] section */
1271
5.06k
  if (!parse_autoinstall_section(filename, key_file, c, &ierror)) {
1272
0
    g_propagate_error(error, ierror);
1273
0
    return NULL;
1274
0
  }
1275
1276
  /* parse [handlers] section */
1277
5.06k
  if (!parse_handlers_section(filename, key_file, c, &ierror)) {
1278
0
    g_propagate_error(error, ierror);
1279
0
    return NULL;
1280
0
  }
1281
1282
5.06k
  if (!r_event_log_parse_config_sections(key_file, c, &ierror)) {
1283
0
    g_propagate_error(error, ierror);
1284
0
    return NULL;
1285
0
  }
1286
1287
  /* parse [slot.*.#] sections */
1288
5.06k
  c->slots = parse_slots(filename, c->data_directory, key_file, &ierror);
1289
5.06k
  if (!c->slots) {
1290
0
    g_propagate_error(error, ierror);
1291
0
    return NULL;
1292
0
  }
1293
1294
  /* parse [artifacts.*] sections */
1295
5.06k
  c->artifact_repos = parse_artifact_repos(filename, c->data_directory, key_file, &ierror);
1296
5.06k
  if (!c->artifact_repos) {
1297
0
    g_propagate_error(error, ierror);
1298
0
    return NULL;
1299
0
  }
1300
1301
5.06k
  if (!check_remaining_groups(key_file, &ierror)) {
1302
0
    g_propagate_error(error, ierror);
1303
0
    return FALSE;
1304
0
  }
1305
1306
5.06k
  return g_steal_pointer(&c);
1307
5.06k
}
1308
1309
gboolean default_config(RaucConfig **config, GError **error)
1310
5.06k
{
1311
5.06k
  GError *ierror = NULL;
1312
1313
5.06k
  g_return_val_if_fail(config && *config == NULL, FALSE);
1314
5.06k
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1315
1316
  /* Use an empty system section to honor defaults from parse_system_section.
1317
   * After we have implemented explicit defaults there, we can use an empty
1318
   * string here. */
1319
5.06k
  const gchar *data = "[system]";
1320
5.06k
  RaucConfig *c = parse_config(NULL, data, strlen(data), &ierror);
1321
5.06k
  if (!c) {
1322
0
    g_propagate_prefixed_error(error, ierror, "Failed to initialize default config: ");
1323
0
    return FALSE;
1324
0
  }
1325
1326
5.06k
  *config = c;
1327
1328
5.06k
  return TRUE;
1329
5.06k
}
1330
1331
gboolean load_config(const gchar *filename, RaucConfig **config, GError **error)
1332
0
{
1333
0
  GError *ierror = NULL;
1334
0
  g_autofree gchar *data = NULL;
1335
0
  gsize length;
1336
0
  g_autoptr(RaucConfig) c = NULL;
1337
1338
0
  g_return_val_if_fail(filename, FALSE);
1339
0
  g_return_val_if_fail(config && *config == NULL, FALSE);
1340
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1341
1342
  /* We store checksum for later comparison */
1343
0
  if (!g_file_get_contents(filename, &data, &length, &ierror)) {
1344
0
    g_propagate_error(error, ierror);
1345
0
    return FALSE;
1346
0
  }
1347
0
  if (length == 0) {
1348
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_EMPTY_FILE,
1349
0
        "Input file is empty");
1350
0
    return FALSE;
1351
0
  }
1352
1353
0
  c = parse_config(filename, data, length, &ierror);
1354
0
  if (!c) {
1355
0
    g_propagate_error(error, ierror);
1356
0
    return FALSE;
1357
0
  }
1358
1359
0
  if (!check_unique_slotclasses(c, &ierror)) {
1360
0
    g_propagate_error(error, ierror);
1361
0
    return FALSE;
1362
0
  }
1363
1364
0
  if (!check_config_target(c, &ierror)) {
1365
0
    g_propagate_error(error, ierror);
1366
0
    return FALSE;
1367
0
  }
1368
1369
  /* on success, return config struct */
1370
0
  *config = g_steal_pointer(&c);
1371
1372
0
  return TRUE;
1373
0
}
1374
1375
gboolean check_config_target(const RaucConfig *config, GError **error)
1376
0
{
1377
0
  if (!config->system_compatible) {
1378
0
    g_set_error_literal(error, R_CONFIG_ERROR, R_CONFIG_ERROR_MISSING_OPTION,
1379
0
        "System compatible string is not set");
1380
0
    return FALSE;
1381
0
  }
1382
1383
0
  if (!config->system_bootloader) {
1384
0
    g_set_error_literal(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
1385
0
        "No bootloader selected in system config");
1386
0
    return FALSE;
1387
0
  }
1388
0
  if (!r_boot_is_supported_bootloader(config->system_bootloader)) {
1389
0
    g_set_error(error, R_CONFIG_ERROR, R_CONFIG_ERROR_BOOTLOADER,
1390
0
        "Unsupported bootloader '%s' selected in system config",
1391
0
        config->system_bootloader);
1392
0
    return FALSE;
1393
0
  }
1394
1395
0
  return TRUE;
1396
0
}
1397
1398
RaucSlot *find_config_slot_by_device(RaucConfig *config, const gchar *device)
1399
0
{
1400
0
  g_return_val_if_fail(config, NULL);
1401
1402
0
  return r_slot_find_by_device(config->slots, device);
1403
0
}
1404
1405
RaucSlot *find_config_slot_by_name(RaucConfig *config, const gchar *name)
1406
0
{
1407
0
  g_return_val_if_fail(config, NULL);
1408
0
  g_return_val_if_fail(config->slots, NULL);
1409
0
  g_return_val_if_fail(name, NULL);
1410
1411
0
  return g_hash_table_lookup(config->slots, name);
1412
0
}
1413
1414
void free_config(RaucConfig *config)
1415
5.06k
{
1416
5.06k
  if (!config)
1417
0
    return;
1418
1419
5.06k
  g_free(config->system_compatible);
1420
5.06k
  g_free(config->system_min_bundle_version);
1421
5.06k
  g_free(config->system_variant);
1422
5.06k
  g_free(config->system_bootloader);
1423
5.06k
  g_free(config->system_bb_statename);
1424
5.06k
  g_free(config->system_bb_dtbpath);
1425
5.06k
  g_free(config->mount_prefix);
1426
5.06k
  g_free(config->store_path);
1427
5.06k
  g_free(config->tmp_path);
1428
5.06k
  g_free(config->casync_install_args);
1429
5.06k
  g_free(config->grubenv_path);
1430
5.06k
  g_free(config->data_directory);
1431
5.06k
  g_free(config->statusfile_path);
1432
5.06k
  g_free(config->keyring_path);
1433
5.06k
  g_free(config->keyring_directory);
1434
5.06k
  g_free(config->keyring_check_purpose);
1435
5.06k
  g_strfreev(config->keyring_allowed_signer_cns);
1436
5.06k
  g_free(config->autoinstall_path);
1437
5.06k
  g_free(config->systeminfo_handler);
1438
5.06k
  g_free(config->preinstall_handler);
1439
5.06k
  g_free(config->postinstall_handler);
1440
5.06k
  g_free(config->streaming_sandbox_user);
1441
5.06k
  g_free(config->streaming_tls_cert);
1442
5.06k
  g_free(config->streaming_tls_key);
1443
5.06k
  g_free(config->streaming_tls_ca);
1444
5.06k
  g_strfreev(config->enabled_headers);
1445
5.06k
  g_free(config->encryption_key);
1446
5.06k
  g_free(config->encryption_cert);
1447
5.06k
  g_list_free_full(config->loggers, (GDestroyNotify)r_event_log_free_logger);
1448
5.06k
  g_clear_pointer(&config->slots, g_hash_table_destroy);
1449
5.06k
  g_free(config->custom_bootloader_backend);
1450
5.06k
  g_free(config->file_checksum);
1451
  g_clear_pointer(&config->artifact_repos, g_hash_table_destroy);
1452
5.06k
  g_free(config);
1453
5.06k
}