blob: 1109afc50382b28b9d30ad306b460332e05b435f [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes5c9b1fc2021-01-07 14:20:49 +00002 * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
Richard Hughesb5976832018-05-18 10:02:09 +01003 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughesb5976832018-05-18 10:02:09 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuMain"
8
Richard Hughesb5976832018-05-18 10:02:09 +01009#include "config.h"
10
11#include <fwupd.h>
Mario Limonciello3f243a92019-01-21 22:05:23 -060012#include <glib/gstdio.h>
Richard Hughesb5976832018-05-18 10:02:09 +010013#include <glib/gi18n.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000014#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +010015#include <glib-unix.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000016#endif
Richard Hughes7d82a092019-11-22 09:42:31 +000017#include <fcntl.h>
Richard Hughesb5976832018-05-18 10:02:09 +010018#include <locale.h>
19#include <stdlib.h>
20#include <unistd.h>
Richard Hughesd5aab652020-02-25 12:47:50 +000021#include <jcat.h>
Richard Hughesb5976832018-05-18 10:02:09 +010022
Mario Limonciello7a3df4b2019-01-31 10:27:22 -060023#include "fu-device-private.h"
Richard Hughes98ca9932018-05-18 10:24:07 +010024#include "fu-engine.h"
Mario Limonciello96a0dd52019-02-25 13:50:03 -060025#include "fu-history.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010026#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010027#include "fu-progressbar.h"
Richard Hughesf58ac732020-05-12 15:23:44 +010028#include "fu-security-attrs-private.h"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060029#include "fu-smbios-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010030#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050031#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060032#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060033#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010034
Richard Hughes3d005222019-05-17 14:02:41 +010035#ifdef HAVE_SYSTEMD
36#include "fu-systemd.h"
37#endif
38
Richard Hughesb5976832018-05-18 10:02:09 +010039/* custom return code */
40#define EXIT_NOTHING_TO_DO 2
41
Mario Limonciello3f243a92019-01-21 22:05:23 -060042typedef enum {
43 FU_UTIL_OPERATION_UNKNOWN,
44 FU_UTIL_OPERATION_UPDATE,
45 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000046 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060047 FU_UTIL_OPERATION_LAST
48} FuUtilOperation;
49
Richard Hughesc77e1112019-03-01 10:16:26 +000050struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010051 GCancellable *cancellable;
Richard Hughes6ed25f52021-01-10 19:27:33 +000052 GMainContext *main_ctx;
Richard Hughesb5976832018-05-18 10:02:09 +010053 GMainLoop *loop;
54 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010055 FuEngine *engine;
Richard Hughesdf89cd52020-06-26 20:25:18 +010056 FuEngineRequest *request;
Richard Hughesb5976832018-05-18 10:02:09 +010057 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060058 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050059 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000060 gboolean prepare_blob;
61 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060062 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010063 FwupdInstallFlags flags;
Richard Hughes5c82b942020-09-14 12:24:06 +010064 gboolean show_all;
Richard Hughes0e46b222019-09-05 12:13:35 +010065 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050066 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060067 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050068 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060069 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060070 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010071 FwupdDeviceFlags filter_include;
72 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000073};
Richard Hughesb5976832018-05-18 10:02:09 +010074
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050075static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060076fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
77{
78 g_autoptr(JsonBuilder) builder = NULL;
79 g_autoptr(JsonGenerator) json_generator = NULL;
80 g_autoptr(JsonNode) json_root = NULL;
81 g_autoptr(GPtrArray) devices = NULL;
82 g_autofree gchar *state = NULL;
83 g_autofree gchar *dirname = NULL;
84 g_autofree gchar *filename = NULL;
85
86 if (!priv->enable_json_state)
87 return TRUE;
88
89 devices = fu_engine_get_devices (priv->engine, error);
90 if (devices == NULL)
91 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000092 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060093
94 /* create header */
95 builder = json_builder_new ();
96 json_builder_begin_object (builder);
97
98 /* add each device */
99 json_builder_set_member_name (builder, "Devices");
100 json_builder_begin_array (builder);
101 for (guint i = 0; i < devices->len; i++) {
102 FwupdDevice *dev = g_ptr_array_index (devices, i);
103 json_builder_begin_object (builder);
104 fwupd_device_to_json (dev, builder);
105 json_builder_end_object (builder);
106 }
107 json_builder_end_array (builder);
108 json_builder_end_object (builder);
109
110 /* export as a string */
111 json_root = json_builder_get_root (builder);
112 json_generator = json_generator_new ();
113 json_generator_set_pretty (json_generator, TRUE);
114 json_generator_set_root (json_generator, json_root);
115 state = json_generator_to_data (json_generator, NULL);
116 if (state == NULL)
117 return FALSE;
118 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
119 filename = g_build_filename (dirname, "state.json", NULL);
120 return g_file_set_contents (filename, state, -1, error);
121}
122
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100123static void
124fu_util_show_plugin_warnings (FuUtilPrivate *priv)
125{
126 FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE;
127 GPtrArray *plugins;
128
129 /* get a superset so we do not show the same message more than once */
130 plugins = fu_engine_get_plugins (priv->engine);
131 for (guint i = 0; i < plugins->len; i++) {
132 FwupdPlugin *plugin = g_ptr_array_index (plugins, i);
133 if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING))
134 continue;
135 flags |= fwupd_plugin_get_flags (plugin);
136 }
137
138 /* never show these, they're way too generic */
139 flags &= ~FWUPD_PLUGIN_FLAG_DISABLED;
140 flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE;
Richard Hughesd94286b2021-03-01 21:12:18 +0000141 flags &= ~FWUPD_PLUGIN_FLAG_REQUIRE_HWID;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100142
143 /* print */
144 for (guint i = 0; i < 64; i++) {
Richard Hughes1df9b822020-10-30 15:18:45 +0000145 FwupdPluginFlags flag = (guint64) 1 << i;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100146 const gchar *tmp;
147 g_autofree gchar *fmt = NULL;
Richard Hughes1df9b822020-10-30 15:18:45 +0000148 g_autofree gchar *url= NULL;
149 g_autoptr(GString) str = g_string_new (NULL);
150 if ((flags & flag) == 0)
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100151 continue;
152 tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i);
153 if (tmp == NULL)
154 continue;
155 /* TRANSLATORS: this is a prefix on the console */
156 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes1df9b822020-10-30 15:18:45 +0000157 g_string_append_printf (str, "%s %s\n", fmt, tmp);
158
159 url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
160 fwupd_plugin_flag_to_string (flag));
161 g_string_append (str, " ");
162 /* TRANSLATORS: %s is a link to a website */
163 g_string_append_printf (str, _("See %s for more information."), url);
164 g_string_append (str, "\n");
165 g_printerr ("%s", str->str);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100166 }
167}
168
Mario Limonciello3143bad2019-02-27 07:31:00 -0600169static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000170fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600171{
Richard Hughesd92ccca2019-05-20 11:28:31 +0100172#ifdef HAVE_SYSTEMD
Richard Hughes3a0ee302020-12-14 14:22:18 +0000173 g_autoptr(GError) error_local = NULL;
Richard Hughes3d005222019-05-17 14:02:41 +0100174 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500175 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100176#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000177 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000178 return FALSE;
179 if (fu_engine_get_tainted (priv->engine)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100180 g_autofree gchar *fmt = NULL;
181
182 /* TRANSLATORS: this is a prefix on the console */
183 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
184 g_printerr ("%s This tool has loaded 3rd party code and "
185 "is no longer supported by the upstream developers!\n",
186 fmt);
Richard Hughesf425d292019-01-18 17:57:39 +0000187 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100188 fu_util_show_plugin_warnings (priv);
Mario Limonciellobd60de12020-11-11 13:09:43 -0600189 fu_util_show_unsupported_warn ();
Richard Hughesf425d292019-01-18 17:57:39 +0000190 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500191}
192
Richard Hughesb5976832018-05-18 10:02:09 +0100193static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500194fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
195{
196 g_autofree gchar *path = g_path_get_dirname (value);
197 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
198 g_prefix_error (error,
199 "Unable to access %s. You may need to copy %s to %s: ",
200 path, value, g_getenv ("HOME"));
201 }
202}
203
204static void
Richard Hughesb5976832018-05-18 10:02:09 +0100205fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
206{
207 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
208 /* TRANSLATORS: this is when a device ctrl+c's a watch */
209 g_print ("%s\n", _("Cancelled"));
210 g_main_loop_quit (priv->loop);
211}
212
213static gboolean
214fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
215{
216 g_autofree gchar *tmp = NULL;
217 g_autoptr(FuSmbios) smbios = NULL;
218 if (g_strv_length (values) < 1) {
219 g_set_error_literal (error,
220 FWUPD_ERROR,
221 FWUPD_ERROR_INVALID_ARGS,
222 "Invalid arguments");
223 return FALSE;
224 }
225 smbios = fu_smbios_new ();
226 if (!fu_smbios_setup_from_file (smbios, values[0], error))
227 return FALSE;
228 tmp = fu_smbios_to_string (smbios);
229 g_print ("%s\n", tmp);
230 return TRUE;
231}
232
Richard Hughes9e5675e2019-11-22 09:35:03 +0000233#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100234static gboolean
235fu_util_sigint_cb (gpointer user_data)
236{
237 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
238 g_debug ("Handling SIGINT");
239 g_cancellable_cancel (priv->cancellable);
240 return FALSE;
241}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000242#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100243
244static void
245fu_util_private_free (FuUtilPrivate *priv)
246{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500247 if (priv->current_device != NULL)
248 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100249 if (priv->engine != NULL)
250 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100251 if (priv->request != NULL)
252 g_object_unref (priv->request);
Richard Hughes6ed25f52021-01-10 19:27:33 +0000253 if (priv->main_ctx != NULL)
254 g_main_context_unref (priv->main_ctx);
Richard Hughesb5976832018-05-18 10:02:09 +0100255 if (priv->loop != NULL)
256 g_main_loop_unref (priv->loop);
257 if (priv->cancellable != NULL)
258 g_object_unref (priv->cancellable);
259 if (priv->progressbar != NULL)
260 g_object_unref (priv->progressbar);
261 if (priv->context != NULL)
262 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600263 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100264 g_free (priv);
265}
266
267#pragma clang diagnostic push
268#pragma clang diagnostic ignored "-Wunused-function"
269G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
270#pragma clang diagnostic pop
271
Richard Hughes98ca9932018-05-18 10:24:07 +0100272
273static void
274fu_main_engine_device_added_cb (FuEngine *engine,
275 FuDevice *device,
276 FuUtilPrivate *priv)
277{
278 g_autofree gchar *tmp = fu_device_to_string (device);
279 g_debug ("ADDED:\n%s", tmp);
280}
281
282static void
283fu_main_engine_device_removed_cb (FuEngine *engine,
284 FuDevice *device,
285 FuUtilPrivate *priv)
286{
287 g_autofree gchar *tmp = fu_device_to_string (device);
288 g_debug ("REMOVED:\n%s", tmp);
289}
290
291static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100292fu_main_engine_status_changed_cb (FuEngine *engine,
293 FwupdStatus status,
294 FuUtilPrivate *priv)
295{
296 fu_progressbar_update (priv->progressbar, status, 0);
297}
298
299static void
300fu_main_engine_percentage_changed_cb (FuEngine *engine,
301 guint percentage,
302 FuUtilPrivate *priv)
303{
304 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
305}
306
307static gboolean
308fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
309{
Richard Hughesc7d870a2020-12-10 10:05:35 +0000310 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100311 return FALSE;
312 g_main_loop_run (priv->loop);
313 return TRUE;
314}
315
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100316static gint
317fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
318{
319 return fu_plugin_name_compare (*item1, *item2);
320}
321
322static gboolean
323fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
324{
325 GPtrArray *plugins;
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100326
327 /* load engine */
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100328 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100329 return FALSE;
330
331 /* print */
332 plugins = fu_engine_get_plugins (priv->engine);
333 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
334 for (guint i = 0; i < plugins->len; i++) {
335 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100336 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
337 g_print ("%s\n", str);
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100338 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100339 if (plugins->len == 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100340 /* TRANSLATORS: nothing found */
341 g_print ("%s\n", _("No plugins found"));
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100342 }
343
344 return TRUE;
345}
346
Richard Hughes98ca9932018-05-18 10:24:07 +0100347static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100348fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
349{
350 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
351 if (!fwupd_device_has_flag (dev, priv->filter_include))
352 return FALSE;
353 }
354 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
355 if (fwupd_device_has_flag (dev, priv->filter_exclude))
356 return FALSE;
357 }
358 return TRUE;
359}
360
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500361static gchar *
362fu_util_get_tree_title (FuUtilPrivate *priv)
363{
364 return g_strdup (fu_engine_get_host_product (priv->engine));
365}
366
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600367static FuDevice *
368fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
369{
370 FuDevice *dev;
371 guint idx;
372 g_autoptr(GPtrArray) devices = NULL;
373 g_autoptr(GPtrArray) devices_filtered = NULL;
374
375 /* get devices from daemon */
376 if (devices_opt != NULL) {
377 devices = g_ptr_array_ref (devices_opt);
378 } else {
379 devices = fu_engine_get_devices (priv->engine, error);
380 if (devices == NULL)
381 return NULL;
382 }
383 fwupd_device_array_ensure_parents (devices);
384
385 /* filter results */
386 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
387 for (guint i = 0; i < devices->len; i++) {
388 dev = g_ptr_array_index (devices, i);
389 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
390 continue;
391 g_ptr_array_add (devices_filtered, g_object_ref (dev));
392 }
393
394 /* nothing */
395 if (devices_filtered->len == 0) {
396 g_set_error_literal (error,
397 FWUPD_ERROR,
398 FWUPD_ERROR_NOTHING_TO_DO,
399 "No supported devices");
400 return NULL;
401 }
402
403 /* exactly one */
404 if (devices_filtered->len == 1) {
405 dev = g_ptr_array_index (devices_filtered, 0);
406 /* TRANSLATORS: Device has been chosen by the daemon for the user */
407 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
408 return g_object_ref (dev);
409 }
410
411 /* TRANSLATORS: get interactive prompt */
412 g_print ("%s\n", _("Choose a device:"));
413 /* TRANSLATORS: this is to abort the interactive prompt */
414 g_print ("0.\t%s\n", _("Cancel"));
415 for (guint i = 0; i < devices_filtered->len; i++) {
416 dev = g_ptr_array_index (devices_filtered, i);
417 g_print ("%u.\t%s (%s)\n",
418 i + 1,
419 fu_device_get_id (dev),
420 fu_device_get_name (dev));
421 }
422 idx = fu_util_prompt_for_number (devices_filtered->len);
423 if (idx == 0) {
424 g_set_error_literal (error,
425 FWUPD_ERROR,
426 FWUPD_ERROR_NOTHING_TO_DO,
427 "Request canceled");
428 return NULL;
429 }
430 dev = g_ptr_array_index (devices_filtered, idx - 1);
431 return g_object_ref (dev);
432}
433
434static FuDevice *
435fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
436{
437 if (fwupd_guid_is_valid (id)) {
438 g_autoptr(GPtrArray) devices = NULL;
439 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
440 if (devices == NULL)
441 return NULL;
442 return fu_util_prompt_for_device (priv, devices, error);
443 }
444
445 /* did this look like a GUID? */
446 for (guint i = 0; id[i] != '\0'; i++) {
447 if (id[i] == '-') {
448 g_set_error_literal (error,
449 FWUPD_ERROR,
450 FWUPD_ERROR_INVALID_ARGS,
451 "Invalid arguments");
452 return NULL;
453 }
454 }
455 return fu_engine_get_device (priv->engine, id, error);
456}
457
Richard Hughes747f5702019-08-06 14:27:26 +0100458static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600459fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
460{
461 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500462 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600463 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500464 gboolean no_updates_header = FALSE;
465 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600466
467 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000468 if (!fu_util_start_engine (priv,
469 FU_ENGINE_LOAD_FLAG_COLDPLUG |
470 FU_ENGINE_LOAD_FLAG_HWINFO |
471 FU_ENGINE_LOAD_FLAG_REMOTES,
472 error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600473 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600474 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600475
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600476 /* parse arguments */
477 if (g_strv_length (values) == 0) {
478 devices = fu_engine_get_devices (priv->engine, error);
479 if (devices == NULL)
480 return FALSE;
481 } else if (g_strv_length (values) == 1) {
482 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600483 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600484 if (device == NULL)
485 return FALSE;
486 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
487 g_ptr_array_add (devices, device);
488 } else {
489 g_set_error_literal (error,
490 FWUPD_ERROR,
491 FWUPD_ERROR_INVALID_ARGS,
492 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600493 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600494 }
495
Richard Hughes0ef47202020-01-06 13:59:09 +0000496 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500497 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600498 for (guint i = 0; i < devices->len; i++) {
499 FwupdDevice *dev = g_ptr_array_index (devices, i);
500 g_autoptr(GPtrArray) rels = NULL;
501 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500502 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600503
Richard Hughes747f5702019-08-06 14:27:26 +0100504 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600505 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600506 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600507 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500508 if (!no_updates_header) {
509 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
510 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
511 no_updates_header = TRUE;
512 }
513 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600514 continue;
515 }
Richard Hughes747f5702019-08-06 14:27:26 +0100516 if (!fu_util_filter_device (priv, dev))
517 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600518
519 /* get the releases for this device and filter for validity */
520 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100521 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600522 fwupd_device_get_id (dev),
523 &error_local);
524 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500525 if (!latest_header) {
526 /* TRANSLATORS: message letting the user know no device upgrade available */
527 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
528 latest_header = TRUE;
529 }
530 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600531 /* discard the actual reason from user, but leave for debugging */
532 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600533 continue;
534 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500535 child = g_node_append_data (root, dev);
536
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600537 for (guint j = 0; j < rels->len; j++) {
538 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500539 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600540 }
541 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600542 /* save the device state for other applications to see */
543 if (!fu_util_save_current_state (priv, error))
544 return FALSE;
545
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500546 /* updates */
547 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
548 g_set_error_literal (error,
549 FWUPD_ERROR,
550 FWUPD_ERROR_NOTHING_TO_DO,
551 "No updates available for remaining devices");
552 return FALSE;
553 }
554
555 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600556 return TRUE;
557}
558
559static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500560fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
561{
562 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500563 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600564 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500565 gint fd;
566
567 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000568 if (!fu_util_start_engine (priv,
569 FU_ENGINE_LOAD_FLAG_COLDPLUG |
570 FU_ENGINE_LOAD_FLAG_HWINFO |
571 FU_ENGINE_LOAD_FLAG_REMOTES,
572 error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500573 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600574 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500575
576 /* check args */
577 if (g_strv_length (values) != 1) {
578 g_set_error_literal (error,
579 FWUPD_ERROR,
580 FWUPD_ERROR_INVALID_ARGS,
581 "Invalid arguments");
582 return FALSE;
583 }
584
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600585 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100586 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600587
Mario Limonciello716ab272018-05-29 12:34:37 -0500588 /* open file */
589 fd = open (values[0], O_RDONLY);
590 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500591 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500592 g_set_error (error,
593 FWUPD_ERROR,
594 FWUPD_ERROR_INVALID_FILE,
595 "failed to open %s",
596 values[0]);
597 return FALSE;
598 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100599 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500600 close (fd);
601
602 if (array == NULL)
603 return FALSE;
604 for (guint i = 0; i < array->len; i++) {
605 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600606 FwupdRelease *rel;
607 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100608 if (!fu_util_filter_device (priv, dev))
609 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600610 child = g_node_append_data (root, dev);
611 rel = fwupd_device_get_release_default (dev);
612 if (rel != NULL)
613 g_node_append_data (child, rel);
614
Mario Limonciello716ab272018-05-29 12:34:37 -0500615 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500616 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500617
Mario Limonciello716ab272018-05-29 12:34:37 -0500618 return TRUE;
619}
620
621static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100622fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
623{
624 g_autoptr(GString) str = g_string_new (NULL);
625
626 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
627 const gchar *tmp = fwupd_device_flag_to_string (i);
628 if (tmp == NULL)
629 break;
630 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
631 g_string_append (str, " ");
632 g_string_append (str, tmp);
633 g_string_append (str, " ~");
634 g_string_append (str, tmp);
635 }
636 g_print ("%s\n", str->str);
637
638 return TRUE;
639}
640
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500641static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100642fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500643{
644 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100645 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100646 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
647 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100648 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500649 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500650 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100651 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500652 GNode *child = g_node_append_data (root, dev_tmp);
653 fu_util_build_device_tree (priv, child, devs, dev_tmp);
654 }
655 }
656}
657
658static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100659fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500660{
661 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600662 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500663 g_autoptr(GPtrArray) devs = NULL;
664
665 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000666 if (!fu_util_start_engine (priv,
667 FU_ENGINE_LOAD_FLAG_COLDPLUG |
668 FU_ENGINE_LOAD_FLAG_HWINFO |
669 FU_ENGINE_LOAD_FLAG_REMOTES,
670 error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500671 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600672 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500673
Mario Limonciello76196652021-01-13 22:53:26 -0600674 /* get devices and build tree */
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500675 devs = fu_engine_get_devices (priv->engine, error);
676 if (devs == NULL)
677 return FALSE;
Mario Limonciello76196652021-01-13 22:53:26 -0600678 if (devs->len > 0) {
679 fwupd_device_array_ensure_parents (devs);
680 fu_util_build_device_tree (priv, root, devs, NULL);
681 }
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500682
683 /* print */
Mario Limonciello76196652021-01-13 22:53:26 -0600684 if (g_node_n_children (root) == 0) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500685 /* TRANSLATORS: nothing attached that can be upgraded */
686 g_print ("%s\n", _("No hardware detected with firmware update capability"));
687 return TRUE;
688 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500689 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500690
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100691 /* save the device state for other applications to see */
692 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500693}
694
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500695static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600696fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500697 FwupdDevice *device,
698 FuUtilPrivate *priv)
699{
700 g_autofree gchar *str = NULL;
701
Richard Hughes809abea2019-03-23 11:06:18 +0000702 /* allowed to set whenever the device has changed */
703 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
704 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
705 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
706 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
707
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500708 /* same as last time, so ignore */
709 if (priv->current_device != NULL &&
710 fwupd_device_compare (priv->current_device, device) == 0)
711 return;
712
Richard Hughesee562b52020-04-07 14:32:52 +0100713 /* ignore indirect devices that might have changed */
714 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
715 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
716 g_debug ("ignoring %s with status %s",
717 fwupd_device_get_name (device),
718 fwupd_status_to_string (fwupd_device_get_status (device)));
719 return;
720 }
721
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500722 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600723 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
724 /* TRANSLATORS: %1 is a device name */
725 str = g_strdup_printf (_("Updating %s…"),
726 fwupd_device_get_name (device));
727 fu_progressbar_set_title (priv->progressbar, str);
728 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
729 /* TRANSLATORS: %1 is a device name */
730 str = g_strdup_printf (_("Installing on %s…"),
731 fwupd_device_get_name (device));
732 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000733 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
734 /* TRANSLATORS: %1 is a device name */
735 str = g_strdup_printf (_("Reading from %s…"),
736 fwupd_device_get_name (device));
737 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600738 } else {
739 g_warning ("no FuUtilOperation set");
740 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500741 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600742
Mario Limonciello32241f42019-01-24 10:12:41 -0600743 if (priv->current_message == NULL) {
744 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
745 if (tmp != NULL)
746 priv->current_message = g_strdup (tmp);
747 }
748}
749
750static void
751fu_util_display_current_message (FuUtilPrivate *priv)
752{
753 if (priv->current_message == NULL)
754 return;
755 g_print ("%s\n", priv->current_message);
756 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500757}
758
Richard Hughes98ca9932018-05-18 10:24:07 +0100759static gboolean
760fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
761{
762 g_autoptr(FuDevice) device = NULL;
763 g_autoptr(GBytes) blob_fw = NULL;
764
765 /* invalid args */
766 if (g_strv_length (values) == 0) {
767 g_set_error_literal (error,
768 FWUPD_ERROR,
769 FWUPD_ERROR_INVALID_ARGS,
770 "Invalid arguments");
771 return FALSE;
772 }
773
774 /* parse blob */
775 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500776 if (blob_fw == NULL) {
777 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100778 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500779 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100780
781 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000782 if (!fu_util_start_engine (priv,
783 FU_ENGINE_LOAD_FLAG_COLDPLUG |
784 FU_ENGINE_LOAD_FLAG_HWINFO |
785 FU_ENGINE_LOAD_FLAG_REMOTES,
786 error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100787 return FALSE;
788
789 /* get device */
790 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100791 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100792 if (device == NULL)
793 return FALSE;
794 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100795 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100796 if (device == NULL)
797 return FALSE;
798 }
799
Mario Limonciello3f243a92019-01-21 22:05:23 -0600800 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500801 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600802 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500803
Richard Hughes98ca9932018-05-18 10:24:07 +0100804 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000805 if (priv->prepare_blob) {
806 g_autoptr(GPtrArray) devices = NULL;
807 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
808 g_ptr_array_add (devices, g_object_ref (device));
809 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
810 g_prefix_error (error, "failed to prepare composite action: ");
811 return FALSE;
812 }
813 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100814 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000815 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600816 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000817 if (priv->cleanup_blob) {
818 g_autoptr(FuDevice) device_new = NULL;
819 g_autoptr(GError) error_local = NULL;
820
821 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100822 device_new = fu_util_get_device (priv,
823 fu_device_get_id (device),
824 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000825 if (device_new == NULL) {
826 g_debug ("failed to find new device: %s",
827 error_local->message);
828 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600829 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000830 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
831 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
832 g_prefix_error (error, "failed to cleanup composite action: ");
833 return FALSE;
834 }
835 }
836 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600837
Mario Limonciello32241f42019-01-24 10:12:41 -0600838 fu_util_display_current_message (priv);
839
Mario Limonciello3f243a92019-01-21 22:05:23 -0600840 /* success */
841 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100842}
843
Richard Hughesa58510b2019-10-30 10:03:12 +0000844static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100845fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000846{
847 g_autoptr(FuDevice) device = NULL;
848 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
849 g_autoptr(GBytes) blob_fw = NULL;
850
851 /* invalid args */
852 if (g_strv_length (values) == 0) {
853 g_set_error_literal (error,
854 FWUPD_ERROR,
855 FWUPD_ERROR_INVALID_ARGS,
856 "Invalid arguments");
857 return FALSE;
858 }
859
860 /* file already exists */
861 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
862 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
863 g_set_error_literal (error,
864 FWUPD_ERROR,
865 FWUPD_ERROR_INVALID_ARGS,
866 "Filename already exists");
867 return FALSE;
868 }
869
Richard Hughes02792c02019-11-01 14:21:20 +0000870 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000871 * avoid failing at the end of a potentially lengthy operation */
872 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
873 return FALSE;
874
875 /* load engine */
Richard Hughesda7b8982021-01-26 21:37:11 +0000876 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughesa58510b2019-10-30 10:03:12 +0000877 return FALSE;
878
879 /* get device */
880 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100881 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000882 if (device == NULL)
883 return FALSE;
884 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100885 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000886 if (device == NULL)
887 return FALSE;
888 }
889 priv->current_operation = FU_UTIL_OPERATION_READ;
890 g_signal_connect (priv->engine, "device-changed",
891 G_CALLBACK (fu_util_update_device_changed_cb), priv);
892
893 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100894 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000895 if (blob_fw == NULL)
896 return FALSE;
897 return fu_common_set_contents_bytes (values[0], blob_fw, error);
898}
899
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100900static gint
901fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
902{
903 FuInstallTask *task1 = *((FuInstallTask **) a);
904 FuInstallTask *task2 = *((FuInstallTask **) b);
905 return fu_install_task_compare (task1, task2);
906}
907
908static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100909fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
910{
Filipe Laínse0914272019-09-20 10:04:43 +0100911 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100912 { "curl", uri, "--output", fn, NULL },
913 { NULL } };
914 for (guint i = 0; argv[i][0] != NULL; i++) {
915 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100916 g_autofree gchar *fn_tmp = NULL;
917 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
918 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100919 g_debug ("%s", error_local->message);
920 continue;
921 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000922 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100923 }
924 g_set_error_literal (error,
925 FWUPD_ERROR,
926 FWUPD_ERROR_NOT_FOUND,
927 "no supported out-of-process downloaders found");
928 return FALSE;
929}
930
931static gchar *
932fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
933{
934 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100935
936 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000937 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
938 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000939 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100940 return g_strdup (perhapsfn);
941
942 /* download the firmware to a cachedir */
943 filename = fu_util_get_user_cache_path (perhapsfn);
944 if (!fu_common_mkdir_parent (filename, error))
945 return NULL;
946 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
947 return NULL;
948 return g_steal_pointer (&filename);
949}
950
951static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100952fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
953{
Richard Hughes3d178be2018-08-30 11:14:24 +0100954 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100955 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100956 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100957 g_autoptr(GPtrArray) devices_possible = NULL;
958 g_autoptr(GPtrArray) errors = NULL;
959 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100960 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100961
Mario Limonciello8949e892018-05-25 08:03:06 -0500962 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000963 if (!fu_util_start_engine (priv,
964 FU_ENGINE_LOAD_FLAG_COLDPLUG |
965 FU_ENGINE_LOAD_FLAG_HWINFO |
966 FU_ENGINE_LOAD_FLAG_REMOTES,
967 error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500968 return FALSE;
969
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100970 /* handle both forms */
971 if (g_strv_length (values) == 1) {
972 devices_possible = fu_engine_get_devices (priv->engine, error);
973 if (devices_possible == NULL)
974 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000975 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100976 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100977 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100978 if (device == NULL)
979 return FALSE;
980 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
981 g_ptr_array_add (devices_possible, device);
982 } else {
983 g_set_error_literal (error,
984 FWUPD_ERROR,
985 FWUPD_ERROR_INVALID_ARGS,
986 "Invalid arguments");
987 return FALSE;
988 }
989
Richard Hughes3d178be2018-08-30 11:14:24 +0100990 /* download if required */
991 filename = fu_util_download_if_required (priv, values[0], error);
992 if (filename == NULL)
993 return FALSE;
994
Richard Hughes481aa2a2018-09-18 20:51:46 +0100995 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100996 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500997 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100998 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100999 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -05001000 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001001 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
1002 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001003 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -06001004 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001005 if (components == NULL)
1006 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001007
Richard Hughes481aa2a2018-09-18 20:51:46 +01001008 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001009 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
1010 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001011 for (guint i = 0; i < components->len; i++) {
1012 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001013
1014 /* do any devices pass the requirements */
1015 for (guint j = 0; j < devices_possible->len; j++) {
1016 FuDevice *device = g_ptr_array_index (devices_possible, j);
1017 g_autoptr(FuInstallTask) task = NULL;
1018 g_autoptr(GError) error_local = NULL;
1019
1020 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001021 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001022 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001023 priv->request,
1024 task,
1025 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001026 &error_local)) {
1027 g_debug ("first pass requirement on %s:%s failed: %s",
1028 fu_device_get_id (device),
1029 xb_node_query_text (component, "id", NULL),
1030 error_local->message);
1031 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1032 continue;
1033 }
1034
1035 /* make a second pass using possibly updated version format now */
1036 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1037 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001038 priv->request,
1039 task,
1040 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001041 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001042 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001043 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001044 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001045 error_local->message);
1046 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1047 continue;
1048 }
1049
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001050 /* if component should have an update message from CAB */
1051 fu_device_incorporate_from_component (device, component);
1052
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001053 /* success */
1054 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1055 }
1056 }
1057
1058 /* order the install tasks by the device priority */
1059 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1060
1061 /* nothing suitable */
1062 if (install_tasks->len == 0) {
1063 GError *error_tmp = fu_common_error_array_get_best (errors);
1064 g_propagate_error (error, error_tmp);
1065 return FALSE;
1066 }
1067
Mario Limonciello3f243a92019-01-21 22:05:23 -06001068 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001069 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001070 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001071
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001072 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001073 if (!fu_engine_install_tasks (priv->engine,
1074 priv->request,
1075 install_tasks,
1076 blob_cab,
1077 priv->flags,
1078 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001079 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001080
Mario Limonciello32241f42019-01-24 10:12:41 -06001081 fu_util_display_current_message (priv);
1082
Mario Limonciello3f243a92019-01-21 22:05:23 -06001083 /* we don't want to ask anything */
1084 if (priv->no_reboot_check) {
1085 g_debug ("skipping reboot check");
1086 return TRUE;
1087 }
1088
Mario Limonciello3143bad2019-02-27 07:31:00 -06001089 /* save the device state for other applications to see */
1090 if (!fu_util_save_current_state (priv, error))
1091 return FALSE;
1092
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001093 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001094 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001095}
1096
Richard Hughes98ca9932018-05-18 10:24:07 +01001097static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001098fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001099{
Mario Limonciellofd734852019-08-01 16:41:42 -05001100 FwupdRemote *remote;
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001101 GPtrArray *locations;
Mario Limonciellofd734852019-08-01 16:41:42 -05001102 const gchar *remote_id;
1103 const gchar *uri_tmp;
1104 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001105
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001106 /* get the default release only until other parts of fwupd can cope */
1107 locations = fwupd_release_get_locations (rel);
1108 if (locations->len == 0) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001109 g_set_error_literal (error,
1110 FWUPD_ERROR,
1111 FWUPD_ERROR_INVALID_FILE,
1112 "release missing URI");
1113 return FALSE;
1114 }
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001115 uri_tmp = g_ptr_array_index (locations, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001116 remote_id = fwupd_release_get_remote_id (rel);
1117 if (remote_id == NULL) {
1118 g_set_error (error,
1119 FWUPD_ERROR,
1120 FWUPD_ERROR_INVALID_FILE,
1121 "failed to find remote for %s",
1122 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001123 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001124 }
1125
1126 remote = fu_engine_get_remote_by_id (priv->engine,
1127 remote_id,
1128 error);
1129 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001130 return FALSE;
1131
Mario Limonciellofd734852019-08-01 16:41:42 -05001132 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001133 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001134 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1135 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001136 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1137 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1138 argv[0] = g_build_filename (path, uri_tmp, NULL);
1139 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1140 argv[0] = g_strdup (uri_tmp + 7);
1141 /* web remote, fu_util_install will download file */
1142 } else {
1143 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1144 }
1145 return fu_util_install (priv, argv, error);
1146}
1147
1148static gboolean
1149fu_util_update_all (FuUtilPrivate *priv, GError **error)
1150{
1151 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001152 gboolean no_updates_header = FALSE;
1153 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001154
Mario Limonciello46aaee82019-01-10 12:58:00 -06001155 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001156 if (devices == NULL)
1157 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001158 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001159 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001160 for (guint i = 0; i < devices->len; i++) {
1161 FwupdDevice *dev = g_ptr_array_index (devices, i);
1162 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001163 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001164 g_autoptr(GPtrArray) rels = NULL;
1165 g_autoptr(GError) error_local = NULL;
1166
1167 if (!fu_util_is_interesting_device (dev))
1168 continue;
1169 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001170 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001171 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001172 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001173 if (!no_updates_header) {
1174 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1175 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1176 no_updates_header = TRUE;
1177 }
1178 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001179 continue;
1180 }
Richard Hughes747f5702019-08-06 14:27:26 +01001181 if (!fu_util_filter_device (priv, dev))
1182 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001183
1184 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001185 rels = fu_engine_get_upgrades (priv->engine,
1186 priv->request,
1187 device_id,
1188 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001189 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001190 if (!latest_header) {
1191 /* TRANSLATORS: message letting the user know no device upgrade available */
1192 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1193 latest_header = TRUE;
1194 }
1195 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001196 /* discard the actual reason from user, but leave for debugging */
1197 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001198 continue;
1199 }
1200
Mario Limonciello98b95162019-10-30 09:20:43 -05001201 if (!priv->no_safety_check) {
1202 if (!fu_util_prompt_warning (dev,
1203 fu_util_get_tree_title (priv),
1204 error))
1205 return FALSE;
1206 }
1207
Mario Limonciello46aaee82019-01-10 12:58:00 -06001208 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001209 if (!fu_util_install_release (priv, rel, &error_local)) {
1210 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001211 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001212 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001213 fu_util_display_current_message (priv);
1214 }
1215 return TRUE;
1216}
1217
1218static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001219fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001220{
1221 FwupdRelease *rel;
1222 g_autoptr(FuDevice) dev = NULL;
1223 g_autoptr(GPtrArray) rels = NULL;
1224
Mario Limonciello9917bb42020-04-20 13:41:27 -05001225 /* do not allow a partial device-id, lookup GUIDs */
1226 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001227 if (dev == NULL)
1228 return FALSE;
1229
1230 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001231 rels = fu_engine_get_upgrades (priv->engine,
1232 priv->request,
1233 fu_device_get_id (dev),
1234 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001235 if (rels == NULL)
1236 return FALSE;
1237 rel = g_ptr_array_index (rels, 0);
1238 if (!fu_util_install_release (priv, rel, error))
1239 return FALSE;
1240 fu_util_display_current_message (priv);
1241
1242 return TRUE;
1243}
1244
1245static gboolean
1246fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1247{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001248 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1249 g_set_error_literal (error,
1250 FWUPD_ERROR,
1251 FWUPD_ERROR_INVALID_ARGS,
1252 "--allow-older is not supported for this command");
1253 return FALSE;
1254 }
1255
1256 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1257 g_set_error_literal (error,
1258 FWUPD_ERROR,
1259 FWUPD_ERROR_INVALID_ARGS,
1260 "--allow-reinstall is not supported for this command");
1261 return FALSE;
1262 }
1263
Mario Limonciellofd734852019-08-01 16:41:42 -05001264 if (g_strv_length (values) > 1) {
1265 g_set_error_literal (error,
1266 FWUPD_ERROR,
1267 FWUPD_ERROR_INVALID_ARGS,
1268 "Invalid arguments");
1269 return FALSE;
1270 }
1271
Richard Hughesc7d870a2020-12-10 10:05:35 +00001272 if (!fu_util_start_engine (priv,
1273 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1274 FU_ENGINE_LOAD_FLAG_HWINFO |
1275 FU_ENGINE_LOAD_FLAG_REMOTES,
1276 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001277 return FALSE;
1278
1279 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1280 g_signal_connect (priv->engine, "device-changed",
1281 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1282
1283 if (g_strv_length (values) == 1) {
1284 if (!fu_util_update_by_id (priv, values[0], error))
1285 return FALSE;
1286 } else {
1287 if (!fu_util_update_all (priv, error))
1288 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001289 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001290
1291 /* we don't want to ask anything */
1292 if (priv->no_reboot_check) {
1293 g_debug ("skipping reboot check");
1294 return TRUE;
1295 }
1296
Mario Limonciello3143bad2019-02-27 07:31:00 -06001297 /* save the device state for other applications to see */
1298 if (!fu_util_save_current_state (priv, error))
1299 return FALSE;
1300
Mario Limonciello3f243a92019-01-21 22:05:23 -06001301 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001302}
1303
1304static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001305fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1306{
1307 g_autoptr(FwupdRelease) rel = NULL;
1308 g_autoptr(GPtrArray) rels = NULL;
1309 g_autoptr(FuDevice) dev = NULL;
1310
1311 if (g_strv_length (values) != 1) {
1312 g_set_error_literal (error,
1313 FWUPD_ERROR,
1314 FWUPD_ERROR_INVALID_ARGS,
1315 "Invalid arguments");
1316 return FALSE;
1317 }
1318
Richard Hughesc7d870a2020-12-10 10:05:35 +00001319 if (!fu_util_start_engine (priv,
1320 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1321 FU_ENGINE_LOAD_FLAG_HWINFO |
1322 FU_ENGINE_LOAD_FLAG_REMOTES,
1323 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001324 return FALSE;
1325
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001326 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001327 if (dev == NULL)
1328 return FALSE;
1329
1330 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001331 rels = fu_engine_get_releases_for_device (priv->engine,
1332 priv->request,
1333 dev,
1334 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001335 if (rels == NULL)
1336 return FALSE;
1337
1338 for (guint j = 0; j < rels->len; j++) {
1339 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1340 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1341 fu_device_get_version (dev),
1342 fu_device_get_version_format (dev)) == 0) {
1343 rel = g_object_ref (rel_tmp);
1344 break;
1345 }
1346 }
1347 if (rel == NULL) {
1348 g_set_error (error,
1349 FWUPD_ERROR,
1350 FWUPD_ERROR_NOT_SUPPORTED,
1351 "Unable to locate release for %s version %s",
1352 fu_device_get_name (dev),
1353 fu_device_get_version (dev));
1354 return FALSE;
1355 }
1356
1357 /* update the console if composite devices are also updated */
1358 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1359 g_signal_connect (priv->engine, "device-changed",
1360 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1361 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1362 if (!fu_util_install_release (priv, rel, error))
1363 return FALSE;
1364 fu_util_display_current_message (priv);
1365
1366 /* we don't want to ask anything */
1367 if (priv->no_reboot_check) {
1368 g_debug ("skipping reboot check");
1369 return TRUE;
1370 }
1371
1372 /* save the device state for other applications to see */
1373 if (!fu_util_save_current_state (priv, error))
1374 return FALSE;
1375
1376 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1377}
1378
1379static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001380fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1381{
1382 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001383 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001384
1385 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001386 if (!fu_util_start_engine (priv,
1387 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1388 FU_ENGINE_LOAD_FLAG_HWINFO |
1389 FU_ENGINE_LOAD_FLAG_REMOTES,
1390 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001391 return FALSE;
1392
Richard Hughes98ca9932018-05-18 10:24:07 +01001393 /* get device */
1394 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001395 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001396 if (device == NULL)
1397 return FALSE;
1398 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001399 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001400 if (device == NULL)
1401 return FALSE;
1402 }
1403
1404 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001405 locker = fu_device_locker_new (device, error);
1406 if (locker == NULL)
1407 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001408 return fu_device_detach (device, error);
1409}
1410
1411static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001412fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1413{
1414 g_autoptr(FuDevice) device = NULL;
1415 g_autoptr(FuDeviceLocker) locker = NULL;
1416
1417 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001418 if (!fu_util_start_engine (priv,
1419 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1420 FU_ENGINE_LOAD_FLAG_HWINFO |
1421 FU_ENGINE_LOAD_FLAG_REMOTES,
1422 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001423 return FALSE;
1424
1425 /* get device */
1426 if (g_strv_length (values) == 1) {
1427 device = fu_util_get_device (priv, values[0], error);
1428 } else {
1429 device = fu_util_prompt_for_device (priv, NULL, error);
1430 }
1431 if (device == NULL)
1432 return FALSE;
1433
1434 /* run vfunc */
1435 locker = fu_device_locker_new (device, error);
1436 if (locker == NULL)
1437 return FALSE;
1438 return fu_device_unbind_driver (device, error);
1439}
1440
1441static gboolean
1442fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1443{
1444 g_autoptr(FuDevice) device = NULL;
1445 g_autoptr(FuDeviceLocker) locker = NULL;
1446
1447 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001448 if (!fu_util_start_engine (priv,
1449 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1450 FU_ENGINE_LOAD_FLAG_HWINFO |
1451 FU_ENGINE_LOAD_FLAG_REMOTES,
1452 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001453 return FALSE;
1454
1455 /* get device */
1456 if (g_strv_length (values) == 3) {
1457 device = fu_util_get_device (priv, values[2], error);
1458 if (device == NULL)
1459 return FALSE;
1460 } else if (g_strv_length (values) == 2) {
1461 device = fu_util_prompt_for_device (priv, NULL, error);
1462 if (device == NULL)
1463 return FALSE;
1464 } else {
1465 g_set_error_literal (error,
1466 FWUPD_ERROR,
1467 FWUPD_ERROR_INVALID_ARGS,
1468 "Invalid arguments");
1469 return FALSE;
1470 }
1471
1472 /* run vfunc */
1473 locker = fu_device_locker_new (device, error);
1474 if (locker == NULL)
1475 return FALSE;
1476 return fu_device_bind_driver (device, values[0], values[1], error);
1477}
1478
1479static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001480fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1481{
1482 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001483 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001484
1485 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001486 if (!fu_util_start_engine (priv,
1487 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1488 FU_ENGINE_LOAD_FLAG_HWINFO |
1489 FU_ENGINE_LOAD_FLAG_REMOTES,
1490 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001491 return FALSE;
1492
Richard Hughes98ca9932018-05-18 10:24:07 +01001493 /* get device */
1494 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001495 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001496 if (device == NULL)
1497 return FALSE;
1498 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001499 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001500 if (device == NULL)
1501 return FALSE;
1502 }
1503
1504 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001505 locker = fu_device_locker_new (device, error);
1506 if (locker == NULL)
1507 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001508 return fu_device_attach (device, error);
1509}
1510
Richard Hughes1d894f12018-08-31 13:05:51 +01001511static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001512fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001513{
1514 gboolean has_pending = FALSE;
1515 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001516 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1517 if (devices == NULL)
1518 return FALSE;
1519
1520 /* only start up the plugins needed */
1521 for (guint i = 0; i < devices->len; i++) {
1522 FuDevice *dev = g_ptr_array_index (devices, i);
1523 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1524 fu_engine_add_plugin_filter (priv->engine,
1525 fu_device_get_plugin (dev));
1526 has_pending = TRUE;
1527 }
1528 }
1529
1530 if (!has_pending) {
1531 g_set_error_literal (error,
1532 FWUPD_ERROR,
1533 FWUPD_ERROR_NOTHING_TO_DO,
1534 "No devices to activate");
1535 return FALSE;
1536 }
1537
1538 return TRUE;
1539}
1540
1541static gboolean
1542fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1543{
1544 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001545 g_autoptr(GPtrArray) devices = NULL;
1546
1547 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001548 if (!fu_util_check_activation_needed (priv, error))
1549 return FALSE;
1550
1551 /* load engine */
Mario Limoncielloc4f43202021-03-10 16:07:48 -06001552 if (!fu_util_start_engine (priv,
1553 FU_ENGINE_LOAD_FLAG_READONLY |
1554 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1555 FU_ENGINE_LOAD_FLAG_REMOTES |
1556 FU_ENGINE_LOAD_FLAG_HWINFO,
1557 error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001558 return FALSE;
1559
1560 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001561 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001562 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001563 if (devices == NULL)
1564 return FALSE;
1565 } else if (g_strv_length (values) == 1) {
1566 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001567 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001568 if (device == NULL)
1569 return FALSE;
1570 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1571 g_ptr_array_add (devices, device);
1572 } else {
1573 g_set_error_literal (error,
1574 FWUPD_ERROR,
1575 FWUPD_ERROR_INVALID_ARGS,
1576 "Invalid arguments");
1577 return FALSE;
1578 }
1579
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001580 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001581 /* order by device priority */
1582 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001583 for (guint i = 0; i < devices->len; i++) {
1584 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001585 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1586 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001587 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1588 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001589 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001590 /* TRANSLATORS: shown when shutting down to switch to the new version */
1591 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1592 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1593 return FALSE;
1594 }
1595
Mario Limonciello02085a02020-09-11 14:59:35 -05001596 if (!has_pending) {
1597 g_set_error_literal (error,
1598 FWUPD_ERROR,
1599 FWUPD_ERROR_NOTHING_TO_DO,
1600 "No devices to activate");
1601 return FALSE;
1602 }
1603
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001604 return TRUE;
1605}
1606
1607static gboolean
Richard Hughesf6751cd2021-01-15 16:48:25 +00001608fu_util_export_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1609{
1610 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1611 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1612 g_autoptr(GKeyFile) kf = g_key_file_new ();
1613 g_autoptr(GPtrArray) hwid_keys = NULL;
1614
1615 /* check args */
1616 if (g_strv_length (values) != 1) {
1617 g_set_error_literal (error,
1618 FWUPD_ERROR,
1619 FWUPD_ERROR_INVALID_ARGS,
1620 "Invalid arguments, expected HWIDS-FILE");
1621 return FALSE;
1622 }
1623
1624 /* setup default hwids */
1625 if (!fu_smbios_setup (smbios, error))
1626 return FALSE;
1627 if (!fu_hwids_setup (hwids, smbios, error))
1628 return FALSE;
1629
1630 /* save all keys */
1631 hwid_keys = fu_hwids_get_keys (hwids);
1632 for (guint i = 0; i < hwid_keys->len; i++) {
1633 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1634 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1635 g_key_file_set_string (kf, "HwIds", hwid_key, value);
1636 }
1637
1638 /* success */
1639 return g_key_file_save_to_file (kf, values[0], error);
1640}
1641
1642static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001643fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1644{
Richard Hughesf6751cd2021-01-15 16:48:25 +00001645 g_autoptr(FuSmbios) smbios = NULL;
Richard Hughes1d894f12018-08-31 13:05:51 +01001646 g_autoptr(FuHwids) hwids = fu_hwids_new ();
Richard Hughesf6751cd2021-01-15 16:48:25 +00001647 g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys (hwids);
Richard Hughes1d894f12018-08-31 13:05:51 +01001648
1649 /* read DMI data */
1650 if (g_strv_length (values) == 0) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001651 smbios = fu_smbios_new ();
Richard Hughes1d894f12018-08-31 13:05:51 +01001652 if (!fu_smbios_setup (smbios, error))
1653 return FALSE;
1654 } else if (g_strv_length (values) == 1) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001655 /* a keyfile with overrides */
1656 g_autoptr(GKeyFile) kf = g_key_file_new ();
1657 if (g_key_file_load_from_file (kf, values[0], G_KEY_FILE_NONE, NULL)) {
1658 for (guint i = 0; i < hwid_keys->len; i++) {
1659 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1660 g_autofree gchar *tmp = NULL;
1661 tmp = g_key_file_get_string (kf, "HwIds", hwid_key, NULL);
1662 fu_hwids_add_smbios_override (hwids, hwid_key, tmp);
1663 }
1664 /* a DMI blob */
1665 } else {
1666 smbios = fu_smbios_new ();
1667 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1668 return FALSE;
1669 }
Richard Hughes1d894f12018-08-31 13:05:51 +01001670 } else {
1671 g_set_error_literal (error,
1672 FWUPD_ERROR,
1673 FWUPD_ERROR_INVALID_ARGS,
1674 "Invalid arguments");
1675 return FALSE;
1676 }
1677 if (!fu_hwids_setup (hwids, smbios, error))
1678 return FALSE;
1679
1680 /* show debug output */
1681 g_print ("Computer Information\n");
1682 g_print ("--------------------\n");
Richard Hughesf6751cd2021-01-15 16:48:25 +00001683 for (guint i = 0; i < hwid_keys->len; i++) {
1684 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1685 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1686 if (value == NULL)
Richard Hughes1d894f12018-08-31 13:05:51 +01001687 continue;
Richard Hughesf6751cd2021-01-15 16:48:25 +00001688 if (g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1689 g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1690 guint64 val = g_ascii_strtoull (value, NULL, 16);
1691 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
Richard Hughes1d894f12018-08-31 13:05:51 +01001692 } else {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001693 g_print ("%s: %s\n", hwid_key, value);
Richard Hughes1d894f12018-08-31 13:05:51 +01001694 }
1695 }
1696
1697 /* show GUIDs */
1698 g_print ("\nHardware IDs\n");
1699 g_print ("------------\n");
1700 for (guint i = 0; i < 15; i++) {
1701 const gchar *keys = NULL;
1702 g_autofree gchar *guid = NULL;
1703 g_autofree gchar *key = NULL;
1704 g_autofree gchar *keys_str = NULL;
1705 g_auto(GStrv) keysv = NULL;
1706 g_autoptr(GError) error_local = NULL;
1707
1708 /* get the GUID */
1709 key = g_strdup_printf ("HardwareID-%u", i);
1710 keys = fu_hwids_get_replace_keys (hwids, key);
1711 guid = fu_hwids_get_guid (hwids, key, &error_local);
1712 if (guid == NULL) {
1713 g_print ("%s\n", error_local->message);
1714 continue;
1715 }
1716
1717 /* show what makes up the GUID */
1718 keysv = g_strsplit (keys, "&", -1);
1719 keys_str = g_strjoinv (" + ", keysv);
1720 g_print ("{%s} <- %s\n", guid, keys_str);
1721 }
1722
1723 return TRUE;
1724}
1725
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001726static gboolean
1727fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1728{
1729 const gchar *script_fn = "startup.sh";
1730 const gchar *output_fn = "firmware.bin";
1731 g_autoptr(GBytes) archive_blob = NULL;
1732 g_autoptr(GBytes) firmware_blob = NULL;
1733 if (g_strv_length (values) < 2) {
1734 g_set_error_literal (error,
1735 FWUPD_ERROR,
1736 FWUPD_ERROR_INVALID_ARGS,
1737 "Invalid arguments");
1738 return FALSE;
1739 }
1740 archive_blob = fu_common_get_contents_bytes (values[0], error);
1741 if (archive_blob == NULL)
1742 return FALSE;
1743 if (g_strv_length (values) > 2)
1744 script_fn = values[2];
1745 if (g_strv_length (values) > 3)
1746 output_fn = values[3];
1747 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1748 if (firmware_blob == NULL)
1749 return FALSE;
1750 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1751}
1752
Richard Hughes3d607622019-03-07 16:59:27 +00001753static gboolean
1754fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1755{
1756 g_autofree gchar *sig = NULL;
1757
1758 /* check args */
1759 if (g_strv_length (values) != 1) {
1760 g_set_error_literal (error,
1761 FWUPD_ERROR,
1762 FWUPD_ERROR_INVALID_ARGS,
1763 "Invalid arguments: value expected");
1764 return FALSE;
1765 }
1766
1767 /* start engine */
1768 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1769 return FALSE;
1770 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001771 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1772 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001773 if (sig == NULL)
1774 return FALSE;
1775 g_print ("%s\n", sig);
1776 return TRUE;
1777}
1778
Mario Limonciello62f84862018-10-18 13:15:23 -05001779static void
1780fu_util_device_added_cb (FwupdClient *client,
1781 FwupdDevice *device,
1782 gpointer user_data)
1783{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001784 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001785 /* TRANSLATORS: this is when a device is hotplugged */
1786 g_print ("%s\n%s", _("Device added:"), tmp);
1787}
1788
1789static void
1790fu_util_device_removed_cb (FwupdClient *client,
1791 FwupdDevice *device,
1792 gpointer user_data)
1793{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001794 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001795 /* TRANSLATORS: this is when a device is hotplugged */
1796 g_print ("%s\n%s", _("Device removed:"), tmp);
1797}
1798
1799static void
1800fu_util_device_changed_cb (FwupdClient *client,
1801 FwupdDevice *device,
1802 gpointer user_data)
1803{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001804 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001805 /* TRANSLATORS: this is when a device has been updated */
1806 g_print ("%s\n%s", _("Device changed:"), tmp);
1807}
1808
1809static void
1810fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1811{
1812 /* TRANSLATORS: this is when the daemon state changes */
1813 g_print ("%s\n", _("Changed"));
1814}
1815
1816static gboolean
1817fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1818{
1819 g_autoptr(FwupdClient) client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00001820 fwupd_client_set_main_context (client, priv->main_ctx);
Mario Limonciello62f84862018-10-18 13:15:23 -05001821
1822 /* get all the devices */
1823 if (!fwupd_client_connect (client, priv->cancellable, error))
1824 return FALSE;
1825
1826 /* watch for any hotplugged device */
1827 g_signal_connect (client, "changed",
1828 G_CALLBACK (fu_util_changed_cb), priv);
1829 g_signal_connect (client, "device-added",
1830 G_CALLBACK (fu_util_device_added_cb), priv);
1831 g_signal_connect (client, "device-removed",
1832 G_CALLBACK (fu_util_device_removed_cb), priv);
1833 g_signal_connect (client, "device-changed",
1834 G_CALLBACK (fu_util_device_changed_cb), priv);
1835 g_signal_connect (priv->cancellable, "cancelled",
1836 G_CALLBACK (fu_util_cancelled_cb), priv);
1837 g_main_loop_run (priv->loop);
1838 return TRUE;
1839}
1840
Richard Hughes15684492019-03-15 16:27:50 +00001841static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001842fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1843{
1844 g_autoptr(GPtrArray) firmware_types = NULL;
1845
1846 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001847 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001848 return FALSE;
1849
1850 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1851 for (guint i = 0; i < firmware_types->len; i++) {
1852 const gchar *id = g_ptr_array_index (firmware_types, i);
1853 g_print ("%s\n", id);
1854 }
1855 if (firmware_types->len == 0) {
1856 /* TRANSLATORS: nothing found */
1857 g_print ("%s\n", _("No firmware IDs found"));
1858 return TRUE;
1859 }
1860
1861 return TRUE;
1862}
1863
1864static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001865fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001866{
1867 g_autoptr(GPtrArray) firmware_types = NULL;
1868 guint idx;
1869 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1870
1871 /* TRANSLATORS: get interactive prompt */
1872 g_print ("%s\n", _("Choose a firmware type:"));
1873 /* TRANSLATORS: this is to abort the interactive prompt */
1874 g_print ("0.\t%s\n", _("Cancel"));
1875 for (guint i = 0; i < firmware_types->len; i++) {
1876 const gchar *id = g_ptr_array_index (firmware_types, i);
1877 g_print ("%u.\t%s\n", i + 1, id);
1878 }
1879 idx = fu_util_prompt_for_number (firmware_types->len);
1880 if (idx == 0) {
1881 g_set_error_literal (error,
1882 FWUPD_ERROR,
1883 FWUPD_ERROR_NOTHING_TO_DO,
1884 "Request canceled");
1885 return NULL;
1886 }
1887
1888 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1889}
1890
1891static gboolean
1892fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1893{
1894 GType gtype;
1895 g_autoptr(GBytes) blob = NULL;
1896 g_autoptr(FuFirmware) firmware = NULL;
1897 g_autofree gchar *firmware_type = NULL;
1898 g_autofree gchar *str = NULL;
1899
1900 /* check args */
1901 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1902 g_set_error_literal (error,
1903 FWUPD_ERROR,
1904 FWUPD_ERROR_INVALID_ARGS,
1905 "Invalid arguments: filename required");
1906 return FALSE;
1907 }
1908
1909 if (g_strv_length (values) == 2)
1910 firmware_type = g_strdup (values[1]);
1911
1912 /* load file */
1913 blob = fu_common_get_contents_bytes (values[0], error);
1914 if (blob == NULL)
1915 return FALSE;
1916
1917 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001918 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001919 return FALSE;
1920
1921 /* find the GType to use */
1922 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001923 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001924 if (firmware_type == NULL)
1925 return FALSE;
1926 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1927 if (gtype == G_TYPE_INVALID) {
1928 g_set_error (error,
1929 G_IO_ERROR,
1930 G_IO_ERROR_NOT_FOUND,
1931 "GType %s not supported", firmware_type);
1932 return FALSE;
1933 }
1934 firmware = g_object_new (gtype, NULL);
1935 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1936 return FALSE;
1937 str = fu_firmware_to_string (firmware);
1938 g_print ("%s", str);
1939 return TRUE;
1940}
1941
Richard Hughesdd653442020-09-22 10:23:52 +01001942static gboolean
Richard Hughes52441f22021-03-12 21:21:10 +00001943fu_util_firmware_export (FuUtilPrivate *priv, gchar **values, GError **error)
1944{
1945 FuFirmwareExportFlags flags = FU_FIRMWARE_EXPORT_FLAG_NONE;
1946 GType gtype;
1947 g_autoptr(GBytes) blob = NULL;
1948 g_autoptr(FuFirmware) firmware = NULL;
1949 g_autofree gchar *firmware_type = NULL;
1950 g_autofree gchar *str = NULL;
1951
1952 /* check args */
1953 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1954 g_set_error_literal (error,
1955 FWUPD_ERROR,
1956 FWUPD_ERROR_INVALID_ARGS,
1957 "Invalid arguments: filename required");
1958 return FALSE;
1959 }
1960
1961 if (g_strv_length (values) == 2)
1962 firmware_type = g_strdup (values[1]);
1963
1964 /* load file */
1965 blob = fu_common_get_contents_bytes (values[0], error);
1966 if (blob == NULL)
1967 return FALSE;
1968
1969 /* load engine */
1970 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
1971 return FALSE;
1972
1973 /* find the GType to use */
1974 if (firmware_type == NULL)
1975 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1976 if (firmware_type == NULL)
1977 return FALSE;
1978 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1979 if (gtype == G_TYPE_INVALID) {
1980 g_set_error (error,
1981 G_IO_ERROR,
1982 G_IO_ERROR_NOT_FOUND,
1983 "GType %s not supported", firmware_type);
1984 return FALSE;
1985 }
1986 firmware = g_object_new (gtype, NULL);
1987 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1988 return FALSE;
1989 if (priv->show_all)
1990 flags |= FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG;
1991 str = fu_firmware_export_to_xml (firmware, flags, error);
1992 if (str == NULL)
1993 return FALSE;
1994 g_print ("%s", str);
1995 return TRUE;
1996}
1997
1998static gboolean
Richard Hughesdd653442020-09-22 10:23:52 +01001999fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
2000{
2001 GType gtype;
2002 g_autofree gchar *firmware_type = NULL;
2003 g_autofree gchar *str = NULL;
2004 g_autoptr(FuFirmware) firmware = NULL;
2005 g_autoptr(GBytes) blob = NULL;
2006 g_autoptr(GPtrArray) images = NULL;
2007
2008 /* check args */
2009 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
2010 g_set_error_literal (error,
2011 FWUPD_ERROR,
2012 FWUPD_ERROR_INVALID_ARGS,
2013 "Invalid arguments: filename required");
2014 return FALSE;
2015 }
2016 if (g_strv_length (values) == 2)
2017 firmware_type = g_strdup (values[1]);
2018
2019 /* load file */
2020 blob = fu_common_get_contents_bytes (values[0], error);
2021 if (blob == NULL)
2022 return FALSE;
2023
2024 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002025 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01002026 return FALSE;
2027
2028 /* find the GType to use */
2029 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002030 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01002031 if (firmware_type == NULL)
2032 return FALSE;
2033 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
2034 if (gtype == G_TYPE_INVALID) {
2035 g_set_error (error,
2036 G_IO_ERROR,
2037 G_IO_ERROR_NOT_FOUND,
2038 "GType %s not supported", firmware_type);
2039 return FALSE;
2040 }
2041 firmware = g_object_new (gtype, NULL);
2042 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
2043 return FALSE;
2044 str = fu_firmware_to_string (firmware);
2045 g_print ("%s", str);
2046 images = fu_firmware_get_images (firmware);
2047 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00002048 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesdd653442020-09-22 10:23:52 +01002049 g_autofree gchar *fn = NULL;
2050 g_autoptr(GBytes) blob_img = NULL;
2051
Richard Hughes88dd7c42020-09-22 16:54:40 +01002052 /* get raw image without generated header, footer or crc */
Richard Hughes1981c632021-03-09 09:44:12 +00002053 blob_img = fu_firmware_get_bytes (img, error);
2054 if (blob_img == NULL)
2055 return FALSE;
2056 if (g_bytes_get_size (blob_img) == 0)
Richard Hughes88dd7c42020-09-22 16:54:40 +01002057 continue;
2058
Richard Hughesdd653442020-09-22 10:23:52 +01002059 /* use suitable filename */
Richard Hughes1981c632021-03-09 09:44:12 +00002060 if (fu_firmware_get_filename (img) != NULL) {
2061 fn = g_strdup (fu_firmware_get_filename (img));
2062 } else if (fu_firmware_get_id (img) != NULL) {
2063 fn = g_strdup_printf ("id-%s.fw", fu_firmware_get_id (img));
2064 } else if (fu_firmware_get_idx (img) != 0x0) {
2065 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_get_idx (img));
Richard Hughesdd653442020-09-22 10:23:52 +01002066 } else {
2067 fn = g_strdup_printf ("img-0x%x.fw", i);
2068 }
2069 /* TRANSLATORS: decompressing images from a container firmware */
2070 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01002071 if (!fu_common_set_contents_bytes (fn, blob_img, error))
2072 return FALSE;
2073 }
2074
2075 /* success */
2076 return TRUE;
2077}
2078
Richard Hughes0924c932020-09-22 19:07:05 +01002079static gboolean
2080fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01002081{
Richard Hughes0924c932020-09-22 19:07:05 +01002082 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01002083 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01002084 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002085 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01002086 g_autoptr(FuFirmware) firmware_dst = NULL;
2087 g_autoptr(GBytes) blob_dst = NULL;
2088 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002089 g_autoptr(XbBuilder) builder = xb_builder_new ();
2090 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2091 g_autoptr(XbNode) n = NULL;
2092 g_autoptr(XbSilo) silo = NULL;
2093
Richard Hughes0924c932020-09-22 19:07:05 +01002094 /* check args */
2095 if (g_strv_length (values) != 2) {
2096 g_set_error_literal (error,
2097 FWUPD_ERROR,
2098 FWUPD_ERROR_INVALID_ARGS,
2099 "Invalid arguments: filename required");
2100 return FALSE;
2101 }
2102
2103 /* load file */
2104 blob_src = fu_common_get_contents_bytes (values[0], error);
2105 if (blob_src == NULL)
2106 return FALSE;
2107
2108 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002109 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002110 return FALSE;
2111
Richard Hughes41400a82020-09-21 13:43:15 +01002112 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002113 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002114 XB_BUILDER_SOURCE_FLAG_NONE,
2115 error)) {
2116 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002117 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002118 }
2119 xb_builder_import_source (builder, source);
2120 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2121 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002122 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002123
2124 /* create FuFirmware of specific GType */
2125 n = xb_silo_query_first (silo, "firmware", error);
2126 if (n == NULL)
2127 return FALSE;
2128 tmp = xb_node_get_attr (n, "gtype");
2129 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002130 gtype = g_type_from_name (tmp);
2131 if (gtype == G_TYPE_INVALID) {
2132 g_set_error (error,
2133 G_IO_ERROR,
2134 G_IO_ERROR_NOT_FOUND,
2135 "GType %s not registered", tmp);
2136 return FALSE;
2137 }
2138 }
2139 tmp = xb_node_get_attr (n, "id");
2140 if (tmp != NULL) {
2141 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002142 if (gtype == G_TYPE_INVALID) {
2143 g_set_error (error,
2144 G_IO_ERROR,
2145 G_IO_ERROR_NOT_FOUND,
2146 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002147 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002148 }
Richard Hughes41400a82020-09-21 13:43:15 +01002149 }
Richard Hughes0924c932020-09-22 19:07:05 +01002150 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002151 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002152 return FALSE;
2153
2154 /* write new file */
2155 blob_dst = fu_firmware_write (firmware, error);
2156 if (blob_dst == NULL)
2157 return FALSE;
2158 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2159 return FALSE;
2160
2161 /* show what we wrote */
2162 firmware_dst = g_object_new (gtype, NULL);
2163 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2164 return FALSE;
2165 str = fu_firmware_to_string (firmware_dst);
2166 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002167
2168 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002169 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002170}
2171
Richard Hughes95c98a92019-10-22 16:03:15 +01002172static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002173fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2174{
2175 GType gtype_dst;
2176 GType gtype_src;
2177 g_autofree gchar *firmware_type_dst = NULL;
2178 g_autofree gchar *firmware_type_src = NULL;
2179 g_autofree gchar *str_dst = NULL;
2180 g_autofree gchar *str_src = NULL;
2181 g_autoptr(FuFirmware) firmware_dst = NULL;
2182 g_autoptr(FuFirmware) firmware_src = NULL;
2183 g_autoptr(GBytes) blob_dst = NULL;
2184 g_autoptr(GBytes) blob_src = NULL;
2185 g_autoptr(GPtrArray) images = NULL;
2186
2187 /* check args */
2188 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2189 g_set_error_literal (error,
2190 FWUPD_ERROR,
2191 FWUPD_ERROR_INVALID_ARGS,
2192 "Invalid arguments: filename required");
2193 return FALSE;
2194 }
2195
2196 if (g_strv_length (values) > 2)
2197 firmware_type_src = g_strdup (values[2]);
2198 if (g_strv_length (values) > 3)
2199 firmware_type_dst = g_strdup (values[3]);
2200
2201 /* load file */
2202 blob_src = fu_common_get_contents_bytes (values[0], error);
2203 if (blob_src == NULL)
2204 return FALSE;
2205
2206 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002207 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002208 return FALSE;
2209
2210 /* find the GType to use */
2211 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002212 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002213 if (firmware_type_src == NULL)
2214 return FALSE;
2215 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002216 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002217 if (firmware_type_dst == NULL)
2218 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002219 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2220 if (gtype_src == G_TYPE_INVALID) {
2221 g_set_error (error,
2222 G_IO_ERROR,
2223 G_IO_ERROR_NOT_FOUND,
2224 "GType %s not supported", firmware_type_src);
2225 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002226 }
Richard Hughes0924c932020-09-22 19:07:05 +01002227 firmware_src = g_object_new (gtype_src, NULL);
2228 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2229 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002230 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2231 if (gtype_dst == G_TYPE_INVALID) {
2232 g_set_error (error,
2233 G_IO_ERROR,
2234 G_IO_ERROR_NOT_FOUND,
2235 "GType %s not supported", firmware_type_dst);
2236 return FALSE;
2237 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002238 str_src = fu_firmware_to_string (firmware_src);
2239 g_print ("%s", str_src);
2240
2241 /* copy images */
2242 firmware_dst = g_object_new (gtype_dst, NULL);
2243 images = fu_firmware_get_images (firmware_src);
2244 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00002245 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002246 fu_firmware_add_image (firmware_dst, img);
2247 }
2248
2249 /* write new file */
2250 blob_dst = fu_firmware_write (firmware_dst, error);
2251 if (blob_dst == NULL)
2252 return FALSE;
2253 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2254 return FALSE;
2255 str_dst = fu_firmware_to_string (firmware_dst);
2256 g_print ("%s", str_dst);
2257
2258 /* success */
2259 return TRUE;
2260}
2261
2262static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002263fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2264{
2265 g_autofree gchar *str = NULL;
2266 g_autoptr(FuDevice) dev = NULL;
2267
2268 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002269 if (!fu_util_start_engine (priv,
2270 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2271 FU_ENGINE_LOAD_FLAG_HWINFO |
2272 FU_ENGINE_LOAD_FLAG_REMOTES,
2273 error))
Richard Hughes15684492019-03-15 16:27:50 +00002274 return FALSE;
2275
2276 /* get device */
2277 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002278 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002279 if (dev == NULL)
2280 return FALSE;
2281 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002282 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002283 if (dev == NULL)
2284 return FALSE;
2285 }
2286
2287 /* add checksums */
2288 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2289 return FALSE;
2290
2291 /* show checksums */
2292 str = fu_device_to_string (dev);
2293 g_print ("%s\n", str);
2294 return TRUE;
2295}
2296
Mario Limonciellofe593942019-04-03 13:48:52 -05002297static gboolean
2298fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2299{
2300 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002301 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002302 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002303
2304 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002305 if (!fu_util_start_engine (priv,
2306 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2307 FU_ENGINE_LOAD_FLAG_HWINFO |
2308 FU_ENGINE_LOAD_FLAG_REMOTES,
2309 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002310 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002311 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002312
2313 /* get all devices from the history database */
2314 devices = fu_engine_get_history (priv->engine, error);
2315 if (devices == NULL)
2316 return FALSE;
2317
2318 /* show each device */
2319 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002320 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002321 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002322 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002323 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002324 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002325
Richard Hughes747f5702019-08-06 14:27:26 +01002326 if (!fu_util_filter_device (priv, dev))
2327 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002328 child = g_node_append_data (root, dev);
2329
2330 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002331 if (rel == NULL)
2332 continue;
2333 remote = fwupd_release_get_remote_id (rel);
2334
2335 /* doesn't actually map to remote */
2336 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002337 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002338 continue;
2339 }
2340
2341 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002342 rels = fu_engine_get_releases (priv->engine,
2343 priv->request,
2344 fwupd_device_get_id (dev),
2345 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002346 if (rels == NULL)
2347 return FALSE;
2348
2349 /* map to a release in client */
2350 for (guint j = 0; j < rels->len; j++) {
2351 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2352 if (g_strcmp0 (remote,
2353 fwupd_release_get_remote_id (rel2)) != 0)
2354 continue;
2355 if (g_strcmp0 (fwupd_release_get_version (rel),
2356 fwupd_release_get_version (rel2)) != 0)
2357 continue;
2358 g_node_append_data (child, g_object_ref (rel2));
2359 rel = NULL;
2360 break;
2361 }
2362
2363 /* didn't match anything */
2364 if (rels->len == 0 || rel != NULL) {
2365 g_node_append_data (child, rel);
2366 continue;
2367 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002368
Mario Limonciellofe593942019-04-03 13:48:52 -05002369 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002370 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002371
2372 return TRUE;
2373}
2374
Richard Hughesfd7e9942020-01-17 14:09:13 +00002375static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002376fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2377{
Richard Hughesc5710d92020-06-26 11:08:25 +01002378 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002379 g_autofree gchar *fn_raw = NULL;
2380 g_autofree gchar *fn_sig = NULL;
2381 g_autoptr(GBytes) bytes_raw = NULL;
2382 g_autoptr(GBytes) bytes_sig = NULL;
2383
Richard Hughes4959baa2020-01-17 14:33:00 +00002384 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002385 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2386 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002387 g_set_error (error,
2388 FWUPD_ERROR,
2389 FWUPD_ERROR_NOTHING_TO_DO,
2390 "no metadata signature URI available for %s",
2391 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002392 return FALSE;
2393 }
2394 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002395 if (!fu_common_mkdir_parent (fn_sig, error))
2396 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002397 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002398 return FALSE;
2399 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2400 if (bytes_sig == NULL)
2401 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002402 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2403 return FALSE;
2404
2405 /* payload */
2406 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2407 if (metadata_uri == NULL) {
2408 g_set_error (error,
2409 FWUPD_ERROR,
2410 FWUPD_ERROR_NOTHING_TO_DO,
2411 "no metadata URI available for %s",
2412 fwupd_remote_get_id (remote));
2413 return FALSE;
2414 }
2415 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2416 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2417 return FALSE;
2418 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2419 if (bytes_raw == NULL)
2420 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002421
2422 /* send to daemon */
2423 g_debug ("updating %s", fwupd_remote_get_id (remote));
2424 return fu_engine_update_metadata_bytes (priv->engine,
2425 fwupd_remote_get_id (remote),
2426 bytes_raw,
2427 bytes_sig,
2428 error);
2429
2430}
2431
2432static gboolean
2433fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2434{
2435 g_autoptr(GPtrArray) remotes = NULL;
2436
2437 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002438 if (!fu_util_start_engine (priv,
2439 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2440 FU_ENGINE_LOAD_FLAG_HWINFO |
2441 FU_ENGINE_LOAD_FLAG_REMOTES,
2442 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002443 return FALSE;
2444
2445 /* download new metadata */
2446 remotes = fu_engine_get_remotes (priv->engine, error);
2447 if (remotes == NULL)
2448 return FALSE;
2449 for (guint i = 0; i < remotes->len; i++) {
2450 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2451 if (!fwupd_remote_get_enabled (remote))
2452 continue;
2453 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2454 continue;
2455 if (!fu_util_refresh_remote (priv, remote, error))
2456 return FALSE;
2457 }
2458 return TRUE;
2459}
2460
2461static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002462fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2463{
2464 g_autoptr(GNode) root = g_node_new (NULL);
2465 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002466 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002467
2468 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002469 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002470 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002471 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002472
2473 /* list remotes */
2474 remotes = fu_engine_get_remotes (priv->engine, error);
2475 if (remotes == NULL)
2476 return FALSE;
2477 if (remotes->len == 0) {
2478 g_set_error_literal (error,
2479 FWUPD_ERROR,
2480 FWUPD_ERROR_NOTHING_TO_DO,
2481 "no remotes available");
2482 return FALSE;
2483 }
2484 for (guint i = 0; i < remotes->len; i++) {
2485 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2486 g_node_append_data (root, remote_tmp);
2487 }
2488 fu_util_print_tree (root, title);
2489
2490 return TRUE;
2491}
2492
Richard Hughes196c6c62020-05-11 19:42:47 +01002493static gboolean
2494fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2495{
Richard Hughes5c82b942020-09-14 12:24:06 +01002496 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002497 g_autoptr(FuSecurityAttrs) attrs = NULL;
2498 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002499 g_autofree gchar *str = NULL;
2500
2501 /* not ready yet */
2502 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2503 g_set_error_literal (error,
2504 FWUPD_ERROR,
2505 FWUPD_ERROR_NOT_SUPPORTED,
2506 "The HSI specification is not yet complete. "
2507 "To ignore this warning, use --force");
2508 return FALSE;
2509 }
2510
Richard Hughesc7d870a2020-12-10 10:05:35 +00002511 if (!fu_util_start_engine (priv,
2512 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2513 FU_ENGINE_LOAD_FLAG_HWINFO |
2514 FU_ENGINE_LOAD_FLAG_REMOTES,
2515 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002516 return FALSE;
2517
2518 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2519 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2520 fu_engine_get_host_security_id (priv->engine));
2521
Richard Hughes5c82b942020-09-14 12:24:06 +01002522 /* show or hide different elements */
2523 if (priv->show_all) {
2524 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2525 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2526 }
2527
Richard Hughes196c6c62020-05-11 19:42:47 +01002528 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002529 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002530 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002531 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002532 g_print ("%s\n", str);
2533 return TRUE;
2534}
2535
Richard Hughesa83deb42020-08-12 12:45:36 +01002536static FuVolume *
2537fu_util_prompt_for_volume (GError **error)
2538{
2539 FuVolume *volume;
2540 guint idx;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002541 gboolean is_fallback = FALSE;
Richard Hughesa83deb42020-08-12 12:45:36 +01002542 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002543 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2544 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002545
2546 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002547 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2548 if (volumes == NULL) {
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002549 is_fallback = TRUE;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002550 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2551 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2552 if (volumes == NULL) {
2553 g_prefix_error (error, "%s: ", error_local->message);
2554 return NULL;
2555 }
2556 }
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002557 /* on fallback: only add internal vfat partitions */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002558 for (guint i = 0; i < volumes->len; i++) {
2559 FuVolume *vol = g_ptr_array_index (volumes, i);
2560 g_autofree gchar *type = fu_volume_get_id_type (vol);
2561 if (type == NULL)
2562 continue;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002563 if (is_fallback && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002564 continue;
2565 if (g_strcmp0 (type, "vfat") == 0)
2566 g_ptr_array_add (volumes_vfat, vol);
2567 }
2568 if (volumes_vfat->len == 1) {
2569 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002570 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002571 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2572 return g_object_ref (volume);
2573 }
2574
2575 /* TRANSLATORS: get interactive prompt */
2576 g_print ("%s\n", _("Choose a volume:"));
2577 /* TRANSLATORS: this is to abort the interactive prompt */
2578 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002579 for (guint i = 0; i < volumes_vfat->len; i++) {
2580 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002581 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2582 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002583 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002584 if (idx == 0) {
2585 g_set_error_literal (error,
2586 FWUPD_ERROR,
2587 FWUPD_ERROR_NOTHING_TO_DO,
2588 "Request canceled");
2589 return NULL;
2590 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002591 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002592 return g_object_ref (volume);
2593
2594}
2595
2596static gboolean
2597fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2598{
2599 g_autoptr(FuVolume) volume = NULL;
2600 volume = fu_util_prompt_for_volume (error);
2601 if (volume == NULL)
2602 return FALSE;
2603 return fu_volume_mount (volume, error);
2604}
2605
2606static gboolean
2607fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2608{
2609 g_autoptr(FuVolume) volume = NULL;
2610 volume = fu_util_prompt_for_volume (error);
2611 if (volume == NULL)
2612 return FALSE;
2613 return fu_volume_unmount (volume, error);
2614}
2615
2616static gboolean
2617fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2618{
2619 g_autofree gchar *mount_point = NULL;
2620 g_autoptr(FuDeviceLocker) locker = NULL;
2621 g_autoptr(FuVolume) volume = NULL;
2622 g_autoptr(GPtrArray) files = NULL;
2623
2624 volume = fu_util_prompt_for_volume (error);
2625 if (volume == NULL)
2626 return FALSE;
2627 locker = fu_volume_locker (volume, error);
2628 if (locker == NULL)
2629 return FALSE;
2630 mount_point = fu_volume_get_mount_point (volume);
2631 files = fu_common_get_files_recursive (mount_point, error);
2632 if (files == NULL)
2633 return FALSE;
2634 for (guint i = 0; i < files->len; i++) {
2635 const gchar *fn = g_ptr_array_index (files, i);
2636 g_print ("%s\n", fn);
2637 }
2638 return TRUE;
2639}
2640
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002641static gboolean
2642_g_str_equal0 (gconstpointer str1, gconstpointer str2)
2643{
2644 return g_strcmp0 (str1, str2) == 0;
2645}
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002646
2647static gboolean
2648fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2649{
2650 const gchar *branch;
2651 g_autoptr(FwupdRelease) rel = NULL;
2652 g_autoptr(GPtrArray) rels = NULL;
2653 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2654 g_autoptr(FuDevice) dev = NULL;
2655
2656 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002657 if (!fu_util_start_engine (priv,
2658 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2659 FU_ENGINE_LOAD_FLAG_HWINFO |
2660 FU_ENGINE_LOAD_FLAG_REMOTES,
2661 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002662 return FALSE;
2663
2664 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002665 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00002666 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002667 if (g_strv_length (values) == 1)
2668 dev = fu_util_get_device (priv, values[1], error);
2669 else
2670 dev = fu_util_prompt_for_device (priv, NULL, error);
2671 if (dev == NULL)
2672 return FALSE;
2673 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2674 g_set_error_literal (error,
2675 FWUPD_ERROR,
2676 FWUPD_ERROR_NOT_SUPPORTED,
2677 "Multiple branches not available");
2678 return FALSE;
2679 }
2680
2681 /* get all releases, including the alternate branch versions */
2682 rels = fu_engine_get_releases (priv->engine,
2683 priv->request,
2684 fu_device_get_id (dev),
2685 error);
2686 if (rels == NULL)
2687 return FALSE;
2688
2689 /* get all the unique branches */
2690 for (guint i = 0; i < rels->len; i++) {
2691 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002692 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002693#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002694 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002695 _g_str_equal0, NULL))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002696 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002697#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002698 g_ptr_array_add (branches, g_strdup (branch_tmp));
2699 }
2700
2701 /* branch name is optional */
2702 if (g_strv_length (values) > 1) {
2703 branch = values[1];
2704 } else if (branches->len == 1) {
2705 branch = g_ptr_array_index (branches, 0);
2706 } else {
2707 guint idx;
2708
2709 /* TRANSLATORS: get interactive prompt, where branch is the
2710 * supplier of the firmware, e.g. "non-free" or "free" */
2711 g_print ("%s\n", _("Choose a branch:"));
2712 /* TRANSLATORS: this is to abort the interactive prompt */
2713 g_print ("0.\t%s\n", _("Cancel"));
2714 for (guint i = 0; i < branches->len; i++) {
2715 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002716 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002717 }
2718 idx = fu_util_prompt_for_number (branches->len);
2719 if (idx == 0) {
2720 g_set_error_literal (error,
2721 FWUPD_ERROR,
2722 FWUPD_ERROR_NOTHING_TO_DO,
2723 "Request canceled");
2724 return FALSE;
2725 }
2726 branch = g_ptr_array_index (branches, idx - 1);
2727 }
2728
2729 /* sanity check */
2730 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2731 g_set_error (error,
2732 FWUPD_ERROR,
2733 FWUPD_ERROR_NOT_SUPPORTED,
2734 "Device %s is already on branch %s",
2735 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002736 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002737 return FALSE;
2738 }
2739
2740 /* the releases are ordered by version */
2741 for (guint j = 0; j < rels->len; j++) {
2742 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2743 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2744 rel = g_object_ref (rel_tmp);
2745 break;
2746 }
2747 }
2748 if (rel == NULL) {
2749 g_set_error (error,
2750 FWUPD_ERROR,
2751 FWUPD_ERROR_NOT_SUPPORTED,
2752 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002753 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002754 return FALSE;
2755 }
2756
2757 /* we're switching branch */
2758 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2759 return FALSE;
2760
2761 /* update the console if composite devices are also updated */
2762 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2763 g_signal_connect (priv->engine, "device-changed",
2764 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2765 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2766 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2767 if (!fu_util_install_release (priv, rel, error))
2768 return FALSE;
2769 fu_util_display_current_message (priv);
2770
2771 /* we don't want to ask anything */
2772 if (priv->no_reboot_check) {
2773 g_debug ("skipping reboot check");
2774 return TRUE;
2775 }
2776
2777 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2778}
2779
Richard Hughesb5976832018-05-18 10:02:09 +01002780int
2781main (int argc, char *argv[])
2782{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002783 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002784 gboolean allow_older = FALSE;
2785 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002786 gboolean force = FALSE;
2787 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002788 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002789 gboolean ignore_checksum = FALSE;
2790 gboolean ignore_power = FALSE;
2791 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002792 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002793 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002794 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2795 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002796 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002797 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002798 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002799 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002800 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2801 /* TRANSLATORS: command line option */
2802 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002803 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2804 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002805 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002806 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2807 /* TRANSLATORS: command line option */
2808 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002809 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2810 /* TRANSLATORS: command line option */
2811 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002812 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2813 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002814 _("Force the action by relaxing some runtime checks"), NULL },
2815 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2816 /* TRANSLATORS: command line option */
2817 _("Ignore firmware checksum failures"), NULL },
2818 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2819 /* TRANSLATORS: command line option */
2820 _("Ignore firmware hardware mismatch failures"), NULL },
2821 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2822 /* TRANSLATORS: command line option */
2823 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002824 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2825 /* TRANSLATORS: command line option */
2826 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002827 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2828 /* TRANSLATORS: command line option */
2829 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002830 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2831 /* TRANSLATORS: command line option */
2832 _("Show all results"), NULL },
2833 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002834 /* TRANSLATORS: command line option */
2835 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002836 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002837 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002838 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002839 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2840 /* TRANSLATORS: command line option */
2841 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002842 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002843 /* TRANSLATORS: command line option */
2844 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002845 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002846 /* TRANSLATORS: command line option */
2847 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002848 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2849 /* TRANSLATORS: command line option */
2850 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002851 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2852 /* TRANSLATORS: command line option */
2853 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002854 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2855 /* TRANSLATORS: command line option */
2856 _("Filter with a set of device flags using a ~ prefix to "
2857 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002858 { NULL}
2859 };
2860
Richard Hughes429f72b2020-01-16 12:18:19 +00002861#ifdef _WIN32
2862 /* workaround Windows setting the codepage to 1252 */
2863 g_setenv ("LANG", "C.UTF-8", FALSE);
2864#endif
2865
Richard Hughesb5976832018-05-18 10:02:09 +01002866 setlocale (LC_ALL, "");
2867
Richard Hughes668ee212019-11-22 09:17:46 +00002868 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002869 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2870 textdomain (GETTEXT_PACKAGE);
2871
Richard Hughes01c0bad2019-11-22 09:08:51 +00002872#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002873 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002874 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002875 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002876 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002877#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002878
2879 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002880 priv->main_ctx = g_main_context_new ();
2881 priv->loop = g_main_loop_new (priv->main_ctx, FALSE);
Richard Hughesb5976832018-05-18 10:02:09 +01002882 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002883 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002884
2885 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002886 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002887 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002888 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2889 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002890 /* TRANSLATORS: command description */
2891 _("Build firmware using a sandbox"),
2892 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002893 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002894 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002895 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2896 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002897 /* TRANSLATORS: command description */
2898 _("Dump SMBIOS data from a file"),
2899 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002900 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002901 "get-plugins",
2902 NULL,
2903 /* TRANSLATORS: command description */
2904 _("Get all enabled plugins registered with the system"),
2905 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002906 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002907 "get-details",
2908 NULL,
2909 /* TRANSLATORS: command description */
2910 _("Gets details about a firmware file"),
2911 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002912 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002913 "get-history",
2914 NULL,
2915 /* TRANSLATORS: command description */
2916 _("Show history of firmware updates"),
2917 fu_util_get_history);
2918 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002919 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002920 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2921 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002922 /* TRANSLATORS: command description */
2923 _("Gets the list of updates for connected hardware"),
2924 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002925 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002926 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002927 NULL,
2928 /* TRANSLATORS: command description */
2929 _("Get all devices that support firmware updates"),
2930 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002931 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002932 "get-device-flags",
2933 NULL,
2934 /* TRANSLATORS: command description */
2935 _("Get all device flags supported by fwupd"),
2936 fu_util_get_device_flags);
2937 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002938 "watch",
2939 NULL,
2940 /* TRANSLATORS: command description */
Piotr Drąg472fa592018-06-06 14:53:48 +02002941 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002942 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002943 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002944 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002945 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2946 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002947 /* TRANSLATORS: command description */
2948 _("Install a firmware blob on a device"),
2949 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002950 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002951 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002952 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2953 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002954 /* TRANSLATORS: command description */
2955 _("Install a firmware file on this hardware"),
2956 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002957 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002958 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002959 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2960 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002961 /* TRANSLATORS: command description */
2962 _("Reinstall firmware on a device"),
2963 fu_util_reinstall);
2964 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002965 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002966 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2967 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002968 /* TRANSLATORS: command description */
2969 _("Attach to firmware mode"),
2970 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002971 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002972 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002973 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2974 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002975 /* TRANSLATORS: command description */
2976 _("Detach to bootloader mode"),
2977 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002978 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002979 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002980 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2981 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002982 /* TRANSLATORS: command description */
2983 _("Unbind current driver"),
2984 fu_util_unbind_driver);
2985 fu_util_cmd_array_add (cmd_array,
2986 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002987 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2988 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002989 /* TRANSLATORS: command description */
2990 _("Bind new kernel driver"),
2991 fu_util_bind_driver);
2992 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002993 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002994 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2995 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002996 /* TRANSLATORS: command description */
2997 _("Activate pending devices"),
2998 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002999 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01003000 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003001 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
Richard Hughesf6751cd2021-01-15 16:48:25 +00003002 _("[SMBIOS-FILE|HWIDS-FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01003003 /* TRANSLATORS: command description */
3004 _("Return all the hardware IDs for the machine"),
3005 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00003006 fu_util_cmd_array_add (cmd_array,
Richard Hughesf6751cd2021-01-15 16:48:25 +00003007 "export-hwids",
3008 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3009 _("HWIDS-FILE"),
3010 /* TRANSLATORS: command description */
3011 _("Save a file that allows generation of hardware IDs"),
3012 fu_util_export_hwids);
3013 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05003014 "monitor",
3015 NULL,
3016 /* TRANSLATORS: command description */
3017 _("Monitor the daemon for events"),
3018 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00003019 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05003020 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003021 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3022 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06003023 /* TRANSLATORS: command description */
3024 _("Update all devices that match local metadata"),
3025 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00003026 fu_util_cmd_array_add (cmd_array,
3027 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003028 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3029 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00003030 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00003031 C_("command-description",
3032 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00003033 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00003034 fu_util_cmd_array_add (cmd_array,
3035 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003036 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3037 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00003038 /* TRANSLATORS: command description */
3039 _("Update the stored metadata with current contents"),
3040 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01003041 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01003042 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003043 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3044 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00003045 /* TRANSLATORS: command description */
3046 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01003047 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00003048 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00003049 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003050 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3051 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00003052 /* TRANSLATORS: command description */
3053 _("Convert a firmware file"),
3054 fu_util_firmware_convert);
3055 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01003056 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003057 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3058 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01003059 /* TRANSLATORS: command description */
3060 _("Build a firmware file"),
3061 fu_util_firmware_build);
3062 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003063 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003064 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3065 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01003066 /* TRANSLATORS: command description */
3067 _("Parse and show details about a firmware file"),
3068 fu_util_firmware_parse);
3069 fu_util_cmd_array_add (cmd_array,
Richard Hughes52441f22021-03-12 21:21:10 +00003070 "firmware-export",
3071 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3072 _("FILENAME [FIRMWARE-TYPE]"),
3073 /* TRANSLATORS: command description */
3074 _("Export a firmware file structure to XML"),
3075 fu_util_firmware_export);
3076 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01003077 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003078 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3079 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01003080 /* TRANSLATORS: command description */
3081 _("Extract a firmware blob to images"),
3082 fu_util_firmware_extract);
3083 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003084 "get-firmware-types",
3085 NULL,
3086 /* TRANSLATORS: command description */
3087 _("List the available firmware types"),
3088 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00003089 fu_util_cmd_array_add (cmd_array,
3090 "get-remotes",
3091 NULL,
3092 /* TRANSLATORS: command description */
3093 _("Gets the configured remotes"),
3094 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00003095 fu_util_cmd_array_add (cmd_array,
3096 "refresh",
3097 NULL,
3098 /* TRANSLATORS: command description */
3099 _("Refresh metadata from remote server"),
3100 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01003101 fu_util_cmd_array_add (cmd_array,
3102 "security",
3103 NULL,
3104 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003105 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003106 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01003107 fu_util_cmd_array_add (cmd_array,
3108 "esp-mount",
3109 NULL,
3110 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003111 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003112 fu_util_esp_mount);
3113 fu_util_cmd_array_add (cmd_array,
3114 "esp-unmount",
3115 NULL,
3116 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003117 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003118 fu_util_esp_unmount);
3119 fu_util_cmd_array_add (cmd_array,
3120 "esp-list",
3121 NULL,
3122 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003123 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003124 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003125 fu_util_cmd_array_add (cmd_array,
3126 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003127 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3128 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003129 /* TRANSLATORS: command description */
3130 _("Switch the firmware branch on the device"),
3131 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01003132
3133 /* do stuff on ctrl+c */
3134 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003135#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003136 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3137 SIGINT, fu_util_sigint_cb,
3138 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003139#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003140 g_signal_connect (priv->cancellable, "cancelled",
3141 G_CALLBACK (fu_util_cancelled_cb), priv);
3142
3143 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003144 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003145
Mario Limonciello3f243a92019-01-21 22:05:23 -06003146 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003147 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003148 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003149 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003150 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003151 } else {
3152 /* set our implemented feature set */
3153 fu_engine_request_set_feature_flags (priv->request,
3154 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003155 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003156 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003157 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003158
Richard Hughesb5976832018-05-18 10:02:09 +01003159 /* get a list of the commands */
3160 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003161 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003162 g_option_context_set_summary (priv->context, cmd_descriptions);
3163 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003164 /* TRANSLATORS: CLI description */
3165 _("This tool allows an administrator to use the fwupd plugins "
3166 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003167
3168 /* TRANSLATORS: program name */
3169 g_set_application_name (_("Firmware Utility"));
3170 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003171 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003172 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3173 if (!ret) {
3174 /* TRANSLATORS: the user didn't read the man page */
3175 g_print ("%s: %s\n", _("Failed to parse arguments"),
3176 error->message);
3177 return EXIT_FAILURE;
3178 }
3179
Richard Hughes0e46b222019-09-05 12:13:35 +01003180 /* allow disabling SSL strict mode for broken corporate proxies */
3181 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003182 g_autofree gchar *fmt = NULL;
3183 /* TRANSLATORS: this is a prefix on the console */
3184 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003185 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003186 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3187 "to do this automatically in the future "
3188 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003189 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3190 }
3191
Richard Hughes747f5702019-08-06 14:27:26 +01003192 /* parse filter flags */
3193 if (filter != NULL) {
3194 if (!fu_util_parse_filter_flags (filter,
3195 &priv->filter_include,
3196 &priv->filter_exclude,
3197 &error)) {
3198 /* TRANSLATORS: the user didn't read the man page */
3199 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3200 error->message);
3201 return EXIT_FAILURE;
3202 }
3203 }
3204
3205
Richard Hughes460226a2018-05-21 20:56:21 +01003206 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003207 if (allow_reinstall)
3208 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3209 if (allow_older)
3210 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003211 if (allow_branch_switch)
3212 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3213 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003214 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003215 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3216 }
3217 if (ignore_checksum)
3218 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3219 if (ignore_vid_pid)
3220 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3221 if (ignore_power)
3222 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003223
Richard Hughes98ca9932018-05-18 10:24:07 +01003224 /* load engine */
3225 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3226 g_signal_connect (priv->engine, "device-added",
3227 G_CALLBACK (fu_main_engine_device_added_cb),
3228 priv);
3229 g_signal_connect (priv->engine, "device-removed",
3230 G_CALLBACK (fu_main_engine_device_removed_cb),
3231 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003232 g_signal_connect (priv->engine, "status-changed",
3233 G_CALLBACK (fu_main_engine_status_changed_cb),
3234 priv);
3235 g_signal_connect (priv->engine, "percentage-changed",
3236 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3237 priv);
3238
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003239 /* just show versions and exit */
3240 if (version) {
3241 g_autofree gchar *version_str = fu_util_get_versions ();
3242 g_print ("%s\n", version_str);
3243 return EXIT_SUCCESS;
3244 }
3245
Richard Hughes85226fd2020-06-30 14:43:48 +01003246 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003247 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3248 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3249
Richard Hughesb5976832018-05-18 10:02:09 +01003250 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003251 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003252 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003253 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003254 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003255 /* TRANSLATORS: error message explaining command to run to how to get help */
3256 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3257 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3258 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003259 return EXIT_NOTHING_TO_DO;
3260 }
Richard Hughesb5976832018-05-18 10:02:09 +01003261 return EXIT_FAILURE;
3262 }
3263
3264 /* success */
3265 return EXIT_SUCCESS;
3266}