/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 | } |