blob: e88e580e34deeee8751a7ffaed54a0a666ac164e [file] [log] [blame]
Richard Hughes943d2c92017-06-21 09:04:39 +01001/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include <config.h>
23
24#include <gio/gunixinputstream.h>
Richard Hughes954dd9f2017-08-08 13:36:25 +010025#include <glib/gstdio.h>
26
Richard Hughes94f939a2017-08-08 12:21:39 +010027#include <archive_entry.h>
28#include <archive.h>
Richard Hughes7ee42fe2017-08-15 14:06:21 +010029#include <errno.h>
Richard Hughesae252cd2017-12-08 10:48:15 +000030#include <string.h>
Richard Hughes943d2c92017-06-21 09:04:39 +010031
32#include "fwupd-error.h"
33
34#include "fu-common.h"
35
36/**
Richard Hughes4eada342017-10-03 21:20:32 +010037 * SECTION:fu-common
38 * @short_description: common functionality for plugins to use
39 *
40 * Helper functions that can be used by the daemon and plugins.
41 *
42 * See also: #FuPlugin
43 */
44
45/**
Richard Hughes954dd9f2017-08-08 13:36:25 +010046 * fu_common_rmtree:
47 * @directory: a directory name
48 * @error: A #GError or %NULL
49 *
50 * Recursively removes a directory.
51 *
52 * Returns: %TRUE for success, %FALSE otherwise
53 **/
54gboolean
55fu_common_rmtree (const gchar *directory, GError **error)
56{
57 const gchar *filename;
58 g_autoptr(GDir) dir = NULL;
59
60 /* try to open */
Richard Hughes455fdd32017-08-16 12:26:44 +010061 g_debug ("removing %s", directory);
Richard Hughes954dd9f2017-08-08 13:36:25 +010062 dir = g_dir_open (directory, 0, error);
63 if (dir == NULL)
64 return FALSE;
65
66 /* find each */
67 while ((filename = g_dir_read_name (dir))) {
68 g_autofree gchar *src = NULL;
69 src = g_build_filename (directory, filename, NULL);
70 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
71 if (!fu_common_rmtree (src, error))
72 return FALSE;
73 } else {
74 if (g_unlink (src) != 0) {
75 g_set_error (error,
76 FWUPD_ERROR,
77 FWUPD_ERROR_INTERNAL,
78 "Failed to delete: %s", src);
79 return FALSE;
80 }
81 }
82 }
83 if (g_remove (directory) != 0) {
84 g_set_error (error,
85 FWUPD_ERROR,
86 FWUPD_ERROR_INTERNAL,
87 "Failed to delete: %s", directory);
88 return FALSE;
89 }
90 return TRUE;
91}
92
Richard Hughes89e968b2018-03-07 10:01:08 +000093static gboolean
94fu_common_get_file_list_internal (GPtrArray *files, const gchar *directory, GError **error)
95{
96 const gchar *filename;
97 g_autoptr(GDir) dir = NULL;
98
99 /* try to open */
100 dir = g_dir_open (directory, 0, error);
101 if (dir == NULL)
102 return FALSE;
103
104 /* find each */
105 while ((filename = g_dir_read_name (dir))) {
106 g_autofree gchar *src = g_build_filename (directory, filename, NULL);
107 if (g_file_test (src, G_FILE_TEST_IS_DIR)) {
108 if (!fu_common_get_file_list_internal (files, src, error))
109 return FALSE;
110 } else {
111 g_ptr_array_add (files, g_steal_pointer (&src));
112 }
113 }
114 return TRUE;
115
116}
117
118/**
119 * fu_common_get_files_recursive:
120 * @directory: a directory name
121 * @error: A #GError or %NULL
122 *
123 * Returns every file found under @directory, and any subdirectory.
124 * If any path under @directory cannot be accessed due to permissions an error
125 * will be returned.
126 *
127 * Returns: (element-type: utf8) (transfer container): array of files, or %NULL for error
128 **/
129GPtrArray *
130fu_common_get_files_recursive (const gchar *path, GError **error)
131{
132 g_autoptr(GPtrArray) files = g_ptr_array_new_with_free_func (g_free);
133 if (!fu_common_get_file_list_internal (files, path, error))
134 return NULL;
135 return g_steal_pointer (&files);
136}
Richard Hughes954dd9f2017-08-08 13:36:25 +0100137/**
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100138 * fu_common_mkdir_parent:
139 * @filename: A full pathname
140 * @error: A #GError, or %NULL
141 *
142 * Creates any required directories, including any parent directories.
143 *
144 * Returns: %TRUE for success
145 **/
146gboolean
147fu_common_mkdir_parent (const gchar *filename, GError **error)
148{
149 g_autofree gchar *parent = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100150
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100151 parent = g_path_get_dirname (filename);
Richard Hughes455fdd32017-08-16 12:26:44 +0100152 g_debug ("creating path %s", parent);
Richard Hughes7ee42fe2017-08-15 14:06:21 +0100153 if (g_mkdir_with_parents (parent, 0755) == -1) {
154 g_set_error (error,
155 FWUPD_ERROR,
156 FWUPD_ERROR_INTERNAL,
157 "Failed to create '%s': %s",
158 parent, g_strerror (errno));
159 return FALSE;
160 }
161 return TRUE;
162}
163
164/**
Richard Hughes943d2c92017-06-21 09:04:39 +0100165 * fu_common_set_contents_bytes:
166 * @filename: A filename
167 * @bytes: The data to write
168 * @error: A #GError, or %NULL
169 *
170 * Writes a blob of data to a filename, creating the parent directories as
171 * required.
172 *
173 * Returns: %TRUE for success
174 **/
175gboolean
176fu_common_set_contents_bytes (const gchar *filename, GBytes *bytes, GError **error)
177{
178 const gchar *data;
179 gsize size;
180 g_autoptr(GFile) file = NULL;
181 g_autoptr(GFile) file_parent = NULL;
182
183 file = g_file_new_for_path (filename);
184 file_parent = g_file_get_parent (file);
185 if (!g_file_query_exists (file_parent, NULL)) {
186 if (!g_file_make_directory_with_parents (file_parent, NULL, error))
187 return FALSE;
188 }
189 data = g_bytes_get_data (bytes, &size);
Richard Hughes455fdd32017-08-16 12:26:44 +0100190 g_debug ("writing %s with %" G_GSIZE_FORMAT " bytes", filename, size);
Richard Hughes943d2c92017-06-21 09:04:39 +0100191 return g_file_set_contents (filename, data, size, error);
192}
193
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100194/**
195 * fu_common_get_contents_bytes:
196 * @filename: A filename
197 * @error: A #GError, or %NULL
198 *
199 * Reads a blob of data from a file.
200 *
201 * Returns: a #GBytes, or %NULL for failure
202 **/
203GBytes *
204fu_common_get_contents_bytes (const gchar *filename, GError **error)
205{
206 gchar *data = NULL;
207 gsize len = 0;
208 if (!g_file_get_contents (filename, &data, &len, error))
209 return NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100210 g_debug ("reading %s with %" G_GSIZE_FORMAT " bytes", filename, len);
Richard Hughesd0d2ae62017-08-08 12:22:30 +0100211 return g_bytes_new_take (data, len);
212}
Richard Hughes943d2c92017-06-21 09:04:39 +0100213
214/**
215 * fu_common_get_contents_fd:
216 * @fd: A file descriptor
217 * @count: The maximum number of bytes to read
218 * @error: A #GError, or %NULL
219 *
220 * Reads a blob from a specific file descriptor.
221 *
222 * Note: this will close the fd when done
223 *
Richard Hughes4eada342017-10-03 21:20:32 +0100224 * Returns: (transfer full): a #GBytes, or %NULL
Richard Hughes943d2c92017-06-21 09:04:39 +0100225 **/
226GBytes *
227fu_common_get_contents_fd (gint fd, gsize count, GError **error)
228{
229 g_autoptr(GBytes) blob = NULL;
230 g_autoptr(GError) error_local = NULL;
231 g_autoptr(GInputStream) stream = NULL;
232
233 g_return_val_if_fail (fd > 0, NULL);
Richard Hughes943d2c92017-06-21 09:04:39 +0100234 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
235
Richard Hughes919f8ab2018-02-14 10:24:56 +0000236 /* this is invalid */
237 if (count == 0) {
238 g_set_error_literal (error,
239 FWUPD_ERROR,
240 FWUPD_ERROR_NOT_SUPPORTED,
241 "A maximum read size must be specified");
242 return NULL;
243 }
244
Richard Hughes943d2c92017-06-21 09:04:39 +0100245 /* read the entire fd to a data blob */
246 stream = g_unix_input_stream_new (fd, TRUE);
247 blob = g_input_stream_read_bytes (stream, count, NULL, &error_local);
248 if (blob == NULL) {
249 g_set_error_literal (error,
250 FWUPD_ERROR,
251 FWUPD_ERROR_INVALID_FILE,
252 error_local->message);
253 return NULL;
254 }
255 return g_steal_pointer (&blob);
256}
Richard Hughes94f939a2017-08-08 12:21:39 +0100257
258static gboolean
259fu_common_extract_archive_entry (struct archive_entry *entry, const gchar *dir)
260{
261 const gchar *tmp;
262 g_autofree gchar *buf = NULL;
263
264 /* no output file */
265 if (archive_entry_pathname (entry) == NULL)
266 return FALSE;
267
268 /* update output path */
269 tmp = archive_entry_pathname (entry);
270 buf = g_build_filename (dir, tmp, NULL);
271 archive_entry_update_pathname_utf8 (entry, buf);
272 return TRUE;
273}
274
275/**
276 * fu_common_extract_archive:
277 * @blob: a #GBytes archive as a blob
Richard Hughes4eada342017-10-03 21:20:32 +0100278 * @dir: a directory name to extract to
Richard Hughes94f939a2017-08-08 12:21:39 +0100279 * @error: A #GError, or %NULL
280 *
281 * Extracts an achive to a directory.
282 *
283 * Returns: %TRUE for success
284 **/
285gboolean
286fu_common_extract_archive (GBytes *blob, const gchar *dir, GError **error)
287{
288 gboolean ret = TRUE;
289 int r;
290 struct archive *arch = NULL;
291 struct archive_entry *entry;
292
293 /* decompress anything matching either glob */
Richard Hughes455fdd32017-08-16 12:26:44 +0100294 g_debug ("decompressing into %s", dir);
Richard Hughes94f939a2017-08-08 12:21:39 +0100295 arch = archive_read_new ();
296 archive_read_support_format_all (arch);
297 archive_read_support_filter_all (arch);
298 r = archive_read_open_memory (arch,
299 (void *) g_bytes_get_data (blob, NULL),
300 (size_t) g_bytes_get_size (blob));
301 if (r != 0) {
302 ret = FALSE;
303 g_set_error (error,
304 FWUPD_ERROR,
305 FWUPD_ERROR_INTERNAL,
306 "Cannot open: %s",
307 archive_error_string (arch));
308 goto out;
309 }
310 for (;;) {
311 gboolean valid;
Richard Hughes94f939a2017-08-08 12:21:39 +0100312 r = archive_read_next_header (arch, &entry);
313 if (r == ARCHIVE_EOF)
314 break;
315 if (r != ARCHIVE_OK) {
316 ret = FALSE;
317 g_set_error (error,
318 FWUPD_ERROR,
319 FWUPD_ERROR_INTERNAL,
320 "Cannot read header: %s",
321 archive_error_string (arch));
322 goto out;
323 }
324
325 /* only extract if valid */
326 valid = fu_common_extract_archive_entry (entry, dir);
327 if (!valid)
328 continue;
329 r = archive_read_extract (arch, entry, 0);
330 if (r != ARCHIVE_OK) {
331 ret = FALSE;
332 g_set_error (error,
333 FWUPD_ERROR,
334 FWUPD_ERROR_INTERNAL,
335 "Cannot extract: %s",
336 archive_error_string (arch));
337 goto out;
338 }
339 }
340out:
341 if (arch != NULL) {
342 archive_read_close (arch);
343 archive_read_free (arch);
344 }
345 return ret;
346}
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100347
348static void
Yehezkel Bernate43f7fb2017-08-30 12:09:34 +0300349fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...) G_GNUC_PRINTF (2, 3);
350
351static void
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100352fu_common_add_argv (GPtrArray *argv, const gchar *fmt, ...)
353{
354 va_list args;
355 g_autofree gchar *tmp = NULL;
356 g_auto(GStrv) split = NULL;
357
358 va_start (args, fmt);
359 tmp = g_strdup_vprintf (fmt, args);
360 va_end (args);
361
362 split = g_strsplit (tmp, " ", -1);
363 for (guint i = 0; split[i] != NULL; i++)
364 g_ptr_array_add (argv, g_strdup (split[i]));
365}
366
367/**
368 * fu_common_firmware_builder:
369 * @bytes: The data to use
Richard Hughes4eada342017-10-03 21:20:32 +0100370 * @script_fn: Name of the script to run in the tarball, e.g. `startup.sh`
371 * @output_fn: Name of the generated firmware, e.g. `firmware.bin`
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100372 * @error: A #GError, or %NULL
373 *
374 * Builds a firmware file using tools from the host session in a bubblewrap
375 * jail. Several things happen during build:
376 *
377 * 1. The @bytes data is untarred to a temporary location
378 * 2. A bubblewrap container is set up
379 * 3. The startup.sh script is run inside the container
380 * 4. The firmware.bin is extracted from the container
381 * 5. The temporary location is deleted
382 *
383 * Returns: a new #GBytes, or %NULL for error
384 **/
385GBytes *
386fu_common_firmware_builder (GBytes *bytes,
387 const gchar *script_fn,
388 const gchar *output_fn,
389 GError **error)
390{
391 gint rc = 0;
392 g_autofree gchar *argv_str = NULL;
393 g_autofree gchar *localstatedir = NULL;
394 g_autofree gchar *output2_fn = NULL;
395 g_autofree gchar *standard_error = NULL;
396 g_autofree gchar *standard_output = NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100397 g_autofree gchar *tmpdir = NULL;
398 g_autoptr(GBytes) firmware_blob = NULL;
399 g_autoptr(GPtrArray) argv = g_ptr_array_new_with_free_func (g_free);
400
401 g_return_val_if_fail (bytes != NULL, NULL);
402 g_return_val_if_fail (script_fn != NULL, NULL);
403 g_return_val_if_fail (output_fn != NULL, NULL);
404 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
405
406 /* untar file to temp location */
407 tmpdir = g_dir_make_tmp ("fwupd-gen-XXXXXX", error);
408 if (tmpdir == NULL)
409 return NULL;
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100410 if (!fu_common_extract_archive (bytes, tmpdir, error))
411 return NULL;
412
413 /* this is shared with the plugins */
414 localstatedir = g_build_filename (LOCALSTATEDIR, "lib", "fwupd", "builder", NULL);
415
416 /* launch bubblewrap and generate firmware */
Richard Hughesf6f72a42017-08-09 16:25:25 +0100417 g_ptr_array_add (argv, g_strdup ("bwrap"));
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100418 fu_common_add_argv (argv, "--die-with-parent");
419 fu_common_add_argv (argv, "--ro-bind /usr /usr");
420 fu_common_add_argv (argv, "--dir /tmp");
421 fu_common_add_argv (argv, "--dir /var");
422 fu_common_add_argv (argv, "--bind %s /tmp", tmpdir);
423 if (g_file_test (localstatedir, G_FILE_TEST_EXISTS))
424 fu_common_add_argv (argv, "--ro-bind %s /boot", localstatedir);
425 fu_common_add_argv (argv, "--dev /dev");
426 fu_common_add_argv (argv, "--symlink usr/lib /lib");
427 fu_common_add_argv (argv, "--symlink usr/lib64 /lib64");
428 fu_common_add_argv (argv, "--symlink usr/bin /bin");
429 fu_common_add_argv (argv, "--symlink usr/sbin /sbin");
430 fu_common_add_argv (argv, "--chdir /tmp");
431 fu_common_add_argv (argv, "--unshare-all");
Richard Hughes443e4092017-08-09 16:07:31 +0100432 fu_common_add_argv (argv, "/tmp/%s", script_fn);
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100433 g_ptr_array_add (argv, NULL);
434 argv_str = g_strjoinv (" ", (gchar **) argv->pdata);
435 g_debug ("running '%s' in %s", argv_str, tmpdir);
436 if (!g_spawn_sync ("/tmp",
437 (gchar **) argv->pdata,
438 NULL,
Richard Hughesf6f72a42017-08-09 16:25:25 +0100439 G_SPAWN_SEARCH_PATH,
Richard Hughes41cbe2a2017-08-08 14:13:35 +0100440 NULL, NULL, /* child_setup */
441 &standard_output,
442 &standard_error,
443 &rc,
444 error)) {
445 g_prefix_error (error, "failed to run '%s': ", argv_str);
446 return NULL;
447 }
448 if (standard_output != NULL && standard_output[0] != '\0')
449 g_debug ("console output was: %s", standard_output);
450 if (rc != 0) {
451 g_set_error (error,
452 FWUPD_ERROR,
453 FWUPD_ERROR_INTERNAL,
454 "failed to build firmware: %s",
455 standard_error);
456 return NULL;
457 }
458
459 /* get generated file */
460 output2_fn = g_build_filename (tmpdir, output_fn, NULL);
461 firmware_blob = fu_common_get_contents_bytes (output2_fn, error);
462 if (firmware_blob == NULL)
463 return NULL;
464
465 /* cleanup temp directory */
466 if (!fu_common_rmtree (tmpdir, error))
467 return NULL;
468
469 /* success */
470 return g_steal_pointer (&firmware_blob);
471}
Richard Hughes049ccc82017-08-09 15:26:56 +0100472
473typedef struct {
474 FuOutputHandler handler_cb;
475 gpointer handler_user_data;
476 GMainLoop *loop;
477 GSource *source;
478 GInputStream *stream;
479 GCancellable *cancellable;
480} FuCommonSpawnHelper;
481
482static void fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper);
483
484static gboolean
485fu_common_spawn_source_pollable_cb (GObject *stream, gpointer user_data)
486{
487 FuCommonSpawnHelper *helper = (FuCommonSpawnHelper *) user_data;
488 gchar buffer[1024];
489 gssize sz;
490 g_auto(GStrv) split = NULL;
491 g_autoptr(GError) error = NULL;
492
493 /* read from stream */
494 sz = g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream),
495 buffer,
496 sizeof(buffer) - 1,
497 NULL,
498 &error);
499 if (sz < 0) {
Richard Hughes67cbe642017-08-16 12:26:14 +0100500 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
501 g_warning ("failed to get read from nonblocking fd: %s",
502 error->message);
503 }
Richard Hughes049ccc82017-08-09 15:26:56 +0100504 return G_SOURCE_REMOVE;
505 }
506
507 /* no read possible */
508 if (sz == 0)
509 g_main_loop_quit (helper->loop);
510
511 /* emit lines */
512 if (helper->handler_cb != NULL) {
513 buffer[sz] = '\0';
514 split = g_strsplit (buffer, "\n", -1);
515 for (guint i = 0; split[i] != NULL; i++) {
516 if (split[i][0] == '\0')
517 continue;
518 helper->handler_cb (split[i], helper->handler_user_data);
519 }
520 }
521
522 /* set up the source for the next read */
523 fu_common_spawn_create_pollable_source (helper);
524 return G_SOURCE_REMOVE;
525}
526
527static void
528fu_common_spawn_create_pollable_source (FuCommonSpawnHelper *helper)
529{
530 if (helper->source != NULL)
531 g_source_destroy (helper->source);
532 helper->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (helper->stream),
533 helper->cancellable);
534 g_source_attach (helper->source, NULL);
535 g_source_set_callback (helper->source, (GSourceFunc) fu_common_spawn_source_pollable_cb, helper, NULL);
536}
537
538static void
539fu_common_spawn_helper_free (FuCommonSpawnHelper *helper)
540{
541 if (helper->stream != NULL)
542 g_object_unref (helper->stream);
543 if (helper->source != NULL)
544 g_source_destroy (helper->source);
545 if (helper->loop != NULL)
546 g_main_loop_unref (helper->loop);
547 g_free (helper);
548}
549
Mario Limoncielloa98df552018-04-16 12:15:51 -0500550#pragma clang diagnostic push
551#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes049ccc82017-08-09 15:26:56 +0100552G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCommonSpawnHelper, fu_common_spawn_helper_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -0500553#pragma clang diagnostic pop
Richard Hughes049ccc82017-08-09 15:26:56 +0100554
555/**
556 * fu_common_spawn_sync:
557 * @argv: The argument list to run
Richard Hughes4eada342017-10-03 21:20:32 +0100558 * @handler_cb: (scope call): A #FuOutputHandler or %NULL
559 * @handler_user_data: the user data to pass to @handler_cb
Richard Hughes049ccc82017-08-09 15:26:56 +0100560 * @cancellable: a #GCancellable, or %NULL
561 * @error: A #GError or %NULL
562 *
563 * Runs a subprocess and waits for it to exit. Any output on standard out or
564 * standard error will be forwarded to @handler_cb as whole lines.
565 *
566 * Returns: %TRUE for success
567 **/
568gboolean
569fu_common_spawn_sync (const gchar * const * argv,
570 FuOutputHandler handler_cb,
571 gpointer handler_user_data,
572 GCancellable *cancellable, GError **error)
573{
574 g_autoptr(FuCommonSpawnHelper) helper = NULL;
575 g_autoptr(GSubprocess) subprocess = NULL;
Richard Hughes455fdd32017-08-16 12:26:44 +0100576 g_autofree gchar *argv_str = NULL;
Richard Hughes049ccc82017-08-09 15:26:56 +0100577
578 /* create subprocess */
Richard Hughes455fdd32017-08-16 12:26:44 +0100579 argv_str = g_strjoinv (" ", (gchar **) argv);
580 g_debug ("running '%s'", argv_str);
Richard Hughes049ccc82017-08-09 15:26:56 +0100581 subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_PIPE |
582 G_SUBPROCESS_FLAGS_STDERR_MERGE, error);
583 if (subprocess == NULL)
584 return FALSE;
585
586 /* watch for process to exit */
587 helper = g_new0 (FuCommonSpawnHelper, 1);
588 helper->handler_cb = handler_cb;
589 helper->handler_user_data = handler_user_data;
590 helper->loop = g_main_loop_new (NULL, FALSE);
591 helper->stream = g_subprocess_get_stdout_pipe (subprocess);
592 helper->cancellable = cancellable;
593 fu_common_spawn_create_pollable_source (helper);
594 g_main_loop_run (helper->loop);
595 return g_subprocess_wait_check (subprocess, cancellable, error);
596}
Richard Hughesae252cd2017-12-08 10:48:15 +0000597
598/**
599 * fu_common_write_uint16:
600 * @buf: A writable buffer
601 * @val_native: a value in host byte-order
602 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
603 *
604 * Writes a value to a buffer using a specified endian.
605 **/
606void
607fu_common_write_uint16 (guint8 *buf, guint16 val_native, FuEndianType endian)
608{
609 guint16 val_hw;
610 switch (endian) {
611 case G_BIG_ENDIAN:
612 val_hw = GUINT16_TO_BE(val_native);
613 break;
614 case G_LITTLE_ENDIAN:
615 val_hw = GUINT16_TO_LE(val_native);
616 break;
617 default:
618 g_assert_not_reached ();
619 }
620 memcpy (buf, &val_hw, sizeof(val_hw));
621}
622
623/**
624 * fu_common_write_uint32:
625 * @buf: A writable buffer
626 * @val_native: a value in host byte-order
627 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
628 *
629 * Writes a value to a buffer using a specified endian.
630 **/
631void
632fu_common_write_uint32 (guint8 *buf, guint32 val_native, FuEndianType endian)
633{
634 guint32 val_hw;
635 switch (endian) {
636 case G_BIG_ENDIAN:
637 val_hw = GUINT32_TO_BE(val_native);
638 break;
639 case G_LITTLE_ENDIAN:
640 val_hw = GUINT32_TO_LE(val_native);
641 break;
642 default:
643 g_assert_not_reached ();
644 }
645 memcpy (buf, &val_hw, sizeof(val_hw));
646}
647
648/**
649 * fu_common_read_uint16:
650 * @buf: A readable buffer
651 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
652 *
653 * Read a value from a buffer using a specified endian.
654 *
655 * Returns: a value in host byte-order
656 **/
657guint16
658fu_common_read_uint16 (const guint8 *buf, FuEndianType endian)
659{
660 guint16 val_hw, val_native;
661 memcpy (&val_hw, buf, sizeof(val_hw));
662 switch (endian) {
663 case G_BIG_ENDIAN:
664 val_native = GUINT16_FROM_BE(val_hw);
665 break;
666 case G_LITTLE_ENDIAN:
667 val_native = GUINT16_FROM_LE(val_hw);
668 break;
669 default:
670 g_assert_not_reached ();
671 }
672 return val_native;
673}
674
675/**
676 * fu_common_read_uint32:
677 * @buf: A readable buffer
678 * @error: A #FuEndianType, e.g. %G_LITTLE_ENDIAN
679 *
680 * Read a value from a buffer using a specified endian.
681 *
682 * Returns: a value in host byte-order
683 **/
684guint32
685fu_common_read_uint32 (const guint8 *buf, FuEndianType endian)
686{
687 guint32 val_hw, val_native;
688 memcpy (&val_hw, buf, sizeof(val_hw));
689 switch (endian) {
690 case G_BIG_ENDIAN:
691 val_native = GUINT32_FROM_BE(val_hw);
692 break;
693 case G_LITTLE_ENDIAN:
694 val_native = GUINT32_FROM_LE(val_hw);
695 break;
696 default:
697 g_assert_not_reached ();
698 }
699 return val_native;
700}