blob: 81dfa3296ff66faf3068929da9c1480059c16e5d [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 Hughes94f939a2017-08-08 12:21:39 +010022#include <archive_entry.h>
23#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010024#include <errno.h>
Richard Hughes484ee292019-03-22 16:10:50 +000025#include <limits.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000026#include <string.h>
Richard Hughes484ee292019-03-22 16:10:50 +000027#include <stdlib.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010028
29#include "fwupd-error.h"
30
31#include "fu-common.h"
32
33/**
Richard Hughes4eada342017-10-03 21:20:32 +010034 * SECTION:fu-common
35 * @short_description: common functionality for plugins to use
36 *
37 * Helper functions that can be used by the daemon and plugins.
38 *
39 * See also: #FuPlugin
40 */
41
42/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010043 * fu_common_rmtree:
44 * @directory: a directory name
45 * @error: A #GError or %NULL
46 *
47 * Recursively removes a directory.
48 *
49 * Returns: %TRUE for success, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -060050 *
51 * Since: 0.9.7
Richard Hughes954dd9f2017-08-08 13:36:25 +010052 **/
53gboolean
54fu_common_rmtree (const gchar *directory, GError **error)
55{
56 const gchar *filename;
57 g_autoptr(GDir) dir = NULL;
58
59 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010060 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010061 dir = g_dir_open (directory, 0, error);
62 if (dir == NULL)
63 return FALSE;
64
65 /* find each */
66 while ((filename = g_dir_read_name (dir))) {
67 g_autofree gchar *src = NULL;
68 src = g_build_filename (directory, filename, NULL);
69 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
70 if (!fu_common_rmtree (src, error))
71 return FALSE;
72 } else {
73 if (g_unlink (src) != 0) {
74 g_set_error (error,
75 FWUPD_ERROR,
76 FWUPD_ERROR_INTERNAL,
77 "Failed to delete: %s", src);
78 return FALSE;
79 }
80 }
81 }
82 if (g_remove (directory) != 0) {
83 g_set_error (error,
84 FWUPD_ERROR,
85 FWUPD_ERROR_INTERNAL,
86 "Failed to delete: %s", directory);
87 return FALSE;
88 }
89 return TRUE;
90}
91
Richard Hughes89e968b2018-03-07 10:01:08 +000092static gboolean
93fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
94{
95 const gchar *filename;
96 g_autoptr(GDir) dir = NULL;
97
98 /* try to open */
99 dir = g_dir_open (directory, 0, error);
100 if (dir == NULL)
101 return FALSE;
102
103 /* find each */
104 while ((filename = g_dir_read_name (dir))) {
105 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
106 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
107 if (!fu_common_get_file_list_internal (files, src, error))
108 return FALSE;
109 } else {
110 g_ptr_array_add (files, g_steal_pointer (&src));
111 }
112 }
113 return TRUE;
114
115}
116
117/**
118 * fu_common_get_files_recursive:
Richard Hughes8aa72392018-05-02 08:38:43 +0100119 * @path: a directory name
Richard Hughes89e968b2018-03-07 10:01:08 +0000120 * @error: A #GError or %NULL
121 *
122 * Returns every file found under @directory, and any subdirectory.
123 * If any path under @directory cannot be accessed due to permissions an error
124 * will be returned.
125 *
Richard Hughesa0d81c72019-11-27 11:41:54 +0000126 * Returns: (transfer container) (element-type utf8): array of files, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600127 *
128 * Since: 1.0.6
Richard Hughes89e968b2018-03-07 10:01:08 +0000129 **/
130GPtrArray *
131fu_common_get_files_recursive (const gchar *path, GError **error)
132{
133 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
134 if (!fu_common_get_file_list_internal (files, path, error))
135 return NULL;
136 return g_steal_pointer (&files);
137}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100138/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100139 * fu_common_mkdir_parent:
140 * @filename: A full pathname
141 * @error: A #GError, or %NULL
142 *
143 * Creates any required directories, including any parent directories.
144 *
145 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600146 *
147 * Since: 0.9.7
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100148 **/
149gboolean
150fu_common_mkdir_parent (const gchar *filename, GError **error)
151{
152 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100153
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100154 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100155 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100156 if (g_mkdir_with_parents (parent, 0755) == -1) {
157 g_set_error (error,
158 FWUPD_ERROR,
159 FWUPD_ERROR_INTERNAL,
160 "Failed to create '%s': %s",
161 parent, g_strerror (errno));
162 return FALSE;
163 }
164 return TRUE;
165}
166
167/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100168 * fu_common_set_contents_bytes:
169 * @filename: A filename
170 * @bytes: The data to write
171 * @error: A #GError, or %NULL
172 *
173 * Writes a blob of data to a filename, creating the parent directories as
174 * required.
175 *
176 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600177 *
178 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100179 **/
180gboolean
181fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
182{
183 const gchar *data;
184 gsize size;
185 g_autoptr(GFile) file = NULL;
186 g_autoptr(GFile) file_parent = NULL;
187
188 file = g_file_new_for_path (filename);
189 file_parent = g_file_get_parent (file);
190 if (!g_file_query_exists (file_parent, NULL)) {
191 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
192 return FALSE;
193 }
194 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100195 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100196 return g_file_set_contents (filename, data, size, error);
197}
198
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100199/**
200 * fu_common_get_contents_bytes:
201 * @filename: A filename
202 * @error: A #GError, or %NULL
203 *
204 * Reads a blob of data from a file.
205 *
206 * Returns: a #GBytes, or %NULL for failure
Mario Limonciello1a680f32019-11-25 19:44:53 -0600207 *
208 * Since: 0.9.7
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100209 **/
210GBytes *
211fu_common_get_contents_bytes (const gchar *filename, GError **error)
212{
213 gchar *data = NULL;
214 gsize len = 0;
215 if (!g_file_get_contents (filename, &data, &len, error))
216 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100217 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100218 return g_bytes_new_take (data, len);
219}
Richard Hughes943d2c92017-06-21 09:04:39 +0100220
221/**
222 * fu_common_get_contents_fd:
223 * @fd: A file descriptor
224 * @count: The maximum number of bytes to read
225 * @error: A #GError, or %NULL
226 *
227 * Reads a blob from a specific file descriptor.
228 *
229 * Note: this will close the fd when done
230 *
Richard Hughes4eada342017-10-03 21:20:32 +0100231 * Returns: (transfer full): a #GBytes, or %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600232 *
233 * Since: 0.9.5
Richard Hughes943d2c92017-06-21 09:04:39 +0100234 **/
235GBytes *
236fu_common_get_contents_fd (gint fd, gsize count, GError **error)
237{
Richard Hughes9e5675e2019-11-22 09:35:03 +0000238#ifdef HAVE_GIO_UNIX
Richard Hughes943d2c92017-06-21 09:04:39 +0100239 g_autoptr(GBytes) blob = NULL;
240 g_autoptr(GError) error_local = NULL;
241 g_autoptr(GInputStream) stream = NULL;
242
243 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100244 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
245
Richard Hughes919f8ab2018-02-14 10:24:56 +0000246 /* this is invalid */
247 if (count == 0) {
248 g_set_error_literal (error,
249 FWUPD_ERROR,
250 FWUPD_ERROR_NOT_SUPPORTED,
251 "A maximum read size must be specified");
252 return NULL;
253 }
254
Richard Hughes943d2c92017-06-21 09:04:39 +0100255 /* read the entire fd to a data blob */
256 stream = g_unix_input_stream_new (fd, TRUE);
257 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
258 if (blob == NULL) {
259 g_set_error_literal (error,
260 FWUPD_ERROR,
261 FWUPD_ERROR_INVALID_FILE,
262 error_local->message);
263 return NULL;
264 }
265 return g_steal_pointer (&blob);
Richard Hughes9e5675e2019-11-22 09:35:03 +0000266#else
267 g_set_error_literal (error,
268 FWUPD_ERROR,
269 FWUPD_ERROR_NOT_SUPPORTED,
270 "Not supported as <glib-unix.h> is unavailable");
271 return NULL;
272#endif
Richard Hughes943d2c92017-06-21 09:04:39 +0100273}
Richard Hughes94f939a2017-08-08 12:21:39 +0100274
275static gboolean
276fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
277{
278 const gchar *tmp;
279 g_autofree gchar *buf = NULL;
280
281 /* no output file */
282 if (archive_entry_pathname (entry) == NULL)
283 return FALSE;
284
285 /* update output path */
286 tmp = archive_entry_pathname (entry);
287 buf = g_build_filename (dir, tmp, NULL);
288 archive_entry_update_pathname_utf8 (entry, buf);
289 return TRUE;
290}
291
292/**
293 * fu_common_extract_archive:
294 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100295 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100296 * @error: A #GError, or %NULL
297 *
Richard Hughes21eaeef2020-01-14 12:10:01 +0000298 * Extracts an archive to a directory.
Richard Hughes94f939a2017-08-08 12:21:39 +0100299 *
300 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600301 *
302 * Since: 0.9.7
Richard Hughes94f939a2017-08-08 12:21:39 +0100303 **/
304gboolean
305fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
306{
307 gboolean ret = TRUE;
308 int r;
309 struct archive *arch = NULL;
310 struct archive_entry *entry;
311
312 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100313 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100314 arch = archive_read_new ();
315 archive_read_support_format_all (arch);
316 archive_read_support_filter_all (arch);
317 r = archive_read_open_memory (arch,
318 (void *) g_bytes_get_data (blob, NULL),
319 (size_t) g_bytes_get_size (blob));
320 if (r != 0) {
321 ret = FALSE;
322 g_set_error (error,
323 FWUPD_ERROR,
324 FWUPD_ERROR_INTERNAL,
325 "Cannot open: %s",
326 archive_error_string (arch));
327 goto out;
328 }
329 for (;;) {
330 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100331 r = archive_read_next_header (arch, &entry);
332 if (r == ARCHIVE_EOF)
333 break;
334 if (r != ARCHIVE_OK) {
335 ret = FALSE;
336 g_set_error (error,
337 FWUPD_ERROR,
338 FWUPD_ERROR_INTERNAL,
339 "Cannot read header: %s",
340 archive_error_string (arch));
341 goto out;
342 }
343
344 /* only extract if valid */
345 valid = fu_common_extract_archive_entry (entry, dir);
346 if (!valid)
347 continue;
348 r = archive_read_extract (arch, entry, 0);
349 if (r != ARCHIVE_OK) {
350 ret = FALSE;
351 g_set_error (error,
352 FWUPD_ERROR,
353 FWUPD_ERROR_INTERNAL,
354 "Cannot extract: %s",
355 archive_error_string (arch));
356 goto out;
357 }
358 }
359out:
360 if (arch != NULL) {
361 archive_read_close (arch);
362 archive_read_free (arch);
363 }
364 return ret;
365}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100366
367static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300368fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
369
370static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100371fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
372{
373 va_list args;
374 g_autofree gchar *tmp = NULL;
375 g_auto(GStrv) split = NULL;
376
377 va_start (args, fmt);
378 tmp = g_strdup_vprintf (fmt, args);
379 va_end (args);
380
381 split = g_strsplit (tmp, " ", -1);
382 for (guint i = 0; split[i] != NULL; i++)
383 g_ptr_array_add (argv, g_strdup (split[i]));
384}
385
Mario Limonciello1a680f32019-11-25 19:44:53 -0600386/**
387 * fu_common_find_program_in_path:
388 * @basename: The program to search
389 * @error: A #GError, or %NULL
390 *
391 * Looks for a program in the PATH variable
392 *
393 * Returns: a new #gchar, or %NULL for error
394 *
395 * Since: 1.1.2
396 **/
Richard Hughes22367e72018-08-30 10:24:04 +0100397gchar *
398fu_common_find_program_in_path (const gchar *basename, GError **error)
399{
400 gchar *fn = g_find_program_in_path (basename);
401 if (fn == NULL) {
402 g_set_error (error,
403 FWUPD_ERROR,
404 FWUPD_ERROR_NOT_SUPPORTED,
405 "missing executable %s in PATH",
406 basename);
407 return NULL;
408 }
409 return fn;
410}
411
412static gboolean
413fu_common_test_namespace_support (GError **error)
414{
415 /* test if CONFIG_USER_NS is valid */
416 if (!g_file_test ("/proc/self/ns/user", G_FILE_TEST_IS_SYMLINK)) {
417 g_set_error (error,
418 FWUPD_ERROR,
419 FWUPD_ERROR_NOT_SUPPORTED,
420 "missing CONFIG_USER_NS in kernel");
421 return FALSE;
422 }
423 if (g_file_test ("/proc/sys/kernel/unprivileged_userns_clone", G_FILE_TEST_EXISTS)) {
424 g_autofree gchar *clone = NULL;
425 if (!g_file_get_contents ("/proc/sys/kernel/unprivileged_userns_clone", &clone, NULL, error))
426 return FALSE;
427 if (g_ascii_strtoll (clone, NULL, 10) == 0) {
428 g_set_error (error,
429 FWUPD_ERROR,
430 FWUPD_ERROR_NOT_SUPPORTED,
431 "unprivileged user namespace clones disabled by distro");
432 return FALSE;
433 }
434 }
435 return TRUE;
436}
437
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100438/**
439 * fu_common_firmware_builder:
440 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100441 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
442 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100443 * @error: A #GError, or %NULL
444 *
445 * Builds a firmware file using tools from the host session in a bubblewrap
446 * jail. Several things happen during build:
447 *
448 * 1. The @bytes data is untarred to a temporary location
449 * 2. A bubblewrap container is set up
450 * 3. The startup.sh script is run inside the container
451 * 4. The firmware.bin is extracted from the container
452 * 5. The temporary location is deleted
453 *
454 * Returns: a new #GBytes, or %NULL for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600455 *
456 * Since: 0.9.7
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100457 **/
458GBytes *
459fu_common_firmware_builder (GBytes *bytes,
460 const gchar *script_fn,
461 const gchar *output_fn,
462 GError **error)
463{
464 gint rc = 0;
465 g_autofree gchar *argv_str = NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500466 g_autofree gchar *bwrap_fn = NULL;
Richard Hughes4be17d12018-05-30 20:36:29 +0100467 g_autofree gchar *localstatebuilderdir = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100468 g_autofree gchar *localstatedir = NULL;
469 g_autofree gchar *output2_fn = NULL;
470 g_autofree gchar *standard_error = NULL;
471 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100472 g_autofree gchar *tmpdir = NULL;
473 g_autoptr(GBytes) firmware_blob = NULL;
474 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
475
476 g_return_val_if_fail (bytes != NULL, NULL);
477 g_return_val_if_fail (script_fn != NULL, NULL);
478 g_return_val_if_fail (output_fn != NULL, NULL);
479 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
480
Mario Limonciello37b59582018-08-13 08:38:01 -0500481 /* find bwrap in the path */
Richard Hughes22367e72018-08-30 10:24:04 +0100482 bwrap_fn = fu_common_find_program_in_path ("bwrap", error);
483 if (bwrap_fn == NULL)
Richard Hughesddb3e202018-08-23 11:29:57 +0100484 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500485
486 /* test if CONFIG_USER_NS is valid */
Richard Hughes22367e72018-08-30 10:24:04 +0100487 if (!fu_common_test_namespace_support (error))
Richard Hughesddb3e202018-08-23 11:29:57 +0100488 return NULL;
Mario Limonciello37b59582018-08-13 08:38:01 -0500489
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100490 /* untar file to temp location */
491 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
492 if (tmpdir == NULL)
493 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100494 if (!fu_common_extract_archive (bytes, tmpdir, error))
495 return NULL;
496
497 /* this is shared with the plugins */
Richard Hughes4be17d12018-05-30 20:36:29 +0100498 localstatedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
499 localstatebuilderdir = g_build_filename (localstatedir, "builder", NULL);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100500
501 /* launch bubblewrap and generate firmware */
Mario Limonciello37b59582018-08-13 08:38:01 -0500502 g_ptr_array_add (argv, g_steal_pointer (&bwrap_fn));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100503 fu_common_add_argv (argv, "--die-with-parent");
504 fu_common_add_argv (argv, "--ro-bind /usr /usr");
Mario Limonciellob8215572018-07-13 09:49:55 -0500505 fu_common_add_argv (argv, "--ro-bind /lib /lib");
506 fu_common_add_argv (argv, "--ro-bind /lib64 /lib64");
507 fu_common_add_argv (argv, "--ro-bind /bin /bin");
508 fu_common_add_argv (argv, "--ro-bind /sbin /sbin");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100509 fu_common_add_argv (argv, "--dir /tmp");
510 fu_common_add_argv (argv, "--dir /var");
511 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
Richard Hughes4be17d12018-05-30 20:36:29 +0100512 if (g_file_test (localstatebuilderdir, G_FILE_TEST_EXISTS))
513 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatebuilderdir);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100514 fu_common_add_argv (argv, "--dev /dev");
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100515 fu_common_add_argv (argv, "--chdir /tmp");
516 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100517 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100518 g_ptr_array_add (argv, NULL);
519 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
520 g_debug ("running '%s' in %s", argv_str, tmpdir);
521 if (!g_spawn_sync ("/tmp",
522 (gchar **) argv->pdata,
523 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100524 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100525 NULL, NULL, /* child_setup */
526 &standard_output,
527 &standard_error,
528 &rc,
529 error)) {
530 g_prefix_error (error, "failed to run '%s': ", argv_str);
531 return NULL;
532 }
533 if (standard_output != NULL && standard_output[0] != '\0')
534 g_debug ("console output was: %s", standard_output);
535 if (rc != 0) {
Mario Limonciello37b59582018-08-13 08:38:01 -0500536 FwupdError code = FWUPD_ERROR_INTERNAL;
537 if (errno == ENOTTY)
538 code = FWUPD_ERROR_PERMISSION_DENIED;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100539 g_set_error (error,
540 FWUPD_ERROR,
Mario Limonciello37b59582018-08-13 08:38:01 -0500541 code,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100542 "failed to build firmware: %s",
543 standard_error);
544 return NULL;
545 }
546
547 /* get generated file */
548 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
549 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
550 if (firmware_blob == NULL)
551 return NULL;
552
553 /* cleanup temp directory */
554 if (!fu_common_rmtree (tmpdir, error))
555 return NULL;
556
557 /* success */
558 return g_steal_pointer (&firmware_blob);
559}
Richard Hughes049ccc82017-08-09 15:26:56 +0100560
561typedef struct {
562 FuOutputHandler handler_cb;
563 gpointer handler_user_data;
564 GMainLoop *loop;
565 GSource *source;
566 GInputStream *stream;
567 GCancellable *cancellable;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000568 guint timeout_id;
Richard Hughes049ccc82017-08-09 15:26:56 +0100569} FuCommonSpawnHelper;
570
571static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
572
573static gboolean
574fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
575{
576 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
577 gchar buffer[1024];
578 gssize sz;
579 g_auto(GStrv) split = NULL;
580 g_autoptr(GError) error = NULL;
581
582 /* read from stream */
583 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
584 buffer,
585 sizeof(buffer) - 1,
586 NULL,
587 &error);
588 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100589 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
590 g_warning ("failed to get read from nonblocking fd: %s",
591 error->message);
592 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100593 return G_SOURCE_REMOVE;
594 }
595
596 /* no read possible */
597 if (sz == 0)
598 g_main_loop_quit (helper->loop);
599
600 /* emit lines */
601 if (helper->handler_cb != NULL) {
602 buffer[sz] = '\0';
603 split = g_strsplit (buffer, "\n", -1);
604 for (guint i = 0; split[i] != NULL; i++) {
605 if (split[i][0] == '\0')
606 continue;
607 helper->handler_cb (split[i], helper->handler_user_data);
608 }
609 }
610
611 /* set up the source for the next read */
612 fu_common_spawn_create_pollable_source (helper);
613 return G_SOURCE_REMOVE;
614}
615
616static void
617fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
618{
619 if (helper->source != NULL)
620 g_source_destroy (helper->source);
621 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
622 helper->cancellable);
623 g_source_attach (helper->source, NULL);
624 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
625}
626
627static void
628fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
629{
Richard Hughesb768e4d2019-02-26 13:55:18 +0000630 g_object_unref (helper->cancellable);
Richard Hughes049ccc82017-08-09 15:26:56 +0100631 if (helper->stream != NULL)
632 g_object_unref (helper->stream);
633 if (helper->source != NULL)
634 g_source_destroy (helper->source);
635 if (helper->loop != NULL)
636 g_main_loop_unref (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000637 if (helper->timeout_id != 0)
638 g_source_remove (helper->timeout_id);
Richard Hughes049ccc82017-08-09 15:26:56 +0100639 g_free (helper);
640}
641
Mario Limoncielloa98df552018-04-16 12:15:51 -0500642#pragma clang diagnostic push
643#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100644G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500645#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100646
Richard Hughesb768e4d2019-02-26 13:55:18 +0000647static gboolean
648fu_common_spawn_timeout_cb (gpointer user_data)
649{
650 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
651 g_cancellable_cancel (helper->cancellable);
652 g_main_loop_quit (helper->loop);
653 helper->timeout_id = 0;
654 return G_SOURCE_REMOVE;
655}
656
657static void
658fu_common_spawn_cancelled_cb (GCancellable *cancellable, FuCommonSpawnHelper *helper)
659{
660 /* just propagate */
661 g_cancellable_cancel (helper->cancellable);
662}
663
Richard Hughes049ccc82017-08-09 15:26:56 +0100664/**
665 * fu_common_spawn_sync:
666 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100667 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
668 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughesb768e4d2019-02-26 13:55:18 +0000669 * @timeout_ms: a timeout in ms, or 0 for no limit
Richard Hughes049ccc82017-08-09 15:26:56 +0100670 * @cancellable: a #GCancellable, or %NULL
671 * @error: A #GError or %NULL
672 *
673 * Runs a subprocess and waits for it to exit. Any output on standard out or
674 * standard error will be forwarded to @handler_cb as whole lines.
675 *
676 * Returns: %TRUE for success
Mario Limonciello1a680f32019-11-25 19:44:53 -0600677 *
678 * Since: 0.9.7
Richard Hughes049ccc82017-08-09 15:26:56 +0100679 **/
680gboolean
681fu_common_spawn_sync (const gchar * const * argv,
682 FuOutputHandler handler_cb,
683 gpointer handler_user_data,
Richard Hughesb768e4d2019-02-26 13:55:18 +0000684 guint timeout_ms,
Richard Hughes049ccc82017-08-09 15:26:56 +0100685 GCancellable *cancellable, GError **error)
686{
687 g_autoptr(FuCommonSpawnHelper) helper = NULL;
688 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100689 g_autofree gchar *argv_str = NULL;
Richard Hughesb768e4d2019-02-26 13:55:18 +0000690 gulong cancellable_id = 0;
Richard Hughes049ccc82017-08-09 15:26:56 +0100691
692 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100693 argv_str = g_strjoinv (" ", (gchar **) argv);
694 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100695 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
696 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
697 if (subprocess == NULL)
698 return FALSE;
699
700 /* watch for process to exit */
701 helper = g_new0 (FuCommonSpawnHelper, 1);
702 helper->handler_cb = handler_cb;
703 helper->handler_user_data = handler_user_data;
704 helper->loop = g_main_loop_new (NULL, FALSE);
705 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000706
707 /* always create a cancellable, and connect up the parent */
708 helper->cancellable = g_cancellable_new ();
709 if (cancellable != NULL) {
710 cancellable_id = g_cancellable_connect (cancellable,
711 G_CALLBACK (fu_common_spawn_cancelled_cb),
712 helper, NULL);
713 }
714
715 /* allow timeout */
716 if (timeout_ms > 0) {
717 helper->timeout_id = g_timeout_add (timeout_ms,
718 fu_common_spawn_timeout_cb,
719 helper);
720 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100721 fu_common_spawn_create_pollable_source (helper);
722 g_main_loop_run (helper->loop);
Richard Hughesb768e4d2019-02-26 13:55:18 +0000723 g_cancellable_disconnect (cancellable, cancellable_id);
724 if (g_cancellable_set_error_if_cancelled (helper->cancellable, error))
725 return FALSE;
Richard Hughes049ccc82017-08-09 15:26:56 +0100726 return g_subprocess_wait_check (subprocess, cancellable, error);
727}
Richard Hughesae252cd2017-12-08 10:48:15 +0000728
729/**
730 * fu_common_write_uint16:
731 * @buf: A writable buffer
732 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100733 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000734 *
735 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600736 *
737 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000738 **/
739void
740fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
741{
742 guint16 val_hw;
743 switch (endian) {
744 case G_BIG_ENDIAN:
745 val_hw = GUINT16_TO_BE(val_native);
746 break;
747 case G_LITTLE_ENDIAN:
748 val_hw = GUINT16_TO_LE(val_native);
749 break;
750 default:
751 g_assert_not_reached ();
752 }
753 memcpy (buf, &val_hw, sizeof(val_hw));
754}
755
756/**
757 * fu_common_write_uint32:
758 * @buf: A writable buffer
759 * @val_native: a value in host byte-order
Richard Hughes8aa72392018-05-02 08:38:43 +0100760 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000761 *
762 * Writes a value to a buffer using a specified endian.
Mario Limonciello1a680f32019-11-25 19:44:53 -0600763 *
764 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000765 **/
766void
767fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
768{
769 guint32 val_hw;
770 switch (endian) {
771 case G_BIG_ENDIAN:
772 val_hw = GUINT32_TO_BE(val_native);
773 break;
774 case G_LITTLE_ENDIAN:
775 val_hw = GUINT32_TO_LE(val_native);
776 break;
777 default:
778 g_assert_not_reached ();
779 }
780 memcpy (buf, &val_hw, sizeof(val_hw));
781}
782
783/**
784 * fu_common_read_uint16:
785 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100786 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000787 *
788 * Read a value from a buffer using a specified endian.
789 *
790 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600791 *
792 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000793 **/
794guint16
795fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
796{
797 guint16 val_hw, val_native;
798 memcpy (&val_hw, buf, sizeof(val_hw));
799 switch (endian) {
800 case G_BIG_ENDIAN:
801 val_native = GUINT16_FROM_BE(val_hw);
802 break;
803 case G_LITTLE_ENDIAN:
804 val_native = GUINT16_FROM_LE(val_hw);
805 break;
806 default:
807 g_assert_not_reached ();
808 }
809 return val_native;
810}
811
812/**
813 * fu_common_read_uint32:
814 * @buf: A readable buffer
Richard Hughes8aa72392018-05-02 08:38:43 +0100815 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
Richard Hughesae252cd2017-12-08 10:48:15 +0000816 *
817 * Read a value from a buffer using a specified endian.
818 *
819 * Returns: a value in host byte-order
Mario Limonciello1a680f32019-11-25 19:44:53 -0600820 *
821 * Since: 1.0.3
Richard Hughesae252cd2017-12-08 10:48:15 +0000822 **/
823guint32
824fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
825{
826 guint32 val_hw, val_native;
827 memcpy (&val_hw, buf, sizeof(val_hw));
828 switch (endian) {
829 case G_BIG_ENDIAN:
830 val_native = GUINT32_FROM_BE(val_hw);
831 break;
832 case G_LITTLE_ENDIAN:
833 val_native = GUINT32_FROM_LE(val_hw);
834 break;
835 default:
836 g_assert_not_reached ();
837 }
838 return val_native;
839}
Richard Hughese82eef32018-05-20 10:41:26 +0100840
Richard Hughes73bf2332018-08-28 09:38:09 +0100841/**
842 * fu_common_strtoull:
843 * @str: A string, e.g. "0x1234"
844 *
845 * Converts a string value to an integer. Values are assumed base 10, unless
846 * prefixed with "0x" where they are parsed as base 16.
847 *
848 * Returns: integer value, or 0x0 for error
Mario Limonciello1a680f32019-11-25 19:44:53 -0600849 *
850 * Since: 1.1.2
Richard Hughes73bf2332018-08-28 09:38:09 +0100851 **/
852guint64
853fu_common_strtoull (const gchar *str)
854{
855 guint base = 10;
856 if (str == NULL)
857 return 0x0;
858 if (g_str_has_prefix (str, "0x")) {
859 str += 2;
860 base = 16;
861 }
862 return g_ascii_strtoull (str, NULL, base);
863}
864
Richard Hughesa574a752018-08-31 13:31:03 +0100865/**
866 * fu_common_strstrip:
867 * @str: A string, e.g. " test "
868 *
869 * Removes leading and trailing whitespace from a constant string.
870 *
871 * Returns: newly allocated string
Mario Limonciello1a680f32019-11-25 19:44:53 -0600872 *
873 * Since: 1.1.2
Richard Hughesa574a752018-08-31 13:31:03 +0100874 **/
875gchar *
876fu_common_strstrip (const gchar *str)
877{
878 guint head = G_MAXUINT;
879 guint tail = 0;
880
881 g_return_val_if_fail (str != NULL, NULL);
882
883 /* find first non-space char */
884 for (guint i = 0; str[i] != '\0'; i++) {
885 if (str[i] != ' ') {
886 head = i;
887 break;
888 }
889 }
890 if (head == G_MAXUINT)
891 return g_strdup ("");
892
893 /* find last non-space char */
894 for (guint i = head; str[i] != '\0'; i++) {
Mario Limoncielloef3c7662019-09-04 23:37:59 -0500895 if (!g_ascii_isspace (str[i]))
Richard Hughesa574a752018-08-31 13:31:03 +0100896 tail = i;
897 }
898 return g_strndup (str + head, tail - head + 1);
899}
900
Richard Hughese82eef32018-05-20 10:41:26 +0100901static const GError *
902fu_common_error_array_find (GPtrArray *errors, FwupdError error_code)
903{
904 for (guint j = 0; j < errors->len; j++) {
905 const GError *error = g_ptr_array_index (errors, j);
906 if (g_error_matches (error, FWUPD_ERROR, error_code))
907 return error;
908 }
909 return NULL;
910}
911
912static guint
913fu_common_error_array_count (GPtrArray *errors, FwupdError error_code)
914{
915 guint cnt = 0;
916 for (guint j = 0; j < errors->len; j++) {
917 const GError *error = g_ptr_array_index (errors, j);
918 if (g_error_matches (error, FWUPD_ERROR, error_code))
919 cnt++;
920 }
921 return cnt;
922}
923
924static gboolean
925fu_common_error_array_matches_any (GPtrArray *errors, FwupdError *error_codes)
926{
927 for (guint j = 0; j < errors->len; j++) {
928 const GError *error = g_ptr_array_index (errors, j);
929 gboolean matches_any = FALSE;
930 for (guint i = 0; error_codes[i] != FWUPD_ERROR_LAST; i++) {
931 if (g_error_matches (error, FWUPD_ERROR, error_codes[i])) {
932 matches_any = TRUE;
933 break;
934 }
935 }
936 if (!matches_any)
937 return FALSE;
938 }
939 return TRUE;
940}
941
942/**
943 * fu_common_error_array_get_best:
944 * @errors: (element-type GError): array of errors
945 *
946 * Finds the 'best' error to show the user from a array of errors, creating a
947 * completely bespoke error where required.
948 *
949 * Returns: (transfer full): a #GError, never %NULL
Mario Limonciello1a680f32019-11-25 19:44:53 -0600950 *
951 * Since: 1.0.8
Richard Hughese82eef32018-05-20 10:41:26 +0100952 **/
953GError *
954fu_common_error_array_get_best (GPtrArray *errors)
955{
956 FwupdError err_prio[] = { FWUPD_ERROR_INVALID_FILE,
957 FWUPD_ERROR_VERSION_SAME,
958 FWUPD_ERROR_VERSION_NEWER,
959 FWUPD_ERROR_NOT_SUPPORTED,
960 FWUPD_ERROR_INTERNAL,
961 FWUPD_ERROR_NOT_FOUND,
962 FWUPD_ERROR_LAST };
963 FwupdError err_all_uptodate[] = { FWUPD_ERROR_VERSION_SAME,
964 FWUPD_ERROR_NOT_FOUND,
965 FWUPD_ERROR_NOT_SUPPORTED,
966 FWUPD_ERROR_LAST };
967 FwupdError err_all_newer[] = { FWUPD_ERROR_VERSION_NEWER,
968 FWUPD_ERROR_VERSION_SAME,
969 FWUPD_ERROR_NOT_FOUND,
970 FWUPD_ERROR_NOT_SUPPORTED,
971 FWUPD_ERROR_LAST };
972
973 /* are all the errors either GUID-not-matched or version-same? */
974 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_SAME) > 1 &&
975 fu_common_error_array_matches_any (errors, err_all_uptodate)) {
976 return g_error_new (FWUPD_ERROR,
977 FWUPD_ERROR_NOTHING_TO_DO,
978 "All updatable firmware is already installed");
979 }
980
981 /* are all the errors either GUID-not-matched or version same or newer? */
982 if (fu_common_error_array_count (errors, FWUPD_ERROR_VERSION_NEWER) > 1 &&
983 fu_common_error_array_matches_any (errors, err_all_newer)) {
984 return g_error_new (FWUPD_ERROR,
985 FWUPD_ERROR_NOTHING_TO_DO,
986 "All updatable devices already have newer versions");
987 }
988
989 /* get the most important single error */
990 for (guint i = 0; err_prio[i] != FWUPD_ERROR_LAST; i++) {
991 const GError *error_tmp = fu_common_error_array_find (errors, err_prio[i]);
992 if (error_tmp != NULL)
993 return g_error_copy (error_tmp);
994 }
995
996 /* fall back to something */
997 return g_error_new (FWUPD_ERROR,
998 FWUPD_ERROR_NOT_FOUND,
999 "No supported devices found");
1000}
Richard Hughes4be17d12018-05-30 20:36:29 +01001001
1002/**
1003 * fu_common_get_path:
1004 * @path_kind: A #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG
1005 *
1006 * Gets a fwupd-specific system path. These can be overridden with various
1007 * environment variables, for instance %FWUPD_DATADIR.
1008 *
1009 * Returns: a system path, or %NULL if invalid
Mario Limonciello1a680f32019-11-25 19:44:53 -06001010 *
1011 * Since: 1.0.8
Richard Hughes4be17d12018-05-30 20:36:29 +01001012 **/
1013gchar *
1014fu_common_get_path (FuPathKind path_kind)
1015{
1016 const gchar *tmp;
1017 g_autofree gchar *basedir = NULL;
1018
1019 switch (path_kind) {
1020 /* /var */
1021 case FU_PATH_KIND_LOCALSTATEDIR:
1022 tmp = g_getenv ("FWUPD_LOCALSTATEDIR");
1023 if (tmp != NULL)
1024 return g_strdup (tmp);
1025 tmp = g_getenv ("SNAP_USER_DATA");
1026 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001027 return g_build_filename (tmp, FWUPD_LOCALSTATEDIR, NULL);
1028 return g_build_filename (FWUPD_LOCALSTATEDIR, NULL);
Richard Hughes282b10d2018-06-22 14:48:00 +01001029 /* /sys/firmware */
1030 case FU_PATH_KIND_SYSFSDIR_FW:
1031 tmp = g_getenv ("FWUPD_SYSFSFWDIR");
1032 if (tmp != NULL)
1033 return g_strdup (tmp);
1034 return g_strdup ("/sys/firmware");
Mario Limonciello39602652019-04-29 21:08:58 -05001035 /* /sys/class/tpm */
Richard Hughesb56015e2018-12-12 09:25:32 +00001036 case FU_PATH_KIND_SYSFSDIR_TPM:
1037 tmp = g_getenv ("FWUPD_SYSFSTPMDIR");
1038 if (tmp != NULL)
1039 return g_strdup (tmp);
1040 return g_strdup ("/sys/class/tpm");
Richard Hughes83390f62018-06-22 20:36:46 +01001041 /* /sys/bus/platform/drivers */
1042 case FU_PATH_KIND_SYSFSDIR_DRIVERS:
1043 tmp = g_getenv ("FWUPD_SYSFSDRIVERDIR");
1044 if (tmp != NULL)
1045 return g_strdup (tmp);
1046 return g_strdup ("/sys/bus/platform/drivers");
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001047 /* /sys/kernel/security */
1048 case FU_PATH_KIND_SYSFSDIR_SECURITY:
1049 tmp = g_getenv ("FWUPD_SYSFSSECURITYDIR");
1050 if (tmp != NULL)
1051 return g_strdup (tmp);
1052 return g_strdup ("/sys/kernel/security");
Richard Hughes4be17d12018-05-30 20:36:29 +01001053 /* /etc */
1054 case FU_PATH_KIND_SYSCONFDIR:
1055 tmp = g_getenv ("FWUPD_SYSCONFDIR");
1056 if (tmp != NULL)
1057 return g_strdup (tmp);
1058 tmp = g_getenv ("SNAP_USER_DATA");
1059 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001060 return g_build_filename (tmp, FWUPD_SYSCONFDIR, NULL);
1061 return g_strdup (FWUPD_SYSCONFDIR);
Richard Hughes4be17d12018-05-30 20:36:29 +01001062 /* /usr/lib/<triplet>/fwupd-plugins-3 */
1063 case FU_PATH_KIND_PLUGINDIR_PKG:
1064 tmp = g_getenv ("FWUPD_PLUGINDIR");
1065 if (tmp != NULL)
1066 return g_strdup (tmp);
1067 tmp = g_getenv ("SNAP");
1068 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001069 return g_build_filename (tmp, FWUPD_PLUGINDIR, NULL);
1070 return g_build_filename (FWUPD_PLUGINDIR, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001071 /* /usr/share/fwupd */
1072 case FU_PATH_KIND_DATADIR_PKG:
1073 tmp = g_getenv ("FWUPD_DATADIR");
1074 if (tmp != NULL)
1075 return g_strdup (tmp);
1076 tmp = g_getenv ("SNAP");
1077 if (tmp != NULL)
Richard Hughes668ee212019-11-22 09:17:46 +00001078 return g_build_filename (tmp, FWUPD_DATADIR, PACKAGE_NAME, NULL);
1079 return g_build_filename (FWUPD_DATADIR, PACKAGE_NAME, NULL);
Mario Limoncielloe6e2bf92018-07-10 12:11:25 -05001080 /* /usr/libexec/fwupd/efi */
1081 case FU_PATH_KIND_EFIAPPDIR:
1082 tmp = g_getenv ("FWUPD_EFIAPPDIR");
1083 if (tmp != NULL)
1084 return g_strdup (tmp);
1085#ifdef EFI_APP_LOCATION
1086 tmp = g_getenv ("SNAP");
1087 if (tmp != NULL)
1088 return g_build_filename (tmp, EFI_APP_LOCATION, NULL);
1089 return g_strdup (EFI_APP_LOCATION);
1090#else
1091 return NULL;
1092#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001093 /* /etc/fwupd */
1094 case FU_PATH_KIND_SYSCONFDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001095 tmp = g_getenv ("CONFIGURATION_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001096 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001097 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001098 basedir = fu_common_get_path (FU_PATH_KIND_SYSCONFDIR);
1099 return g_build_filename (basedir, PACKAGE_NAME, NULL);
1100 /* /var/lib/fwupd */
1101 case FU_PATH_KIND_LOCALSTATEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001102 tmp = g_getenv ("STATE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001103 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001104 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001105 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1106 return g_build_filename (basedir, "lib", PACKAGE_NAME, NULL);
1107 /* /var/cache/fwupd */
1108 case FU_PATH_KIND_CACHEDIR_PKG:
Mario Limonciello277c1962019-08-26 23:42:23 -05001109 tmp = g_getenv ("CACHE_DIRECTORY");
Mario Limonciello695cb582019-12-12 10:45:42 -06001110 if (tmp != NULL && g_file_test (tmp, G_FILE_TEST_EXISTS))
Mario Limonciello277c1962019-08-26 23:42:23 -05001111 return g_build_filename (tmp, NULL);
Richard Hughes4be17d12018-05-30 20:36:29 +01001112 basedir = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR);
1113 return g_build_filename (basedir, "cache", PACKAGE_NAME, NULL);
Richard Hughesafdba372019-11-23 12:57:35 +00001114 case FU_PATH_KIND_OFFLINE_TRIGGER:
1115 tmp = g_getenv ("FWUPD_OFFLINE_TRIGGER");
1116 if (tmp != NULL)
1117 return g_strdup (tmp);
1118 return g_strdup ("/system-update");
Mario Limonciello057c67a2019-05-23 10:44:19 -05001119 case FU_PATH_KIND_POLKIT_ACTIONS:
1120#ifdef POLKIT_ACTIONDIR
1121 return g_strdup (POLKIT_ACTIONDIR);
1122#else
1123 return NULL;
1124#endif
Richard Hughes4be17d12018-05-30 20:36:29 +01001125 /* this shouldn't happen */
1126 default:
Richard Hughesbeb47a82018-09-11 18:28:53 +01001127 g_warning ("cannot build path for unknown kind %u", path_kind);
Richard Hughes4be17d12018-05-30 20:36:29 +01001128 }
1129
1130 return NULL;
1131}
Richard Hughes83e56c12018-10-10 20:24:41 +01001132
1133/**
1134 * fu_common_string_replace:
1135 * @string: The #GString to operate on
1136 * @search: The text to search for
1137 * @replace: The text to use for substitutions
1138 *
1139 * Performs multiple search and replace operations on the given string.
1140 *
1141 * Returns: the number of replacements done, or 0 if @search is not found.
1142 *
1143 * Since: 1.2.0
1144 **/
1145guint
1146fu_common_string_replace (GString *string, const gchar *search, const gchar *replace)
1147{
1148 gchar *tmp;
1149 guint count = 0;
1150 gsize search_idx = 0;
1151 gsize replace_len;
1152 gsize search_len;
1153
1154 g_return_val_if_fail (string != NULL, 0);
1155 g_return_val_if_fail (search != NULL, 0);
1156 g_return_val_if_fail (replace != NULL, 0);
1157
1158 /* nothing to do */
1159 if (string->len == 0)
1160 return 0;
1161
1162 search_len = strlen (search);
1163 replace_len = strlen (replace);
1164
1165 do {
1166 tmp = g_strstr_len (string->str + search_idx, -1, search);
1167 if (tmp == NULL)
1168 break;
1169
1170 /* advance the counter in case @replace contains @search */
1171 search_idx = (gsize) (tmp - string->str);
1172
1173 /* reallocate the string if required */
1174 if (search_len > replace_len) {
1175 g_string_erase (string,
1176 (gssize) search_idx,
1177 (gssize) (search_len - replace_len));
1178 memcpy (tmp, replace, replace_len);
1179 } else if (search_len < replace_len) {
1180 g_string_insert_len (string,
1181 (gssize) search_idx,
1182 replace,
1183 (gssize) (replace_len - search_len));
1184 /* we have to treat this specially as it could have
1185 * been reallocated when the insertion happened */
1186 memcpy (string->str + search_idx, replace, replace_len);
1187 } else {
1188 /* just memcmp in the new string */
1189 memcpy (tmp, replace, replace_len);
1190 }
1191 search_idx += replace_len;
1192 count++;
1193 } while (TRUE);
1194
1195 return count;
1196}
Richard Hughese59cb9a2018-12-05 14:37:40 +00001197
Richard Hughesae96a1f2019-09-23 11:16:36 +01001198/**
1199 * fu_common_strwidth:
1200 * @text: The string to operate on
1201 *
1202 * Returns the width of the string in displayed characters on the console.
1203 *
1204 * Returns: width of text
1205 *
1206 * Since: 1.3.2
1207 **/
1208gsize
1209fu_common_strwidth (const gchar *text)
1210{
1211 const gchar *p = text;
1212 gsize width = 0;
1213 while (*p) {
1214 gunichar c = g_utf8_get_char (p);
1215 if (g_unichar_iswide (c))
1216 width += 2;
1217 else if (!g_unichar_iszerowidth (c))
1218 width += 1;
1219 p = g_utf8_next_char (p);
1220 }
1221 return width;
1222}
1223
Mario Limonciello1a680f32019-11-25 19:44:53 -06001224/**
1225 * fu_common_string_append_kv:
1226 * @str: A #GString
1227 * @idt: The indent
1228 * @key: A string to append
1229 * @value: a string to append
1230 *
1231 * Appends a key and string value to a string
1232 *
1233 * Since: 1.2.4
1234 */
Richard Hughescea28de2019-08-09 11:16:40 +01001235void
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001236fu_common_string_append_kv (GString *str, guint idt, const gchar *key, const gchar *value)
Richard Hughescea28de2019-08-09 11:16:40 +01001237{
Richard Hughes847cae82019-08-27 11:22:23 +01001238 const guint align = 25;
1239 gsize keysz;
Richard Hughescea28de2019-08-09 11:16:40 +01001240
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001241 g_return_if_fail (idt * 2 < align);
Richard Hughescea28de2019-08-09 11:16:40 +01001242
1243 /* ignore */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001244 if (key == NULL)
Richard Hughescea28de2019-08-09 11:16:40 +01001245 return;
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001246 for (gsize i = 0; i < idt; i++)
1247 g_string_append (str, " ");
Mario Limonciellofee8f492019-08-18 12:16:07 -05001248 if (key[0] != '\0') {
1249 g_string_append_printf (str, "%s:", key);
Richard Hughesae96a1f2019-09-23 11:16:36 +01001250 keysz = (idt * 2) + fu_common_strwidth (key) + 1;
Richard Hughes847cae82019-08-27 11:22:23 +01001251 } else {
1252 keysz = idt * 2;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001253 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001254 if (value != NULL) {
Mario Limonciello1dbb82d2019-09-20 14:22:14 -05001255 g_auto(GStrv) split = NULL;
1256 split = g_strsplit (value, "\n", -1);
1257 for (guint i = 0; split[i] != NULL; i++) {
1258 if (i == 0) {
1259 for (gsize j = keysz; j < align; j++)
1260 g_string_append (str, " ");
1261 } else {
1262 for (gsize j = 0; j < idt; j++)
1263 g_string_append (str, " ");
1264 }
1265 g_string_append (str, split[i]);
1266 g_string_append (str, "\n");
1267 }
1268 } else {
1269 g_string_append (str, "\n");
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001270 }
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001271}
1272
Mario Limonciello1a680f32019-11-25 19:44:53 -06001273/**
1274 * fu_common_string_append_ku:
1275 * @str: A #GString
1276 * @idt: The indent
1277 * @key: A string to append
1278 * @value: guint64
1279 *
1280 * Appends a key and unsigned integer to a string
1281 *
1282 * Since: 1.2.4
1283 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001284void
1285fu_common_string_append_ku (GString *str, guint idt, const gchar *key, guint64 value)
1286{
1287 g_autofree gchar *tmp = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
1288 fu_common_string_append_kv (str, idt, key, tmp);
1289}
1290
Mario Limonciello1a680f32019-11-25 19:44:53 -06001291/**
1292 * fu_common_string_append_kx:
1293 * @str: A #GString
1294 * @idt: The indent
1295 * @key: A string to append
1296 * @value: guint64
1297 *
1298 * Appends a key and hex integer to a string
1299 *
1300 * Since: 1.2.4
1301 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001302void
1303fu_common_string_append_kx (GString *str, guint idt, const gchar *key, guint64 value)
1304{
1305 g_autofree gchar *tmp = g_strdup_printf ("0x%x", (guint) value);
1306 fu_common_string_append_kv (str, idt, key, tmp);
1307}
1308
Mario Limonciello1a680f32019-11-25 19:44:53 -06001309/**
1310 * fu_common_string_append_kb:
1311 * @str: A #GString
1312 * @idt: The indent
1313 * @key: A string to append
1314 * @value: Boolean
1315 *
1316 * Appends a key and boolean value to a string
1317 *
1318 * Since: 1.2.4
1319 */
Richard Hughes6e3e62b2019-08-14 10:43:08 +01001320void
1321fu_common_string_append_kb (GString *str, guint idt, const gchar *key, gboolean value)
1322{
1323 fu_common_string_append_kv (str, idt, key, value ? "true" : "false");
Richard Hughescea28de2019-08-09 11:16:40 +01001324}
1325
Richard Hughese59cb9a2018-12-05 14:37:40 +00001326/**
Richard Hughes35481862019-01-06 12:01:58 +00001327 * fu_common_dump_full:
1328 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1329 * @title: prefix title, or %NULL
1330 * @data: buffer to print
1331 * @len: the size of @data
1332 * @columns: break new lines after this many bytes
1333 * @flags: some #FuDumpFlags, e.g. %FU_DUMP_FLAGS_SHOW_ASCII
1334 *
1335 * Dumps a raw buffer to the screen.
1336 *
1337 * Since: 1.2.4
1338 **/
1339void
1340fu_common_dump_full (const gchar *log_domain,
1341 const gchar *title,
1342 const guint8 *data,
1343 gsize len,
1344 guint columns,
1345 FuDumpFlags flags)
1346{
1347 g_autoptr(GString) str = g_string_new (NULL);
1348
1349 /* optional */
1350 if (title != NULL)
1351 g_string_append_printf (str, "%s:", title);
1352
1353 /* if more than can fit on one line then start afresh */
1354 if (len > columns || flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1355 g_string_append (str, "\n");
1356 } else {
1357 for (gsize i = str->len; i < 16; i++)
1358 g_string_append (str, " ");
1359 }
1360
1361 /* offset line */
1362 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES) {
1363 g_string_append (str, " │ ");
1364 for (gsize i = 0; i < columns; i++)
1365 g_string_append_printf (str, "%02x ", (guint) i);
1366 g_string_append (str, "\n───────┼");
1367 for (gsize i = 0; i < columns; i++)
1368 g_string_append (str, "───");
1369 g_string_append_printf (str, "\n0x%04x │ ", (guint) 0);
1370 }
1371
1372 /* print each row */
1373 for (gsize i = 0; i < len; i++) {
1374 g_string_append_printf (str, "%02x ", data[i]);
1375
1376 /* optionally print ASCII char */
1377 if (flags & FU_DUMP_FLAGS_SHOW_ASCII) {
1378 if (g_ascii_isprint (data[i]))
1379 g_string_append_printf (str, "[%c] ", data[i]);
1380 else
1381 g_string_append (str, "[?] ");
1382 }
1383
1384 /* new row required */
1385 if (i > 0 && i != len - 1 && (i + 1) % columns == 0) {
1386 g_string_append (str, "\n");
1387 if (flags & FU_DUMP_FLAGS_SHOW_ADDRESSES)
1388 g_string_append_printf (str, "0x%04x │ ", (guint) i + 1);
1389 }
1390 }
1391 g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", str->str);
1392}
1393
1394/**
Richard Hughese59cb9a2018-12-05 14:37:40 +00001395 * fu_common_dump_raw:
1396 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1397 * @title: prefix title, or %NULL
1398 * @data: buffer to print
1399 * @len: the size of @data
1400 *
1401 * Dumps a raw buffer to the screen.
1402 *
1403 * Since: 1.2.2
1404 **/
1405void
1406fu_common_dump_raw (const gchar *log_domain,
1407 const gchar *title,
1408 const guint8 *data,
1409 gsize len)
1410{
Richard Hughes35481862019-01-06 12:01:58 +00001411 FuDumpFlags flags = FU_DUMP_FLAGS_NONE;
1412 if (len > 64)
1413 flags |= FU_DUMP_FLAGS_SHOW_ADDRESSES;
1414 fu_common_dump_full (log_domain, title, data, len, 32, flags);
Richard Hughese59cb9a2018-12-05 14:37:40 +00001415}
1416
1417/**
Mario Limonciello39602652019-04-29 21:08:58 -05001418 * fu_common_dump_bytes:
Richard Hughese59cb9a2018-12-05 14:37:40 +00001419 * @log_domain: log domain, typically %G_LOG_DOMAIN or %NULL
1420 * @title: prefix title, or %NULL
1421 * @bytes: a #GBytes
1422 *
1423 * Dumps a byte buffer to the screen.
1424 *
1425 * Since: 1.2.2
1426 **/
1427void
1428fu_common_dump_bytes (const gchar *log_domain,
1429 const gchar *title,
1430 GBytes *bytes)
1431{
1432 gsize len = 0;
1433 const guint8 *data = g_bytes_get_data (bytes, &len);
1434 fu_common_dump_raw (log_domain, title, data, len);
1435}
Richard Hughesfc90f392019-01-15 21:21:16 +00001436
1437/**
1438 * fu_common_bytes_align:
1439 * @bytes: a #GBytes
1440 * @blksz: block size in bytes
1441 * @padval: the byte used to pad the byte buffer
1442 *
1443 * Aligns a block of memory to @blksize using the @padval value; if
1444 * the block is already aligned then the original @bytes is returned.
1445 *
1446 * Returns: (transfer full): a #GBytes, possibly @bytes
1447 *
1448 * Since: 1.2.4
1449 **/
1450GBytes *
1451fu_common_bytes_align (GBytes *bytes, gsize blksz, gchar padval)
1452{
1453 const guint8 *data;
1454 gsize sz;
1455
1456 g_return_val_if_fail (bytes != NULL, NULL);
1457 g_return_val_if_fail (blksz > 0, NULL);
1458
1459 /* pad */
1460 data = g_bytes_get_data (bytes, &sz);
1461 if (sz % blksz != 0) {
1462 gsize sz_align = ((sz / blksz) + 1) * blksz;
1463 guint8 *data_align = g_malloc (sz_align);
1464 memcpy (data_align, data, sz);
1465 memset (data_align + sz, padval, sz_align - sz);
1466 g_debug ("aligning 0x%x bytes to 0x%x",
1467 (guint) sz, (guint) sz_align);
1468 return g_bytes_new_take (data_align, sz_align);
1469 }
1470
1471 /* perfectly aligned */
1472 return g_bytes_ref (bytes);
1473}
Richard Hughes36999462019-03-19 20:23:29 +00001474
1475/**
1476 * fu_common_bytes_is_empty:
1477 * @bytes: a #GBytes
1478 *
1479 * Checks if a byte array are just empty (0xff) bytes.
1480 *
1481 * Return value: %TRUE if @bytes is empty
Mario Limonciello1a680f32019-11-25 19:44:53 -06001482 *
1483 * Since: 1.2.6
Richard Hughes36999462019-03-19 20:23:29 +00001484 **/
1485gboolean
1486fu_common_bytes_is_empty (GBytes *bytes)
1487{
1488 gsize sz = 0;
1489 const guint8 *buf = g_bytes_get_data (bytes, &sz);
1490 for (gsize i = 0; i < sz; i++) {
1491 if (buf[i] != 0xff)
1492 return FALSE;
1493 }
1494 return TRUE;
1495}
Richard Hughes2aad1042019-03-21 09:03:32 +00001496
1497/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001498 * fu_common_bytes_compare_raw:
1499 * @buf1: a buffer
1500 * @bufsz1: sizeof @buf1
1501 * @buf2: another buffer
1502 * @bufsz2: sizeof @buf2
Richard Hughes2aad1042019-03-21 09:03:32 +00001503 * @error: A #GError or %NULL
1504 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001505 * Compares the buffers for equality.
Richard Hughes2aad1042019-03-21 09:03:32 +00001506 *
Richard Hughes38245ff2019-09-18 14:46:09 +01001507 * Return value: %TRUE if @buf1 and @buf2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001508 *
1509 * Since: 1.3.2
Richard Hughes2aad1042019-03-21 09:03:32 +00001510 **/
1511gboolean
Richard Hughes38245ff2019-09-18 14:46:09 +01001512fu_common_bytes_compare_raw (const guint8 *buf1, gsize bufsz1,
1513 const guint8 *buf2, gsize bufsz2,
1514 GError **error)
Richard Hughes2aad1042019-03-21 09:03:32 +00001515{
Richard Hughes38245ff2019-09-18 14:46:09 +01001516 g_return_val_if_fail (buf1 != NULL, FALSE);
1517 g_return_val_if_fail (buf2 != NULL, FALSE);
Richard Hughes2aad1042019-03-21 09:03:32 +00001518 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1519
1520 /* not the same length */
Richard Hughes2aad1042019-03-21 09:03:32 +00001521 if (bufsz1 != bufsz2) {
1522 g_set_error (error,
1523 G_IO_ERROR,
1524 G_IO_ERROR_INVALID_DATA,
1525 "got %" G_GSIZE_FORMAT " bytes, expected "
1526 "%" G_GSIZE_FORMAT, bufsz1, bufsz2);
1527 return FALSE;
1528 }
1529
1530 /* check matches */
1531 for (guint i = 0x0; i < bufsz1; i++) {
1532 if (buf1[i] != buf2[i]) {
1533 g_set_error (error,
1534 G_IO_ERROR,
1535 G_IO_ERROR_INVALID_DATA,
1536 "got 0x%02x, expected 0x%02x @ 0x%04x",
1537 buf1[i], buf2[i], i);
1538 return FALSE;
1539 }
1540 }
1541
1542 /* success */
1543 return TRUE;
1544}
Richard Hughes484ee292019-03-22 16:10:50 +00001545
1546/**
Richard Hughes38245ff2019-09-18 14:46:09 +01001547 * fu_common_bytes_compare:
1548 * @bytes1: a #GBytes
1549 * @bytes2: another #GBytes
1550 * @error: A #GError or %NULL
1551 *
1552 * Compares the buffers for equality.
1553 *
1554 * Return value: %TRUE if @bytes1 and @bytes2 are identical
Mario Limonciello1a680f32019-11-25 19:44:53 -06001555 *
1556 * Since: 1.2.6
Richard Hughes38245ff2019-09-18 14:46:09 +01001557 **/
1558gboolean
1559fu_common_bytes_compare (GBytes *bytes1, GBytes *bytes2, GError **error)
1560{
1561 const guint8 *buf1;
1562 const guint8 *buf2;
1563 gsize bufsz1;
1564 gsize bufsz2;
1565
1566 g_return_val_if_fail (bytes1 != NULL, FALSE);
1567 g_return_val_if_fail (bytes2 != NULL, FALSE);
1568 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1569
1570 buf1 = g_bytes_get_data (bytes1, &bufsz1);
1571 buf2 = g_bytes_get_data (bytes2, &bufsz2);
1572 return fu_common_bytes_compare_raw (buf1, bufsz1, buf2, bufsz2, error);
1573}
1574
1575/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001576 * fu_common_bytes_pad:
1577 * @bytes: a #GBytes
1578 * @sz: the desired size in bytes
1579 *
1580 * Pads a GBytes to a given @sz with `0xff`.
1581 *
1582 * Return value: (transfer full): a #GBytes
Mario Limonciello1a680f32019-11-25 19:44:53 -06001583 *
1584 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001585 **/
1586GBytes *
1587fu_common_bytes_pad (GBytes *bytes, gsize sz)
1588{
1589 gsize bytes_sz;
1590
1591 g_return_val_if_fail (g_bytes_get_size (bytes) <= sz, NULL);
1592
1593 /* pad */
1594 bytes_sz = g_bytes_get_size (bytes);
1595 if (bytes_sz < sz) {
1596 const guint8 *data = g_bytes_get_data (bytes, NULL);
1597 guint8 *data_new = g_malloc (sz);
1598 memcpy (data_new, data, bytes_sz);
1599 memset (data_new + bytes_sz, 0xff, sz - bytes_sz);
1600 return g_bytes_new_take (data_new, sz);
1601 }
1602
1603 /* exactly right */
1604 return g_bytes_ref (bytes);
1605}
1606
1607/**
Richard Hughes484ee292019-03-22 16:10:50 +00001608 * fu_common_realpath:
1609 * @filename: a filename
1610 * @error: A #GError or %NULL
1611 *
1612 * Finds the canonicalized absolute filename for a path.
1613 *
1614 * Return value: A filename, or %NULL if invalid or not found
Mario Limonciello1a680f32019-11-25 19:44:53 -06001615 *
1616 * Since: 1.2.6
Richard Hughes484ee292019-03-22 16:10:50 +00001617 **/
1618gchar *
1619fu_common_realpath (const gchar *filename, GError **error)
1620{
1621 char full_tmp[PATH_MAX];
1622
1623 g_return_val_if_fail (filename != NULL, NULL);
1624
Richard Hughes8694dee2019-11-22 09:16:34 +00001625#ifdef HAVE_REALPATH
Richard Hughes484ee292019-03-22 16:10:50 +00001626 if (realpath (filename, full_tmp) == NULL) {
Richard Hughes8694dee2019-11-22 09:16:34 +00001627#else
1628 if (_fullpath (full_tmp, filename, sizeof(full_tmp)) == NULL) {
1629#endif
Richard Hughes484ee292019-03-22 16:10:50 +00001630 g_set_error (error,
1631 G_IO_ERROR,
1632 G_IO_ERROR_INVALID_DATA,
1633 "cannot resolve path: %s",
1634 strerror (errno));
1635 return NULL;
1636 }
Richard Hughes8694dee2019-11-22 09:16:34 +00001637 if (!g_file_test (full_tmp, G_FILE_TEST_EXISTS)) {
1638 g_set_error (error,
1639 G_IO_ERROR,
1640 G_IO_ERROR_INVALID_DATA,
1641 "cannot find path: %s",
1642 full_tmp);
1643 return NULL;
1644 }
Richard Hughes484ee292019-03-22 16:10:50 +00001645 return g_strdup (full_tmp);
1646}
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001647
1648/**
Richard Hughes5c508de2019-11-22 09:57:34 +00001649 * fu_common_fnmatch:
1650 * @pattern: a glob pattern, e.g. `*foo*`
1651 * @str: a string to match against the pattern, e.g. `bazfoobar`
1652 *
1653 * Matches a string against a glob pattern.
1654 *
1655 * Return value: %TRUE if the string matched
1656 *
1657 * Since: 1.3.5
1658 **/
1659gboolean
1660fu_common_fnmatch (const gchar *pattern, const gchar *str)
1661{
1662 g_return_val_if_fail (pattern != NULL, FALSE);
1663 g_return_val_if_fail (str != NULL, FALSE);
1664#ifdef HAVE_FNMATCH_H
1665 return fnmatch (pattern, str, FNM_NOESCAPE) == 0;
Richard Hughes45a00732019-11-22 16:57:14 +00001666#elif _WIN32
1667 g_return_val_if_fail (strlen (pattern) < MAX_PATH, FALSE);
1668 g_return_val_if_fail (strlen (str) < MAX_PATH, FALSE);
1669 return PathMatchSpecA (str, pattern);
Richard Hughes5c508de2019-11-22 09:57:34 +00001670#else
1671 return g_strcmp0 (pattern, str) == 0;
1672#endif
1673}
1674
1675/**
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001676 * fu_common_strnsplit:
1677 * @str: a string to split
1678 * @sz: size of @str
1679 * @delimiter: a string which specifies the places at which to split the string
1680 * @max_tokens: the maximum number of pieces to split @str into
1681 *
1682 * Splits a string into a maximum of @max_tokens pieces, using the given
1683 * delimiter. If @max_tokens is reached, the remainder of string is appended
1684 * to the last token.
1685 *
Richard Hughesa0d81c72019-11-27 11:41:54 +00001686 * Return value: (transfer full): a newly-allocated NULL-terminated array of strings
Mario Limonciello1a680f32019-11-25 19:44:53 -06001687 *
1688 * Since: 1.3.1
Richard Hughes7afd7cb2019-08-07 11:42:42 +01001689 **/
1690gchar **
1691fu_common_strnsplit (const gchar *str, gsize sz,
1692 const gchar *delimiter, gint max_tokens)
1693{
1694 if (str[sz - 1] != '\0') {
1695 g_autofree gchar *str2 = g_strndup (str, sz);
1696 return g_strsplit (str2, delimiter, max_tokens);
1697 }
1698 return g_strsplit (str, delimiter, max_tokens);
1699}
Richard Hughes5308ea42019-08-09 12:25:13 +01001700
1701/**
1702 * fu_memcpy_safe:
1703 * @dst: destination buffer
1704 * @dst_sz: maximum size of @dst, typically `sizeof(dst)`
1705 * @dst_offset: offset in bytes into @dst to copy to
1706 * @src: source buffer
1707 * @src_sz: maximum size of @dst, typically `sizeof(src)`
1708 * @src_offset: offset in bytes into @src to copy from
1709 * @n: number of bytes to copy from @src+@offset from
1710 * @error: A #GError or %NULL
1711 *
1712 * Copies some memory using memcpy in a safe way. Providing the buffer sizes
1713 * of both the destination and the source allows us to check for buffer overflow.
1714 *
1715 * Providing the buffer offsets also allows us to check reading past the end of
1716 * the source buffer. For this reason the caller should NEVER add an offset to
1717 * @src or @dst.
1718 *
1719 * You don't need to use this function in "obviously correct" cases, nor should
1720 * you use it when performance is a concern. Only us it when you're not sure if
1721 * malicious data from a device or firmware could cause memory corruption.
1722 *
1723 * Return value: %TRUE if the bytes were copied, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001724 *
1725 * Since: 1.3.1
Richard Hughes5308ea42019-08-09 12:25:13 +01001726 **/
1727gboolean
1728fu_memcpy_safe (guint8 *dst, gsize dst_sz, gsize dst_offset,
1729 const guint8 *src, gsize src_sz, gsize src_offset,
1730 gsize n, GError **error)
1731{
1732 if (n == 0)
1733 return TRUE;
1734
1735 if (n > src_sz) {
1736 g_set_error (error,
1737 FWUPD_ERROR,
1738 FWUPD_ERROR_READ,
1739 "attempted to read 0x%02x bytes from buffer of 0x%02x",
1740 (guint) n, (guint) src_sz);
1741 return FALSE;
1742 }
1743 if (n + src_offset > src_sz) {
1744 g_set_error (error,
1745 FWUPD_ERROR,
1746 FWUPD_ERROR_READ,
1747 "attempted to read 0x%02x bytes at offset 0x%02x from buffer of 0x%02x",
1748 (guint) n, (guint) src_offset, (guint) src_sz);
1749 return FALSE;
1750 }
1751 if (n > dst_sz) {
1752 g_set_error (error,
1753 FWUPD_ERROR,
1754 FWUPD_ERROR_WRITE,
1755 "attempted to write 0x%02x bytes to buffer of 0x%02x",
1756 (guint) n, (guint) dst_sz);
1757 return FALSE;
1758 }
1759 if (n + dst_offset > dst_sz) {
1760 g_set_error (error,
1761 FWUPD_ERROR,
1762 FWUPD_ERROR_WRITE,
1763 "attempted to write 0x%02x bytes at offset 0x%02x to buffer of 0x%02x",
1764 (guint) n, (guint) dst_offset, (guint) dst_sz);
1765 return FALSE;
1766 }
1767
1768 /* phew! */
1769 memcpy (dst + dst_offset, src + src_offset, n);
1770 return TRUE;
1771}
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001772
Richard Hughes80768f52019-10-22 07:19:14 +01001773/**
Richard Hughesc21a0b92019-10-24 12:24:37 +01001774 * fu_common_read_uint8_safe:
1775 * @buf: source buffer
1776 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1777 * @offset: offset in bytes into @buf to copy from
1778 * @value: (out) (allow-none): the parsed value
1779 * @error: A #GError or %NULL
1780 *
1781 * Read a value from a buffer in a safe way.
1782 *
1783 * You don't need to use this function in "obviously correct" cases, nor should
1784 * you use it when performance is a concern. Only us it when you're not sure if
1785 * malicious data from a device or firmware could cause memory corruption.
1786 *
1787 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001788 *
1789 * Since: 1.3.3
Richard Hughesc21a0b92019-10-24 12:24:37 +01001790 **/
1791gboolean
1792fu_common_read_uint8_safe (const guint8 *buf,
1793 gsize bufsz,
1794 gsize offset,
1795 guint8 *value,
1796 GError **error)
1797{
1798 guint8 tmp;
1799 if (!fu_memcpy_safe (&tmp, sizeof(tmp), 0x0, /* dst */
1800 buf, bufsz, offset, /* src */
1801 sizeof(tmp), error))
1802 return FALSE;
1803 if (value != NULL)
1804 *value = tmp;
1805 return TRUE;
1806}
1807
1808/**
Richard Hughes80768f52019-10-22 07:19:14 +01001809 * fu_common_read_uint16_safe:
1810 * @buf: source buffer
1811 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1812 * @offset: offset in bytes into @buf to copy from
1813 * @value: (out) (allow-none): the parsed value
1814 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1815 * @error: A #GError or %NULL
1816 *
1817 * Read a value from a buffer using a specified endian in a safe way.
1818 *
1819 * You don't need to use this function in "obviously correct" cases, nor should
1820 * you use it when performance is a concern. Only us it when you're not sure if
1821 * malicious data from a device or firmware could cause memory corruption.
1822 *
1823 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001824 *
1825 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001826 **/
1827gboolean
1828fu_common_read_uint16_safe (const guint8 *buf,
1829 gsize bufsz,
1830 gsize offset,
1831 guint16 *value,
1832 FuEndianType endian,
1833 GError **error)
1834{
1835 guint8 dst[2] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001836 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001837 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001838 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001839 return FALSE;
1840 if (value != NULL)
1841 *value = fu_common_read_uint16 (dst, endian);
1842 return TRUE;
1843}
1844
1845/**
1846 * fu_common_read_uint32_safe:
1847 * @buf: source buffer
1848 * @bufsz: maximum size of @buf, typically `sizeof(buf)`
1849 * @offset: offset in bytes into @buf to copy from
1850 * @value: (out) (allow-none): the parsed value
1851 * @endian: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
1852 * @error: A #GError or %NULL
1853 *
1854 * Read a value from a buffer using a specified endian in a safe way.
1855 *
1856 * You don't need to use this function in "obviously correct" cases, nor should
1857 * you use it when performance is a concern. Only us it when you're not sure if
1858 * malicious data from a device or firmware could cause memory corruption.
1859 *
1860 * Return value: %TRUE if @value was set, %FALSE otherwise
Mario Limonciello1a680f32019-11-25 19:44:53 -06001861 *
1862 * Since: 1.3.3
Richard Hughes80768f52019-10-22 07:19:14 +01001863 **/
1864gboolean
1865fu_common_read_uint32_safe (const guint8 *buf,
1866 gsize bufsz,
1867 gsize offset,
1868 guint32 *value,
1869 FuEndianType endian,
1870 GError **error)
1871{
1872 guint8 dst[4] = { 0x0 };
Richard Hughes7d01ac92019-10-23 14:31:46 +01001873 if (!fu_memcpy_safe (dst, sizeof(dst), 0x0, /* dst */
Richard Hughes80768f52019-10-22 07:19:14 +01001874 buf, bufsz, offset, /* src */
Richard Hughes7d01ac92019-10-23 14:31:46 +01001875 sizeof(dst), error))
Richard Hughes80768f52019-10-22 07:19:14 +01001876 return FALSE;
1877 if (value != NULL)
1878 *value = fu_common_read_uint32 (dst, endian);
1879 return TRUE;
1880}
1881
Mario Limonciello1a680f32019-11-25 19:44:53 -06001882/**
1883 * fu_byte_array_append_uint8:
1884 * @array: A #GByteArray
1885 * @data: #guint8
1886 *
1887 * Adds a 8 bit integer to a byte array
1888 *
1889 * Since: 1.3.1
1890 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001891void
1892fu_byte_array_append_uint8 (GByteArray *array, guint8 data)
1893{
1894 g_byte_array_append (array, &data, sizeof(data));
1895}
1896
Mario Limonciello1a680f32019-11-25 19:44:53 -06001897/**
1898 * fu_byte_array_append_uint16:
1899 * @array: A #GByteArray
1900 * @data: #guint16
1901 * @endian: #FuEndianType
1902 *
1903 * Adds a 16 bit integer to a byte array
1904 *
1905 * Since: 1.3.1
1906 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001907void
1908fu_byte_array_append_uint16 (GByteArray *array, guint16 data, FuEndianType endian)
1909{
1910 guint8 buf[2];
1911 fu_common_write_uint16 (buf, data, endian);
1912 g_byte_array_append (array, buf, sizeof(buf));
1913}
1914
Mario Limonciello1a680f32019-11-25 19:44:53 -06001915/**
1916 * fu_byte_array_append_uint32:
1917 * @array: A #GByteArray
1918 * @data: #guint32
1919 * @endian: #FuEndianType
1920 *
1921 * Adds a 32 bit integer to a byte array
1922 *
1923 * Since: 1.3.1
1924 **/
Richard Hughes37c6a7b2019-08-14 21:57:43 +01001925void
1926fu_byte_array_append_uint32 (GByteArray *array, guint32 data, FuEndianType endian)
1927{
1928 guint8 buf[4];
1929 fu_common_write_uint32 (buf, data, endian);
1930 g_byte_array_append (array, buf, sizeof(buf));
1931}
Mario Limonciello9dce1f72020-02-04 09:12:52 -06001932
1933/**
1934 * fu_common_kernel_locked_down:
1935 *
1936 * Determines if kernel lockdown in effect
1937 *
1938 * Since: 1.3.8
1939 **/
1940gboolean
1941fu_common_kernel_locked_down (void)
1942{
1943#ifndef _WIN32
1944 gsize len = 0;
1945 g_autofree gchar *dir = fu_common_get_path (FU_PATH_KIND_SYSFSDIR_SECURITY);
1946 g_autofree gchar *fname = g_build_filename (dir, "lockdown", NULL);
1947 g_autofree gchar *data = NULL;
1948 g_auto(GStrv) options = NULL;
1949
1950 if (!g_file_test (fname, G_FILE_TEST_EXISTS))
1951 return FALSE;
1952 if (!g_file_get_contents (fname, &data, &len, NULL))
1953 return FALSE;
1954 if (len < 1)
1955 return FALSE;
1956 options = g_strsplit (data, " ", -1);
1957 for (guint i = 0; options[i] != NULL; i++) {
1958 if (g_strcmp0 (options[i], "[none]") == 0)
1959 return FALSE;
1960 }
1961 return TRUE;
1962#else
1963 return FALSE;
1964#endif
1965}