blob: fb4b93a4c9ec48111a89db3e85788e638a19ded7 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes943d2c92017-06-21 09:04:39 +01002 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes943d2c92017-06-21 09:04:39 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuCommon"
8
Richard Hughes943d2c92017-06-21 09:04:39 +01009#include <config.h>
10
11#include <gio/gunixinputstream.h>
Richard Hughes954dd9f2017-08-08 13:36:25 +010012#include <glib/gstdio.h>
13
Richard Hughes94f939a2017-08-08 12:21:39 +010014#include <archive_entry.h>
15#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010016#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000017#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000018#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000019#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010020
21#include "fwupd-error.h"
22
23#include "fu-common.h"
24
25/**
Richard Hughes4eada342017-10-03 21:20:32 +010026 * SECTION:fu-common
27 * @short_description: common functionality for plugins to use
28 *
29 * Helper functions that can be used by the daemon and plugins.
30 *
31 * See also: #FuPlugin
32 */
33
34/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010035 * fu_common_rmtree:
36 * @directory: a directory name
37 * @error: A #GError or %NULL
38 *
39 * Recursively removes a directory.
40 *
41 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060042 *
43 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010044 **/
45gboolean
46fu_common_rmtree (const gchar *directory, GError **error)
47{
48 const gchar *filename;
49 g_autoptr(GDir) dir = NULL;
50
51 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010052 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010053 dir = g_dir_open (directory, 0, error);
54 if (dir == NULL)
55 return FALSE;
56
57 /* find each */
58 while ((filename = g_dir_read_name (dir))) {
59 g_autofree gchar *src = NULL;
60 src = g_build_filename (directory, filename, NULL);
61 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
62 if (!fu_common_rmtree (src, error))
63 return FALSE;
64 } else {
65 if (g_unlink (src) != 0) {
66 g_set_error (error,
67 FWUPD_ERROR,
68 FWUPD_ERROR_INTERNAL,
69 "Failed to delete: %s", src);
70 return FALSE;
71 }
72 }
73 }
74 if (g_remove (directory) != 0) {
75 g_set_error (error,
76 FWUPD_ERROR,
77 FWUPD_ERROR_INTERNAL,
78 "Failed to delete: %s", directory);
79 return FALSE;
80 }
81 return TRUE;
82}
83
Richard Hughes89e968b2018-03-07 10:01:08 +000084static gboolean
85fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
86{
87 const gchar *filename;
88 g_autoptr(GDir) dir = NULL;
89
90 /* try to open */
91 dir = g_dir_open (directory, 0, error);
92 if (dir == NULL)
93 return FALSE;
94
95 /* find each */
96 while ((filename = g_dir_read_name (dir))) {
97 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
98 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
99 if (!fu_common_get_file_list_internal (files, src, error))
100 return FALSE;
101 } else {
102 g_ptr_array_add (files, g_steal_pointer (&src));
103 }
104 }
105 return TRUE;
106
107}
108
109/**
110 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100111 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000112 * @error: A #GError or %NULL
113 *
114 * Returns every file found under @directory, and any subdirectory.
115 * If any path under @directory cannot be accessed due to permissions an error
116 * will be returned.
117 *
Mario Limonciello1a680f32019-11-25 19:44:53 -0600118 * Returns: (transfer container): array of files, or %NULL for error
119 *
120 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000121 **/
122GPtrArray *
123fu_common_get_files_recursive (const gchar *path, GError **error)
124{
125 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
126 if (!fu_common_get_file_list_internal (files, path, error))
127 return NULL;
128 return g_steal_pointer (&files);
129}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100130/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100131 * fu_common_mkdir_parent:
132 * @filename: A full pathname
133 * @error: A #GError, or %NULL
134 *
135 * Creates any required directories, including any parent directories.
136 *
137 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600138 *
139 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100140 **/
141gboolean
142fu_common_mkdir_parent (const gchar *filename, GError **error)
143{
144 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100145
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100146 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100147 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100148 if (g_mkdir_with_parents (parent, 0755) == -1) {
149 g_set_error (error,
150 FWUPD_ERROR,
151 FWUPD_ERROR_INTERNAL,
152 "Failed to create '%s': %s",
153 parent, g_strerror (errno));
154 return FALSE;
155 }
156 return TRUE;
157}
158
159/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100160 * fu_common_set_contents_bytes:
161 * @filename: A filename
162 * @bytes: The data to write
163 * @error: A #GError, or %NULL
164 *
165 * Writes a blob of data to a filename, creating the parent directories as
166 * required.
167 *
168 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600169 *
170 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100171 **/
172gboolean
173fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
174{
175 const gchar *data;
176 gsize size;
177 g_autoptr(GFile) file = NULL;
178 g_autoptr(GFile) file_parent = NULL;
179
180 file = g_file_new_for_path (filename);
181 file_parent = g_file_get_parent (file);
182 if (!g_file_query_exists (file_parent, NULL)) {
183 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
184 return FALSE;
185 }
186 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100187 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100188 return g_file_set_contents (filename, data, size, error);
189}
190
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100191/**
192 * fu_common_get_contents_bytes:
193 * @filename: A filename
194 * @error: A #GError, or %NULL
195 *
196 * Reads a blob of data from a file.
197 *
198 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600199 *
200 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100201 **/
202GBytes *
203fu_common_get_contents_bytes (const gchar *filename, GError **error)
204{
205 gchar *data = NULL;
206 gsize len = 0;
207 if (!g_file_get_contents (filename, &data, &len, error))
208 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100209 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100210 return g_bytes_new_take (data, len);
211}
Richard Hughes943d2c92017-06-21 09:04:39 +0100212
213/**
214 * fu_common_get_contents_fd:
215 * @fd: A file descriptor
216 * @count: The maximum number of bytes to read
217 * @error: A #GError, or %NULL
218 *
219 * Reads a blob from a specific file descriptor.
220 *
221 * Note: this will close the fd when done
222 *
Richard Hughes4eada342017-10-03 21:20:32 +0100223 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600224 *
225 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100226 **/
227GBytes *
228fu_common_get_contents_fd (gint fd, gsize count, GError **error)
229{
230 g_autoptr(GBytes) blob = NULL;
231 g_autoptr(GError) error_local = NULL;
232 g_autoptr(GInputStream) stream = NULL;
233
234 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100235 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
236
Richard Hughes919f8ab2018-02-14 10:24:56 +0000237 /* this is invalid */
238 if (count == 0) {
239 g_set_error_literal (error,
240 FWUPD_ERROR,
241 FWUPD_ERROR_NOT_SUPPORTED,
242 "A maximum read size must be specified");
243 return NULL;
244 }
245
Richard Hughes943d2c92017-06-21 09:04:39 +0100246 /* read the entire fd to a data blob */
247 stream = g_unix_input_stream_new (fd, TRUE);
248 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
249 if (blob == NULL) {
250 g_set_error_literal (error,
251 FWUPD_ERROR,
252 FWUPD_ERROR_INVALID_FILE,
253 error_local->message);
254 return NULL;
255 }
256 return g_steal_pointer (&blob);
257}
Richard Hughes94f939a2017-08-08 12:21:39 +0100258
259static gboolean
260fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
261{
262 const gchar *tmp;
263 g_autofree gchar *buf = NULL;
264
265 /* no output file */
266 if (archive_entry_pathname (entry) == NULL)
267 return FALSE;
268
269 /* update output path */
270 tmp = archive_entry_pathname (entry);
271 buf = g_build_filename (dir, tmp, NULL);
272 archive_entry_update_pathname_utf8 (entry, buf);
273 return TRUE;
274}
275
276/**
277 * fu_common_extract_archive:
278 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100279 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100280 * @error: A #GError, or %NULL
281 *
282 * Extracts an achive to a directory.
283 *
284 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600285 *
286 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100287 **/
288gboolean
289fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
290{
291 gboolean ret = TRUE;
292 int r;
293 struct archive *arch = NULL;
294 struct archive_entry *entry;
295
296 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100297 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100298 arch = archive_read_new ();
299 archive_read_support_format_all (arch);
300 archive_read_support_filter_all (arch);
301 r = archive_read_open_memory (arch,
302 (void *) g_bytes_get_data (blob, NULL),
303 (size_t) g_bytes_get_size (blob));
304 if (r != 0) {
305 ret = FALSE;
306 g_set_error (error,
307 FWUPD_ERROR,
308 FWUPD_ERROR_INTERNAL,
309 "Cannot open: %s",
310 archive_error_string (arch));
311 goto out;
312 }
313 for (;;) {
314 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100315 r = archive_read_next_header (arch, &entry);
316 if (r == ARCHIVE_EOF)
317 break;
318 if (r != ARCHIVE_OK) {
319 ret = FALSE;
320 g_set_error (error,
321 FWUPD_ERROR,
322 FWUPD_ERROR_INTERNAL,
323 "Cannot read header: %s",
324 archive_error_string (arch));
325 goto out;
326 }
327
328 /* only extract if valid */
329 valid = fu_common_extract_archive_entry (entry, dir);
330 if (!valid)
331 continue;
332 r = archive_read_extract (arch, entry, 0);
333 if (r != ARCHIVE_OK) {
334 ret = FALSE;
335 g_set_error (error,
336 FWUPD_ERROR,
337 FWUPD_ERROR_INTERNAL,
338 "Cannot extract: %s",
339 archive_error_string (arch));
340 goto out;
341 }
342 }
343out:
344 if (arch != NULL) {
345 archive_read_close (arch);
346 archive_read_free (arch);
347 }
348 return ret;
349}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100350
351static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300352fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
353
354static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100355fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
356{
357 va_list args;
358 g_autofree gchar *tmp = NULL;
359 g_auto(GStrv) split = NULL;
360
361 va_start (args, fmt);
362 tmp = g_strdup_vprintf (fmt, args);
363 va_end (args);
364
365 split = g_strsplit (tmp, " ", -1);
366 for (guint i = 0; split[i] != NULL; i++)
367 g_ptr_array_add (argv, g_strdup (split[i]));
368}
369
Mario Limonciello1a680f32019-11-25 19:44:53 -0600370/**
371 * fu_common_find_program_in_path:
372 * @basename: The program to search
373 * @error: A #GError, or %NULL
374 *
375 * Looks for a program in the PATH variable
376 *
377 * Returns: a new #gchar, or %NULL for error
378 *
379 * Since: 1.1.2
380 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100381gchar *
382fu_common_find_program_in_path (const gchar *basename, GError **error)
383{
384 gchar *fn = g_find_program_in_path (basename);
385 if (fn == NULL) {
386 g_set_error (error,
387 FWUPD_ERROR,
388 FWUPD_ERROR_NOT_SUPPORTED,
389 "missing executable %s in PATH",
390 basename);
391 return NULL;
392 }
393 return fn;
394}
395
396static gboolean
397fu_common_test_namespace_support (GError **error)
398{
399 /* test if CONFIG_USER_NS is valid */
400 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
401 g_set_error (error,
402 FWUPD_ERROR,
403 FWUPD_ERROR_NOT_SUPPORTED,
404 "missing CONFIG_USER_NS in kernel");
405 return FALSE;
406 }
407 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
408 g_autofree gchar *clone = NULL;
409 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
410 return FALSE;
411 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
412 g_set_error (error,
413 FWUPD_ERROR,
414 FWUPD_ERROR_NOT_SUPPORTED,
415 "unprivileged user namespace clones disabled by distro");
416 return FALSE;
417 }
418 }
419 return TRUE;
420}
421
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100422/**
423 * fu_common_firmware_builder:
424 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100425 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
426 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100427 * @error: A #GError, or %NULL
428 *
429 * Builds a firmware file using tools from the host session in a bubblewrap
430 * jail. Several things happen during build:
431 *
432 * 1. The @bytes data is untarred to a temporary location
433 * 2. A bubblewrap container is set up
434 * 3. The startup.sh script is run inside the container
435 * 4. The firmware.bin is extracted from the container
436 * 5. The temporary location is deleted
437 *
438 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600439 *
440 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100441 **/
442GBytes *
443fu_common_firmware_builder (GBytes *bytes,
444 const gchar *script_fn,
445 const gchar *output_fn,
446 GError **error)
447{
448 gint rc = 0;
449 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500450 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100451 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100452 g_autofree gchar *localstatedir = NULL;
453 g_autofree gchar *output2_fn = NULL;
454 g_autofree gchar *standard_error = NULL;
455 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100456 g_autofree gchar *tmpdir = NULL;
457 g_autoptr(GBytes) firmware_blob = NULL;
458 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
459
460 g_return_val_if_fail (bytes != NULL, NULL);
461 g_return_val_if_fail (script_fn != NULL, NULL);
462 g_return_val_if_fail (output_fn != NULL, NULL);
463 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
464
Mario Limonciello37b59582018-08-13 08:38:01 -0500465 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100466 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
467 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100468 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500469
470 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100471 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100472 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500473
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100474 /* untar file to temp location */
475 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
476 if (tmpdir == NULL)
477 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100478 if (!fu_common_extract_archive (bytes, tmpdir, error))
479 return NULL;
480
481 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100482 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
483 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100484
485 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500486 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100487 fu_common_add_argv (argv, "--die-with-parent");
488 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500489 fu_common_add_argv (argv, "--ro-bind /lib /lib");
490 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
491 fu_common_add_argv (argv, "--ro-bind /bin /bin");
492 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100493 fu_common_add_argv (argv, "--dir /tmp");
494 fu_common_add_argv (argv, "--dir /var");
495 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100496 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
497 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100498 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100499 fu_common_add_argv (argv, "--chdir /tmp");
500 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100501 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100502 g_ptr_array_add (argv, NULL);
503 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
504 g_debug ("running '%s' in %s", argv_str, tmpdir);
505 if (!g_spawn_sync ("/tmp",
506 (gchar **) argv->pdata,
507 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100508 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100509 NULL, NULL, /* child_setup */
510 &standard_output,
511 &standard_error,
512 &rc,
513 error)) {
514 g_prefix_error (error, "failed to run '%s': ", argv_str);
515 return NULL;
516 }
517 if (standard_output != NULL && standard_output[0] != '\0')
518 g_debug ("console output was: %s", standard_output);
519 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500520 FwupdError code = FWUPD_ERROR_INTERNAL;
521 if (errno == ENOTTY)
522 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100523 g_set_error (error,
524 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500525 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100526 "failed to build firmware: %s",
527 standard_error);
528 return NULL;
529 }
530
531 /* get generated file */
532 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
533 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
534 if (firmware_blob == NULL)
535 return NULL;
536
537 /* cleanup temp directory */
538 if (!fu_common_rmtree (tmpdir, error))
539 return NULL;
540
541 /* success */
542 return g_steal_pointer (&firmware_blob);
543}
Richard Hughes049ccc82017-08-09 15:26:56 +0100544
545typedef struct {
546 FuOutputHandler handler_cb;
547 gpointer handler_user_data;
548 GMainLoop *loop;
549 GSource *source;
550 GInputStream *stream;
551 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000552 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100553} FuCommonSpawnHelper;
554
555static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
556
557static gboolean
558fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
559{
560 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
561 gchar buffer[1024];
562 gssize sz;
563 g_auto(GStrv) split = NULL;
564 g_autoptr(GError) error = NULL;
565
566 /* read from stream */
567 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
568 buffer,
569 sizeof(buffer) - 1,
570 NULL,
571 &error);
572 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100573 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
574 g_warning ("failed to get read from nonblocking fd: %s",
575 error->message);
576 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100577 return G_SOURCE_REMOVE;
578 }
579
580 /* no read possible */
581 if (sz == 0)
582 g_main_loop_quit (helper->loop);
583
584 /* emit lines */
585 if (helper->handler_cb != NULL) {
586 buffer[sz] = '\0';
587 split = g_strsplit (buffer, "\n", -1);
588 for (guint i = 0; split[i] != NULL; i++) {
589 if (split[i][0] == '\0')
590 continue;
591 helper->handler_cb (split[i], helper->handler_user_data);
592 }
593 }
594
595 /* set up the source for the next read */
596 fu_common_spawn_create_pollable_source (helper);
597 return G_SOURCE_REMOVE;
598}
599
600static void
601fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
602{
603 if (helper->source != NULL)
604 g_source_destroy (helper->source);
605 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
606 helper->cancellable);
607 g_source_attach (helper->source, NULL);
608 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
609}
610
611static void
612fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
613{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000614 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100615 if (helper->stream != NULL)
616 g_object_unref (helper->stream);
617 if (helper->source != NULL)
618 g_source_destroy (helper->source);
619 if (helper->loop != NULL)
620 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000621 if (helper->timeout_id != 0)
622 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100623 g_free (helper);
624}
625
Mario Limoncielloa98df552018-04-16 12:15:51 -0500626#pragma clang diagnostic push
627#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100628G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500629#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100630
Richard Hughesb768e4d2019-02-26 13:55:18 +0000631static gboolean
632fu_common_spawn_timeout_cb (gpointer user_data)
633{
634 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
635 g_cancellable_cancel (helper->cancellable);
636 g_main_loop_quit (helper->loop);
637 helper->timeout_id = 0;
638 return G_SOURCE_REMOVE;
639}
640
641static void
642fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
643{
644 /* just propagate */
645 g_cancellable_cancel (helper->cancellable);
646}
647
Richard Hughes049ccc82017-08-09 15:26:56 +0100648/**
649 * fu_common_spawn_sync:
650 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100651 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
652 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000653 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100654 * @cancellable: a #GCancellable, or %NULL
655 * @error: A #GError or %NULL
656 *
657 * Runs a subprocess and waits for it to exit. Any output on standard out or
658 * standard error will be forwarded to @handler_cb as whole lines.
659 *
660 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600661 *
662 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100663 **/
664gboolean
665fu_common_spawn_sync (const gchar * const * argv,
666 FuOutputHandler handler_cb,
667 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000668 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100669 GCancellable *cancellable, GError **error)
670{
671 g_autoptr(FuCommonSpawnHelper) helper = NULL;
672 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100673 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000674 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100675
676 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100677 argv_str = g_strjoinv (" ", (gchar **) argv);
678 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100679 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
680 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
681 if (subprocess == NULL)
682 return FALSE;
683
684 /* watch for process to exit */
685 helper = g_new0 (FuCommonSpawnHelper, 1);
686 helper->handler_cb = handler_cb;
687 helper->handler_user_data = handler_user_data;
688 helper->loop = g_main_loop_new (NULL, FALSE);
689 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000690
691 /* always create a cancellable, and connect up the parent */
692 helper->cancellable = g_cancellable_new ();
693 if (cancellable != NULL) {
694 cancellable_id = g_cancellable_connect (cancellable,
695 G_CALLBACK (fu_common_spawn_cancelled_cb),
696 helper, NULL);
697 }
698
699 /* allow timeout */
700 if (timeout_ms > 0) {
701 helper->timeout_id = g_timeout_add (timeout_ms,
702 fu_common_spawn_timeout_cb,
703 helper);
704 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100705 fu_common_spawn_create_pollable_source (helper);
706 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000707 g_cancellable_disconnect (cancellable, cancellable_id);
708 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
709 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100710 return g_subprocess_wait_check (subprocess, cancellable, error);
711}
Richard Hughesae252cd2017-12-08 10:48:15 +0000712
713/**
714 * fu_common_write_uint16:
715 * @buf: A writable buffer
716 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100717 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000718 *
719 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600720 *
721 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000722 **/
723void
724fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
725{
726 guint16 val_hw;
727 switch (endian) {
728 case G_BIG_ENDIAN:
729 val_hw = GUINT16_TO_BE(val_native);
730 break;
731 case G_LITTLE_ENDIAN:
732 val_hw = GUINT16_TO_LE(val_native);
733 break;
734 default:
735 g_assert_not_reached ();
736 }
737 memcpy (buf, &val_hw, sizeof(val_hw));
738}
739
740/**
741 * fu_common_write_uint32:
742 * @buf: A writable buffer
743 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100744 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000745 *
746 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600747 *
748 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000749 **/
750void
751fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
752{
753 guint32 val_hw;
754 switch (endian) {
755 case G_BIG_ENDIAN:
756 val_hw = GUINT32_TO_BE(val_native);
757 break;
758 case G_LITTLE_ENDIAN:
759 val_hw = GUINT32_TO_LE(val_native);
760 break;
761 default:
762 g_assert_not_reached ();
763 }
764 memcpy (buf, &val_hw, sizeof(val_hw));
765}
766
767/**
768 * fu_common_read_uint16:
769 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100770 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000771 *
772 * Read a value from a buffer using a specified endian.
773 *
774 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600775 *
776 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000777 **/
778guint16
779fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
780{
781 guint16 val_hw, val_native;
782 memcpy (&val_hw, buf, sizeof(val_hw));
783 switch (endian) {
784 case G_BIG_ENDIAN:
785 val_native = GUINT16_FROM_BE(val_hw);
786 break;
787 case G_LITTLE_ENDIAN:
788 val_native = GUINT16_FROM_LE(val_hw);
789 break;
790 default:
791 g_assert_not_reached ();
792 }
793 return val_native;
794}
795
796/**
797 * fu_common_read_uint32:
798 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100799 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000800 *
801 * Read a value from a buffer using a specified endian.
802 *
803 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600804 *
805 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000806 **/
807guint32
808fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
809{
810 guint32 val_hw, val_native;
811 memcpy (&val_hw, buf, sizeof(val_hw));
812 switch (endian) {
813 case G_BIG_ENDIAN:
814 val_native = GUINT32_FROM_BE(val_hw);
815 break;
816 case G_LITTLE_ENDIAN:
817 val_native = GUINT32_FROM_LE(val_hw);
818 break;
819 default:
820 g_assert_not_reached ();
821 }
822 return val_native;
823}
Richard Hughese82eef32018-05-20 10:41:26 +0100824
Richard Hughes73bf2332018-08-28 09:38:09 +0100825/**
826 * fu_common_strtoull:
827 * @str: A string, e.g. "0x1234"
828 *
829 * Converts a string value to an integer. Values are assumed base 10, unless
830 * prefixed with "0x" where they are parsed as base 16.
831 *
832 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600833 *
834 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100835 **/
836guint64
837fu_common_strtoull (const gchar *str)
838{
839 guint base = 10;
840 if (str == NULL)
841 return 0x0;
842 if (g_str_has_prefix (str, "0x")) {
843 str += 2;
844 base = 16;
845 }
846 return g_ascii_strtoull (str, NULL, base);
847}
848
Richard Hughesa574a752018-08-31 13:31:03 +0100849/**
850 * fu_common_strstrip:
851 * @str: A string, e.g. " test "
852 *
853 * Removes leading and trailing whitespace from a constant string.
854 *
855 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600856 *
857 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100858 **/
859gchar *
860fu_common_strstrip (const gchar *str)
861{
862 guint head = G_MAXUINT;
863 guint tail = 0;
864
865 g_return_val_if_fail (str != NULL, NULL);
866
867 /* find first non-space char */
868 for (guint i = 0; str[i] != '\0'; i++) {
869 if (str[i] != ' ') {
870 head = i;
871 break;
872 }
873 }
874 if (head == G_MAXUINT)
875 return g_strdup ("");
876
877 /* find last non-space char */
878 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500879 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100880 tail = i;
881 }
882 return g_strndup (str + head, tail - head + 1);
883}
884
Richard Hughese82eef32018-05-20 10:41:26 +0100885static const GError *
886fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
887{
888 for (guint j = 0; j < errors->len; j++) {
889 const GError *error = g_ptr_array_index (errors, j);
890 if (g_error_matches (error, FWUPD_ERROR, error_code))
891 return error;
892 }
893 return NULL;
894}
895
896static guint
897fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
898{
899 guint cnt = 0;
900 for (guint j = 0; j < errors->len; j++) {
901 const GError *error = g_ptr_array_index (errors, j);
902 if (g_error_matches (error, FWUPD_ERROR, error_code))
903 cnt++;
904 }
905 return cnt;
906}
907
908static gboolean
909fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
910{
911 for (guint j = 0; j < errors->len; j++) {
912 const GError *error = g_ptr_array_index (errors, j);
913 gboolean matches_any = FALSE;
914 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
915 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
916 matches_any = TRUE;
917 break;
918 }
919 }
920 if (!matches_any)
921 return FALSE;
922 }
923 return TRUE;
924}
925
926/**
927 * fu_common_error_array_get_best:
928 * @errors: (element-type GError): array of errors
929 *
930 * Finds the 'best' error to show the user from a array of errors, creating a
931 * completely bespoke error where required.
932 *
933 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600934 *
935 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100936 **/
937GError *
938fu_common_error_array_get_best (GPtrArray *errors)
939{
940 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
941 FWUPD_ERROR_VERSION_SAME,
942 FWUPD_ERROR_VERSION_NEWER,
943 FWUPD_ERROR_NOT_SUPPORTED,
944 FWUPD_ERROR_INTERNAL,
945 FWUPD_ERROR_NOT_FOUND,
946 FWUPD_ERROR_LAST };
947 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
948 FWUPD_ERROR_NOT_FOUND,
949 FWUPD_ERROR_NOT_SUPPORTED,
950 FWUPD_ERROR_LAST };
951 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
952 FWUPD_ERROR_VERSION_SAME,
953 FWUPD_ERROR_NOT_FOUND,
954 FWUPD_ERROR_NOT_SUPPORTED,
955 FWUPD_ERROR_LAST };
956
957 /* are all the errors either GUID-not-matched or version-same? */
958 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
959 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
960 return g_error_new (FWUPD_ERROR,
961 FWUPD_ERROR_NOTHING_TO_DO,
962 "All updatable firmware is already installed");
963 }
964
965 /* are all the errors either GUID-not-matched or version same or newer? */
966 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
967 fu_common_error_array_matches_any (errors, err_all_newer)) {
968 return g_error_new (FWUPD_ERROR,
969 FWUPD_ERROR_NOTHING_TO_DO,
970 "All updatable devices already have newer versions");
971 }
972
973 /* get the most important single error */
974 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
975 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
976 if (error_tmp != NULL)
977 return g_error_copy (error_tmp);
978 }
979
980 /* fall back to something */
981 return g_error_new (FWUPD_ERROR,
982 FWUPD_ERROR_NOT_FOUND,
983 "No supported devices found");
984}
Richard Hughes4be17d12018-05-30 20:36:29 +0100985
986/**
987 * fu_common_get_path:
988 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
989 *
990 * Gets a fwupd-specific system path. These can be overridden with various
991 * environment variables, for instance %FWUPD_DATADIR.
992 *
993 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -0600994 *
995 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +0100996 **/
997gchar *
998fu_common_get_path (FuPathKind path_kind)
999{
1000 const gchar *tmp;
1001 g_autofree gchar *basedir = NULL;
1002
1003 switch (path_kind) {
1004 /* /var */
1005 case FU_PATH_KIND_LOCALSTATEDIR:
1006 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1007 if (tmp != NULL)
1008 return g_strdup (tmp);
1009 tmp = g_getenv ("SNAP_USER_DATA");
1010 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001011 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1012 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughes282b10d2018-06-22 14:48:00 +01001013 /* /sys/firmware */
1014 case FU_PATH_KIND_SYSFSDIR_FW:
1015 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1016 if (tmp != NULL)
1017 return g_strdup (tmp);
1018 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001019 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001020 case FU_PATH_KIND_SYSFSDIR_TPM:
1021 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1022 if (tmp != NULL)
1023 return g_strdup (tmp);
1024 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001025 /* /sys/bus/platform/drivers */
1026 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1027 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1028 if (tmp != NULL)
1029 return g_strdup (tmp);
1030 return g_strdup ("/sys/bus/platform/drivers");
Richard Hughes4be17d12018-05-30 20:36:29 +01001031 /* /etc */
1032 case FU_PATH_KIND_SYSCONFDIR:
1033 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1034 if (tmp != NULL)
1035 return g_strdup (tmp);
1036 tmp = g_getenv ("SNAP_USER_DATA");
1037 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001038 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1039 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001040 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1041 case FU_PATH_KIND_PLUGINDIR_PKG:
1042 tmp = g_getenv ("FWUPD_PLUGINDIR");
1043 if (tmp != NULL)
1044 return g_strdup (tmp);
1045 tmp = g_getenv ("SNAP");
1046 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001047 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1048 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001049 /* /usr/share/fwupd */
1050 case FU_PATH_KIND_DATADIR_PKG:
1051 tmp = g_getenv ("FWUPD_DATADIR");
1052 if (tmp != NULL)
1053 return g_strdup (tmp);
1054 tmp = g_getenv ("SNAP");
1055 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001056 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1057 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001058 /* /usr/libexec/fwupd/efi */
1059 case FU_PATH_KIND_EFIAPPDIR:
1060 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1061 if (tmp != NULL)
1062 return g_strdup (tmp);
1063#ifdef EFI_APP_LOCATION
1064 tmp = g_getenv ("SNAP");
1065 if (tmp != NULL)
1066 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1067 return g_strdup (EFI_APP_LOCATION);
1068#else
1069 return NULL;
1070#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001071 /* /etc/fwupd */
1072 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001073 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
1074 if (tmp != NULL)
1075 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001076 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1077 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1078 /* /var/lib/fwupd */
1079 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001080 tmp = g_getenv ("STATE_DIRECTORY");
1081 if (tmp != NULL)
1082 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001083 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1084 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1085 /* /var/cache/fwupd */
1086 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001087 tmp = g_getenv ("CACHE_DIRECTORY");
1088 if (tmp != NULL)
1089 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001090 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1091 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001092 case FU_PATH_KIND_OFFLINE_TRIGGER:
1093 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1094 if (tmp != NULL)
1095 return g_strdup (tmp);
1096 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001097 case FU_PATH_KIND_POLKIT_ACTIONS:
1098#ifdef POLKIT_ACTIONDIR
1099 return g_strdup (POLKIT_ACTIONDIR);
1100#else
1101 return NULL;
1102#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001103 /* this shouldn't happen */
1104 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001105 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001106 }
1107
1108 return NULL;
1109}
Richard Hughes83e56c12018-10-10 20:24:41 +01001110
1111/**
1112 * fu_common_string_replace:
1113 * @string: The #GString to operate on
1114 * @search: The text to search for
1115 * @replace: The text to use for substitutions
1116 *
1117 * Performs multiple search and replace operations on the given string.
1118 *
1119 * Returns: the number of replacements done, or 0 if @search is not found.
1120 *
1121 * Since: 1.2.0
1122 **/
1123guint
1124fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1125{
1126 gchar *tmp;
1127 guint count = 0;
1128 gsize search_idx = 0;
1129 gsize replace_len;
1130 gsize search_len;
1131
1132 g_return_val_if_fail (string != NULL, 0);
1133 g_return_val_if_fail (search != NULL, 0);
1134 g_return_val_if_fail (replace != NULL, 0);
1135
1136 /* nothing to do */
1137 if (string->len == 0)
1138 return 0;
1139
1140 search_len = strlen (search);
1141 replace_len = strlen (replace);
1142
1143 do {
1144 tmp = g_strstr_len (string->str + search_idx, -1, search);
1145 if (tmp == NULL)
1146 break;
1147
1148 /* advance the counter in case @replace contains @search */
1149 search_idx = (gsize) (tmp - string->str);
1150
1151 /* reallocate the string if required */
1152 if (search_len > replace_len) {
1153 g_string_erase (string,
1154 (gssize) search_idx,
1155 (gssize) (search_len - replace_len));
1156 memcpy (tmp, replace, replace_len);
1157 } else if (search_len < replace_len) {
1158 g_string_insert_len (string,
1159 (gssize) search_idx,
1160 replace,
1161 (gssize) (replace_len - search_len));
1162 /* we have to treat this specially as it could have
1163 * been reallocated when the insertion happened */
1164 memcpy (string->str + search_idx, replace, replace_len);
1165 } else {
1166 /* just memcmp in the new string */
1167 memcpy (tmp, replace, replace_len);
1168 }
1169 search_idx += replace_len;
1170 count++;
1171 } while (TRUE);
1172
1173 return count;
1174}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001175
Richard Hughesae96a1f2019-09-23 11:16:36 +01001176/**
1177 * fu_common_strwidth:
1178 * @text: The string to operate on
1179 *
1180 * Returns the width of the string in displayed characters on the console.
1181 *
1182 * Returns: width of text
1183 *
1184 * Since: 1.3.2
1185 **/
1186gsize
1187fu_common_strwidth (const gchar *text)
1188{
1189 const gchar *p = text;
1190 gsize width = 0;
1191 while (*p) {
1192 gunichar c = g_utf8_get_char (p);
1193 if (g_unichar_iswide (c))
1194 width += 2;
1195 else if (!g_unichar_iszerowidth (c))
1196 width += 1;
1197 p = g_utf8_next_char (p);
1198 }
1199 return width;
1200}
1201
Mario Limonciello1a680f32019-11-25 19:44:53 -06001202/**
1203 * fu_common_string_append_kv:
1204 * @str: A #GString
1205 * @idt: The indent
1206 * @key: A string to append
1207 * @value: a string to append
1208 *
1209 * Appends a key and string value to a string
1210 *
1211 * Since: 1.2.4
1212 */
Richard Hughescea28de2019-08-09 11:16:40 +01001213void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001214fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001215{
Richard Hughes847cae82019-08-27 11:22:23 +01001216 const guint align = 25;
1217 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001218
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001219 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001220
1221 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001222 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001223 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001224 for (gsize i = 0; i < idt; i++)
1225 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001226 if (key[0] != '\0') {
1227 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001228 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001229 } else {
1230 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001231 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001232 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001233 g_auto(GStrv) split = NULL;
1234 split = g_strsplit (value, "\n", -1);
1235 for (guint i = 0; split[i] != NULL; i++) {
1236 if (i == 0) {
1237 for (gsize j = keysz; j < align; j++)
1238 g_string_append (str, " ");
1239 } else {
1240 for (gsize j = 0; j < idt; j++)
1241 g_string_append (str, " ");
1242 }
1243 g_string_append (str, split[i]);
1244 g_string_append (str, "\n");
1245 }
1246 } else {
1247 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001248 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001249}
1250
Mario Limonciello1a680f32019-11-25 19:44:53 -06001251/**
1252 * fu_common_string_append_ku:
1253 * @str: A #GString
1254 * @idt: The indent
1255 * @key: A string to append
1256 * @value: guint64
1257 *
1258 * Appends a key and unsigned integer to a string
1259 *
1260 * Since: 1.2.4
1261 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001262void
1263fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1264{
1265 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1266 fu_common_string_append_kv (str, idt, key, tmp);
1267}
1268
Mario Limonciello1a680f32019-11-25 19:44:53 -06001269/**
1270 * fu_common_string_append_kx:
1271 * @str: A #GString
1272 * @idt: The indent
1273 * @key: A string to append
1274 * @value: guint64
1275 *
1276 * Appends a key and hex integer to a string
1277 *
1278 * Since: 1.2.4
1279 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001280void
1281fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1282{
1283 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1284 fu_common_string_append_kv (str, idt, key, tmp);
1285}
1286
Mario Limonciello1a680f32019-11-25 19:44:53 -06001287/**
1288 * fu_common_string_append_kb:
1289 * @str: A #GString
1290 * @idt: The indent
1291 * @key: A string to append
1292 * @value: Boolean
1293 *
1294 * Appends a key and boolean value to a string
1295 *
1296 * Since: 1.2.4
1297 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001298void
1299fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1300{
1301 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001302}
1303
Richard Hughese59cb9a2018-12-05 14:37:40 +00001304/**
Richard Hughes35481862019-01-06 12:01:58 +00001305 * fu_common_dump_full:
1306 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1307 * @title: prefix title, or %NULL
1308 * @data: buffer to print
1309 * @len: the size of @data
1310 * @columns: break new lines after this many bytes
1311 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1312 *
1313 * Dumps a raw buffer to the screen.
1314 *
1315 * Since: 1.2.4
1316 **/
1317void
1318fu_common_dump_full (const gchar *log_domain,
1319 const gchar *title,
1320 const guint8 *data,
1321 gsize len,
1322 guint columns,
1323 FuDumpFlags flags)
1324{
1325 g_autoptr(GString) str = g_string_new (NULL);
1326
1327 /* optional */
1328 if (title != NULL)
1329 g_string_append_printf (str, "%s:", title);
1330
1331 /* if more than can fit on one line then start afresh */
1332 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1333 g_string_append (str, "\n");
1334 } else {
1335 for (gsize i = str->len; i < 16; i++)
1336 g_string_append (str, " ");
1337 }
1338
1339 /* offset line */
1340 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1341 g_string_append (str, " │ ");
1342 for (gsize i = 0; i < columns; i++)
1343 g_string_append_printf (str, "%02x ", (guint) i);
1344 g_string_append (str, "\n───────┼");
1345 for (gsize i = 0; i < columns; i++)
1346 g_string_append (str, "───");
1347 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1348 }
1349
1350 /* print each row */
1351 for (gsize i = 0; i < len; i++) {
1352 g_string_append_printf (str, "%02x ", data[i]);
1353
1354 /* optionally print ASCII char */
1355 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1356 if (g_ascii_isprint (data[i]))
1357 g_string_append_printf (str, "[%c] ", data[i]);
1358 else
1359 g_string_append (str, "[?] ");
1360 }
1361
1362 /* new row required */
1363 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1364 g_string_append (str, "\n");
1365 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1366 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1367 }
1368 }
1369 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1370}
1371
1372/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001373 * fu_common_dump_raw:
1374 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1375 * @title: prefix title, or %NULL
1376 * @data: buffer to print
1377 * @len: the size of @data
1378 *
1379 * Dumps a raw buffer to the screen.
1380 *
1381 * Since: 1.2.2
1382 **/
1383void
1384fu_common_dump_raw (const gchar *log_domain,
1385 const gchar *title,
1386 const guint8 *data,
1387 gsize len)
1388{
Richard Hughes35481862019-01-06 12:01:58 +00001389 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1390 if (len > 64)
1391 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1392 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001393}
1394
1395/**
Mario Limonciello39602652019-04-29 21:08:58 -05001396 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001397 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1398 * @title: prefix title, or %NULL
1399 * @bytes: a #GBytes
1400 *
1401 * Dumps a byte buffer to the screen.
1402 *
1403 * Since: 1.2.2
1404 **/
1405void
1406fu_common_dump_bytes (const gchar *log_domain,
1407 const gchar *title,
1408 GBytes *bytes)
1409{
1410 gsize len = 0;
1411 const guint8 *data = g_bytes_get_data (bytes, &len);
1412 fu_common_dump_raw (log_domain, title, data, len);
1413}
Richard Hughesfc90f392019-01-15 21:21:16 +00001414
1415/**
1416 * fu_common_bytes_align:
1417 * @bytes: a #GBytes
1418 * @blksz: block size in bytes
1419 * @padval: the byte used to pad the byte buffer
1420 *
1421 * Aligns a block of memory to @blksize using the @padval value; if
1422 * the block is already aligned then the original @bytes is returned.
1423 *
1424 * Returns: (transfer full): a #GBytes, possibly @bytes
1425 *
1426 * Since: 1.2.4
1427 **/
1428GBytes *
1429fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1430{
1431 const guint8 *data;
1432 gsize sz;
1433
1434 g_return_val_if_fail (bytes != NULL, NULL);
1435 g_return_val_if_fail (blksz > 0, NULL);
1436
1437 /* pad */
1438 data = g_bytes_get_data (bytes, &sz);
1439 if (sz % blksz != 0) {
1440 gsize sz_align = ((sz / blksz) + 1) * blksz;
1441 guint8 *data_align = g_malloc (sz_align);
1442 memcpy (data_align, data, sz);
1443 memset (data_align + sz, padval, sz_align - sz);
1444 g_debug ("aligning 0x%x bytes to 0x%x",
1445 (guint) sz, (guint) sz_align);
1446 return g_bytes_new_take (data_align, sz_align);
1447 }
1448
1449 /* perfectly aligned */
1450 return g_bytes_ref (bytes);
1451}
Richard Hughes36999462019-03-19 20:23:29 +00001452
1453/**
1454 * fu_common_bytes_is_empty:
1455 * @bytes: a #GBytes
1456 *
1457 * Checks if a byte array are just empty (0xff) bytes.
1458 *
1459 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001460 *
1461 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001462 **/
1463gboolean
1464fu_common_bytes_is_empty (GBytes *bytes)
1465{
1466 gsize sz = 0;
1467 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1468 for (gsize i = 0; i < sz; i++) {
1469 if (buf[i] != 0xff)
1470 return FALSE;
1471 }
1472 return TRUE;
1473}
Richard Hughes2aad1042019-03-21 09:03:32 +00001474
1475/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001476 * fu_common_bytes_compare_raw:
1477 * @buf1: a buffer
1478 * @bufsz1: sizeof @buf1
1479 * @buf2: another buffer
1480 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001481 * @error: A #GError or %NULL
1482 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001483 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001484 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001485 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001486 *
1487 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001488 **/
1489gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001490fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1491 const guint8 *buf2, gsize bufsz2,
1492 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001493{
Richard Hughes38245ff2019-09-18 14:46:09 +01001494 g_return_val_if_fail (buf1 != NULL, FALSE);
1495 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001496 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1497
1498 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001499 if (bufsz1 != bufsz2) {
1500 g_set_error (error,
1501 G_IO_ERROR,
1502 G_IO_ERROR_INVALID_DATA,
1503 "got %" G_GSIZE_FORMAT " bytes, expected "
1504 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1505 return FALSE;
1506 }
1507
1508 /* check matches */
1509 for (guint i = 0x0; i < bufsz1; i++) {
1510 if (buf1[i] != buf2[i]) {
1511 g_set_error (error,
1512 G_IO_ERROR,
1513 G_IO_ERROR_INVALID_DATA,
1514 "got 0x%02x, expected 0x%02x @ 0x%04x",
1515 buf1[i], buf2[i], i);
1516 return FALSE;
1517 }
1518 }
1519
1520 /* success */
1521 return TRUE;
1522}
Richard Hughes484ee292019-03-22 16:10:50 +00001523
1524/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001525 * fu_common_bytes_compare:
1526 * @bytes1: a #GBytes
1527 * @bytes2: another #GBytes
1528 * @error: A #GError or %NULL
1529 *
1530 * Compares the buffers for equality.
1531 *
1532 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001533 *
1534 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001535 **/
1536gboolean
1537fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1538{
1539 const guint8 *buf1;
1540 const guint8 *buf2;
1541 gsize bufsz1;
1542 gsize bufsz2;
1543
1544 g_return_val_if_fail (bytes1 != NULL, FALSE);
1545 g_return_val_if_fail (bytes2 != NULL, FALSE);
1546 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1547
1548 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1549 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1550 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1551}
1552
1553/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001554 * fu_common_bytes_pad:
1555 * @bytes: a #GBytes
1556 * @sz: the desired size in bytes
1557 *
1558 * Pads a GBytes to a given @sz with `0xff`.
1559 *
1560 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001561 *
1562 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001563 **/
1564GBytes *
1565fu_common_bytes_pad (GBytes *bytes, gsize sz)
1566{
1567 gsize bytes_sz;
1568
1569 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1570
1571 /* pad */
1572 bytes_sz = g_bytes_get_size (bytes);
1573 if (bytes_sz < sz) {
1574 const guint8 *data = g_bytes_get_data (bytes, NULL);
1575 guint8 *data_new = g_malloc (sz);
1576 memcpy (data_new, data, bytes_sz);
1577 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1578 return g_bytes_new_take (data_new, sz);
1579 }
1580
1581 /* exactly right */
1582 return g_bytes_ref (bytes);
1583}
1584
1585/**
Richard Hughes484ee292019-03-22 16:10:50 +00001586 * fu_common_realpath:
1587 * @filename: a filename
1588 * @error: A #GError or %NULL
1589 *
1590 * Finds the canonicalized absolute filename for a path.
1591 *
1592 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001593 *
1594 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001595 **/
1596gchar *
1597fu_common_realpath (const gchar *filename, GError **error)
1598{
1599 char full_tmp[PATH_MAX];
1600
1601 g_return_val_if_fail (filename != NULL, NULL);
1602
Richard Hughes8694dee2019-11-22 09:16:34 +00001603#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001604 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001605#else
1606 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1607#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001608 g_set_error (error,
1609 G_IO_ERROR,
1610 G_IO_ERROR_INVALID_DATA,
1611 "cannot resolve path: %s",
1612 strerror (errno));
1613 return NULL;
1614 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001615 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1616 g_set_error (error,
1617 G_IO_ERROR,
1618 G_IO_ERROR_INVALID_DATA,
1619 "cannot find path: %s",
1620 full_tmp);
1621 return NULL;
1622 }
Richard Hughes484ee292019-03-22 16:10:50 +00001623 return g_strdup (full_tmp);
1624}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001625
1626/**
1627 * fu_common_strnsplit:
1628 * @str: a string to split
1629 * @sz: size of @str
1630 * @delimiter: a string which specifies the places at which to split the string
1631 * @max_tokens: the maximum number of pieces to split @str into
1632 *
1633 * Splits a string into a maximum of @max_tokens pieces, using the given
1634 * delimiter. If @max_tokens is reached, the remainder of string is appended
1635 * to the last token.
1636 *
1637 * Return value: a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001638 *
1639 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001640 **/
1641gchar **
1642fu_common_strnsplit (const gchar *str, gsize sz,
1643 const gchar *delimiter, gint max_tokens)
1644{
1645 if (str[sz - 1] != '\0') {
1646 g_autofree gchar *str2 = g_strndup (str, sz);
1647 return g_strsplit (str2, delimiter, max_tokens);
1648 }
1649 return g_strsplit (str, delimiter, max_tokens);
1650}
Richard Hughes5308ea42019-08-09 12:25:13 +01001651
1652/**
1653 * fu_memcpy_safe:
1654 * @dst: destination buffer
1655 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1656 * @dst_offset: offset in bytes into @dst to copy to
1657 * @src: source buffer
1658 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1659 * @src_offset: offset in bytes into @src to copy from
1660 * @n: number of bytes to copy from @src+@offset from
1661 * @error: A #GError or %NULL
1662 *
1663 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1664 * of both the destination and the source allows us to check for buffer overflow.
1665 *
1666 * Providing the buffer offsets also allows us to check reading past the end of
1667 * the source buffer. For this reason the caller should NEVER add an offset to
1668 * @src or @dst.
1669 *
1670 * You don't need to use this function in "obviously correct" cases, nor should
1671 * you use it when performance is a concern. Only us it when you're not sure if
1672 * malicious data from a device or firmware could cause memory corruption.
1673 *
1674 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001675 *
1676 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001677 **/
1678gboolean
1679fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1680 const guint8 *src, gsize src_sz, gsize src_offset,
1681 gsize n, GError **error)
1682{
1683 if (n == 0)
1684 return TRUE;
1685
1686 if (n > src_sz) {
1687 g_set_error (error,
1688 FWUPD_ERROR,
1689 FWUPD_ERROR_READ,
1690 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1691 (guint) n, (guint) src_sz);
1692 return FALSE;
1693 }
1694 if (n + src_offset > src_sz) {
1695 g_set_error (error,
1696 FWUPD_ERROR,
1697 FWUPD_ERROR_READ,
1698 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1699 (guint) n, (guint) src_offset, (guint) src_sz);
1700 return FALSE;
1701 }
1702 if (n > dst_sz) {
1703 g_set_error (error,
1704 FWUPD_ERROR,
1705 FWUPD_ERROR_WRITE,
1706 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1707 (guint) n, (guint) dst_sz);
1708 return FALSE;
1709 }
1710 if (n + dst_offset > dst_sz) {
1711 g_set_error (error,
1712 FWUPD_ERROR,
1713 FWUPD_ERROR_WRITE,
1714 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1715 (guint) n, (guint) dst_offset, (guint) dst_sz);
1716 return FALSE;
1717 }
1718
1719 /* phew! */
1720 memcpy (dst + dst_offset, src + src_offset, n);
1721 return TRUE;
1722}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001723
Richard Hughes80768f52019-10-22 07:19:14 +01001724/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001725 * fu_common_read_uint8_safe:
1726 * @buf: source buffer
1727 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1728 * @offset: offset in bytes into @buf to copy from
1729 * @value: (out) (allow-none): the parsed value
1730 * @error: A #GError or %NULL
1731 *
1732 * Read a value from a buffer in a safe way.
1733 *
1734 * You don't need to use this function in "obviously correct" cases, nor should
1735 * you use it when performance is a concern. Only us it when you're not sure if
1736 * malicious data from a device or firmware could cause memory corruption.
1737 *
1738 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001739 *
1740 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001741 **/
1742gboolean
1743fu_common_read_uint8_safe (const guint8 *buf,
1744 gsize bufsz,
1745 gsize offset,
1746 guint8 *value,
1747 GError **error)
1748{
1749 guint8 tmp;
1750 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1751 buf, bufsz, offset, /* src */
1752 sizeof(tmp), error))
1753 return FALSE;
1754 if (value != NULL)
1755 *value = tmp;
1756 return TRUE;
1757}
1758
1759/**
Richard Hughes80768f52019-10-22 07:19:14 +01001760 * fu_common_read_uint16_safe:
1761 * @buf: source buffer
1762 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1763 * @offset: offset in bytes into @buf to copy from
1764 * @value: (out) (allow-none): the parsed value
1765 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1766 * @error: A #GError or %NULL
1767 *
1768 * Read a value from a buffer using a specified endian in a safe way.
1769 *
1770 * You don't need to use this function in "obviously correct" cases, nor should
1771 * you use it when performance is a concern. Only us it when you're not sure if
1772 * malicious data from a device or firmware could cause memory corruption.
1773 *
1774 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001775 *
1776 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001777 **/
1778gboolean
1779fu_common_read_uint16_safe (const guint8 *buf,
1780 gsize bufsz,
1781 gsize offset,
1782 guint16 *value,
1783 FuEndianType endian,
1784 GError **error)
1785{
1786 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001787 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001788 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001789 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001790 return FALSE;
1791 if (value != NULL)
1792 *value = fu_common_read_uint16 (dst, endian);
1793 return TRUE;
1794}
1795
1796/**
1797 * fu_common_read_uint32_safe:
1798 * @buf: source buffer
1799 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1800 * @offset: offset in bytes into @buf to copy from
1801 * @value: (out) (allow-none): the parsed value
1802 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1803 * @error: A #GError or %NULL
1804 *
1805 * Read a value from a buffer using a specified endian in a safe way.
1806 *
1807 * You don't need to use this function in "obviously correct" cases, nor should
1808 * you use it when performance is a concern. Only us it when you're not sure if
1809 * malicious data from a device or firmware could cause memory corruption.
1810 *
1811 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001812 *
1813 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001814 **/
1815gboolean
1816fu_common_read_uint32_safe (const guint8 *buf,
1817 gsize bufsz,
1818 gsize offset,
1819 guint32 *value,
1820 FuEndianType endian,
1821 GError **error)
1822{
1823 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001824 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001825 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001826 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001827 return FALSE;
1828 if (value != NULL)
1829 *value = fu_common_read_uint32 (dst, endian);
1830 return TRUE;
1831}
1832
Mario Limonciello1a680f32019-11-25 19:44:53 -06001833/**
1834 * fu_byte_array_append_uint8:
1835 * @array: A #GByteArray
1836 * @data: #guint8
1837 *
1838 * Adds a 8 bit integer to a byte array
1839 *
1840 * Since: 1.3.1
1841 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001842void
1843fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1844{
1845 g_byte_array_append (array, &data, sizeof(data));
1846}
1847
Mario Limonciello1a680f32019-11-25 19:44:53 -06001848/**
1849 * fu_byte_array_append_uint16:
1850 * @array: A #GByteArray
1851 * @data: #guint16
1852 * @endian: #FuEndianType
1853 *
1854 * Adds a 16 bit integer to a byte array
1855 *
1856 * Since: 1.3.1
1857 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001858void
1859fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1860{
1861 guint8 buf[2];
1862 fu_common_write_uint16 (buf, data, endian);
1863 g_byte_array_append (array, buf, sizeof(buf));
1864}
1865
Mario Limonciello1a680f32019-11-25 19:44:53 -06001866/**
1867 * fu_byte_array_append_uint32:
1868 * @array: A #GByteArray
1869 * @data: #guint32
1870 * @endian: #FuEndianType
1871 *
1872 * Adds a 32 bit integer to a byte array
1873 *
1874 * Since: 1.3.1
1875 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001876void
1877fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1878{
1879 guint8 buf[4];
1880 fu_common_write_uint32 (buf, data, endian);
1881 g_byte_array_append (array, buf, sizeof(buf));
1882}