blob: 790cd6b7cf4119032cc74573b09e831a6030b7b3 [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 Hughes4be17d12018-05-30 20:36:29 +01001061 /* /etc */
1062 case FU_PATH_KIND_SYSCONFDIR:
1063 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1064 if (tmp != NULL)
1065 return g_strdup (tmp);
1066 tmp = g_getenv ("SNAP_USER_DATA");
1067 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001068 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1069 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001070 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1071 case FU_PATH_KIND_PLUGINDIR_PKG:
1072 tmp = g_getenv ("FWUPD_PLUGINDIR");
1073 if (tmp != NULL)
1074 return g_strdup (tmp);
1075 tmp = g_getenv ("SNAP");
1076 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001077 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1078 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001079 /* /usr/share/fwupd */
1080 case FU_PATH_KIND_DATADIR_PKG:
1081 tmp = g_getenv ("FWUPD_DATADIR");
1082 if (tmp != NULL)
1083 return g_strdup (tmp);
1084 tmp = g_getenv ("SNAP");
1085 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001086 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1087 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001088 /* /usr/libexec/fwupd/efi */
1089 case FU_PATH_KIND_EFIAPPDIR:
1090 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1091 if (tmp != NULL)
1092 return g_strdup (tmp);
1093#ifdef EFI_APP_LOCATION
1094 tmp = g_getenv ("SNAP");
1095 if (tmp != NULL)
1096 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1097 return g_strdup (EFI_APP_LOCATION);
1098#else
1099 return NULL;
1100#endif
Richard Hughesb9640a22020-05-05 20:42:47 +01001101 /* /usr/share/fwupd/dbx */
1102 case FU_PATH_KIND_EFIDBXDIR:
1103 tmp = g_getenv ("FWUPD_EFIDBXDIR");
1104 if (tmp != NULL)
1105 return g_strdup (tmp);
1106#ifdef FWUPD_EFI_DBXDIR
1107 tmp = g_getenv ("SNAP");
1108 if (tmp != NULL)
1109 return g_build_filename (tmp, FWUPD_EFI_DBXDIR, NULL);
1110 return g_strdup (FWUPD_EFI_DBXDIR);
1111#else
1112 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
1113 return g_build_filename (basedir, "dbx", NULL);
1114#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001115 /* /etc/fwupd */
1116 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001117 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001118 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001119 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001120 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1121 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1122 /* /var/lib/fwupd */
1123 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001124 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001125 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001126 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001127 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1128 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1129 /* /var/cache/fwupd */
1130 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001131 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001132 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001133 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001134 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1135 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001136 case FU_PATH_KIND_OFFLINE_TRIGGER:
1137 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1138 if (tmp != NULL)
1139 return g_strdup (tmp);
1140 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001141 case FU_PATH_KIND_POLKIT_ACTIONS:
1142#ifdef POLKIT_ACTIONDIR
1143 return g_strdup (POLKIT_ACTIONDIR);
1144#else
1145 return NULL;
1146#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001147 /* this shouldn't happen */
1148 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001149 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001150 }
1151
1152 return NULL;
1153}
Richard Hughes83e56c12018-10-10 20:24:41 +01001154
1155/**
1156 * fu_common_string_replace:
1157 * @string: The #GString to operate on
1158 * @search: The text to search for
1159 * @replace: The text to use for substitutions
1160 *
1161 * Performs multiple search and replace operations on the given string.
1162 *
1163 * Returns: the number of replacements done, or 0 if @search is not found.
1164 *
1165 * Since: 1.2.0
1166 **/
1167guint
1168fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1169{
1170 gchar *tmp;
1171 guint count = 0;
1172 gsize search_idx = 0;
1173 gsize replace_len;
1174 gsize search_len;
1175
1176 g_return_val_if_fail (string != NULL, 0);
1177 g_return_val_if_fail (search != NULL, 0);
1178 g_return_val_if_fail (replace != NULL, 0);
1179
1180 /* nothing to do */
1181 if (string->len == 0)
1182 return 0;
1183
1184 search_len = strlen (search);
1185 replace_len = strlen (replace);
1186
1187 do {
1188 tmp = g_strstr_len (string->str + search_idx, -1, search);
1189 if (tmp == NULL)
1190 break;
1191
1192 /* advance the counter in case @replace contains @search */
1193 search_idx = (gsize) (tmp - string->str);
1194
1195 /* reallocate the string if required */
1196 if (search_len > replace_len) {
1197 g_string_erase (string,
1198 (gssize) search_idx,
1199 (gssize) (search_len - replace_len));
1200 memcpy (tmp, replace, replace_len);
1201 } else if (search_len < replace_len) {
1202 g_string_insert_len (string,
1203 (gssize) search_idx,
1204 replace,
1205 (gssize) (replace_len - search_len));
1206 /* we have to treat this specially as it could have
1207 * been reallocated when the insertion happened */
1208 memcpy (string->str + search_idx, replace, replace_len);
1209 } else {
1210 /* just memcmp in the new string */
1211 memcpy (tmp, replace, replace_len);
1212 }
1213 search_idx += replace_len;
1214 count++;
1215 } while (TRUE);
1216
1217 return count;
1218}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001219
Richard Hughesae96a1f2019-09-23 11:16:36 +01001220/**
1221 * fu_common_strwidth:
1222 * @text: The string to operate on
1223 *
1224 * Returns the width of the string in displayed characters on the console.
1225 *
1226 * Returns: width of text
1227 *
1228 * Since: 1.3.2
1229 **/
1230gsize
1231fu_common_strwidth (const gchar *text)
1232{
1233 const gchar *p = text;
1234 gsize width = 0;
1235 while (*p) {
1236 gunichar c = g_utf8_get_char (p);
1237 if (g_unichar_iswide (c))
1238 width += 2;
1239 else if (!g_unichar_iszerowidth (c))
1240 width += 1;
1241 p = g_utf8_next_char (p);
1242 }
1243 return width;
1244}
1245
Mario Limonciello1a680f32019-11-25 19:44:53 -06001246/**
1247 * fu_common_string_append_kv:
1248 * @str: A #GString
1249 * @idt: The indent
1250 * @key: A string to append
1251 * @value: a string to append
1252 *
1253 * Appends a key and string value to a string
1254 *
1255 * Since: 1.2.4
1256 */
Richard Hughescea28de2019-08-09 11:16:40 +01001257void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001258fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001259{
Richard Hughes847cae82019-08-27 11:22:23 +01001260 const guint align = 25;
1261 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001262
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001263 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001264
1265 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001266 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001267 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001268 for (gsize i = 0; i < idt; i++)
1269 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001270 if (key[0] != '\0') {
1271 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001272 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001273 } else {
1274 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001275 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001276 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001277 g_auto(GStrv) split = NULL;
1278 split = g_strsplit (value, "\n", -1);
1279 for (guint i = 0; split[i] != NULL; i++) {
1280 if (i == 0) {
1281 for (gsize j = keysz; j < align; j++)
1282 g_string_append (str, " ");
1283 } else {
1284 for (gsize j = 0; j < idt; j++)
1285 g_string_append (str, " ");
1286 }
1287 g_string_append (str, split[i]);
1288 g_string_append (str, "\n");
1289 }
1290 } else {
1291 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001292 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001293}
1294
Mario Limonciello1a680f32019-11-25 19:44:53 -06001295/**
1296 * fu_common_string_append_ku:
1297 * @str: A #GString
1298 * @idt: The indent
1299 * @key: A string to append
1300 * @value: guint64
1301 *
1302 * Appends a key and unsigned integer to a string
1303 *
1304 * Since: 1.2.4
1305 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001306void
1307fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1308{
1309 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1310 fu_common_string_append_kv (str, idt, key, tmp);
1311}
1312
Mario Limonciello1a680f32019-11-25 19:44:53 -06001313/**
1314 * fu_common_string_append_kx:
1315 * @str: A #GString
1316 * @idt: The indent
1317 * @key: A string to append
1318 * @value: guint64
1319 *
1320 * Appends a key and hex integer to a string
1321 *
1322 * Since: 1.2.4
1323 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001324void
1325fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1326{
1327 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1328 fu_common_string_append_kv (str, idt, key, tmp);
1329}
1330
Mario Limonciello1a680f32019-11-25 19:44:53 -06001331/**
1332 * fu_common_string_append_kb:
1333 * @str: A #GString
1334 * @idt: The indent
1335 * @key: A string to append
1336 * @value: Boolean
1337 *
1338 * Appends a key and boolean value to a string
1339 *
1340 * Since: 1.2.4
1341 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001342void
1343fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1344{
1345 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001346}
1347
Richard Hughese59cb9a2018-12-05 14:37:40 +00001348/**
Richard Hughes35481862019-01-06 12:01:58 +00001349 * fu_common_dump_full:
1350 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1351 * @title: prefix title, or %NULL
1352 * @data: buffer to print
1353 * @len: the size of @data
1354 * @columns: break new lines after this many bytes
1355 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1356 *
1357 * Dumps a raw buffer to the screen.
1358 *
1359 * Since: 1.2.4
1360 **/
1361void
1362fu_common_dump_full (const gchar *log_domain,
1363 const gchar *title,
1364 const guint8 *data,
1365 gsize len,
1366 guint columns,
1367 FuDumpFlags flags)
1368{
1369 g_autoptr(GString) str = g_string_new (NULL);
1370
1371 /* optional */
1372 if (title != NULL)
1373 g_string_append_printf (str, "%s:", title);
1374
1375 /* if more than can fit on one line then start afresh */
1376 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1377 g_string_append (str, "\n");
1378 } else {
1379 for (gsize i = str->len; i < 16; i++)
1380 g_string_append (str, " ");
1381 }
1382
1383 /* offset line */
1384 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1385 g_string_append (str, " │ ");
1386 for (gsize i = 0; i < columns; i++)
1387 g_string_append_printf (str, "%02x ", (guint) i);
1388 g_string_append (str, "\n───────┼");
1389 for (gsize i = 0; i < columns; i++)
1390 g_string_append (str, "───");
1391 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1392 }
1393
1394 /* print each row */
1395 for (gsize i = 0; i < len; i++) {
1396 g_string_append_printf (str, "%02x ", data[i]);
1397
1398 /* optionally print ASCII char */
1399 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1400 if (g_ascii_isprint (data[i]))
1401 g_string_append_printf (str, "[%c] ", data[i]);
1402 else
1403 g_string_append (str, "[?] ");
1404 }
1405
1406 /* new row required */
1407 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1408 g_string_append (str, "\n");
1409 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1410 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1411 }
1412 }
1413 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1414}
1415
1416/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001417 * fu_common_dump_raw:
1418 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1419 * @title: prefix title, or %NULL
1420 * @data: buffer to print
1421 * @len: the size of @data
1422 *
1423 * Dumps a raw buffer to the screen.
1424 *
1425 * Since: 1.2.2
1426 **/
1427void
1428fu_common_dump_raw (const gchar *log_domain,
1429 const gchar *title,
1430 const guint8 *data,
1431 gsize len)
1432{
Richard Hughes35481862019-01-06 12:01:58 +00001433 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1434 if (len > 64)
1435 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1436 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001437}
1438
1439/**
Mario Limonciello39602652019-04-29 21:08:58 -05001440 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001441 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1442 * @title: prefix title, or %NULL
1443 * @bytes: a #GBytes
1444 *
1445 * Dumps a byte buffer to the screen.
1446 *
1447 * Since: 1.2.2
1448 **/
1449void
1450fu_common_dump_bytes (const gchar *log_domain,
1451 const gchar *title,
1452 GBytes *bytes)
1453{
1454 gsize len = 0;
1455 const guint8 *data = g_bytes_get_data (bytes, &len);
1456 fu_common_dump_raw (log_domain, title, data, len);
1457}
Richard Hughesfc90f392019-01-15 21:21:16 +00001458
1459/**
1460 * fu_common_bytes_align:
1461 * @bytes: a #GBytes
1462 * @blksz: block size in bytes
1463 * @padval: the byte used to pad the byte buffer
1464 *
1465 * Aligns a block of memory to @blksize using the @padval value; if
1466 * the block is already aligned then the original @bytes is returned.
1467 *
1468 * Returns: (transfer full): a #GBytes, possibly @bytes
1469 *
1470 * Since: 1.2.4
1471 **/
1472GBytes *
1473fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1474{
1475 const guint8 *data;
1476 gsize sz;
1477
1478 g_return_val_if_fail (bytes != NULL, NULL);
1479 g_return_val_if_fail (blksz > 0, NULL);
1480
1481 /* pad */
1482 data = g_bytes_get_data (bytes, &sz);
1483 if (sz % blksz != 0) {
1484 gsize sz_align = ((sz / blksz) + 1) * blksz;
1485 guint8 *data_align = g_malloc (sz_align);
1486 memcpy (data_align, data, sz);
1487 memset (data_align + sz, padval, sz_align - sz);
1488 g_debug ("aligning 0x%x bytes to 0x%x",
1489 (guint) sz, (guint) sz_align);
1490 return g_bytes_new_take (data_align, sz_align);
1491 }
1492
1493 /* perfectly aligned */
1494 return g_bytes_ref (bytes);
1495}
Richard Hughes36999462019-03-19 20:23:29 +00001496
1497/**
1498 * fu_common_bytes_is_empty:
1499 * @bytes: a #GBytes
1500 *
1501 * Checks if a byte array are just empty (0xff) bytes.
1502 *
1503 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001504 *
1505 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001506 **/
1507gboolean
1508fu_common_bytes_is_empty (GBytes *bytes)
1509{
1510 gsize sz = 0;
1511 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1512 for (gsize i = 0; i < sz; i++) {
1513 if (buf[i] != 0xff)
1514 return FALSE;
1515 }
1516 return TRUE;
1517}
Richard Hughes2aad1042019-03-21 09:03:32 +00001518
1519/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001520 * fu_common_bytes_compare_raw:
1521 * @buf1: a buffer
1522 * @bufsz1: sizeof @buf1
1523 * @buf2: another buffer
1524 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001525 * @error: A #GError or %NULL
1526 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001527 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001528 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001529 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001530 *
1531 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001532 **/
1533gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001534fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1535 const guint8 *buf2, gsize bufsz2,
1536 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001537{
Richard Hughes38245ff2019-09-18 14:46:09 +01001538 g_return_val_if_fail (buf1 != NULL, FALSE);
1539 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001540 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1541
1542 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001543 if (bufsz1 != bufsz2) {
1544 g_set_error (error,
1545 G_IO_ERROR,
1546 G_IO_ERROR_INVALID_DATA,
1547 "got %" G_GSIZE_FORMAT " bytes, expected "
1548 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1549 return FALSE;
1550 }
1551
1552 /* check matches */
1553 for (guint i = 0x0; i < bufsz1; i++) {
1554 if (buf1[i] != buf2[i]) {
1555 g_set_error (error,
1556 G_IO_ERROR,
1557 G_IO_ERROR_INVALID_DATA,
1558 "got 0x%02x, expected 0x%02x @ 0x%04x",
1559 buf1[i], buf2[i], i);
1560 return FALSE;
1561 }
1562 }
1563
1564 /* success */
1565 return TRUE;
1566}
Richard Hughes484ee292019-03-22 16:10:50 +00001567
1568/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001569 * fu_common_bytes_compare:
1570 * @bytes1: a #GBytes
1571 * @bytes2: another #GBytes
1572 * @error: A #GError or %NULL
1573 *
1574 * Compares the buffers for equality.
1575 *
1576 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001577 *
1578 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001579 **/
1580gboolean
1581fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1582{
1583 const guint8 *buf1;
1584 const guint8 *buf2;
1585 gsize bufsz1;
1586 gsize bufsz2;
1587
1588 g_return_val_if_fail (bytes1 != NULL, FALSE);
1589 g_return_val_if_fail (bytes2 != NULL, FALSE);
1590 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1591
1592 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1593 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1594 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1595}
1596
1597/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001598 * fu_common_bytes_pad:
1599 * @bytes: a #GBytes
1600 * @sz: the desired size in bytes
1601 *
1602 * Pads a GBytes to a given @sz with `0xff`.
1603 *
1604 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001605 *
1606 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001607 **/
1608GBytes *
1609fu_common_bytes_pad (GBytes *bytes, gsize sz)
1610{
1611 gsize bytes_sz;
1612
1613 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1614
1615 /* pad */
1616 bytes_sz = g_bytes_get_size (bytes);
1617 if (bytes_sz < sz) {
1618 const guint8 *data = g_bytes_get_data (bytes, NULL);
1619 guint8 *data_new = g_malloc (sz);
1620 memcpy (data_new, data, bytes_sz);
1621 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1622 return g_bytes_new_take (data_new, sz);
1623 }
1624
1625 /* exactly right */
1626 return g_bytes_ref (bytes);
1627}
1628
1629/**
Richard Hughes484ee292019-03-22 16:10:50 +00001630 * fu_common_realpath:
1631 * @filename: a filename
1632 * @error: A #GError or %NULL
1633 *
1634 * Finds the canonicalized absolute filename for a path.
1635 *
1636 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001637 *
1638 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001639 **/
1640gchar *
1641fu_common_realpath (const gchar *filename, GError **error)
1642{
1643 char full_tmp[PATH_MAX];
1644
1645 g_return_val_if_fail (filename != NULL, NULL);
1646
Richard Hughes8694dee2019-11-22 09:16:34 +00001647#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001648 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001649#else
1650 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1651#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001652 g_set_error (error,
1653 G_IO_ERROR,
1654 G_IO_ERROR_INVALID_DATA,
1655 "cannot resolve path: %s",
1656 strerror (errno));
1657 return NULL;
1658 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001659 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1660 g_set_error (error,
1661 G_IO_ERROR,
1662 G_IO_ERROR_INVALID_DATA,
1663 "cannot find path: %s",
1664 full_tmp);
1665 return NULL;
1666 }
Richard Hughes484ee292019-03-22 16:10:50 +00001667 return g_strdup (full_tmp);
1668}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001669
1670/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001671 * fu_common_fnmatch:
1672 * @pattern: a glob pattern, e.g. `*foo*`
1673 * @str: a string to match against the pattern, e.g. `bazfoobar`
1674 *
1675 * Matches a string against a glob pattern.
1676 *
1677 * Return value: %TRUE if the string matched
1678 *
1679 * Since: 1.3.5
1680 **/
1681gboolean
1682fu_common_fnmatch (const gchar *pattern, const gchar *str)
1683{
1684 g_return_val_if_fail (pattern != NULL, FALSE);
1685 g_return_val_if_fail (str != NULL, FALSE);
1686#ifdef HAVE_FNMATCH_H
1687 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001688#elif _WIN32
1689 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1690 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1691 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001692#else
1693 return g_strcmp0 (pattern, str) == 0;
1694#endif
1695}
1696
Richard Hughesa84d7a72020-05-06 12:11:51 +01001697static gint
1698fu_common_filename_glob_sort_cb (gconstpointer a, gconstpointer b)
1699{
1700 return g_strcmp0 (*(const gchar **)a, *(const gchar **)b);
1701}
1702
1703/**
1704 * fu_common_filename_glob:
1705 * @directory: a directory path
1706 * @pattern: a glob pattern, e.g. `*foo*`
1707 * @error: A #GError or %NULL
1708 *
1709 * Returns all the filenames that match a specific glob pattern.
1710 * Any results are sorted. No matching files will set @error.
1711 *
1712 * Return value: (element-type utf8) (transfer container): matching files, or %NULL
1713 *
1714 * Since: 1.5.0
1715 **/
1716GPtrArray *
1717fu_common_filename_glob (const gchar *directory, const gchar *pattern, GError **error)
1718{
1719 const gchar *basename;
1720 g_autoptr(GDir) dir = g_dir_open (directory, 0, error);
1721 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
1722 if (dir == NULL)
1723 return NULL;
1724 while ((basename = g_dir_read_name (dir)) != NULL) {
1725 if (!fu_common_fnmatch (pattern, basename))
1726 continue;
1727 g_ptr_array_add (files, g_build_filename (directory, basename, NULL));
1728 }
1729 if (files->len == 0) {
1730 g_set_error_literal (error,
1731 G_IO_ERROR,
1732 G_IO_ERROR_NOT_FOUND,
1733 "no files matched pattern");
1734 return NULL;
1735 }
1736 g_ptr_array_sort (files, fu_common_filename_glob_sort_cb);
1737 return g_steal_pointer (&files);
1738}
1739
Richard Hughes5c508de2019-11-22 09:57:34 +00001740/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001741 * fu_common_strnsplit:
1742 * @str: a string to split
1743 * @sz: size of @str
1744 * @delimiter: a string which specifies the places at which to split the string
1745 * @max_tokens: the maximum number of pieces to split @str into
1746 *
1747 * Splits a string into a maximum of @max_tokens pieces, using the given
1748 * delimiter. If @max_tokens is reached, the remainder of string is appended
1749 * to the last token.
1750 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001751 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001752 *
1753 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001754 **/
1755gchar **
1756fu_common_strnsplit (const gchar *str, gsize sz,
1757 const gchar *delimiter, gint max_tokens)
1758{
1759 if (str[sz - 1] != '\0') {
1760 g_autofree gchar *str2 = g_strndup (str, sz);
1761 return g_strsplit (str2, delimiter, max_tokens);
1762 }
1763 return g_strsplit (str, delimiter, max_tokens);
1764}
Richard Hughes5308ea42019-08-09 12:25:13 +01001765
1766/**
1767 * fu_memcpy_safe:
1768 * @dst: destination buffer
1769 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1770 * @dst_offset: offset in bytes into @dst to copy to
1771 * @src: source buffer
1772 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1773 * @src_offset: offset in bytes into @src to copy from
1774 * @n: number of bytes to copy from @src+@offset from
1775 * @error: A #GError or %NULL
1776 *
1777 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1778 * of both the destination and the source allows us to check for buffer overflow.
1779 *
1780 * Providing the buffer offsets also allows us to check reading past the end of
1781 * the source buffer. For this reason the caller should NEVER add an offset to
1782 * @src or @dst.
1783 *
1784 * You don't need to use this function in "obviously correct" cases, nor should
1785 * you use it when performance is a concern. Only us it when you're not sure if
1786 * malicious data from a device or firmware could cause memory corruption.
1787 *
1788 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001789 *
1790 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001791 **/
1792gboolean
1793fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1794 const guint8 *src, gsize src_sz, gsize src_offset,
1795 gsize n, GError **error)
1796{
1797 if (n == 0)
1798 return TRUE;
1799
1800 if (n > src_sz) {
1801 g_set_error (error,
1802 FWUPD_ERROR,
1803 FWUPD_ERROR_READ,
1804 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1805 (guint) n, (guint) src_sz);
1806 return FALSE;
1807 }
1808 if (n + src_offset > src_sz) {
1809 g_set_error (error,
1810 FWUPD_ERROR,
1811 FWUPD_ERROR_READ,
1812 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1813 (guint) n, (guint) src_offset, (guint) src_sz);
1814 return FALSE;
1815 }
1816 if (n > dst_sz) {
1817 g_set_error (error,
1818 FWUPD_ERROR,
1819 FWUPD_ERROR_WRITE,
1820 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1821 (guint) n, (guint) dst_sz);
1822 return FALSE;
1823 }
1824 if (n + dst_offset > dst_sz) {
1825 g_set_error (error,
1826 FWUPD_ERROR,
1827 FWUPD_ERROR_WRITE,
1828 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1829 (guint) n, (guint) dst_offset, (guint) dst_sz);
1830 return FALSE;
1831 }
1832
1833 /* phew! */
1834 memcpy (dst + dst_offset, src + src_offset, n);
1835 return TRUE;
1836}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001837
Richard Hughes80768f52019-10-22 07:19:14 +01001838/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001839 * fu_common_read_uint8_safe:
1840 * @buf: source buffer
1841 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1842 * @offset: offset in bytes into @buf to copy from
1843 * @value: (out) (allow-none): the parsed value
1844 * @error: A #GError or %NULL
1845 *
1846 * Read a value from a buffer in a safe way.
1847 *
1848 * You don't need to use this function in "obviously correct" cases, nor should
1849 * you use it when performance is a concern. Only us it when you're not sure if
1850 * malicious data from a device or firmware could cause memory corruption.
1851 *
1852 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001853 *
1854 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001855 **/
1856gboolean
1857fu_common_read_uint8_safe (const guint8 *buf,
1858 gsize bufsz,
1859 gsize offset,
1860 guint8 *value,
1861 GError **error)
1862{
1863 guint8 tmp;
1864 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1865 buf, bufsz, offset, /* src */
1866 sizeof(tmp), error))
1867 return FALSE;
1868 if (value != NULL)
1869 *value = tmp;
1870 return TRUE;
1871}
1872
1873/**
Richard Hughes80768f52019-10-22 07:19:14 +01001874 * fu_common_read_uint16_safe:
1875 * @buf: source buffer
1876 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1877 * @offset: offset in bytes into @buf to copy from
1878 * @value: (out) (allow-none): the parsed value
1879 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1880 * @error: A #GError or %NULL
1881 *
1882 * Read a value from a buffer using a specified endian in a safe way.
1883 *
1884 * You don't need to use this function in "obviously correct" cases, nor should
1885 * you use it when performance is a concern. Only us it when you're not sure if
1886 * malicious data from a device or firmware could cause memory corruption.
1887 *
1888 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001889 *
1890 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001891 **/
1892gboolean
1893fu_common_read_uint16_safe (const guint8 *buf,
1894 gsize bufsz,
1895 gsize offset,
1896 guint16 *value,
1897 FuEndianType endian,
1898 GError **error)
1899{
1900 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001901 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001902 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001903 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001904 return FALSE;
1905 if (value != NULL)
1906 *value = fu_common_read_uint16 (dst, endian);
1907 return TRUE;
1908}
1909
1910/**
1911 * fu_common_read_uint32_safe:
1912 * @buf: source buffer
1913 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1914 * @offset: offset in bytes into @buf to copy from
1915 * @value: (out) (allow-none): the parsed value
1916 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1917 * @error: A #GError or %NULL
1918 *
1919 * Read a value from a buffer using a specified endian in a safe way.
1920 *
1921 * You don't need to use this function in "obviously correct" cases, nor should
1922 * you use it when performance is a concern. Only us it when you're not sure if
1923 * malicious data from a device or firmware could cause memory corruption.
1924 *
1925 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001926 *
1927 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001928 **/
1929gboolean
1930fu_common_read_uint32_safe (const guint8 *buf,
1931 gsize bufsz,
1932 gsize offset,
1933 guint32 *value,
1934 FuEndianType endian,
1935 GError **error)
1936{
1937 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001938 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001939 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001940 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001941 return FALSE;
1942 if (value != NULL)
1943 *value = fu_common_read_uint32 (dst, endian);
1944 return TRUE;
1945}
1946
Mario Limonciello1a680f32019-11-25 19:44:53 -06001947/**
1948 * fu_byte_array_append_uint8:
1949 * @array: A #GByteArray
1950 * @data: #guint8
1951 *
1952 * Adds a 8 bit integer to a byte array
1953 *
1954 * Since: 1.3.1
1955 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001956void
1957fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1958{
1959 g_byte_array_append (array, &data, sizeof(data));
1960}
1961
Mario Limonciello1a680f32019-11-25 19:44:53 -06001962/**
1963 * fu_byte_array_append_uint16:
1964 * @array: A #GByteArray
1965 * @data: #guint16
1966 * @endian: #FuEndianType
1967 *
1968 * Adds a 16 bit integer to a byte array
1969 *
1970 * Since: 1.3.1
1971 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001972void
1973fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1974{
1975 guint8 buf[2];
1976 fu_common_write_uint16 (buf, data, endian);
1977 g_byte_array_append (array, buf, sizeof(buf));
1978}
1979
Mario Limonciello1a680f32019-11-25 19:44:53 -06001980/**
1981 * fu_byte_array_append_uint32:
1982 * @array: A #GByteArray
1983 * @data: #guint32
1984 * @endian: #FuEndianType
1985 *
1986 * Adds a 32 bit integer to a byte array
1987 *
1988 * Since: 1.3.1
1989 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001990void
1991fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1992{
1993 guint8 buf[4];
1994 fu_common_write_uint32 (buf, data, endian);
1995 g_byte_array_append (array, buf, sizeof(buf));
1996}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001997
1998/**
1999 * fu_common_kernel_locked_down:
2000 *
2001 * Determines if kernel lockdown in effect
2002 *
2003 * Since: 1.3.8
2004 **/
2005gboolean
2006fu_common_kernel_locked_down (void)
2007{
2008#ifndef _WIN32
2009 gsize len = 0;
2010 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
2011 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
2012 g_autofree gchar *data = NULL;
2013 g_auto(GStrv) options = NULL;
2014
2015 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
2016 return FALSE;
2017 if (!g_file_get_contents (fname, &data, &len, NULL))
2018 return FALSE;
2019 if (len < 1)
2020 return FALSE;
2021 options = g_strsplit (data, " ", -1);
2022 for (guint i = 0; options[i] != NULL; i++) {
2023 if (g_strcmp0 (options[i], "[none]") == 0)
2024 return FALSE;
2025 }
2026 return TRUE;
2027#else
2028 return FALSE;
2029#endif
2030}
Richard Hughes9223c892020-05-09 20:32:08 +01002031
2032/**
2033 * fu_common_is_cpu_intel:
2034 *
2035 * Uses CPUID to discover the CPU vendor and check if it is Intel.
2036 *
2037 * Return value: %TRUE if the vendor was Intel.
2038 *
2039 * Since: 1.5.0
2040 **/
2041gboolean
2042fu_common_is_cpu_intel (void)
2043{
2044 guint eax = 0;
2045 guint ebx = 0;
2046 guint ecx = 0;
2047 guint edx = 0;
2048 guint level = 0;
2049
2050 /* get vendor */
2051 __get_cpuid(level, &eax, &ebx, &ecx, &edx);
2052 if (ebx == signature_INTEL_ebx &&
2053 edx == signature_INTEL_edx &&
2054 ecx == signature_INTEL_ecx) {
2055 return TRUE;
2056 }
2057 return FALSE;
2058}