blob: 5eb9c279d42d7b97d4c3de3763a593ef3fe38a8b [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
Richard Hughes9e5675e2019-11-22 09:35:03 +000011#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +010012#include <gio/gunixinputstream.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000013#endif
Richard Hughes954dd9f2017-08-08 13:36:25 +010014#include <glib/gstdio.h>
15
Richard Hughes5c508de2019-11-22 09:57:34 +000016#ifdef HAVE_FNMATCH_H
17#include <fnmatch.h>
Richard Hughes45a00732019-11-22 16:57:14 +000018#elif _WIN32
19#include <shlwapi.h>
Richard Hughes5c508de2019-11-22 09:57:34 +000020#endif
21
Richard Hughes9223c892020-05-09 20:32:08 +010022#include <cpuid.h>
23
Richard Hughes94f939a2017-08-08 12:21:39 +010024#include <archive_entry.h>
25#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010026#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000027#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000028#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000029#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010030
31#include "fwupd-error.h"
32
33#include "fu-common.h"
34
35/**
Richard Hughes4eada342017-10-03 21:20:32 +010036 * SECTION:fu-common
37 * @short_description: common functionality for plugins to use
38 *
39 * Helper functions that can be used by the daemon and plugins.
40 *
41 * See also: #FuPlugin
42 */
43
44/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010045 * fu_common_rmtree:
46 * @directory: a directory name
47 * @error: A #GError or %NULL
48 *
49 * Recursively removes a directory.
50 *
51 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060052 *
53 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010054 **/
55gboolean
56fu_common_rmtree (const gchar *directory, GError **error)
57{
58 const gchar *filename;
59 g_autoptr(GDir) dir = NULL;
60
61 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010062 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010063 dir = g_dir_open (directory, 0, error);
64 if (dir == NULL)
65 return FALSE;
66
67 /* find each */
68 while ((filename = g_dir_read_name (dir))) {
69 g_autofree gchar *src = NULL;
70 src = g_build_filename (directory, filename, NULL);
71 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
72 if (!fu_common_rmtree (src, error))
73 return FALSE;
74 } else {
75 if (g_unlink (src) != 0) {
76 g_set_error (error,
77 FWUPD_ERROR,
78 FWUPD_ERROR_INTERNAL,
79 "Failed to delete: %s", src);
80 return FALSE;
81 }
82 }
83 }
84 if (g_remove (directory) != 0) {
85 g_set_error (error,
86 FWUPD_ERROR,
87 FWUPD_ERROR_INTERNAL,
88 "Failed to delete: %s", directory);
89 return FALSE;
90 }
91 return TRUE;
92}
93
Richard Hughes89e968b2018-03-07 10:01:08 +000094static gboolean
95fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
96{
97 const gchar *filename;
98 g_autoptr(GDir) dir = NULL;
99
100 /* try to open */
101 dir = g_dir_open (directory, 0, error);
102 if (dir == NULL)
103 return FALSE;
104
105 /* find each */
106 while ((filename = g_dir_read_name (dir))) {
107 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
108 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
109 if (!fu_common_get_file_list_internal (files, src, error))
110 return FALSE;
111 } else {
112 g_ptr_array_add (files, g_steal_pointer (&src));
113 }
114 }
115 return TRUE;
116
117}
118
119/**
120 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100121 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000122 * @error: A #GError or %NULL
123 *
124 * Returns every file found under @directory, and any subdirectory.
125 * If any path under @directory cannot be accessed due to permissions an error
126 * will be returned.
127 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000128 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600129 *
130 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000131 **/
132GPtrArray *
133fu_common_get_files_recursive (const gchar *path, GError **error)
134{
135 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
136 if (!fu_common_get_file_list_internal (files, path, error))
137 return NULL;
138 return g_steal_pointer (&files);
139}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100140/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100141 * fu_common_mkdir_parent:
142 * @filename: A full pathname
143 * @error: A #GError, or %NULL
144 *
145 * Creates any required directories, including any parent directories.
146 *
147 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600148 *
149 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100150 **/
151gboolean
152fu_common_mkdir_parent (const gchar *filename, GError **error)
153{
154 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100155
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100156 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100157 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100158 if (g_mkdir_with_parents (parent, 0755) == -1) {
159 g_set_error (error,
160 FWUPD_ERROR,
161 FWUPD_ERROR_INTERNAL,
162 "Failed to create '%s': %s",
163 parent, g_strerror (errno));
164 return FALSE;
165 }
166 return TRUE;
167}
168
169/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100170 * fu_common_set_contents_bytes:
171 * @filename: A filename
172 * @bytes: The data to write
173 * @error: A #GError, or %NULL
174 *
175 * Writes a blob of data to a filename, creating the parent directories as
176 * required.
177 *
178 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600179 *
180 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100181 **/
182gboolean
183fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
184{
185 const gchar *data;
186 gsize size;
187 g_autoptr(GFile) file = NULL;
188 g_autoptr(GFile) file_parent = NULL;
189
190 file = g_file_new_for_path (filename);
191 file_parent = g_file_get_parent (file);
192 if (!g_file_query_exists (file_parent, NULL)) {
193 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
194 return FALSE;
195 }
196 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100197 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100198 return g_file_set_contents (filename, data, size, error);
199}
200
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100201/**
202 * fu_common_get_contents_bytes:
203 * @filename: A filename
204 * @error: A #GError, or %NULL
205 *
206 * Reads a blob of data from a file.
207 *
208 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600209 *
210 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100211 **/
212GBytes *
213fu_common_get_contents_bytes (const gchar *filename, GError **error)
214{
215 gchar *data = NULL;
216 gsize len = 0;
217 if (!g_file_get_contents (filename, &data, &len, error))
218 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100219 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100220 return g_bytes_new_take (data, len);
221}
Richard Hughes943d2c92017-06-21 09:04:39 +0100222
223/**
224 * fu_common_get_contents_fd:
225 * @fd: A file descriptor
226 * @count: The maximum number of bytes to read
227 * @error: A #GError, or %NULL
228 *
229 * Reads a blob from a specific file descriptor.
230 *
231 * Note: this will close the fd when done
232 *
Richard Hughes4eada342017-10-03 21:20:32 +0100233 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600234 *
235 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100236 **/
237GBytes *
238fu_common_get_contents_fd (gint fd, gsize count, GError **error)
239{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000240#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100241 g_autoptr(GBytes) blob = NULL;
242 g_autoptr(GError) error_local = NULL;
243 g_autoptr(GInputStream) stream = NULL;
244
245 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100246 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
247
Richard Hughes919f8ab2018-02-14 10:24:56 +0000248 /* this is invalid */
249 if (count == 0) {
250 g_set_error_literal (error,
251 FWUPD_ERROR,
252 FWUPD_ERROR_NOT_SUPPORTED,
253 "A maximum read size must be specified");
254 return NULL;
255 }
256
Richard Hughes943d2c92017-06-21 09:04:39 +0100257 /* read the entire fd to a data blob */
258 stream = g_unix_input_stream_new (fd, TRUE);
259 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
260 if (blob == NULL) {
261 g_set_error_literal (error,
262 FWUPD_ERROR,
263 FWUPD_ERROR_INVALID_FILE,
264 error_local->message);
265 return NULL;
266 }
267 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000268#else
269 g_set_error_literal (error,
270 FWUPD_ERROR,
271 FWUPD_ERROR_NOT_SUPPORTED,
272 "Not supported as <glib-unix.h> is unavailable");
273 return NULL;
274#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100275}
Richard Hughes94f939a2017-08-08 12:21:39 +0100276
277static gboolean
278fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
279{
280 const gchar *tmp;
281 g_autofree gchar *buf = NULL;
282
283 /* no output file */
284 if (archive_entry_pathname (entry) == NULL)
285 return FALSE;
286
287 /* update output path */
288 tmp = archive_entry_pathname (entry);
289 buf = g_build_filename (dir, tmp, NULL);
290 archive_entry_update_pathname_utf8 (entry, buf);
291 return TRUE;
292}
293
294/**
295 * fu_common_extract_archive:
296 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100297 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100298 * @error: A #GError, or %NULL
299 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000300 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100301 *
302 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600303 *
304 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100305 **/
306gboolean
307fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
308{
309 gboolean ret = TRUE;
310 int r;
311 struct archive *arch = NULL;
312 struct archive_entry *entry;
313
314 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100315 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100316 arch = archive_read_new ();
317 archive_read_support_format_all (arch);
318 archive_read_support_filter_all (arch);
319 r = archive_read_open_memory (arch,
320 (void *) g_bytes_get_data (blob, NULL),
321 (size_t) g_bytes_get_size (blob));
322 if (r != 0) {
323 ret = FALSE;
324 g_set_error (error,
325 FWUPD_ERROR,
326 FWUPD_ERROR_INTERNAL,
327 "Cannot open: %s",
328 archive_error_string (arch));
329 goto out;
330 }
331 for (;;) {
332 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100333 r = archive_read_next_header (arch, &entry);
334 if (r == ARCHIVE_EOF)
335 break;
336 if (r != ARCHIVE_OK) {
337 ret = FALSE;
338 g_set_error (error,
339 FWUPD_ERROR,
340 FWUPD_ERROR_INTERNAL,
341 "Cannot read header: %s",
342 archive_error_string (arch));
343 goto out;
344 }
345
346 /* only extract if valid */
347 valid = fu_common_extract_archive_entry (entry, dir);
348 if (!valid)
349 continue;
350 r = archive_read_extract (arch, entry, 0);
351 if (r != ARCHIVE_OK) {
352 ret = FALSE;
353 g_set_error (error,
354 FWUPD_ERROR,
355 FWUPD_ERROR_INTERNAL,
356 "Cannot extract: %s",
357 archive_error_string (arch));
358 goto out;
359 }
360 }
361out:
362 if (arch != NULL) {
363 archive_read_close (arch);
364 archive_read_free (arch);
365 }
366 return ret;
367}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100368
369static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300370fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
371
372static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100373fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
374{
375 va_list args;
376 g_autofree gchar *tmp = NULL;
377 g_auto(GStrv) split = NULL;
378
379 va_start (args, fmt);
380 tmp = g_strdup_vprintf (fmt, args);
381 va_end (args);
382
383 split = g_strsplit (tmp, " ", -1);
384 for (guint i = 0; split[i] != NULL; i++)
385 g_ptr_array_add (argv, g_strdup (split[i]));
386}
387
Mario Limonciello1a680f32019-11-25 19:44:53 -0600388/**
389 * fu_common_find_program_in_path:
390 * @basename: The program to search
391 * @error: A #GError, or %NULL
392 *
393 * Looks for a program in the PATH variable
394 *
395 * Returns: a new #gchar, or %NULL for error
396 *
397 * Since: 1.1.2
398 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100399gchar *
400fu_common_find_program_in_path (const gchar *basename, GError **error)
401{
402 gchar *fn = g_find_program_in_path (basename);
403 if (fn == NULL) {
404 g_set_error (error,
405 FWUPD_ERROR,
406 FWUPD_ERROR_NOT_SUPPORTED,
407 "missing executable %s in PATH",
408 basename);
409 return NULL;
410 }
411 return fn;
412}
413
414static gboolean
415fu_common_test_namespace_support (GError **error)
416{
417 /* test if CONFIG_USER_NS is valid */
418 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
419 g_set_error (error,
420 FWUPD_ERROR,
421 FWUPD_ERROR_NOT_SUPPORTED,
422 "missing CONFIG_USER_NS in kernel");
423 return FALSE;
424 }
425 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
426 g_autofree gchar *clone = NULL;
427 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
428 return FALSE;
429 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
430 g_set_error (error,
431 FWUPD_ERROR,
432 FWUPD_ERROR_NOT_SUPPORTED,
433 "unprivileged user namespace clones disabled by distro");
434 return FALSE;
435 }
436 }
437 return TRUE;
438}
439
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100440/**
441 * fu_common_firmware_builder:
442 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100443 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
444 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100445 * @error: A #GError, or %NULL
446 *
447 * Builds a firmware file using tools from the host session in a bubblewrap
448 * jail. Several things happen during build:
449 *
450 * 1. The @bytes data is untarred to a temporary location
451 * 2. A bubblewrap container is set up
452 * 3. The startup.sh script is run inside the container
453 * 4. The firmware.bin is extracted from the container
454 * 5. The temporary location is deleted
455 *
456 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600457 *
458 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100459 **/
460GBytes *
461fu_common_firmware_builder (GBytes *bytes,
462 const gchar *script_fn,
463 const gchar *output_fn,
464 GError **error)
465{
466 gint rc = 0;
467 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500468 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100469 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100470 g_autofree gchar *localstatedir = NULL;
471 g_autofree gchar *output2_fn = NULL;
472 g_autofree gchar *standard_error = NULL;
473 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100474 g_autofree gchar *tmpdir = NULL;
475 g_autoptr(GBytes) firmware_blob = NULL;
476 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
477
478 g_return_val_if_fail (bytes != NULL, NULL);
479 g_return_val_if_fail (script_fn != NULL, NULL);
480 g_return_val_if_fail (output_fn != NULL, NULL);
481 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
482
Mario Limonciello37b59582018-08-13 08:38:01 -0500483 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100484 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
485 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100486 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500487
488 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100489 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100490 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500491
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100492 /* untar file to temp location */
493 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
494 if (tmpdir == NULL)
495 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100496 if (!fu_common_extract_archive (bytes, tmpdir, error))
497 return NULL;
498
499 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100500 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
501 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100502
503 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500504 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100505 fu_common_add_argv (argv, "--die-with-parent");
506 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500507 fu_common_add_argv (argv, "--ro-bind /lib /lib");
508 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
509 fu_common_add_argv (argv, "--ro-bind /bin /bin");
510 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100511 fu_common_add_argv (argv, "--dir /tmp");
512 fu_common_add_argv (argv, "--dir /var");
513 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100514 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
515 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100516 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100517 fu_common_add_argv (argv, "--chdir /tmp");
518 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100519 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100520 g_ptr_array_add (argv, NULL);
521 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
522 g_debug ("running '%s' in %s", argv_str, tmpdir);
523 if (!g_spawn_sync ("/tmp",
524 (gchar **) argv->pdata,
525 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100526 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100527 NULL, NULL, /* child_setup */
528 &standard_output,
529 &standard_error,
530 &rc,
531 error)) {
532 g_prefix_error (error, "failed to run '%s': ", argv_str);
533 return NULL;
534 }
535 if (standard_output != NULL && standard_output[0] != '\0')
536 g_debug ("console output was: %s", standard_output);
537 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500538 FwupdError code = FWUPD_ERROR_INTERNAL;
539 if (errno == ENOTTY)
540 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100541 g_set_error (error,
542 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500543 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100544 "failed to build firmware: %s",
545 standard_error);
546 return NULL;
547 }
548
549 /* get generated file */
550 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
551 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
552 if (firmware_blob == NULL)
553 return NULL;
554
555 /* cleanup temp directory */
556 if (!fu_common_rmtree (tmpdir, error))
557 return NULL;
558
559 /* success */
560 return g_steal_pointer (&firmware_blob);
561}
Richard Hughes049ccc82017-08-09 15:26:56 +0100562
563typedef struct {
564 FuOutputHandler handler_cb;
565 gpointer handler_user_data;
566 GMainLoop *loop;
567 GSource *source;
568 GInputStream *stream;
569 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000570 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100571} FuCommonSpawnHelper;
572
573static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
574
575static gboolean
576fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
577{
578 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
579 gchar buffer[1024];
580 gssize sz;
581 g_auto(GStrv) split = NULL;
582 g_autoptr(GError) error = NULL;
583
584 /* read from stream */
585 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
586 buffer,
587 sizeof(buffer) - 1,
588 NULL,
589 &error);
590 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100591 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
592 g_warning ("failed to get read from nonblocking fd: %s",
593 error->message);
594 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100595 return G_SOURCE_REMOVE;
596 }
597
598 /* no read possible */
599 if (sz == 0)
600 g_main_loop_quit (helper->loop);
601
602 /* emit lines */
603 if (helper->handler_cb != NULL) {
604 buffer[sz] = '\0';
605 split = g_strsplit (buffer, "\n", -1);
606 for (guint i = 0; split[i] != NULL; i++) {
607 if (split[i][0] == '\0')
608 continue;
609 helper->handler_cb (split[i], helper->handler_user_data);
610 }
611 }
612
613 /* set up the source for the next read */
614 fu_common_spawn_create_pollable_source (helper);
615 return G_SOURCE_REMOVE;
616}
617
618static void
619fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
620{
621 if (helper->source != NULL)
622 g_source_destroy (helper->source);
623 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
624 helper->cancellable);
625 g_source_attach (helper->source, NULL);
626 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
627}
628
629static void
630fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
631{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000632 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100633 if (helper->stream != NULL)
634 g_object_unref (helper->stream);
635 if (helper->source != NULL)
636 g_source_destroy (helper->source);
637 if (helper->loop != NULL)
638 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000639 if (helper->timeout_id != 0)
640 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100641 g_free (helper);
642}
643
Mario Limoncielloa98df552018-04-16 12:15:51 -0500644#pragma clang diagnostic push
645#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100646G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500647#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100648
Richard Hughesb768e4d2019-02-26 13:55:18 +0000649static gboolean
650fu_common_spawn_timeout_cb (gpointer user_data)
651{
652 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
653 g_cancellable_cancel (helper->cancellable);
654 g_main_loop_quit (helper->loop);
655 helper->timeout_id = 0;
656 return G_SOURCE_REMOVE;
657}
658
659static void
660fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
661{
662 /* just propagate */
663 g_cancellable_cancel (helper->cancellable);
664}
665
Richard Hughes049ccc82017-08-09 15:26:56 +0100666/**
667 * fu_common_spawn_sync:
668 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100669 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
670 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000671 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100672 * @cancellable: a #GCancellable, or %NULL
673 * @error: A #GError or %NULL
674 *
675 * Runs a subprocess and waits for it to exit. Any output on standard out or
676 * standard error will be forwarded to @handler_cb as whole lines.
677 *
678 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600679 *
680 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100681 **/
682gboolean
683fu_common_spawn_sync (const gchar * const * argv,
684 FuOutputHandler handler_cb,
685 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000686 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100687 GCancellable *cancellable, GError **error)
688{
689 g_autoptr(FuCommonSpawnHelper) helper = NULL;
690 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100691 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000692 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100693
694 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100695 argv_str = g_strjoinv (" ", (gchar **) argv);
696 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100697 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
698 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
699 if (subprocess == NULL)
700 return FALSE;
701
702 /* watch for process to exit */
703 helper = g_new0 (FuCommonSpawnHelper, 1);
704 helper->handler_cb = handler_cb;
705 helper->handler_user_data = handler_user_data;
706 helper->loop = g_main_loop_new (NULL, FALSE);
707 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000708
709 /* always create a cancellable, and connect up the parent */
710 helper->cancellable = g_cancellable_new ();
711 if (cancellable != NULL) {
712 cancellable_id = g_cancellable_connect (cancellable,
713 G_CALLBACK (fu_common_spawn_cancelled_cb),
714 helper, NULL);
715 }
716
717 /* allow timeout */
718 if (timeout_ms > 0) {
719 helper->timeout_id = g_timeout_add (timeout_ms,
720 fu_common_spawn_timeout_cb,
721 helper);
722 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100723 fu_common_spawn_create_pollable_source (helper);
724 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000725 g_cancellable_disconnect (cancellable, cancellable_id);
726 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
727 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100728 return g_subprocess_wait_check (subprocess, cancellable, error);
729}
Richard Hughesae252cd2017-12-08 10:48:15 +0000730
731/**
732 * fu_common_write_uint16:
733 * @buf: A writable buffer
734 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100735 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000736 *
737 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600738 *
739 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000740 **/
741void
742fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
743{
744 guint16 val_hw;
745 switch (endian) {
746 case G_BIG_ENDIAN:
747 val_hw = GUINT16_TO_BE(val_native);
748 break;
749 case G_LITTLE_ENDIAN:
750 val_hw = GUINT16_TO_LE(val_native);
751 break;
752 default:
753 g_assert_not_reached ();
754 }
755 memcpy (buf, &val_hw, sizeof(val_hw));
756}
757
758/**
759 * fu_common_write_uint32:
760 * @buf: A writable buffer
761 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100762 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000763 *
764 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600765 *
766 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000767 **/
768void
769fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
770{
771 guint32 val_hw;
772 switch (endian) {
773 case G_BIG_ENDIAN:
774 val_hw = GUINT32_TO_BE(val_native);
775 break;
776 case G_LITTLE_ENDIAN:
777 val_hw = GUINT32_TO_LE(val_native);
778 break;
779 default:
780 g_assert_not_reached ();
781 }
782 memcpy (buf, &val_hw, sizeof(val_hw));
783}
784
785/**
786 * fu_common_read_uint16:
787 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100788 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000789 *
790 * Read a value from a buffer using a specified endian.
791 *
792 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600793 *
794 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000795 **/
796guint16
797fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
798{
799 guint16 val_hw, val_native;
800 memcpy (&val_hw, buf, sizeof(val_hw));
801 switch (endian) {
802 case G_BIG_ENDIAN:
803 val_native = GUINT16_FROM_BE(val_hw);
804 break;
805 case G_LITTLE_ENDIAN:
806 val_native = GUINT16_FROM_LE(val_hw);
807 break;
808 default:
809 g_assert_not_reached ();
810 }
811 return val_native;
812}
813
814/**
815 * fu_common_read_uint32:
816 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100817 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000818 *
819 * Read a value from a buffer using a specified endian.
820 *
821 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600822 *
823 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000824 **/
825guint32
826fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
827{
828 guint32 val_hw, val_native;
829 memcpy (&val_hw, buf, sizeof(val_hw));
830 switch (endian) {
831 case G_BIG_ENDIAN:
832 val_native = GUINT32_FROM_BE(val_hw);
833 break;
834 case G_LITTLE_ENDIAN:
835 val_native = GUINT32_FROM_LE(val_hw);
836 break;
837 default:
838 g_assert_not_reached ();
839 }
840 return val_native;
841}
Richard Hughese82eef32018-05-20 10:41:26 +0100842
Richard Hughes73bf2332018-08-28 09:38:09 +0100843/**
844 * fu_common_strtoull:
845 * @str: A string, e.g. "0x1234"
846 *
847 * Converts a string value to an integer. Values are assumed base 10, unless
848 * prefixed with "0x" where they are parsed as base 16.
849 *
850 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600851 *
852 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100853 **/
854guint64
855fu_common_strtoull (const gchar *str)
856{
857 guint base = 10;
858 if (str == NULL)
859 return 0x0;
860 if (g_str_has_prefix (str, "0x")) {
861 str += 2;
862 base = 16;
863 }
864 return g_ascii_strtoull (str, NULL, base);
865}
866
Richard Hughesa574a752018-08-31 13:31:03 +0100867/**
868 * fu_common_strstrip:
869 * @str: A string, e.g. " test "
870 *
871 * Removes leading and trailing whitespace from a constant string.
872 *
873 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600874 *
875 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100876 **/
877gchar *
878fu_common_strstrip (const gchar *str)
879{
880 guint head = G_MAXUINT;
881 guint tail = 0;
882
883 g_return_val_if_fail (str != NULL, NULL);
884
885 /* find first non-space char */
886 for (guint i = 0; str[i] != '\0'; i++) {
887 if (str[i] != ' ') {
888 head = i;
889 break;
890 }
891 }
892 if (head == G_MAXUINT)
893 return g_strdup ("");
894
895 /* find last non-space char */
896 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500897 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100898 tail = i;
899 }
900 return g_strndup (str + head, tail - head + 1);
901}
902
Richard Hughese82eef32018-05-20 10:41:26 +0100903static const GError *
904fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
905{
906 for (guint j = 0; j < errors->len; j++) {
907 const GError *error = g_ptr_array_index (errors, j);
908 if (g_error_matches (error, FWUPD_ERROR, error_code))
909 return error;
910 }
911 return NULL;
912}
913
914static guint
915fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
916{
917 guint cnt = 0;
918 for (guint j = 0; j < errors->len; j++) {
919 const GError *error = g_ptr_array_index (errors, j);
920 if (g_error_matches (error, FWUPD_ERROR, error_code))
921 cnt++;
922 }
923 return cnt;
924}
925
926static gboolean
927fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
928{
929 for (guint j = 0; j < errors->len; j++) {
930 const GError *error = g_ptr_array_index (errors, j);
931 gboolean matches_any = FALSE;
932 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
933 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
934 matches_any = TRUE;
935 break;
936 }
937 }
938 if (!matches_any)
939 return FALSE;
940 }
941 return TRUE;
942}
943
944/**
945 * fu_common_error_array_get_best:
946 * @errors: (element-type GError): array of errors
947 *
948 * Finds the 'best' error to show the user from a array of errors, creating a
949 * completely bespoke error where required.
950 *
951 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600952 *
953 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100954 **/
955GError *
956fu_common_error_array_get_best (GPtrArray *errors)
957{
958 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
959 FWUPD_ERROR_VERSION_SAME,
960 FWUPD_ERROR_VERSION_NEWER,
961 FWUPD_ERROR_NOT_SUPPORTED,
962 FWUPD_ERROR_INTERNAL,
963 FWUPD_ERROR_NOT_FOUND,
964 FWUPD_ERROR_LAST };
965 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
966 FWUPD_ERROR_NOT_FOUND,
967 FWUPD_ERROR_NOT_SUPPORTED,
968 FWUPD_ERROR_LAST };
969 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
970 FWUPD_ERROR_VERSION_SAME,
971 FWUPD_ERROR_NOT_FOUND,
972 FWUPD_ERROR_NOT_SUPPORTED,
973 FWUPD_ERROR_LAST };
974
975 /* are all the errors either GUID-not-matched or version-same? */
976 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
977 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
978 return g_error_new (FWUPD_ERROR,
979 FWUPD_ERROR_NOTHING_TO_DO,
980 "All updatable firmware is already installed");
981 }
982
983 /* are all the errors either GUID-not-matched or version same or newer? */
984 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
985 fu_common_error_array_matches_any (errors, err_all_newer)) {
986 return g_error_new (FWUPD_ERROR,
987 FWUPD_ERROR_NOTHING_TO_DO,
988 "All updatable devices already have newer versions");
989 }
990
991 /* get the most important single error */
992 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
993 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
994 if (error_tmp != NULL)
995 return g_error_copy (error_tmp);
996 }
997
998 /* fall back to something */
999 return g_error_new (FWUPD_ERROR,
1000 FWUPD_ERROR_NOT_FOUND,
1001 "No supported devices found");
1002}
Richard Hughes4be17d12018-05-30 20:36:29 +01001003
1004/**
1005 * fu_common_get_path:
1006 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1007 *
1008 * Gets a fwupd-specific system path. These can be overridden with various
1009 * environment variables, for instance %FWUPD_DATADIR.
1010 *
1011 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001012 *
1013 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001014 **/
1015gchar *
1016fu_common_get_path (FuPathKind path_kind)
1017{
1018 const gchar *tmp;
1019 g_autofree gchar *basedir = NULL;
1020
1021 switch (path_kind) {
1022 /* /var */
1023 case FU_PATH_KIND_LOCALSTATEDIR:
1024 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1025 if (tmp != NULL)
1026 return g_strdup (tmp);
1027 tmp = g_getenv ("SNAP_USER_DATA");
1028 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001029 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1030 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001031 /* /proc */
1032 case FU_PATH_KIND_PROCFS:
1033 tmp = g_getenv ("FWUPD_PROCFS");
1034 if (tmp != NULL)
1035 return g_strdup (tmp);
1036 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001037 /* /sys/firmware */
1038 case FU_PATH_KIND_SYSFSDIR_FW:
1039 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1040 if (tmp != NULL)
1041 return g_strdup (tmp);
1042 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001043 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001044 case FU_PATH_KIND_SYSFSDIR_TPM:
1045 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1046 if (tmp != NULL)
1047 return g_strdup (tmp);
1048 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001049 /* /sys/bus/platform/drivers */
1050 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1051 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1052 if (tmp != NULL)
1053 return g_strdup (tmp);
1054 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001055 /* /sys/kernel/security */
1056 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1057 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1058 if (tmp != NULL)
1059 return g_strdup (tmp);
1060 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001061 /* /sys/firmware/acpi/tables */
1062 case FU_PATH_KIND_ACPI_TABLES:
1063 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1064 if (tmp != NULL)
1065 return g_strdup (tmp);
1066 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001067 /* /etc */
1068 case FU_PATH_KIND_SYSCONFDIR:
1069 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1070 if (tmp != NULL)
1071 return g_strdup (tmp);
1072 tmp = g_getenv ("SNAP_USER_DATA");
1073 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001074 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1075 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001076 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1077 case FU_PATH_KIND_PLUGINDIR_PKG:
1078 tmp = g_getenv ("FWUPD_PLUGINDIR");
1079 if (tmp != NULL)
1080 return g_strdup (tmp);
1081 tmp = g_getenv ("SNAP");
1082 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001083 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1084 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001085 /* /usr/share/fwupd */
1086 case FU_PATH_KIND_DATADIR_PKG:
1087 tmp = g_getenv ("FWUPD_DATADIR");
1088 if (tmp != NULL)
1089 return g_strdup (tmp);
1090 tmp = g_getenv ("SNAP");
1091 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001092 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1093 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001094 /* /usr/libexec/fwupd/efi */
1095 case FU_PATH_KIND_EFIAPPDIR:
1096 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1097 if (tmp != NULL)
1098 return g_strdup (tmp);
1099#ifdef EFI_APP_LOCATION
1100 tmp = g_getenv ("SNAP");
1101 if (tmp != NULL)
1102 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1103 return g_strdup (EFI_APP_LOCATION);
1104#else
1105 return NULL;
1106#endif
Richard Hughesb9640a22020-05-05 20:42:47 +01001107 /* /usr/share/fwupd/dbx */
1108 case FU_PATH_KIND_EFIDBXDIR:
1109 tmp = g_getenv ("FWUPD_EFIDBXDIR");
1110 if (tmp != NULL)
1111 return g_strdup (tmp);
1112#ifdef FWUPD_EFI_DBXDIR
1113 tmp = g_getenv ("SNAP");
1114 if (tmp != NULL)
1115 return g_build_filename (tmp, FWUPD_EFI_DBXDIR, NULL);
1116 return g_strdup (FWUPD_EFI_DBXDIR);
1117#else
1118 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1119 return g_build_filename (basedir, "dbx", NULL);
1120#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001121 /* /etc/fwupd */
1122 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001123 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001124 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001125 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001126 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1127 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1128 /* /var/lib/fwupd */
1129 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001130 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001131 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001132 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001133 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1134 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1135 /* /var/cache/fwupd */
1136 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001137 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001138 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001139 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001140 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1141 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001142 case FU_PATH_KIND_OFFLINE_TRIGGER:
1143 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1144 if (tmp != NULL)
1145 return g_strdup (tmp);
1146 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001147 case FU_PATH_KIND_POLKIT_ACTIONS:
1148#ifdef POLKIT_ACTIONDIR
1149 return g_strdup (POLKIT_ACTIONDIR);
1150#else
1151 return NULL;
1152#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001153 /* this shouldn't happen */
1154 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001155 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001156 }
1157
1158 return NULL;
1159}
Richard Hughes83e56c12018-10-10 20:24:41 +01001160
1161/**
1162 * fu_common_string_replace:
1163 * @string: The #GString to operate on
1164 * @search: The text to search for
1165 * @replace: The text to use for substitutions
1166 *
1167 * Performs multiple search and replace operations on the given string.
1168 *
1169 * Returns: the number of replacements done, or 0 if @search is not found.
1170 *
1171 * Since: 1.2.0
1172 **/
1173guint
1174fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1175{
1176 gchar *tmp;
1177 guint count = 0;
1178 gsize search_idx = 0;
1179 gsize replace_len;
1180 gsize search_len;
1181
1182 g_return_val_if_fail (string != NULL, 0);
1183 g_return_val_if_fail (search != NULL, 0);
1184 g_return_val_if_fail (replace != NULL, 0);
1185
1186 /* nothing to do */
1187 if (string->len == 0)
1188 return 0;
1189
1190 search_len = strlen (search);
1191 replace_len = strlen (replace);
1192
1193 do {
1194 tmp = g_strstr_len (string->str + search_idx, -1, search);
1195 if (tmp == NULL)
1196 break;
1197
1198 /* advance the counter in case @replace contains @search */
1199 search_idx = (gsize) (tmp - string->str);
1200
1201 /* reallocate the string if required */
1202 if (search_len > replace_len) {
1203 g_string_erase (string,
1204 (gssize) search_idx,
1205 (gssize) (search_len - replace_len));
1206 memcpy (tmp, replace, replace_len);
1207 } else if (search_len < replace_len) {
1208 g_string_insert_len (string,
1209 (gssize) search_idx,
1210 replace,
1211 (gssize) (replace_len - search_len));
1212 /* we have to treat this specially as it could have
1213 * been reallocated when the insertion happened */
1214 memcpy (string->str + search_idx, replace, replace_len);
1215 } else {
1216 /* just memcmp in the new string */
1217 memcpy (tmp, replace, replace_len);
1218 }
1219 search_idx += replace_len;
1220 count++;
1221 } while (TRUE);
1222
1223 return count;
1224}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001225
Richard Hughesae96a1f2019-09-23 11:16:36 +01001226/**
1227 * fu_common_strwidth:
1228 * @text: The string to operate on
1229 *
1230 * Returns the width of the string in displayed characters on the console.
1231 *
1232 * Returns: width of text
1233 *
1234 * Since: 1.3.2
1235 **/
1236gsize
1237fu_common_strwidth (const gchar *text)
1238{
1239 const gchar *p = text;
1240 gsize width = 0;
1241 while (*p) {
1242 gunichar c = g_utf8_get_char (p);
1243 if (g_unichar_iswide (c))
1244 width += 2;
1245 else if (!g_unichar_iszerowidth (c))
1246 width += 1;
1247 p = g_utf8_next_char (p);
1248 }
1249 return width;
1250}
1251
Mario Limonciello1a680f32019-11-25 19:44:53 -06001252/**
1253 * fu_common_string_append_kv:
1254 * @str: A #GString
1255 * @idt: The indent
1256 * @key: A string to append
1257 * @value: a string to append
1258 *
1259 * Appends a key and string value to a string
1260 *
1261 * Since: 1.2.4
1262 */
Richard Hughescea28de2019-08-09 11:16:40 +01001263void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001264fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001265{
Richard Hughes847cae82019-08-27 11:22:23 +01001266 const guint align = 25;
1267 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001268
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001269 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001270
1271 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001272 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001273 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001274 for (gsize i = 0; i < idt; i++)
1275 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001276 if (key[0] != '\0') {
1277 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001278 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001279 } else {
1280 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001281 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001282 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001283 g_auto(GStrv) split = NULL;
1284 split = g_strsplit (value, "\n", -1);
1285 for (guint i = 0; split[i] != NULL; i++) {
1286 if (i == 0) {
1287 for (gsize j = keysz; j < align; j++)
1288 g_string_append (str, " ");
1289 } else {
1290 for (gsize j = 0; j < idt; j++)
1291 g_string_append (str, " ");
1292 }
1293 g_string_append (str, split[i]);
1294 g_string_append (str, "\n");
1295 }
1296 } else {
1297 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001298 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001299}
1300
Mario Limonciello1a680f32019-11-25 19:44:53 -06001301/**
1302 * fu_common_string_append_ku:
1303 * @str: A #GString
1304 * @idt: The indent
1305 * @key: A string to append
1306 * @value: guint64
1307 *
1308 * Appends a key and unsigned integer to a string
1309 *
1310 * Since: 1.2.4
1311 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001312void
1313fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1314{
1315 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1316 fu_common_string_append_kv (str, idt, key, tmp);
1317}
1318
Mario Limonciello1a680f32019-11-25 19:44:53 -06001319/**
1320 * fu_common_string_append_kx:
1321 * @str: A #GString
1322 * @idt: The indent
1323 * @key: A string to append
1324 * @value: guint64
1325 *
1326 * Appends a key and hex integer to a string
1327 *
1328 * Since: 1.2.4
1329 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001330void
1331fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1332{
1333 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1334 fu_common_string_append_kv (str, idt, key, tmp);
1335}
1336
Mario Limonciello1a680f32019-11-25 19:44:53 -06001337/**
1338 * fu_common_string_append_kb:
1339 * @str: A #GString
1340 * @idt: The indent
1341 * @key: A string to append
1342 * @value: Boolean
1343 *
1344 * Appends a key and boolean value to a string
1345 *
1346 * Since: 1.2.4
1347 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001348void
1349fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1350{
1351 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001352}
1353
Richard Hughese59cb9a2018-12-05 14:37:40 +00001354/**
Richard Hughes35481862019-01-06 12:01:58 +00001355 * fu_common_dump_full:
1356 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1357 * @title: prefix title, or %NULL
1358 * @data: buffer to print
1359 * @len: the size of @data
1360 * @columns: break new lines after this many bytes
1361 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1362 *
1363 * Dumps a raw buffer to the screen.
1364 *
1365 * Since: 1.2.4
1366 **/
1367void
1368fu_common_dump_full (const gchar *log_domain,
1369 const gchar *title,
1370 const guint8 *data,
1371 gsize len,
1372 guint columns,
1373 FuDumpFlags flags)
1374{
1375 g_autoptr(GString) str = g_string_new (NULL);
1376
1377 /* optional */
1378 if (title != NULL)
1379 g_string_append_printf (str, "%s:", title);
1380
1381 /* if more than can fit on one line then start afresh */
1382 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1383 g_string_append (str, "\n");
1384 } else {
1385 for (gsize i = str->len; i < 16; i++)
1386 g_string_append (str, " ");
1387 }
1388
1389 /* offset line */
1390 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1391 g_string_append (str, " │ ");
1392 for (gsize i = 0; i < columns; i++)
1393 g_string_append_printf (str, "%02x ", (guint) i);
1394 g_string_append (str, "\n───────┼");
1395 for (gsize i = 0; i < columns; i++)
1396 g_string_append (str, "───");
1397 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1398 }
1399
1400 /* print each row */
1401 for (gsize i = 0; i < len; i++) {
1402 g_string_append_printf (str, "%02x ", data[i]);
1403
1404 /* optionally print ASCII char */
1405 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1406 if (g_ascii_isprint (data[i]))
1407 g_string_append_printf (str, "[%c] ", data[i]);
1408 else
1409 g_string_append (str, "[?] ");
1410 }
1411
1412 /* new row required */
1413 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1414 g_string_append (str, "\n");
1415 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1416 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1417 }
1418 }
1419 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1420}
1421
1422/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001423 * fu_common_dump_raw:
1424 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1425 * @title: prefix title, or %NULL
1426 * @data: buffer to print
1427 * @len: the size of @data
1428 *
1429 * Dumps a raw buffer to the screen.
1430 *
1431 * Since: 1.2.2
1432 **/
1433void
1434fu_common_dump_raw (const gchar *log_domain,
1435 const gchar *title,
1436 const guint8 *data,
1437 gsize len)
1438{
Richard Hughes35481862019-01-06 12:01:58 +00001439 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1440 if (len > 64)
1441 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1442 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001443}
1444
1445/**
Mario Limonciello39602652019-04-29 21:08:58 -05001446 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001447 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1448 * @title: prefix title, or %NULL
1449 * @bytes: a #GBytes
1450 *
1451 * Dumps a byte buffer to the screen.
1452 *
1453 * Since: 1.2.2
1454 **/
1455void
1456fu_common_dump_bytes (const gchar *log_domain,
1457 const gchar *title,
1458 GBytes *bytes)
1459{
1460 gsize len = 0;
1461 const guint8 *data = g_bytes_get_data (bytes, &len);
1462 fu_common_dump_raw (log_domain, title, data, len);
1463}
Richard Hughesfc90f392019-01-15 21:21:16 +00001464
1465/**
1466 * fu_common_bytes_align:
1467 * @bytes: a #GBytes
1468 * @blksz: block size in bytes
1469 * @padval: the byte used to pad the byte buffer
1470 *
1471 * Aligns a block of memory to @blksize using the @padval value; if
1472 * the block is already aligned then the original @bytes is returned.
1473 *
1474 * Returns: (transfer full): a #GBytes, possibly @bytes
1475 *
1476 * Since: 1.2.4
1477 **/
1478GBytes *
1479fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1480{
1481 const guint8 *data;
1482 gsize sz;
1483
1484 g_return_val_if_fail (bytes != NULL, NULL);
1485 g_return_val_if_fail (blksz > 0, NULL);
1486
1487 /* pad */
1488 data = g_bytes_get_data (bytes, &sz);
1489 if (sz % blksz != 0) {
1490 gsize sz_align = ((sz / blksz) + 1) * blksz;
1491 guint8 *data_align = g_malloc (sz_align);
1492 memcpy (data_align, data, sz);
1493 memset (data_align + sz, padval, sz_align - sz);
1494 g_debug ("aligning 0x%x bytes to 0x%x",
1495 (guint) sz, (guint) sz_align);
1496 return g_bytes_new_take (data_align, sz_align);
1497 }
1498
1499 /* perfectly aligned */
1500 return g_bytes_ref (bytes);
1501}
Richard Hughes36999462019-03-19 20:23:29 +00001502
1503/**
1504 * fu_common_bytes_is_empty:
1505 * @bytes: a #GBytes
1506 *
1507 * Checks if a byte array are just empty (0xff) bytes.
1508 *
1509 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001510 *
1511 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001512 **/
1513gboolean
1514fu_common_bytes_is_empty (GBytes *bytes)
1515{
1516 gsize sz = 0;
1517 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1518 for (gsize i = 0; i < sz; i++) {
1519 if (buf[i] != 0xff)
1520 return FALSE;
1521 }
1522 return TRUE;
1523}
Richard Hughes2aad1042019-03-21 09:03:32 +00001524
1525/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001526 * fu_common_bytes_compare_raw:
1527 * @buf1: a buffer
1528 * @bufsz1: sizeof @buf1
1529 * @buf2: another buffer
1530 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001531 * @error: A #GError or %NULL
1532 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001533 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001534 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001535 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001536 *
1537 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001538 **/
1539gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001540fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1541 const guint8 *buf2, gsize bufsz2,
1542 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001543{
Richard Hughes38245ff2019-09-18 14:46:09 +01001544 g_return_val_if_fail (buf1 != NULL, FALSE);
1545 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001546 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1547
1548 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001549 if (bufsz1 != bufsz2) {
1550 g_set_error (error,
1551 G_IO_ERROR,
1552 G_IO_ERROR_INVALID_DATA,
1553 "got %" G_GSIZE_FORMAT " bytes, expected "
1554 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1555 return FALSE;
1556 }
1557
1558 /* check matches */
1559 for (guint i = 0x0; i < bufsz1; i++) {
1560 if (buf1[i] != buf2[i]) {
1561 g_set_error (error,
1562 G_IO_ERROR,
1563 G_IO_ERROR_INVALID_DATA,
1564 "got 0x%02x, expected 0x%02x @ 0x%04x",
1565 buf1[i], buf2[i], i);
1566 return FALSE;
1567 }
1568 }
1569
1570 /* success */
1571 return TRUE;
1572}
Richard Hughes484ee292019-03-22 16:10:50 +00001573
1574/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001575 * fu_common_bytes_compare:
1576 * @bytes1: a #GBytes
1577 * @bytes2: another #GBytes
1578 * @error: A #GError or %NULL
1579 *
1580 * Compares the buffers for equality.
1581 *
1582 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001583 *
1584 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001585 **/
1586gboolean
1587fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1588{
1589 const guint8 *buf1;
1590 const guint8 *buf2;
1591 gsize bufsz1;
1592 gsize bufsz2;
1593
1594 g_return_val_if_fail (bytes1 != NULL, FALSE);
1595 g_return_val_if_fail (bytes2 != NULL, FALSE);
1596 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1597
1598 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1599 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1600 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1601}
1602
1603/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001604 * fu_common_bytes_pad:
1605 * @bytes: a #GBytes
1606 * @sz: the desired size in bytes
1607 *
1608 * Pads a GBytes to a given @sz with `0xff`.
1609 *
1610 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001611 *
1612 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001613 **/
1614GBytes *
1615fu_common_bytes_pad (GBytes *bytes, gsize sz)
1616{
1617 gsize bytes_sz;
1618
1619 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1620
1621 /* pad */
1622 bytes_sz = g_bytes_get_size (bytes);
1623 if (bytes_sz < sz) {
1624 const guint8 *data = g_bytes_get_data (bytes, NULL);
1625 guint8 *data_new = g_malloc (sz);
1626 memcpy (data_new, data, bytes_sz);
1627 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1628 return g_bytes_new_take (data_new, sz);
1629 }
1630
1631 /* exactly right */
1632 return g_bytes_ref (bytes);
1633}
1634
1635/**
Richard Hughes484ee292019-03-22 16:10:50 +00001636 * fu_common_realpath:
1637 * @filename: a filename
1638 * @error: A #GError or %NULL
1639 *
1640 * Finds the canonicalized absolute filename for a path.
1641 *
1642 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001643 *
1644 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001645 **/
1646gchar *
1647fu_common_realpath (const gchar *filename, GError **error)
1648{
1649 char full_tmp[PATH_MAX];
1650
1651 g_return_val_if_fail (filename != NULL, NULL);
1652
Richard Hughes8694dee2019-11-22 09:16:34 +00001653#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001654 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001655#else
1656 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1657#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001658 g_set_error (error,
1659 G_IO_ERROR,
1660 G_IO_ERROR_INVALID_DATA,
1661 "cannot resolve path: %s",
1662 strerror (errno));
1663 return NULL;
1664 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001665 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1666 g_set_error (error,
1667 G_IO_ERROR,
1668 G_IO_ERROR_INVALID_DATA,
1669 "cannot find path: %s",
1670 full_tmp);
1671 return NULL;
1672 }
Richard Hughes484ee292019-03-22 16:10:50 +00001673 return g_strdup (full_tmp);
1674}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001675
1676/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001677 * fu_common_fnmatch:
1678 * @pattern: a glob pattern, e.g. `*foo*`
1679 * @str: a string to match against the pattern, e.g. `bazfoobar`
1680 *
1681 * Matches a string against a glob pattern.
1682 *
1683 * Return value: %TRUE if the string matched
1684 *
1685 * Since: 1.3.5
1686 **/
1687gboolean
1688fu_common_fnmatch (const gchar *pattern, const gchar *str)
1689{
1690 g_return_val_if_fail (pattern != NULL, FALSE);
1691 g_return_val_if_fail (str != NULL, FALSE);
1692#ifdef HAVE_FNMATCH_H
1693 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001694#elif _WIN32
1695 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1696 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1697 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001698#else
1699 return g_strcmp0 (pattern, str) == 0;
1700#endif
1701}
1702
Richard Hughesa84d7a72020-05-06 12:11:51 +01001703static gint
1704fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1705{
1706 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1707}
1708
1709/**
1710 * fu_common_filename_glob:
1711 * @directory: a directory path
1712 * @pattern: a glob pattern, e.g. `*foo*`
1713 * @error: A #GError or %NULL
1714 *
1715 * Returns all the filenames that match a specific glob pattern.
1716 * Any results are sorted. No matching files will set @error.
1717 *
1718 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1719 *
1720 * Since: 1.5.0
1721 **/
1722GPtrArray *
1723fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1724{
1725 const gchar *basename;
1726 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1727 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1728 if (dir == NULL)
1729 return NULL;
1730 while ((basename = g_dir_read_name (dir)) != NULL) {
1731 if (!fu_common_fnmatch (pattern, basename))
1732 continue;
1733 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1734 }
1735 if (files->len == 0) {
1736 g_set_error_literal (error,
1737 G_IO_ERROR,
1738 G_IO_ERROR_NOT_FOUND,
1739 "no files matched pattern");
1740 return NULL;
1741 }
1742 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1743 return g_steal_pointer (&files);
1744}
1745
Richard Hughes5c508de2019-11-22 09:57:34 +00001746/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001747 * fu_common_strnsplit:
1748 * @str: a string to split
1749 * @sz: size of @str
1750 * @delimiter: a string which specifies the places at which to split the string
1751 * @max_tokens: the maximum number of pieces to split @str into
1752 *
1753 * Splits a string into a maximum of @max_tokens pieces, using the given
1754 * delimiter. If @max_tokens is reached, the remainder of string is appended
1755 * to the last token.
1756 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001757 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001758 *
1759 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001760 **/
1761gchar **
1762fu_common_strnsplit (const gchar *str, gsize sz,
1763 const gchar *delimiter, gint max_tokens)
1764{
1765 if (str[sz - 1] != '\0') {
1766 g_autofree gchar *str2 = g_strndup (str, sz);
1767 return g_strsplit (str2, delimiter, max_tokens);
1768 }
1769 return g_strsplit (str, delimiter, max_tokens);
1770}
Richard Hughes5308ea42019-08-09 12:25:13 +01001771
1772/**
1773 * fu_memcpy_safe:
1774 * @dst: destination buffer
1775 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1776 * @dst_offset: offset in bytes into @dst to copy to
1777 * @src: source buffer
1778 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1779 * @src_offset: offset in bytes into @src to copy from
1780 * @n: number of bytes to copy from @src+@offset from
1781 * @error: A #GError or %NULL
1782 *
1783 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1784 * of both the destination and the source allows us to check for buffer overflow.
1785 *
1786 * Providing the buffer offsets also allows us to check reading past the end of
1787 * the source buffer. For this reason the caller should NEVER add an offset to
1788 * @src or @dst.
1789 *
1790 * You don't need to use this function in "obviously correct" cases, nor should
1791 * you use it when performance is a concern. Only us it when you're not sure if
1792 * malicious data from a device or firmware could cause memory corruption.
1793 *
1794 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001795 *
1796 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001797 **/
1798gboolean
1799fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1800 const guint8 *src, gsize src_sz, gsize src_offset,
1801 gsize n, GError **error)
1802{
1803 if (n == 0)
1804 return TRUE;
1805
1806 if (n > src_sz) {
1807 g_set_error (error,
1808 FWUPD_ERROR,
1809 FWUPD_ERROR_READ,
1810 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1811 (guint) n, (guint) src_sz);
1812 return FALSE;
1813 }
1814 if (n + src_offset > src_sz) {
1815 g_set_error (error,
1816 FWUPD_ERROR,
1817 FWUPD_ERROR_READ,
1818 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1819 (guint) n, (guint) src_offset, (guint) src_sz);
1820 return FALSE;
1821 }
1822 if (n > dst_sz) {
1823 g_set_error (error,
1824 FWUPD_ERROR,
1825 FWUPD_ERROR_WRITE,
1826 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1827 (guint) n, (guint) dst_sz);
1828 return FALSE;
1829 }
1830 if (n + dst_offset > dst_sz) {
1831 g_set_error (error,
1832 FWUPD_ERROR,
1833 FWUPD_ERROR_WRITE,
1834 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1835 (guint) n, (guint) dst_offset, (guint) dst_sz);
1836 return FALSE;
1837 }
1838
1839 /* phew! */
1840 memcpy (dst + dst_offset, src + src_offset, n);
1841 return TRUE;
1842}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001843
Richard Hughes80768f52019-10-22 07:19:14 +01001844/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001845 * fu_common_read_uint8_safe:
1846 * @buf: source buffer
1847 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1848 * @offset: offset in bytes into @buf to copy from
1849 * @value: (out) (allow-none): the parsed value
1850 * @error: A #GError or %NULL
1851 *
1852 * Read a value from a buffer in a safe way.
1853 *
1854 * You don't need to use this function in "obviously correct" cases, nor should
1855 * you use it when performance is a concern. Only us it when you're not sure if
1856 * malicious data from a device or firmware could cause memory corruption.
1857 *
1858 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001859 *
1860 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001861 **/
1862gboolean
1863fu_common_read_uint8_safe (const guint8 *buf,
1864 gsize bufsz,
1865 gsize offset,
1866 guint8 *value,
1867 GError **error)
1868{
1869 guint8 tmp;
1870 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1871 buf, bufsz, offset, /* src */
1872 sizeof(tmp), error))
1873 return FALSE;
1874 if (value != NULL)
1875 *value = tmp;
1876 return TRUE;
1877}
1878
1879/**
Richard Hughes80768f52019-10-22 07:19:14 +01001880 * fu_common_read_uint16_safe:
1881 * @buf: source buffer
1882 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1883 * @offset: offset in bytes into @buf to copy from
1884 * @value: (out) (allow-none): the parsed value
1885 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1886 * @error: A #GError or %NULL
1887 *
1888 * Read a value from a buffer using a specified endian in a safe way.
1889 *
1890 * You don't need to use this function in "obviously correct" cases, nor should
1891 * you use it when performance is a concern. Only us it when you're not sure if
1892 * malicious data from a device or firmware could cause memory corruption.
1893 *
1894 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001895 *
1896 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001897 **/
1898gboolean
1899fu_common_read_uint16_safe (const guint8 *buf,
1900 gsize bufsz,
1901 gsize offset,
1902 guint16 *value,
1903 FuEndianType endian,
1904 GError **error)
1905{
1906 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001907 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001908 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001909 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001910 return FALSE;
1911 if (value != NULL)
1912 *value = fu_common_read_uint16 (dst, endian);
1913 return TRUE;
1914}
1915
1916/**
1917 * fu_common_read_uint32_safe:
1918 * @buf: source buffer
1919 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1920 * @offset: offset in bytes into @buf to copy from
1921 * @value: (out) (allow-none): the parsed value
1922 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1923 * @error: A #GError or %NULL
1924 *
1925 * Read a value from a buffer using a specified endian in a safe way.
1926 *
1927 * You don't need to use this function in "obviously correct" cases, nor should
1928 * you use it when performance is a concern. Only us it when you're not sure if
1929 * malicious data from a device or firmware could cause memory corruption.
1930 *
1931 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001932 *
1933 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001934 **/
1935gboolean
1936fu_common_read_uint32_safe (const guint8 *buf,
1937 gsize bufsz,
1938 gsize offset,
1939 guint32 *value,
1940 FuEndianType endian,
1941 GError **error)
1942{
1943 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001944 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001945 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001946 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001947 return FALSE;
1948 if (value != NULL)
1949 *value = fu_common_read_uint32 (dst, endian);
1950 return TRUE;
1951}
1952
Mario Limonciello1a680f32019-11-25 19:44:53 -06001953/**
1954 * fu_byte_array_append_uint8:
1955 * @array: A #GByteArray
1956 * @data: #guint8
1957 *
1958 * Adds a 8 bit integer to a byte array
1959 *
1960 * Since: 1.3.1
1961 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001962void
1963fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1964{
1965 g_byte_array_append (array, &data, sizeof(data));
1966}
1967
Mario Limonciello1a680f32019-11-25 19:44:53 -06001968/**
1969 * fu_byte_array_append_uint16:
1970 * @array: A #GByteArray
1971 * @data: #guint16
1972 * @endian: #FuEndianType
1973 *
1974 * Adds a 16 bit integer to a byte array
1975 *
1976 * Since: 1.3.1
1977 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001978void
1979fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1980{
1981 guint8 buf[2];
1982 fu_common_write_uint16 (buf, data, endian);
1983 g_byte_array_append (array, buf, sizeof(buf));
1984}
1985
Mario Limonciello1a680f32019-11-25 19:44:53 -06001986/**
1987 * fu_byte_array_append_uint32:
1988 * @array: A #GByteArray
1989 * @data: #guint32
1990 * @endian: #FuEndianType
1991 *
1992 * Adds a 32 bit integer to a byte array
1993 *
1994 * Since: 1.3.1
1995 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001996void
1997fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1998{
1999 guint8 buf[4];
2000 fu_common_write_uint32 (buf, data, endian);
2001 g_byte_array_append (array, buf, sizeof(buf));
2002}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002003
2004/**
2005 * fu_common_kernel_locked_down:
2006 *
2007 * Determines if kernel lockdown in effect
2008 *
2009 * Since: 1.3.8
2010 **/
2011gboolean
2012fu_common_kernel_locked_down (void)
2013{
2014#ifndef _WIN32
2015 gsize len = 0;
2016 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2017 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2018 g_autofree gchar *data = NULL;
2019 g_auto(GStrv) options = NULL;
2020
2021 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2022 return FALSE;
2023 if (!g_file_get_contents (fname, &data, &len, NULL))
2024 return FALSE;
2025 if (len < 1)
2026 return FALSE;
2027 options = g_strsplit (data, " ", -1);
2028 for (guint i = 0; options[i] != NULL; i++) {
2029 if (g_strcmp0 (options[i], "[none]") == 0)
2030 return FALSE;
2031 }
2032 return TRUE;
2033#else
2034 return FALSE;
2035#endif
2036}
Richard Hughes9223c892020-05-09 20:32:08 +01002037
2038/**
2039 * fu_common_is_cpu_intel:
2040 *
2041 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2042 *
2043 * Return value: %TRUE if the vendor was Intel.
2044 *
2045 * Since: 1.5.0
2046 **/
2047gboolean
2048fu_common_is_cpu_intel (void)
2049{
2050 guint eax = 0;
2051 guint ebx = 0;
2052 guint ecx = 0;
2053 guint edx = 0;
2054 guint level = 0;
2055
2056 /* get vendor */
2057 __get_cpuid(level, &eax, &ebx, &ecx, &edx);
2058 if (ebx == signature_INTEL_ebx &&
2059 edx == signature_INTEL_edx &&
2060 ecx == signature_INTEL_ecx) {
2061 return TRUE;
2062 }
2063 return FALSE;
2064}