blob: 67ffd90980542c0af3b375b87f56a5992f1a4169 [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 Hughesbd444322020-05-21 12:05:03 +010022#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +010023#include <cpuid.h>
Richard Hughesbd444322020-05-21 12:05:03 +010024#endif
Richard Hughes9223c892020-05-09 20:32:08 +010025
Richard Hughes94f939a2017-08-08 12:21:39 +010026#include <archive_entry.h>
27#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010028#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000029#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000031#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010032
33#include "fwupd-error.h"
34
35#include "fu-common.h"
36
37/**
Richard Hughes4eada342017-10-03 21:20:32 +010038 * SECTION:fu-common
39 * @short_description: common functionality for plugins to use
40 *
41 * Helper functions that can be used by the daemon and plugins.
42 *
43 * See also: #FuPlugin
44 */
45
46/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010047 * fu_common_rmtree:
48 * @directory: a directory name
49 * @error: A #GError or %NULL
50 *
51 * Recursively removes a directory.
52 *
53 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060054 *
55 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010056 **/
57gboolean
58fu_common_rmtree (const gchar *directory, GError **error)
59{
60 const gchar *filename;
61 g_autoptr(GDir) dir = NULL;
62
63 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010064 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010065 dir = g_dir_open (directory, 0, error);
66 if (dir == NULL)
67 return FALSE;
68
69 /* find each */
70 while ((filename = g_dir_read_name (dir))) {
71 g_autofree gchar *src = NULL;
72 src = g_build_filename (directory, filename, NULL);
73 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
74 if (!fu_common_rmtree (src, error))
75 return FALSE;
76 } else {
77 if (g_unlink (src) != 0) {
78 g_set_error (error,
79 FWUPD_ERROR,
80 FWUPD_ERROR_INTERNAL,
81 "Failed to delete: %s", src);
82 return FALSE;
83 }
84 }
85 }
86 if (g_remove (directory) != 0) {
87 g_set_error (error,
88 FWUPD_ERROR,
89 FWUPD_ERROR_INTERNAL,
90 "Failed to delete: %s", directory);
91 return FALSE;
92 }
93 return TRUE;
94}
95
Richard Hughes89e968b2018-03-07 10:01:08 +000096static gboolean
97fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
98{
99 const gchar *filename;
100 g_autoptr(GDir) dir = NULL;
101
102 /* try to open */
103 dir = g_dir_open (directory, 0, error);
104 if (dir == NULL)
105 return FALSE;
106
107 /* find each */
108 while ((filename = g_dir_read_name (dir))) {
109 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
110 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
111 if (!fu_common_get_file_list_internal (files, src, error))
112 return FALSE;
113 } else {
114 g_ptr_array_add (files, g_steal_pointer (&src));
115 }
116 }
117 return TRUE;
118
119}
120
121/**
122 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100123 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000124 * @error: A #GError or %NULL
125 *
126 * Returns every file found under @directory, and any subdirectory.
127 * If any path under @directory cannot be accessed due to permissions an error
128 * will be returned.
129 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000130 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600131 *
132 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000133 **/
134GPtrArray *
135fu_common_get_files_recursive (const gchar *path, GError **error)
136{
137 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
138 if (!fu_common_get_file_list_internal (files, path, error))
139 return NULL;
140 return g_steal_pointer (&files);
141}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100142/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100143 * fu_common_mkdir_parent:
144 * @filename: A full pathname
145 * @error: A #GError, or %NULL
146 *
147 * Creates any required directories, including any parent directories.
148 *
149 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600150 *
151 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100152 **/
153gboolean
154fu_common_mkdir_parent (const gchar *filename, GError **error)
155{
156 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100157
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100158 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100159 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100160 if (g_mkdir_with_parents (parent, 0755) == -1) {
161 g_set_error (error,
162 FWUPD_ERROR,
163 FWUPD_ERROR_INTERNAL,
164 "Failed to create '%s': %s",
165 parent, g_strerror (errno));
166 return FALSE;
167 }
168 return TRUE;
169}
170
171/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100172 * fu_common_set_contents_bytes:
173 * @filename: A filename
174 * @bytes: The data to write
175 * @error: A #GError, or %NULL
176 *
177 * Writes a blob of data to a filename, creating the parent directories as
178 * required.
179 *
180 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600181 *
182 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100183 **/
184gboolean
185fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
186{
187 const gchar *data;
188 gsize size;
189 g_autoptr(GFile) file = NULL;
190 g_autoptr(GFile) file_parent = NULL;
191
192 file = g_file_new_for_path (filename);
193 file_parent = g_file_get_parent (file);
194 if (!g_file_query_exists (file_parent, NULL)) {
195 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
196 return FALSE;
197 }
198 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100199 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100200 return g_file_set_contents (filename, data, size, error);
201}
202
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100203/**
204 * fu_common_get_contents_bytes:
205 * @filename: A filename
206 * @error: A #GError, or %NULL
207 *
208 * Reads a blob of data from a file.
209 *
210 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600211 *
212 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100213 **/
214GBytes *
215fu_common_get_contents_bytes (const gchar *filename, GError **error)
216{
217 gchar *data = NULL;
218 gsize len = 0;
219 if (!g_file_get_contents (filename, &data, &len, error))
220 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100221 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100222 return g_bytes_new_take (data, len);
223}
Richard Hughes943d2c92017-06-21 09:04:39 +0100224
225/**
226 * fu_common_get_contents_fd:
227 * @fd: A file descriptor
228 * @count: The maximum number of bytes to read
229 * @error: A #GError, or %NULL
230 *
231 * Reads a blob from a specific file descriptor.
232 *
233 * Note: this will close the fd when done
234 *
Richard Hughes4eada342017-10-03 21:20:32 +0100235 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600236 *
237 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100238 **/
239GBytes *
240fu_common_get_contents_fd (gint fd, gsize count, GError **error)
241{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000242#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100243 g_autoptr(GBytes) blob = NULL;
244 g_autoptr(GError) error_local = NULL;
245 g_autoptr(GInputStream) stream = NULL;
246
247 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100248 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
249
Richard Hughes919f8ab2018-02-14 10:24:56 +0000250 /* this is invalid */
251 if (count == 0) {
252 g_set_error_literal (error,
253 FWUPD_ERROR,
254 FWUPD_ERROR_NOT_SUPPORTED,
255 "A maximum read size must be specified");
256 return NULL;
257 }
258
Richard Hughes943d2c92017-06-21 09:04:39 +0100259 /* read the entire fd to a data blob */
260 stream = g_unix_input_stream_new (fd, TRUE);
261 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
262 if (blob == NULL) {
263 g_set_error_literal (error,
264 FWUPD_ERROR,
265 FWUPD_ERROR_INVALID_FILE,
266 error_local->message);
267 return NULL;
268 }
269 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000270#else
271 g_set_error_literal (error,
272 FWUPD_ERROR,
273 FWUPD_ERROR_NOT_SUPPORTED,
274 "Not supported as <glib-unix.h> is unavailable");
275 return NULL;
276#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100277}
Richard Hughes94f939a2017-08-08 12:21:39 +0100278
279static gboolean
280fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
281{
282 const gchar *tmp;
283 g_autofree gchar *buf = NULL;
284
285 /* no output file */
286 if (archive_entry_pathname (entry) == NULL)
287 return FALSE;
288
289 /* update output path */
290 tmp = archive_entry_pathname (entry);
291 buf = g_build_filename (dir, tmp, NULL);
292 archive_entry_update_pathname_utf8 (entry, buf);
293 return TRUE;
294}
295
296/**
297 * fu_common_extract_archive:
298 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100299 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100300 * @error: A #GError, or %NULL
301 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000302 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100303 *
304 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600305 *
306 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100307 **/
308gboolean
309fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
310{
311 gboolean ret = TRUE;
312 int r;
313 struct archive *arch = NULL;
314 struct archive_entry *entry;
315
316 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100317 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100318 arch = archive_read_new ();
319 archive_read_support_format_all (arch);
320 archive_read_support_filter_all (arch);
321 r = archive_read_open_memory (arch,
322 (void *) g_bytes_get_data (blob, NULL),
323 (size_t) g_bytes_get_size (blob));
324 if (r != 0) {
325 ret = FALSE;
326 g_set_error (error,
327 FWUPD_ERROR,
328 FWUPD_ERROR_INTERNAL,
329 "Cannot open: %s",
330 archive_error_string (arch));
331 goto out;
332 }
333 for (;;) {
334 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100335 r = archive_read_next_header (arch, &entry);
336 if (r == ARCHIVE_EOF)
337 break;
338 if (r != ARCHIVE_OK) {
339 ret = FALSE;
340 g_set_error (error,
341 FWUPD_ERROR,
342 FWUPD_ERROR_INTERNAL,
343 "Cannot read header: %s",
344 archive_error_string (arch));
345 goto out;
346 }
347
348 /* only extract if valid */
349 valid = fu_common_extract_archive_entry (entry, dir);
350 if (!valid)
351 continue;
352 r = archive_read_extract (arch, entry, 0);
353 if (r != ARCHIVE_OK) {
354 ret = FALSE;
355 g_set_error (error,
356 FWUPD_ERROR,
357 FWUPD_ERROR_INTERNAL,
358 "Cannot extract: %s",
359 archive_error_string (arch));
360 goto out;
361 }
362 }
363out:
364 if (arch != NULL) {
365 archive_read_close (arch);
366 archive_read_free (arch);
367 }
368 return ret;
369}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100370
371static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300372fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
373
374static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100375fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
376{
377 va_list args;
378 g_autofree gchar *tmp = NULL;
379 g_auto(GStrv) split = NULL;
380
381 va_start (args, fmt);
382 tmp = g_strdup_vprintf (fmt, args);
383 va_end (args);
384
385 split = g_strsplit (tmp, " ", -1);
386 for (guint i = 0; split[i] != NULL; i++)
387 g_ptr_array_add (argv, g_strdup (split[i]));
388}
389
Mario Limonciello1a680f32019-11-25 19:44:53 -0600390/**
391 * fu_common_find_program_in_path:
392 * @basename: The program to search
393 * @error: A #GError, or %NULL
394 *
395 * Looks for a program in the PATH variable
396 *
397 * Returns: a new #gchar, or %NULL for error
398 *
399 * Since: 1.1.2
400 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100401gchar *
402fu_common_find_program_in_path (const gchar *basename, GError **error)
403{
404 gchar *fn = g_find_program_in_path (basename);
405 if (fn == NULL) {
406 g_set_error (error,
407 FWUPD_ERROR,
408 FWUPD_ERROR_NOT_SUPPORTED,
409 "missing executable %s in PATH",
410 basename);
411 return NULL;
412 }
413 return fn;
414}
415
416static gboolean
417fu_common_test_namespace_support (GError **error)
418{
419 /* test if CONFIG_USER_NS is valid */
420 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
421 g_set_error (error,
422 FWUPD_ERROR,
423 FWUPD_ERROR_NOT_SUPPORTED,
424 "missing CONFIG_USER_NS in kernel");
425 return FALSE;
426 }
427 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
428 g_autofree gchar *clone = NULL;
429 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
430 return FALSE;
431 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
432 g_set_error (error,
433 FWUPD_ERROR,
434 FWUPD_ERROR_NOT_SUPPORTED,
435 "unprivileged user namespace clones disabled by distro");
436 return FALSE;
437 }
438 }
439 return TRUE;
440}
441
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100442/**
443 * fu_common_firmware_builder:
444 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100445 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
446 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100447 * @error: A #GError, or %NULL
448 *
449 * Builds a firmware file using tools from the host session in a bubblewrap
450 * jail. Several things happen during build:
451 *
452 * 1. The @bytes data is untarred to a temporary location
453 * 2. A bubblewrap container is set up
454 * 3. The startup.sh script is run inside the container
455 * 4. The firmware.bin is extracted from the container
456 * 5. The temporary location is deleted
457 *
458 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600459 *
460 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100461 **/
462GBytes *
463fu_common_firmware_builder (GBytes *bytes,
464 const gchar *script_fn,
465 const gchar *output_fn,
466 GError **error)
467{
468 gint rc = 0;
469 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500470 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100471 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100472 g_autofree gchar *localstatedir = NULL;
473 g_autofree gchar *output2_fn = NULL;
474 g_autofree gchar *standard_error = NULL;
475 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100476 g_autofree gchar *tmpdir = NULL;
477 g_autoptr(GBytes) firmware_blob = NULL;
478 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
479
480 g_return_val_if_fail (bytes != NULL, NULL);
481 g_return_val_if_fail (script_fn != NULL, NULL);
482 g_return_val_if_fail (output_fn != NULL, NULL);
483 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
484
Mario Limonciello37b59582018-08-13 08:38:01 -0500485 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100486 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
487 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100488 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500489
490 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100491 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100492 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500493
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100494 /* untar file to temp location */
495 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
496 if (tmpdir == NULL)
497 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100498 if (!fu_common_extract_archive (bytes, tmpdir, error))
499 return NULL;
500
501 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100502 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
503 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100504
505 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500506 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100507 fu_common_add_argv (argv, "--die-with-parent");
508 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500509 fu_common_add_argv (argv, "--ro-bind /lib /lib");
510 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
511 fu_common_add_argv (argv, "--ro-bind /bin /bin");
512 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100513 fu_common_add_argv (argv, "--dir /tmp");
514 fu_common_add_argv (argv, "--dir /var");
515 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100516 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
517 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100518 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100519 fu_common_add_argv (argv, "--chdir /tmp");
520 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100521 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100522 g_ptr_array_add (argv, NULL);
523 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
524 g_debug ("running '%s' in %s", argv_str, tmpdir);
525 if (!g_spawn_sync ("/tmp",
526 (gchar **) argv->pdata,
527 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100528 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100529 NULL, NULL, /* child_setup */
530 &standard_output,
531 &standard_error,
532 &rc,
533 error)) {
534 g_prefix_error (error, "failed to run '%s': ", argv_str);
535 return NULL;
536 }
537 if (standard_output != NULL && standard_output[0] != '\0')
538 g_debug ("console output was: %s", standard_output);
539 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500540 FwupdError code = FWUPD_ERROR_INTERNAL;
541 if (errno == ENOTTY)
542 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100543 g_set_error (error,
544 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500545 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100546 "failed to build firmware: %s",
547 standard_error);
548 return NULL;
549 }
550
551 /* get generated file */
552 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
553 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
554 if (firmware_blob == NULL)
555 return NULL;
556
557 /* cleanup temp directory */
558 if (!fu_common_rmtree (tmpdir, error))
559 return NULL;
560
561 /* success */
562 return g_steal_pointer (&firmware_blob);
563}
Richard Hughes049ccc82017-08-09 15:26:56 +0100564
565typedef struct {
566 FuOutputHandler handler_cb;
567 gpointer handler_user_data;
568 GMainLoop *loop;
569 GSource *source;
570 GInputStream *stream;
571 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000572 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100573} FuCommonSpawnHelper;
574
575static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
576
577static gboolean
578fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
579{
580 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
581 gchar buffer[1024];
582 gssize sz;
583 g_auto(GStrv) split = NULL;
584 g_autoptr(GError) error = NULL;
585
586 /* read from stream */
587 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
588 buffer,
589 sizeof(buffer) - 1,
590 NULL,
591 &error);
592 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100593 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
594 g_warning ("failed to get read from nonblocking fd: %s",
595 error->message);
596 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100597 return G_SOURCE_REMOVE;
598 }
599
600 /* no read possible */
601 if (sz == 0)
602 g_main_loop_quit (helper->loop);
603
604 /* emit lines */
605 if (helper->handler_cb != NULL) {
606 buffer[sz] = '\0';
607 split = g_strsplit (buffer, "\n", -1);
608 for (guint i = 0; split[i] != NULL; i++) {
609 if (split[i][0] == '\0')
610 continue;
611 helper->handler_cb (split[i], helper->handler_user_data);
612 }
613 }
614
615 /* set up the source for the next read */
616 fu_common_spawn_create_pollable_source (helper);
617 return G_SOURCE_REMOVE;
618}
619
620static void
621fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
622{
623 if (helper->source != NULL)
624 g_source_destroy (helper->source);
625 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
626 helper->cancellable);
627 g_source_attach (helper->source, NULL);
628 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
629}
630
631static void
632fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
633{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000634 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100635 if (helper->stream != NULL)
636 g_object_unref (helper->stream);
637 if (helper->source != NULL)
638 g_source_destroy (helper->source);
639 if (helper->loop != NULL)
640 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000641 if (helper->timeout_id != 0)
642 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100643 g_free (helper);
644}
645
Mario Limoncielloa98df552018-04-16 12:15:51 -0500646#pragma clang diagnostic push
647#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100648G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500649#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100650
Richard Hughesb768e4d2019-02-26 13:55:18 +0000651static gboolean
652fu_common_spawn_timeout_cb (gpointer user_data)
653{
654 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
655 g_cancellable_cancel (helper->cancellable);
656 g_main_loop_quit (helper->loop);
657 helper->timeout_id = 0;
658 return G_SOURCE_REMOVE;
659}
660
661static void
662fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
663{
664 /* just propagate */
665 g_cancellable_cancel (helper->cancellable);
666}
667
Richard Hughes049ccc82017-08-09 15:26:56 +0100668/**
669 * fu_common_spawn_sync:
670 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100671 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
672 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000673 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100674 * @cancellable: a #GCancellable, or %NULL
675 * @error: A #GError or %NULL
676 *
677 * Runs a subprocess and waits for it to exit. Any output on standard out or
678 * standard error will be forwarded to @handler_cb as whole lines.
679 *
680 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600681 *
682 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100683 **/
684gboolean
685fu_common_spawn_sync (const gchar * const * argv,
686 FuOutputHandler handler_cb,
687 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000688 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100689 GCancellable *cancellable, GError **error)
690{
691 g_autoptr(FuCommonSpawnHelper) helper = NULL;
692 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100693 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000694 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100695
696 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100697 argv_str = g_strjoinv (" ", (gchar **) argv);
698 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100699 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
700 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
701 if (subprocess == NULL)
702 return FALSE;
703
704 /* watch for process to exit */
705 helper = g_new0 (FuCommonSpawnHelper, 1);
706 helper->handler_cb = handler_cb;
707 helper->handler_user_data = handler_user_data;
708 helper->loop = g_main_loop_new (NULL, FALSE);
709 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000710
711 /* always create a cancellable, and connect up the parent */
712 helper->cancellable = g_cancellable_new ();
713 if (cancellable != NULL) {
714 cancellable_id = g_cancellable_connect (cancellable,
715 G_CALLBACK (fu_common_spawn_cancelled_cb),
716 helper, NULL);
717 }
718
719 /* allow timeout */
720 if (timeout_ms > 0) {
721 helper->timeout_id = g_timeout_add (timeout_ms,
722 fu_common_spawn_timeout_cb,
723 helper);
724 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100725 fu_common_spawn_create_pollable_source (helper);
726 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000727 g_cancellable_disconnect (cancellable, cancellable_id);
728 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
729 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100730 return g_subprocess_wait_check (subprocess, cancellable, error);
731}
Richard Hughesae252cd2017-12-08 10:48:15 +0000732
733/**
734 * fu_common_write_uint16:
735 * @buf: A writable buffer
736 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100737 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000738 *
739 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600740 *
741 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000742 **/
743void
744fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
745{
746 guint16 val_hw;
747 switch (endian) {
748 case G_BIG_ENDIAN:
749 val_hw = GUINT16_TO_BE(val_native);
750 break;
751 case G_LITTLE_ENDIAN:
752 val_hw = GUINT16_TO_LE(val_native);
753 break;
754 default:
755 g_assert_not_reached ();
756 }
757 memcpy (buf, &val_hw, sizeof(val_hw));
758}
759
760/**
761 * fu_common_write_uint32:
762 * @buf: A writable buffer
763 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100764 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000765 *
766 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600767 *
768 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000769 **/
770void
771fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
772{
773 guint32 val_hw;
774 switch (endian) {
775 case G_BIG_ENDIAN:
776 val_hw = GUINT32_TO_BE(val_native);
777 break;
778 case G_LITTLE_ENDIAN:
779 val_hw = GUINT32_TO_LE(val_native);
780 break;
781 default:
782 g_assert_not_reached ();
783 }
784 memcpy (buf, &val_hw, sizeof(val_hw));
785}
786
787/**
788 * fu_common_read_uint16:
789 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100790 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000791 *
792 * Read a value from a buffer using a specified endian.
793 *
794 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600795 *
796 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000797 **/
798guint16
799fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
800{
801 guint16 val_hw, val_native;
802 memcpy (&val_hw, buf, sizeof(val_hw));
803 switch (endian) {
804 case G_BIG_ENDIAN:
805 val_native = GUINT16_FROM_BE(val_hw);
806 break;
807 case G_LITTLE_ENDIAN:
808 val_native = GUINT16_FROM_LE(val_hw);
809 break;
810 default:
811 g_assert_not_reached ();
812 }
813 return val_native;
814}
815
816/**
817 * fu_common_read_uint32:
818 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100819 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000820 *
821 * Read a value from a buffer using a specified endian.
822 *
823 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600824 *
825 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000826 **/
827guint32
828fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
829{
830 guint32 val_hw, val_native;
831 memcpy (&val_hw, buf, sizeof(val_hw));
832 switch (endian) {
833 case G_BIG_ENDIAN:
834 val_native = GUINT32_FROM_BE(val_hw);
835 break;
836 case G_LITTLE_ENDIAN:
837 val_native = GUINT32_FROM_LE(val_hw);
838 break;
839 default:
840 g_assert_not_reached ();
841 }
842 return val_native;
843}
Richard Hughese82eef32018-05-20 10:41:26 +0100844
Richard Hughes73bf2332018-08-28 09:38:09 +0100845/**
846 * fu_common_strtoull:
847 * @str: A string, e.g. "0x1234"
848 *
849 * Converts a string value to an integer. Values are assumed base 10, unless
850 * prefixed with "0x" where they are parsed as base 16.
851 *
852 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600853 *
854 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100855 **/
856guint64
857fu_common_strtoull (const gchar *str)
858{
859 guint base = 10;
860 if (str == NULL)
861 return 0x0;
862 if (g_str_has_prefix (str, "0x")) {
863 str += 2;
864 base = 16;
865 }
866 return g_ascii_strtoull (str, NULL, base);
867}
868
Richard Hughesa574a752018-08-31 13:31:03 +0100869/**
870 * fu_common_strstrip:
871 * @str: A string, e.g. " test "
872 *
873 * Removes leading and trailing whitespace from a constant string.
874 *
875 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600876 *
877 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100878 **/
879gchar *
880fu_common_strstrip (const gchar *str)
881{
882 guint head = G_MAXUINT;
883 guint tail = 0;
884
885 g_return_val_if_fail (str != NULL, NULL);
886
887 /* find first non-space char */
888 for (guint i = 0; str[i] != '\0'; i++) {
889 if (str[i] != ' ') {
890 head = i;
891 break;
892 }
893 }
894 if (head == G_MAXUINT)
895 return g_strdup ("");
896
897 /* find last non-space char */
898 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500899 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100900 tail = i;
901 }
902 return g_strndup (str + head, tail - head + 1);
903}
904
Richard Hughese82eef32018-05-20 10:41:26 +0100905static const GError *
906fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
907{
908 for (guint j = 0; j < errors->len; j++) {
909 const GError *error = g_ptr_array_index (errors, j);
910 if (g_error_matches (error, FWUPD_ERROR, error_code))
911 return error;
912 }
913 return NULL;
914}
915
916static guint
917fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
918{
919 guint cnt = 0;
920 for (guint j = 0; j < errors->len; j++) {
921 const GError *error = g_ptr_array_index (errors, j);
922 if (g_error_matches (error, FWUPD_ERROR, error_code))
923 cnt++;
924 }
925 return cnt;
926}
927
928static gboolean
929fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
930{
931 for (guint j = 0; j < errors->len; j++) {
932 const GError *error = g_ptr_array_index (errors, j);
933 gboolean matches_any = FALSE;
934 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
935 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
936 matches_any = TRUE;
937 break;
938 }
939 }
940 if (!matches_any)
941 return FALSE;
942 }
943 return TRUE;
944}
945
946/**
947 * fu_common_error_array_get_best:
948 * @errors: (element-type GError): array of errors
949 *
950 * Finds the 'best' error to show the user from a array of errors, creating a
951 * completely bespoke error where required.
952 *
953 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600954 *
955 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100956 **/
957GError *
958fu_common_error_array_get_best (GPtrArray *errors)
959{
960 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
961 FWUPD_ERROR_VERSION_SAME,
962 FWUPD_ERROR_VERSION_NEWER,
963 FWUPD_ERROR_NOT_SUPPORTED,
964 FWUPD_ERROR_INTERNAL,
965 FWUPD_ERROR_NOT_FOUND,
966 FWUPD_ERROR_LAST };
967 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
968 FWUPD_ERROR_NOT_FOUND,
969 FWUPD_ERROR_NOT_SUPPORTED,
970 FWUPD_ERROR_LAST };
971 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
972 FWUPD_ERROR_VERSION_SAME,
973 FWUPD_ERROR_NOT_FOUND,
974 FWUPD_ERROR_NOT_SUPPORTED,
975 FWUPD_ERROR_LAST };
976
977 /* are all the errors either GUID-not-matched or version-same? */
978 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
979 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
980 return g_error_new (FWUPD_ERROR,
981 FWUPD_ERROR_NOTHING_TO_DO,
982 "All updatable firmware is already installed");
983 }
984
985 /* are all the errors either GUID-not-matched or version same or newer? */
986 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
987 fu_common_error_array_matches_any (errors, err_all_newer)) {
988 return g_error_new (FWUPD_ERROR,
989 FWUPD_ERROR_NOTHING_TO_DO,
990 "All updatable devices already have newer versions");
991 }
992
993 /* get the most important single error */
994 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
995 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
996 if (error_tmp != NULL)
997 return g_error_copy (error_tmp);
998 }
999
1000 /* fall back to something */
1001 return g_error_new (FWUPD_ERROR,
1002 FWUPD_ERROR_NOT_FOUND,
1003 "No supported devices found");
1004}
Richard Hughes4be17d12018-05-30 20:36:29 +01001005
1006/**
1007 * fu_common_get_path:
1008 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1009 *
1010 * Gets a fwupd-specific system path. These can be overridden with various
1011 * environment variables, for instance %FWUPD_DATADIR.
1012 *
1013 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001014 *
1015 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001016 **/
1017gchar *
1018fu_common_get_path (FuPathKind path_kind)
1019{
1020 const gchar *tmp;
1021 g_autofree gchar *basedir = NULL;
1022
1023 switch (path_kind) {
1024 /* /var */
1025 case FU_PATH_KIND_LOCALSTATEDIR:
1026 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1027 if (tmp != NULL)
1028 return g_strdup (tmp);
1029 tmp = g_getenv ("SNAP_USER_DATA");
1030 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001031 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1032 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughesc3689582020-05-06 12:35:20 +01001033 /* /proc */
1034 case FU_PATH_KIND_PROCFS:
1035 tmp = g_getenv ("FWUPD_PROCFS");
1036 if (tmp != NULL)
1037 return g_strdup (tmp);
1038 return g_strdup ("/proc");
Richard Hughes282b10d2018-06-22 14:48:00 +01001039 /* /sys/firmware */
1040 case FU_PATH_KIND_SYSFSDIR_FW:
1041 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1042 if (tmp != NULL)
1043 return g_strdup (tmp);
1044 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001045 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001046 case FU_PATH_KIND_SYSFSDIR_TPM:
1047 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1048 if (tmp != NULL)
1049 return g_strdup (tmp);
1050 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001051 /* /sys/bus/platform/drivers */
1052 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1053 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1054 if (tmp != NULL)
1055 return g_strdup (tmp);
1056 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001057 /* /sys/kernel/security */
1058 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1059 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1060 if (tmp != NULL)
1061 return g_strdup (tmp);
1062 return g_strdup ("/sys/kernel/security");
Richard Hughesa7157912020-05-11 17:14:05 +01001063 /* /sys/firmware/acpi/tables */
1064 case FU_PATH_KIND_ACPI_TABLES:
1065 tmp = g_getenv ("FWUPD_ACPITABLESDIR");
1066 if (tmp != NULL)
1067 return g_strdup (tmp);
1068 return g_strdup ("/sys/firmware/acpi/tables");
Richard Hughes4be17d12018-05-30 20:36:29 +01001069 /* /etc */
1070 case FU_PATH_KIND_SYSCONFDIR:
1071 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1072 if (tmp != NULL)
1073 return g_strdup (tmp);
1074 tmp = g_getenv ("SNAP_USER_DATA");
1075 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001076 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1077 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001078 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1079 case FU_PATH_KIND_PLUGINDIR_PKG:
1080 tmp = g_getenv ("FWUPD_PLUGINDIR");
1081 if (tmp != NULL)
1082 return g_strdup (tmp);
1083 tmp = g_getenv ("SNAP");
1084 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001085 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1086 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001087 /* /usr/share/fwupd */
1088 case FU_PATH_KIND_DATADIR_PKG:
1089 tmp = g_getenv ("FWUPD_DATADIR");
1090 if (tmp != NULL)
1091 return g_strdup (tmp);
1092 tmp = g_getenv ("SNAP");
1093 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001094 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1095 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001096 /* /usr/libexec/fwupd/efi */
1097 case FU_PATH_KIND_EFIAPPDIR:
1098 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1099 if (tmp != NULL)
1100 return g_strdup (tmp);
1101#ifdef EFI_APP_LOCATION
1102 tmp = g_getenv ("SNAP");
1103 if (tmp != NULL)
1104 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1105 return g_strdup (EFI_APP_LOCATION);
1106#else
1107 return NULL;
1108#endif
Richard Hughesb9640a22020-05-05 20:42:47 +01001109 /* /usr/share/fwupd/dbx */
1110 case FU_PATH_KIND_EFIDBXDIR:
1111 tmp = g_getenv ("FWUPD_EFIDBXDIR");
1112 if (tmp != NULL)
1113 return g_strdup (tmp);
1114#ifdef FWUPD_EFI_DBXDIR
1115 tmp = g_getenv ("SNAP");
1116 if (tmp != NULL)
1117 return g_build_filename (tmp, FWUPD_EFI_DBXDIR, NULL);
1118 return g_strdup (FWUPD_EFI_DBXDIR);
1119#else
1120 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1121 return g_build_filename (basedir, "dbx", NULL);
1122#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001123 /* /etc/fwupd */
1124 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001125 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001126 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001127 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001128 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1129 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1130 /* /var/lib/fwupd */
1131 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001132 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001133 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001134 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001135 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1136 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1137 /* /var/cache/fwupd */
1138 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001139 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001140 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001141 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001142 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1143 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001144 case FU_PATH_KIND_OFFLINE_TRIGGER:
1145 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1146 if (tmp != NULL)
1147 return g_strdup (tmp);
1148 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001149 case FU_PATH_KIND_POLKIT_ACTIONS:
1150#ifdef POLKIT_ACTIONDIR
1151 return g_strdup (POLKIT_ACTIONDIR);
1152#else
1153 return NULL;
1154#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001155 /* this shouldn't happen */
1156 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001157 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001158 }
1159
1160 return NULL;
1161}
Richard Hughes83e56c12018-10-10 20:24:41 +01001162
1163/**
1164 * fu_common_string_replace:
1165 * @string: The #GString to operate on
1166 * @search: The text to search for
1167 * @replace: The text to use for substitutions
1168 *
1169 * Performs multiple search and replace operations on the given string.
1170 *
1171 * Returns: the number of replacements done, or 0 if @search is not found.
1172 *
1173 * Since: 1.2.0
1174 **/
1175guint
1176fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1177{
1178 gchar *tmp;
1179 guint count = 0;
1180 gsize search_idx = 0;
1181 gsize replace_len;
1182 gsize search_len;
1183
1184 g_return_val_if_fail (string != NULL, 0);
1185 g_return_val_if_fail (search != NULL, 0);
1186 g_return_val_if_fail (replace != NULL, 0);
1187
1188 /* nothing to do */
1189 if (string->len == 0)
1190 return 0;
1191
1192 search_len = strlen (search);
1193 replace_len = strlen (replace);
1194
1195 do {
1196 tmp = g_strstr_len (string->str + search_idx, -1, search);
1197 if (tmp == NULL)
1198 break;
1199
1200 /* advance the counter in case @replace contains @search */
1201 search_idx = (gsize) (tmp - string->str);
1202
1203 /* reallocate the string if required */
1204 if (search_len > replace_len) {
1205 g_string_erase (string,
1206 (gssize) search_idx,
1207 (gssize) (search_len - replace_len));
1208 memcpy (tmp, replace, replace_len);
1209 } else if (search_len < replace_len) {
1210 g_string_insert_len (string,
1211 (gssize) search_idx,
1212 replace,
1213 (gssize) (replace_len - search_len));
1214 /* we have to treat this specially as it could have
1215 * been reallocated when the insertion happened */
1216 memcpy (string->str + search_idx, replace, replace_len);
1217 } else {
1218 /* just memcmp in the new string */
1219 memcpy (tmp, replace, replace_len);
1220 }
1221 search_idx += replace_len;
1222 count++;
1223 } while (TRUE);
1224
1225 return count;
1226}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001227
Richard Hughesae96a1f2019-09-23 11:16:36 +01001228/**
1229 * fu_common_strwidth:
1230 * @text: The string to operate on
1231 *
1232 * Returns the width of the string in displayed characters on the console.
1233 *
1234 * Returns: width of text
1235 *
1236 * Since: 1.3.2
1237 **/
1238gsize
1239fu_common_strwidth (const gchar *text)
1240{
1241 const gchar *p = text;
1242 gsize width = 0;
Richard Hughes4d2c0f82020-07-07 12:02:30 +01001243
1244 g_return_val_if_fail (text != NULL, 0);
1245
Richard Hughesae96a1f2019-09-23 11:16:36 +01001246 while (*p) {
1247 gunichar c = g_utf8_get_char (p);
1248 if (g_unichar_iswide (c))
1249 width += 2;
1250 else if (!g_unichar_iszerowidth (c))
1251 width += 1;
1252 p = g_utf8_next_char (p);
1253 }
1254 return width;
1255}
1256
Mario Limonciello1a680f32019-11-25 19:44:53 -06001257/**
1258 * fu_common_string_append_kv:
1259 * @str: A #GString
1260 * @idt: The indent
1261 * @key: A string to append
1262 * @value: a string to append
1263 *
1264 * Appends a key and string value to a string
1265 *
1266 * Since: 1.2.4
1267 */
Richard Hughescea28de2019-08-09 11:16:40 +01001268void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001269fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001270{
Richard Hughes847cae82019-08-27 11:22:23 +01001271 const guint align = 25;
1272 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001273
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001274 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001275
1276 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001277 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001278 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001279 for (gsize i = 0; i < idt; i++)
1280 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001281 if (key[0] != '\0') {
1282 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001283 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001284 } else {
1285 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001286 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001287 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001288 g_auto(GStrv) split = NULL;
1289 split = g_strsplit (value, "\n", -1);
1290 for (guint i = 0; split[i] != NULL; i++) {
1291 if (i == 0) {
1292 for (gsize j = keysz; j < align; j++)
1293 g_string_append (str, " ");
1294 } else {
1295 for (gsize j = 0; j < idt; j++)
1296 g_string_append (str, " ");
1297 }
1298 g_string_append (str, split[i]);
1299 g_string_append (str, "\n");
1300 }
1301 } else {
1302 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001303 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001304}
1305
Mario Limonciello1a680f32019-11-25 19:44:53 -06001306/**
1307 * fu_common_string_append_ku:
1308 * @str: A #GString
1309 * @idt: The indent
1310 * @key: A string to append
1311 * @value: guint64
1312 *
1313 * Appends a key and unsigned integer to a string
1314 *
1315 * Since: 1.2.4
1316 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001317void
1318fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1319{
1320 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1321 fu_common_string_append_kv (str, idt, key, tmp);
1322}
1323
Mario Limonciello1a680f32019-11-25 19:44:53 -06001324/**
1325 * fu_common_string_append_kx:
1326 * @str: A #GString
1327 * @idt: The indent
1328 * @key: A string to append
1329 * @value: guint64
1330 *
1331 * Appends a key and hex integer to a string
1332 *
1333 * Since: 1.2.4
1334 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001335void
1336fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1337{
1338 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1339 fu_common_string_append_kv (str, idt, key, tmp);
1340}
1341
Mario Limonciello1a680f32019-11-25 19:44:53 -06001342/**
1343 * fu_common_string_append_kb:
1344 * @str: A #GString
1345 * @idt: The indent
1346 * @key: A string to append
1347 * @value: Boolean
1348 *
1349 * Appends a key and boolean value to a string
1350 *
1351 * Since: 1.2.4
1352 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001353void
1354fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1355{
1356 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001357}
1358
Richard Hughese59cb9a2018-12-05 14:37:40 +00001359/**
Richard Hughes35481862019-01-06 12:01:58 +00001360 * fu_common_dump_full:
1361 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1362 * @title: prefix title, or %NULL
1363 * @data: buffer to print
1364 * @len: the size of @data
1365 * @columns: break new lines after this many bytes
1366 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1367 *
1368 * Dumps a raw buffer to the screen.
1369 *
1370 * Since: 1.2.4
1371 **/
1372void
1373fu_common_dump_full (const gchar *log_domain,
1374 const gchar *title,
1375 const guint8 *data,
1376 gsize len,
1377 guint columns,
1378 FuDumpFlags flags)
1379{
1380 g_autoptr(GString) str = g_string_new (NULL);
1381
1382 /* optional */
1383 if (title != NULL)
1384 g_string_append_printf (str, "%s:", title);
1385
1386 /* if more than can fit on one line then start afresh */
1387 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1388 g_string_append (str, "\n");
1389 } else {
1390 for (gsize i = str->len; i < 16; i++)
1391 g_string_append (str, " ");
1392 }
1393
1394 /* offset line */
1395 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1396 g_string_append (str, " │ ");
1397 for (gsize i = 0; i < columns; i++)
1398 g_string_append_printf (str, "%02x ", (guint) i);
1399 g_string_append (str, "\n───────┼");
1400 for (gsize i = 0; i < columns; i++)
1401 g_string_append (str, "───");
1402 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1403 }
1404
1405 /* print each row */
1406 for (gsize i = 0; i < len; i++) {
1407 g_string_append_printf (str, "%02x ", data[i]);
1408
1409 /* optionally print ASCII char */
1410 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1411 if (g_ascii_isprint (data[i]))
1412 g_string_append_printf (str, "[%c] ", data[i]);
1413 else
1414 g_string_append (str, "[?] ");
1415 }
1416
1417 /* new row required */
1418 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1419 g_string_append (str, "\n");
1420 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1421 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1422 }
1423 }
1424 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1425}
1426
1427/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001428 * fu_common_dump_raw:
1429 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1430 * @title: prefix title, or %NULL
1431 * @data: buffer to print
1432 * @len: the size of @data
1433 *
1434 * Dumps a raw buffer to the screen.
1435 *
1436 * Since: 1.2.2
1437 **/
1438void
1439fu_common_dump_raw (const gchar *log_domain,
1440 const gchar *title,
1441 const guint8 *data,
1442 gsize len)
1443{
Richard Hughes35481862019-01-06 12:01:58 +00001444 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1445 if (len > 64)
1446 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1447 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001448}
1449
1450/**
Mario Limonciello39602652019-04-29 21:08:58 -05001451 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001452 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1453 * @title: prefix title, or %NULL
1454 * @bytes: a #GBytes
1455 *
1456 * Dumps a byte buffer to the screen.
1457 *
1458 * Since: 1.2.2
1459 **/
1460void
1461fu_common_dump_bytes (const gchar *log_domain,
1462 const gchar *title,
1463 GBytes *bytes)
1464{
1465 gsize len = 0;
1466 const guint8 *data = g_bytes_get_data (bytes, &len);
1467 fu_common_dump_raw (log_domain, title, data, len);
1468}
Richard Hughesfc90f392019-01-15 21:21:16 +00001469
1470/**
1471 * fu_common_bytes_align:
1472 * @bytes: a #GBytes
1473 * @blksz: block size in bytes
1474 * @padval: the byte used to pad the byte buffer
1475 *
1476 * Aligns a block of memory to @blksize using the @padval value; if
1477 * the block is already aligned then the original @bytes is returned.
1478 *
1479 * Returns: (transfer full): a #GBytes, possibly @bytes
1480 *
1481 * Since: 1.2.4
1482 **/
1483GBytes *
1484fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1485{
1486 const guint8 *data;
1487 gsize sz;
1488
1489 g_return_val_if_fail (bytes != NULL, NULL);
1490 g_return_val_if_fail (blksz > 0, NULL);
1491
1492 /* pad */
1493 data = g_bytes_get_data (bytes, &sz);
1494 if (sz % blksz != 0) {
1495 gsize sz_align = ((sz / blksz) + 1) * blksz;
1496 guint8 *data_align = g_malloc (sz_align);
1497 memcpy (data_align, data, sz);
1498 memset (data_align + sz, padval, sz_align - sz);
1499 g_debug ("aligning 0x%x bytes to 0x%x",
1500 (guint) sz, (guint) sz_align);
1501 return g_bytes_new_take (data_align, sz_align);
1502 }
1503
1504 /* perfectly aligned */
1505 return g_bytes_ref (bytes);
1506}
Richard Hughes36999462019-03-19 20:23:29 +00001507
1508/**
1509 * fu_common_bytes_is_empty:
1510 * @bytes: a #GBytes
1511 *
1512 * Checks if a byte array are just empty (0xff) bytes.
1513 *
1514 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001515 *
1516 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001517 **/
1518gboolean
1519fu_common_bytes_is_empty (GBytes *bytes)
1520{
1521 gsize sz = 0;
1522 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1523 for (gsize i = 0; i < sz; i++) {
1524 if (buf[i] != 0xff)
1525 return FALSE;
1526 }
1527 return TRUE;
1528}
Richard Hughes2aad1042019-03-21 09:03:32 +00001529
1530/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001531 * fu_common_bytes_compare_raw:
1532 * @buf1: a buffer
1533 * @bufsz1: sizeof @buf1
1534 * @buf2: another buffer
1535 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001536 * @error: A #GError or %NULL
1537 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001538 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001539 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001540 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001541 *
1542 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001543 **/
1544gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001545fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1546 const guint8 *buf2, gsize bufsz2,
1547 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001548{
Richard Hughes38245ff2019-09-18 14:46:09 +01001549 g_return_val_if_fail (buf1 != NULL, FALSE);
1550 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001551 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1552
1553 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001554 if (bufsz1 != bufsz2) {
1555 g_set_error (error,
1556 G_IO_ERROR,
1557 G_IO_ERROR_INVALID_DATA,
1558 "got %" G_GSIZE_FORMAT " bytes, expected "
1559 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1560 return FALSE;
1561 }
1562
1563 /* check matches */
1564 for (guint i = 0x0; i < bufsz1; i++) {
1565 if (buf1[i] != buf2[i]) {
1566 g_set_error (error,
1567 G_IO_ERROR,
1568 G_IO_ERROR_INVALID_DATA,
1569 "got 0x%02x, expected 0x%02x @ 0x%04x",
1570 buf1[i], buf2[i], i);
1571 return FALSE;
1572 }
1573 }
1574
1575 /* success */
1576 return TRUE;
1577}
Richard Hughes484ee292019-03-22 16:10:50 +00001578
1579/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001580 * fu_common_bytes_compare:
1581 * @bytes1: a #GBytes
1582 * @bytes2: another #GBytes
1583 * @error: A #GError or %NULL
1584 *
1585 * Compares the buffers for equality.
1586 *
1587 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001588 *
1589 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001590 **/
1591gboolean
1592fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1593{
1594 const guint8 *buf1;
1595 const guint8 *buf2;
1596 gsize bufsz1;
1597 gsize bufsz2;
1598
1599 g_return_val_if_fail (bytes1 != NULL, FALSE);
1600 g_return_val_if_fail (bytes2 != NULL, FALSE);
1601 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1602
1603 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1604 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1605 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1606}
1607
1608/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001609 * fu_common_bytes_pad:
1610 * @bytes: a #GBytes
1611 * @sz: the desired size in bytes
1612 *
1613 * Pads a GBytes to a given @sz with `0xff`.
1614 *
1615 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001616 *
1617 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001618 **/
1619GBytes *
1620fu_common_bytes_pad (GBytes *bytes, gsize sz)
1621{
1622 gsize bytes_sz;
1623
1624 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1625
1626 /* pad */
1627 bytes_sz = g_bytes_get_size (bytes);
1628 if (bytes_sz < sz) {
1629 const guint8 *data = g_bytes_get_data (bytes, NULL);
1630 guint8 *data_new = g_malloc (sz);
1631 memcpy (data_new, data, bytes_sz);
1632 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1633 return g_bytes_new_take (data_new, sz);
1634 }
1635
1636 /* exactly right */
1637 return g_bytes_ref (bytes);
1638}
1639
1640/**
Richard Hughes484ee292019-03-22 16:10:50 +00001641 * fu_common_realpath:
1642 * @filename: a filename
1643 * @error: A #GError or %NULL
1644 *
1645 * Finds the canonicalized absolute filename for a path.
1646 *
1647 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001648 *
1649 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001650 **/
1651gchar *
1652fu_common_realpath (const gchar *filename, GError **error)
1653{
1654 char full_tmp[PATH_MAX];
1655
1656 g_return_val_if_fail (filename != NULL, NULL);
1657
Richard Hughes8694dee2019-11-22 09:16:34 +00001658#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001659 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001660#else
1661 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1662#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001663 g_set_error (error,
1664 G_IO_ERROR,
1665 G_IO_ERROR_INVALID_DATA,
1666 "cannot resolve path: %s",
1667 strerror (errno));
1668 return NULL;
1669 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001670 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1671 g_set_error (error,
1672 G_IO_ERROR,
1673 G_IO_ERROR_INVALID_DATA,
1674 "cannot find path: %s",
1675 full_tmp);
1676 return NULL;
1677 }
Richard Hughes484ee292019-03-22 16:10:50 +00001678 return g_strdup (full_tmp);
1679}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001680
1681/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001682 * fu_common_fnmatch:
1683 * @pattern: a glob pattern, e.g. `*foo*`
1684 * @str: a string to match against the pattern, e.g. `bazfoobar`
1685 *
1686 * Matches a string against a glob pattern.
1687 *
1688 * Return value: %TRUE if the string matched
1689 *
1690 * Since: 1.3.5
1691 **/
1692gboolean
1693fu_common_fnmatch (const gchar *pattern, const gchar *str)
1694{
1695 g_return_val_if_fail (pattern != NULL, FALSE);
1696 g_return_val_if_fail (str != NULL, FALSE);
1697#ifdef HAVE_FNMATCH_H
1698 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001699#elif _WIN32
1700 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1701 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1702 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001703#else
1704 return g_strcmp0 (pattern, str) == 0;
1705#endif
1706}
1707
Richard Hughesa84d7a72020-05-06 12:11:51 +01001708static gint
1709fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1710{
1711 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1712}
1713
1714/**
1715 * fu_common_filename_glob:
1716 * @directory: a directory path
1717 * @pattern: a glob pattern, e.g. `*foo*`
1718 * @error: A #GError or %NULL
1719 *
1720 * Returns all the filenames that match a specific glob pattern.
1721 * Any results are sorted. No matching files will set @error.
1722 *
1723 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1724 *
1725 * Since: 1.5.0
1726 **/
1727GPtrArray *
1728fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1729{
1730 const gchar *basename;
1731 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1732 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1733 if (dir == NULL)
1734 return NULL;
1735 while ((basename = g_dir_read_name (dir)) != NULL) {
1736 if (!fu_common_fnmatch (pattern, basename))
1737 continue;
1738 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1739 }
1740 if (files->len == 0) {
1741 g_set_error_literal (error,
1742 G_IO_ERROR,
1743 G_IO_ERROR_NOT_FOUND,
1744 "no files matched pattern");
1745 return NULL;
1746 }
1747 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1748 return g_steal_pointer (&files);
1749}
1750
Richard Hughes5c508de2019-11-22 09:57:34 +00001751/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001752 * fu_common_strnsplit:
1753 * @str: a string to split
1754 * @sz: size of @str
1755 * @delimiter: a string which specifies the places at which to split the string
1756 * @max_tokens: the maximum number of pieces to split @str into
1757 *
1758 * Splits a string into a maximum of @max_tokens pieces, using the given
1759 * delimiter. If @max_tokens is reached, the remainder of string is appended
1760 * to the last token.
1761 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001762 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001763 *
1764 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001765 **/
1766gchar **
1767fu_common_strnsplit (const gchar *str, gsize sz,
1768 const gchar *delimiter, gint max_tokens)
1769{
1770 if (str[sz - 1] != '\0') {
1771 g_autofree gchar *str2 = g_strndup (str, sz);
1772 return g_strsplit (str2, delimiter, max_tokens);
1773 }
1774 return g_strsplit (str, delimiter, max_tokens);
1775}
Richard Hughes5308ea42019-08-09 12:25:13 +01001776
1777/**
1778 * fu_memcpy_safe:
1779 * @dst: destination buffer
1780 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1781 * @dst_offset: offset in bytes into @dst to copy to
1782 * @src: source buffer
1783 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1784 * @src_offset: offset in bytes into @src to copy from
1785 * @n: number of bytes to copy from @src+@offset from
1786 * @error: A #GError or %NULL
1787 *
1788 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1789 * of both the destination and the source allows us to check for buffer overflow.
1790 *
1791 * Providing the buffer offsets also allows us to check reading past the end of
1792 * the source buffer. For this reason the caller should NEVER add an offset to
1793 * @src or @dst.
1794 *
1795 * You don't need to use this function in "obviously correct" cases, nor should
1796 * you use it when performance is a concern. Only us it when you're not sure if
1797 * malicious data from a device or firmware could cause memory corruption.
1798 *
1799 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001800 *
1801 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001802 **/
1803gboolean
1804fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1805 const guint8 *src, gsize src_sz, gsize src_offset,
1806 gsize n, GError **error)
1807{
1808 if (n == 0)
1809 return TRUE;
1810
1811 if (n > src_sz) {
1812 g_set_error (error,
1813 FWUPD_ERROR,
1814 FWUPD_ERROR_READ,
1815 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1816 (guint) n, (guint) src_sz);
1817 return FALSE;
1818 }
1819 if (n + src_offset > src_sz) {
1820 g_set_error (error,
1821 FWUPD_ERROR,
1822 FWUPD_ERROR_READ,
1823 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1824 (guint) n, (guint) src_offset, (guint) src_sz);
1825 return FALSE;
1826 }
1827 if (n > dst_sz) {
1828 g_set_error (error,
1829 FWUPD_ERROR,
1830 FWUPD_ERROR_WRITE,
1831 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1832 (guint) n, (guint) dst_sz);
1833 return FALSE;
1834 }
1835 if (n + dst_offset > dst_sz) {
1836 g_set_error (error,
1837 FWUPD_ERROR,
1838 FWUPD_ERROR_WRITE,
1839 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1840 (guint) n, (guint) dst_offset, (guint) dst_sz);
1841 return FALSE;
1842 }
1843
1844 /* phew! */
1845 memcpy (dst + dst_offset, src + src_offset, n);
1846 return TRUE;
1847}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001848
Richard Hughes80768f52019-10-22 07:19:14 +01001849/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001850 * fu_common_read_uint8_safe:
1851 * @buf: source buffer
1852 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1853 * @offset: offset in bytes into @buf to copy from
1854 * @value: (out) (allow-none): the parsed value
1855 * @error: A #GError or %NULL
1856 *
1857 * Read a value from a buffer in a safe way.
1858 *
1859 * You don't need to use this function in "obviously correct" cases, nor should
1860 * you use it when performance is a concern. Only us it when you're not sure if
1861 * malicious data from a device or firmware could cause memory corruption.
1862 *
1863 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001864 *
1865 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001866 **/
1867gboolean
1868fu_common_read_uint8_safe (const guint8 *buf,
1869 gsize bufsz,
1870 gsize offset,
1871 guint8 *value,
1872 GError **error)
1873{
1874 guint8 tmp;
1875 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1876 buf, bufsz, offset, /* src */
1877 sizeof(tmp), error))
1878 return FALSE;
1879 if (value != NULL)
1880 *value = tmp;
1881 return TRUE;
1882}
1883
1884/**
Richard Hughes80768f52019-10-22 07:19:14 +01001885 * fu_common_read_uint16_safe:
1886 * @buf: source buffer
1887 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1888 * @offset: offset in bytes into @buf to copy from
1889 * @value: (out) (allow-none): the parsed value
1890 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1891 * @error: A #GError or %NULL
1892 *
1893 * Read a value from a buffer using a specified endian in a safe way.
1894 *
1895 * You don't need to use this function in "obviously correct" cases, nor should
1896 * you use it when performance is a concern. Only us it when you're not sure if
1897 * malicious data from a device or firmware could cause memory corruption.
1898 *
1899 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001900 *
1901 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001902 **/
1903gboolean
1904fu_common_read_uint16_safe (const guint8 *buf,
1905 gsize bufsz,
1906 gsize offset,
1907 guint16 *value,
1908 FuEndianType endian,
1909 GError **error)
1910{
1911 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001912 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001913 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001914 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001915 return FALSE;
1916 if (value != NULL)
1917 *value = fu_common_read_uint16 (dst, endian);
1918 return TRUE;
1919}
1920
1921/**
1922 * fu_common_read_uint32_safe:
1923 * @buf: source buffer
1924 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1925 * @offset: offset in bytes into @buf to copy from
1926 * @value: (out) (allow-none): the parsed value
1927 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1928 * @error: A #GError or %NULL
1929 *
1930 * Read a value from a buffer using a specified endian in a safe way.
1931 *
1932 * You don't need to use this function in "obviously correct" cases, nor should
1933 * you use it when performance is a concern. Only us it when you're not sure if
1934 * malicious data from a device or firmware could cause memory corruption.
1935 *
1936 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001937 *
1938 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001939 **/
1940gboolean
1941fu_common_read_uint32_safe (const guint8 *buf,
1942 gsize bufsz,
1943 gsize offset,
1944 guint32 *value,
1945 FuEndianType endian,
1946 GError **error)
1947{
1948 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001949 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001950 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001951 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001952 return FALSE;
1953 if (value != NULL)
1954 *value = fu_common_read_uint32 (dst, endian);
1955 return TRUE;
1956}
1957
Mario Limonciello1a680f32019-11-25 19:44:53 -06001958/**
1959 * fu_byte_array_append_uint8:
1960 * @array: A #GByteArray
1961 * @data: #guint8
1962 *
1963 * Adds a 8 bit integer to a byte array
1964 *
1965 * Since: 1.3.1
1966 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001967void
1968fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1969{
1970 g_byte_array_append (array, &data, sizeof(data));
1971}
1972
Mario Limonciello1a680f32019-11-25 19:44:53 -06001973/**
1974 * fu_byte_array_append_uint16:
1975 * @array: A #GByteArray
1976 * @data: #guint16
1977 * @endian: #FuEndianType
1978 *
1979 * Adds a 16 bit integer to a byte array
1980 *
1981 * Since: 1.3.1
1982 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001983void
1984fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1985{
1986 guint8 buf[2];
1987 fu_common_write_uint16 (buf, data, endian);
1988 g_byte_array_append (array, buf, sizeof(buf));
1989}
1990
Mario Limonciello1a680f32019-11-25 19:44:53 -06001991/**
1992 * fu_byte_array_append_uint32:
1993 * @array: A #GByteArray
1994 * @data: #guint32
1995 * @endian: #FuEndianType
1996 *
1997 * Adds a 32 bit integer to a byte array
1998 *
1999 * Since: 1.3.1
2000 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01002001void
2002fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
2003{
2004 guint8 buf[4];
2005 fu_common_write_uint32 (buf, data, endian);
2006 g_byte_array_append (array, buf, sizeof(buf));
2007}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06002008
2009/**
2010 * fu_common_kernel_locked_down:
2011 *
2012 * Determines if kernel lockdown in effect
2013 *
2014 * Since: 1.3.8
2015 **/
2016gboolean
2017fu_common_kernel_locked_down (void)
2018{
2019#ifndef _WIN32
2020 gsize len = 0;
2021 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2022 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2023 g_autofree gchar *data = NULL;
2024 g_auto(GStrv) options = NULL;
2025
2026 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2027 return FALSE;
2028 if (!g_file_get_contents (fname, &data, &len, NULL))
2029 return FALSE;
2030 if (len < 1)
2031 return FALSE;
2032 options = g_strsplit (data, " ", -1);
2033 for (guint i = 0; options[i] != NULL; i++) {
2034 if (g_strcmp0 (options[i], "[none]") == 0)
2035 return FALSE;
2036 }
2037 return TRUE;
2038#else
2039 return FALSE;
2040#endif
2041}
Richard Hughes9223c892020-05-09 20:32:08 +01002042
2043/**
2044 * fu_common_is_cpu_intel:
2045 *
2046 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2047 *
2048 * Return value: %TRUE if the vendor was Intel.
2049 *
2050 * Since: 1.5.0
2051 **/
2052gboolean
2053fu_common_is_cpu_intel (void)
2054{
Richard Hughesbd444322020-05-21 12:05:03 +01002055#ifdef HAVE_CPUID_H
Richard Hughes9223c892020-05-09 20:32:08 +01002056 guint eax = 0;
2057 guint ebx = 0;
2058 guint ecx = 0;
2059 guint edx = 0;
2060 guint level = 0;
2061
2062 /* get vendor */
2063 __get_cpuid(level, &eax, &ebx, &ecx, &edx);
2064 if (ebx == signature_INTEL_ebx &&
2065 edx == signature_INTEL_edx &&
2066 ecx == signature_INTEL_ecx) {
2067 return TRUE;
2068 }
Richard Hughesbd444322020-05-21 12:05:03 +01002069#endif
Richard Hughes9223c892020-05-09 20:32:08 +01002070 return FALSE;
2071}