blob: 6a0ca5f7d8f018bf690775c589ccf88d16bdf551 [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 */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001552 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY, error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001553 return FALSE;
1554
1555 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001556 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001557 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001558 if (devices == NULL)
1559 return FALSE;
1560 } else if (g_strv_length (values) == 1) {
1561 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001562 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001563 if (device == NULL)
1564 return FALSE;
1565 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1566 g_ptr_array_add (devices, device);
1567 } else {
1568 g_set_error_literal (error,
1569 FWUPD_ERROR,
1570 FWUPD_ERROR_INVALID_ARGS,
1571 "Invalid arguments");
1572 return FALSE;
1573 }
1574
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001575 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001576 /* order by device priority */
1577 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001578 for (guint i = 0; i < devices->len; i++) {
1579 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001580 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1581 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001582 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1583 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001584 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001585 /* TRANSLATORS: shown when shutting down to switch to the new version */
1586 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1587 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1588 return FALSE;
1589 }
1590
Mario Limonciello02085a02020-09-11 14:59:35 -05001591 if (!has_pending) {
1592 g_set_error_literal (error,
1593 FWUPD_ERROR,
1594 FWUPD_ERROR_NOTHING_TO_DO,
1595 "No devices to activate");
1596 return FALSE;
1597 }
1598
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001599 return TRUE;
1600}
1601
1602static gboolean
Richard Hughesf6751cd2021-01-15 16:48:25 +00001603fu_util_export_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1604{
1605 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1606 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1607 g_autoptr(GKeyFile) kf = g_key_file_new ();
1608 g_autoptr(GPtrArray) hwid_keys = NULL;
1609
1610 /* check args */
1611 if (g_strv_length (values) != 1) {
1612 g_set_error_literal (error,
1613 FWUPD_ERROR,
1614 FWUPD_ERROR_INVALID_ARGS,
1615 "Invalid arguments, expected HWIDS-FILE");
1616 return FALSE;
1617 }
1618
1619 /* setup default hwids */
1620 if (!fu_smbios_setup (smbios, error))
1621 return FALSE;
1622 if (!fu_hwids_setup (hwids, smbios, error))
1623 return FALSE;
1624
1625 /* save all keys */
1626 hwid_keys = fu_hwids_get_keys (hwids);
1627 for (guint i = 0; i < hwid_keys->len; i++) {
1628 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1629 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1630 g_key_file_set_string (kf, "HwIds", hwid_key, value);
1631 }
1632
1633 /* success */
1634 return g_key_file_save_to_file (kf, values[0], error);
1635}
1636
1637static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001638fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1639{
Richard Hughesf6751cd2021-01-15 16:48:25 +00001640 g_autoptr(FuSmbios) smbios = NULL;
Richard Hughes1d894f12018-08-31 13:05:51 +01001641 g_autoptr(FuHwids) hwids = fu_hwids_new ();
Richard Hughesf6751cd2021-01-15 16:48:25 +00001642 g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys (hwids);
Richard Hughes1d894f12018-08-31 13:05:51 +01001643
1644 /* read DMI data */
1645 if (g_strv_length (values) == 0) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001646 smbios = fu_smbios_new ();
Richard Hughes1d894f12018-08-31 13:05:51 +01001647 if (!fu_smbios_setup (smbios, error))
1648 return FALSE;
1649 } else if (g_strv_length (values) == 1) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001650 /* a keyfile with overrides */
1651 g_autoptr(GKeyFile) kf = g_key_file_new ();
1652 if (g_key_file_load_from_file (kf, values[0], G_KEY_FILE_NONE, NULL)) {
1653 for (guint i = 0; i < hwid_keys->len; i++) {
1654 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1655 g_autofree gchar *tmp = NULL;
1656 tmp = g_key_file_get_string (kf, "HwIds", hwid_key, NULL);
1657 fu_hwids_add_smbios_override (hwids, hwid_key, tmp);
1658 }
1659 /* a DMI blob */
1660 } else {
1661 smbios = fu_smbios_new ();
1662 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1663 return FALSE;
1664 }
Richard Hughes1d894f12018-08-31 13:05:51 +01001665 } else {
1666 g_set_error_literal (error,
1667 FWUPD_ERROR,
1668 FWUPD_ERROR_INVALID_ARGS,
1669 "Invalid arguments");
1670 return FALSE;
1671 }
1672 if (!fu_hwids_setup (hwids, smbios, error))
1673 return FALSE;
1674
1675 /* show debug output */
1676 g_print ("Computer Information\n");
1677 g_print ("--------------------\n");
Richard Hughesf6751cd2021-01-15 16:48:25 +00001678 for (guint i = 0; i < hwid_keys->len; i++) {
1679 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1680 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1681 if (value == NULL)
Richard Hughes1d894f12018-08-31 13:05:51 +01001682 continue;
Richard Hughesf6751cd2021-01-15 16:48:25 +00001683 if (g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1684 g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1685 guint64 val = g_ascii_strtoull (value, NULL, 16);
1686 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
Richard Hughes1d894f12018-08-31 13:05:51 +01001687 } else {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001688 g_print ("%s: %s\n", hwid_key, value);
Richard Hughes1d894f12018-08-31 13:05:51 +01001689 }
1690 }
1691
1692 /* show GUIDs */
1693 g_print ("\nHardware IDs\n");
1694 g_print ("------------\n");
1695 for (guint i = 0; i < 15; i++) {
1696 const gchar *keys = NULL;
1697 g_autofree gchar *guid = NULL;
1698 g_autofree gchar *key = NULL;
1699 g_autofree gchar *keys_str = NULL;
1700 g_auto(GStrv) keysv = NULL;
1701 g_autoptr(GError) error_local = NULL;
1702
1703 /* get the GUID */
1704 key = g_strdup_printf ("HardwareID-%u", i);
1705 keys = fu_hwids_get_replace_keys (hwids, key);
1706 guid = fu_hwids_get_guid (hwids, key, &error_local);
1707 if (guid == NULL) {
1708 g_print ("%s\n", error_local->message);
1709 continue;
1710 }
1711
1712 /* show what makes up the GUID */
1713 keysv = g_strsplit (keys, "&", -1);
1714 keys_str = g_strjoinv (" + ", keysv);
1715 g_print ("{%s} <- %s\n", guid, keys_str);
1716 }
1717
1718 return TRUE;
1719}
1720
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001721static gboolean
1722fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1723{
1724 const gchar *script_fn = "startup.sh";
1725 const gchar *output_fn = "firmware.bin";
1726 g_autoptr(GBytes) archive_blob = NULL;
1727 g_autoptr(GBytes) firmware_blob = NULL;
1728 if (g_strv_length (values) < 2) {
1729 g_set_error_literal (error,
1730 FWUPD_ERROR,
1731 FWUPD_ERROR_INVALID_ARGS,
1732 "Invalid arguments");
1733 return FALSE;
1734 }
1735 archive_blob = fu_common_get_contents_bytes (values[0], error);
1736 if (archive_blob == NULL)
1737 return FALSE;
1738 if (g_strv_length (values) > 2)
1739 script_fn = values[2];
1740 if (g_strv_length (values) > 3)
1741 output_fn = values[3];
1742 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1743 if (firmware_blob == NULL)
1744 return FALSE;
1745 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1746}
1747
Richard Hughes3d607622019-03-07 16:59:27 +00001748static gboolean
1749fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1750{
1751 g_autofree gchar *sig = NULL;
1752
1753 /* check args */
1754 if (g_strv_length (values) != 1) {
1755 g_set_error_literal (error,
1756 FWUPD_ERROR,
1757 FWUPD_ERROR_INVALID_ARGS,
1758 "Invalid arguments: value expected");
1759 return FALSE;
1760 }
1761
1762 /* start engine */
1763 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1764 return FALSE;
1765 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001766 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1767 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001768 if (sig == NULL)
1769 return FALSE;
1770 g_print ("%s\n", sig);
1771 return TRUE;
1772}
1773
Mario Limonciello62f84862018-10-18 13:15:23 -05001774static void
1775fu_util_device_added_cb (FwupdClient *client,
1776 FwupdDevice *device,
1777 gpointer user_data)
1778{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001779 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001780 /* TRANSLATORS: this is when a device is hotplugged */
1781 g_print ("%s\n%s", _("Device added:"), tmp);
1782}
1783
1784static void
1785fu_util_device_removed_cb (FwupdClient *client,
1786 FwupdDevice *device,
1787 gpointer user_data)
1788{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001789 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001790 /* TRANSLATORS: this is when a device is hotplugged */
1791 g_print ("%s\n%s", _("Device removed:"), tmp);
1792}
1793
1794static void
1795fu_util_device_changed_cb (FwupdClient *client,
1796 FwupdDevice *device,
1797 gpointer user_data)
1798{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001799 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001800 /* TRANSLATORS: this is when a device has been updated */
1801 g_print ("%s\n%s", _("Device changed:"), tmp);
1802}
1803
1804static void
1805fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1806{
1807 /* TRANSLATORS: this is when the daemon state changes */
1808 g_print ("%s\n", _("Changed"));
1809}
1810
1811static gboolean
1812fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1813{
1814 g_autoptr(FwupdClient) client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00001815 fwupd_client_set_main_context (client, priv->main_ctx);
Mario Limonciello62f84862018-10-18 13:15:23 -05001816
1817 /* get all the devices */
1818 if (!fwupd_client_connect (client, priv->cancellable, error))
1819 return FALSE;
1820
1821 /* watch for any hotplugged device */
1822 g_signal_connect (client, "changed",
1823 G_CALLBACK (fu_util_changed_cb), priv);
1824 g_signal_connect (client, "device-added",
1825 G_CALLBACK (fu_util_device_added_cb), priv);
1826 g_signal_connect (client, "device-removed",
1827 G_CALLBACK (fu_util_device_removed_cb), priv);
1828 g_signal_connect (client, "device-changed",
1829 G_CALLBACK (fu_util_device_changed_cb), priv);
1830 g_signal_connect (priv->cancellable, "cancelled",
1831 G_CALLBACK (fu_util_cancelled_cb), priv);
1832 g_main_loop_run (priv->loop);
1833 return TRUE;
1834}
1835
Richard Hughes15684492019-03-15 16:27:50 +00001836static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001837fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1838{
1839 g_autoptr(GPtrArray) firmware_types = NULL;
1840
1841 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001842 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001843 return FALSE;
1844
1845 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1846 for (guint i = 0; i < firmware_types->len; i++) {
1847 const gchar *id = g_ptr_array_index (firmware_types, i);
1848 g_print ("%s\n", id);
1849 }
1850 if (firmware_types->len == 0) {
1851 /* TRANSLATORS: nothing found */
1852 g_print ("%s\n", _("No firmware IDs found"));
1853 return TRUE;
1854 }
1855
1856 return TRUE;
1857}
1858
1859static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001860fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001861{
1862 g_autoptr(GPtrArray) firmware_types = NULL;
1863 guint idx;
1864 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1865
1866 /* TRANSLATORS: get interactive prompt */
1867 g_print ("%s\n", _("Choose a firmware type:"));
1868 /* TRANSLATORS: this is to abort the interactive prompt */
1869 g_print ("0.\t%s\n", _("Cancel"));
1870 for (guint i = 0; i < firmware_types->len; i++) {
1871 const gchar *id = g_ptr_array_index (firmware_types, i);
1872 g_print ("%u.\t%s\n", i + 1, id);
1873 }
1874 idx = fu_util_prompt_for_number (firmware_types->len);
1875 if (idx == 0) {
1876 g_set_error_literal (error,
1877 FWUPD_ERROR,
1878 FWUPD_ERROR_NOTHING_TO_DO,
1879 "Request canceled");
1880 return NULL;
1881 }
1882
1883 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1884}
1885
1886static gboolean
1887fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1888{
1889 GType gtype;
1890 g_autoptr(GBytes) blob = NULL;
1891 g_autoptr(FuFirmware) firmware = NULL;
1892 g_autofree gchar *firmware_type = NULL;
1893 g_autofree gchar *str = NULL;
1894
1895 /* check args */
1896 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1897 g_set_error_literal (error,
1898 FWUPD_ERROR,
1899 FWUPD_ERROR_INVALID_ARGS,
1900 "Invalid arguments: filename required");
1901 return FALSE;
1902 }
1903
1904 if (g_strv_length (values) == 2)
1905 firmware_type = g_strdup (values[1]);
1906
1907 /* load file */
1908 blob = fu_common_get_contents_bytes (values[0], error);
1909 if (blob == NULL)
1910 return FALSE;
1911
1912 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001913 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001914 return FALSE;
1915
1916 /* find the GType to use */
1917 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001918 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001919 if (firmware_type == NULL)
1920 return FALSE;
1921 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1922 if (gtype == G_TYPE_INVALID) {
1923 g_set_error (error,
1924 G_IO_ERROR,
1925 G_IO_ERROR_NOT_FOUND,
1926 "GType %s not supported", firmware_type);
1927 return FALSE;
1928 }
1929 firmware = g_object_new (gtype, NULL);
1930 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1931 return FALSE;
1932 str = fu_firmware_to_string (firmware);
1933 g_print ("%s", str);
1934 return TRUE;
1935}
1936
Richard Hughesdd653442020-09-22 10:23:52 +01001937static gboolean
1938fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1939{
1940 GType gtype;
1941 g_autofree gchar *firmware_type = NULL;
1942 g_autofree gchar *str = NULL;
1943 g_autoptr(FuFirmware) firmware = NULL;
1944 g_autoptr(GBytes) blob = NULL;
1945 g_autoptr(GPtrArray) images = NULL;
1946
1947 /* check args */
1948 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1949 g_set_error_literal (error,
1950 FWUPD_ERROR,
1951 FWUPD_ERROR_INVALID_ARGS,
1952 "Invalid arguments: filename required");
1953 return FALSE;
1954 }
1955 if (g_strv_length (values) == 2)
1956 firmware_type = g_strdup (values[1]);
1957
1958 /* load file */
1959 blob = fu_common_get_contents_bytes (values[0], error);
1960 if (blob == NULL)
1961 return FALSE;
1962
1963 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001964 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01001965 return FALSE;
1966
1967 /* find the GType to use */
1968 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001969 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001970 if (firmware_type == NULL)
1971 return FALSE;
1972 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1973 if (gtype == G_TYPE_INVALID) {
1974 g_set_error (error,
1975 G_IO_ERROR,
1976 G_IO_ERROR_NOT_FOUND,
1977 "GType %s not supported", firmware_type);
1978 return FALSE;
1979 }
1980 firmware = g_object_new (gtype, NULL);
1981 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1982 return FALSE;
1983 str = fu_firmware_to_string (firmware);
1984 g_print ("%s", str);
1985 images = fu_firmware_get_images (firmware);
1986 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00001987 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesdd653442020-09-22 10:23:52 +01001988 g_autofree gchar *fn = NULL;
1989 g_autoptr(GBytes) blob_img = NULL;
1990
Richard Hughes88dd7c42020-09-22 16:54:40 +01001991 /* get raw image without generated header, footer or crc */
Richard Hughes1981c632021-03-09 09:44:12 +00001992 blob_img = fu_firmware_get_bytes (img, error);
1993 if (blob_img == NULL)
1994 return FALSE;
1995 if (g_bytes_get_size (blob_img) == 0)
Richard Hughes88dd7c42020-09-22 16:54:40 +01001996 continue;
1997
Richard Hughesdd653442020-09-22 10:23:52 +01001998 /* use suitable filename */
Richard Hughes1981c632021-03-09 09:44:12 +00001999 if (fu_firmware_get_filename (img) != NULL) {
2000 fn = g_strdup (fu_firmware_get_filename (img));
2001 } else if (fu_firmware_get_id (img) != NULL) {
2002 fn = g_strdup_printf ("id-%s.fw", fu_firmware_get_id (img));
2003 } else if (fu_firmware_get_idx (img) != 0x0) {
2004 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_get_idx (img));
Richard Hughesdd653442020-09-22 10:23:52 +01002005 } else {
2006 fn = g_strdup_printf ("img-0x%x.fw", i);
2007 }
2008 /* TRANSLATORS: decompressing images from a container firmware */
2009 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01002010 if (!fu_common_set_contents_bytes (fn, blob_img, error))
2011 return FALSE;
2012 }
2013
2014 /* success */
2015 return TRUE;
2016}
2017
Richard Hughes0924c932020-09-22 19:07:05 +01002018static gboolean
2019fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01002020{
Richard Hughes0924c932020-09-22 19:07:05 +01002021 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01002022 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01002023 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002024 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01002025 g_autoptr(FuFirmware) firmware_dst = NULL;
2026 g_autoptr(GBytes) blob_dst = NULL;
2027 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002028 g_autoptr(XbBuilder) builder = xb_builder_new ();
2029 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2030 g_autoptr(XbNode) n = NULL;
2031 g_autoptr(XbSilo) silo = NULL;
2032
Richard Hughes0924c932020-09-22 19:07:05 +01002033 /* check args */
2034 if (g_strv_length (values) != 2) {
2035 g_set_error_literal (error,
2036 FWUPD_ERROR,
2037 FWUPD_ERROR_INVALID_ARGS,
2038 "Invalid arguments: filename required");
2039 return FALSE;
2040 }
2041
2042 /* load file */
2043 blob_src = fu_common_get_contents_bytes (values[0], error);
2044 if (blob_src == NULL)
2045 return FALSE;
2046
2047 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002048 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002049 return FALSE;
2050
Richard Hughes41400a82020-09-21 13:43:15 +01002051 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002052 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002053 XB_BUILDER_SOURCE_FLAG_NONE,
2054 error)) {
2055 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002056 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002057 }
2058 xb_builder_import_source (builder, source);
2059 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2060 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002061 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002062
2063 /* create FuFirmware of specific GType */
2064 n = xb_silo_query_first (silo, "firmware", error);
2065 if (n == NULL)
2066 return FALSE;
2067 tmp = xb_node_get_attr (n, "gtype");
2068 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002069 gtype = g_type_from_name (tmp);
2070 if (gtype == G_TYPE_INVALID) {
2071 g_set_error (error,
2072 G_IO_ERROR,
2073 G_IO_ERROR_NOT_FOUND,
2074 "GType %s not registered", tmp);
2075 return FALSE;
2076 }
2077 }
2078 tmp = xb_node_get_attr (n, "id");
2079 if (tmp != NULL) {
2080 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002081 if (gtype == G_TYPE_INVALID) {
2082 g_set_error (error,
2083 G_IO_ERROR,
2084 G_IO_ERROR_NOT_FOUND,
2085 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002086 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002087 }
Richard Hughes41400a82020-09-21 13:43:15 +01002088 }
Richard Hughes0924c932020-09-22 19:07:05 +01002089 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002090 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002091 return FALSE;
2092
2093 /* write new file */
2094 blob_dst = fu_firmware_write (firmware, error);
2095 if (blob_dst == NULL)
2096 return FALSE;
2097 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2098 return FALSE;
2099
2100 /* show what we wrote */
2101 firmware_dst = g_object_new (gtype, NULL);
2102 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2103 return FALSE;
2104 str = fu_firmware_to_string (firmware_dst);
2105 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002106
2107 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002108 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002109}
2110
Richard Hughes95c98a92019-10-22 16:03:15 +01002111static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002112fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2113{
2114 GType gtype_dst;
2115 GType gtype_src;
2116 g_autofree gchar *firmware_type_dst = NULL;
2117 g_autofree gchar *firmware_type_src = NULL;
2118 g_autofree gchar *str_dst = NULL;
2119 g_autofree gchar *str_src = NULL;
2120 g_autoptr(FuFirmware) firmware_dst = NULL;
2121 g_autoptr(FuFirmware) firmware_src = NULL;
2122 g_autoptr(GBytes) blob_dst = NULL;
2123 g_autoptr(GBytes) blob_src = NULL;
2124 g_autoptr(GPtrArray) images = NULL;
2125
2126 /* check args */
2127 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2128 g_set_error_literal (error,
2129 FWUPD_ERROR,
2130 FWUPD_ERROR_INVALID_ARGS,
2131 "Invalid arguments: filename required");
2132 return FALSE;
2133 }
2134
2135 if (g_strv_length (values) > 2)
2136 firmware_type_src = g_strdup (values[2]);
2137 if (g_strv_length (values) > 3)
2138 firmware_type_dst = g_strdup (values[3]);
2139
2140 /* load file */
2141 blob_src = fu_common_get_contents_bytes (values[0], error);
2142 if (blob_src == NULL)
2143 return FALSE;
2144
2145 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002146 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002147 return FALSE;
2148
2149 /* find the GType to use */
2150 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002151 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002152 if (firmware_type_src == NULL)
2153 return FALSE;
2154 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002155 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002156 if (firmware_type_dst == NULL)
2157 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002158 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2159 if (gtype_src == G_TYPE_INVALID) {
2160 g_set_error (error,
2161 G_IO_ERROR,
2162 G_IO_ERROR_NOT_FOUND,
2163 "GType %s not supported", firmware_type_src);
2164 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002165 }
Richard Hughes0924c932020-09-22 19:07:05 +01002166 firmware_src = g_object_new (gtype_src, NULL);
2167 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2168 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002169 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2170 if (gtype_dst == G_TYPE_INVALID) {
2171 g_set_error (error,
2172 G_IO_ERROR,
2173 G_IO_ERROR_NOT_FOUND,
2174 "GType %s not supported", firmware_type_dst);
2175 return FALSE;
2176 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002177 str_src = fu_firmware_to_string (firmware_src);
2178 g_print ("%s", str_src);
2179
2180 /* copy images */
2181 firmware_dst = g_object_new (gtype_dst, NULL);
2182 images = fu_firmware_get_images (firmware_src);
2183 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00002184 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002185 fu_firmware_add_image (firmware_dst, img);
2186 }
2187
2188 /* write new file */
2189 blob_dst = fu_firmware_write (firmware_dst, error);
2190 if (blob_dst == NULL)
2191 return FALSE;
2192 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2193 return FALSE;
2194 str_dst = fu_firmware_to_string (firmware_dst);
2195 g_print ("%s", str_dst);
2196
2197 /* success */
2198 return TRUE;
2199}
2200
2201static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002202fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2203{
2204 g_autofree gchar *str = NULL;
2205 g_autoptr(FuDevice) dev = NULL;
2206
2207 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002208 if (!fu_util_start_engine (priv,
2209 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2210 FU_ENGINE_LOAD_FLAG_HWINFO |
2211 FU_ENGINE_LOAD_FLAG_REMOTES,
2212 error))
Richard Hughes15684492019-03-15 16:27:50 +00002213 return FALSE;
2214
2215 /* get device */
2216 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002217 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002218 if (dev == NULL)
2219 return FALSE;
2220 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002221 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002222 if (dev == NULL)
2223 return FALSE;
2224 }
2225
2226 /* add checksums */
2227 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2228 return FALSE;
2229
2230 /* show checksums */
2231 str = fu_device_to_string (dev);
2232 g_print ("%s\n", str);
2233 return TRUE;
2234}
2235
Mario Limonciellofe593942019-04-03 13:48:52 -05002236static gboolean
2237fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2238{
2239 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002240 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002241 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002242
2243 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002244 if (!fu_util_start_engine (priv,
2245 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2246 FU_ENGINE_LOAD_FLAG_HWINFO |
2247 FU_ENGINE_LOAD_FLAG_REMOTES,
2248 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002249 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002250 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002251
2252 /* get all devices from the history database */
2253 devices = fu_engine_get_history (priv->engine, error);
2254 if (devices == NULL)
2255 return FALSE;
2256
2257 /* show each device */
2258 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002259 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002260 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002261 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002262 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002263 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002264
Richard Hughes747f5702019-08-06 14:27:26 +01002265 if (!fu_util_filter_device (priv, dev))
2266 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002267 child = g_node_append_data (root, dev);
2268
2269 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002270 if (rel == NULL)
2271 continue;
2272 remote = fwupd_release_get_remote_id (rel);
2273
2274 /* doesn't actually map to remote */
2275 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002276 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002277 continue;
2278 }
2279
2280 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002281 rels = fu_engine_get_releases (priv->engine,
2282 priv->request,
2283 fwupd_device_get_id (dev),
2284 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002285 if (rels == NULL)
2286 return FALSE;
2287
2288 /* map to a release in client */
2289 for (guint j = 0; j < rels->len; j++) {
2290 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2291 if (g_strcmp0 (remote,
2292 fwupd_release_get_remote_id (rel2)) != 0)
2293 continue;
2294 if (g_strcmp0 (fwupd_release_get_version (rel),
2295 fwupd_release_get_version (rel2)) != 0)
2296 continue;
2297 g_node_append_data (child, g_object_ref (rel2));
2298 rel = NULL;
2299 break;
2300 }
2301
2302 /* didn't match anything */
2303 if (rels->len == 0 || rel != NULL) {
2304 g_node_append_data (child, rel);
2305 continue;
2306 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002307
Mario Limonciellofe593942019-04-03 13:48:52 -05002308 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002309 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002310
2311 return TRUE;
2312}
2313
Richard Hughesfd7e9942020-01-17 14:09:13 +00002314static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002315fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2316{
Richard Hughesc5710d92020-06-26 11:08:25 +01002317 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002318 g_autofree gchar *fn_raw = NULL;
2319 g_autofree gchar *fn_sig = NULL;
2320 g_autoptr(GBytes) bytes_raw = NULL;
2321 g_autoptr(GBytes) bytes_sig = NULL;
2322
Richard Hughes4959baa2020-01-17 14:33:00 +00002323 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002324 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2325 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002326 g_set_error (error,
2327 FWUPD_ERROR,
2328 FWUPD_ERROR_NOTHING_TO_DO,
2329 "no metadata signature URI available for %s",
2330 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002331 return FALSE;
2332 }
2333 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002334 if (!fu_common_mkdir_parent (fn_sig, error))
2335 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002336 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002337 return FALSE;
2338 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2339 if (bytes_sig == NULL)
2340 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002341 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2342 return FALSE;
2343
2344 /* payload */
2345 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2346 if (metadata_uri == NULL) {
2347 g_set_error (error,
2348 FWUPD_ERROR,
2349 FWUPD_ERROR_NOTHING_TO_DO,
2350 "no metadata URI available for %s",
2351 fwupd_remote_get_id (remote));
2352 return FALSE;
2353 }
2354 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2355 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2356 return FALSE;
2357 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2358 if (bytes_raw == NULL)
2359 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002360
2361 /* send to daemon */
2362 g_debug ("updating %s", fwupd_remote_get_id (remote));
2363 return fu_engine_update_metadata_bytes (priv->engine,
2364 fwupd_remote_get_id (remote),
2365 bytes_raw,
2366 bytes_sig,
2367 error);
2368
2369}
2370
2371static gboolean
2372fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2373{
2374 g_autoptr(GPtrArray) remotes = NULL;
2375
2376 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002377 if (!fu_util_start_engine (priv,
2378 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2379 FU_ENGINE_LOAD_FLAG_HWINFO |
2380 FU_ENGINE_LOAD_FLAG_REMOTES,
2381 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002382 return FALSE;
2383
2384 /* download new metadata */
2385 remotes = fu_engine_get_remotes (priv->engine, error);
2386 if (remotes == NULL)
2387 return FALSE;
2388 for (guint i = 0; i < remotes->len; i++) {
2389 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2390 if (!fwupd_remote_get_enabled (remote))
2391 continue;
2392 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2393 continue;
2394 if (!fu_util_refresh_remote (priv, remote, error))
2395 return FALSE;
2396 }
2397 return TRUE;
2398}
2399
2400static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002401fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2402{
2403 g_autoptr(GNode) root = g_node_new (NULL);
2404 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002405 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002406
2407 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002408 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002409 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002410 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002411
2412 /* list remotes */
2413 remotes = fu_engine_get_remotes (priv->engine, error);
2414 if (remotes == NULL)
2415 return FALSE;
2416 if (remotes->len == 0) {
2417 g_set_error_literal (error,
2418 FWUPD_ERROR,
2419 FWUPD_ERROR_NOTHING_TO_DO,
2420 "no remotes available");
2421 return FALSE;
2422 }
2423 for (guint i = 0; i < remotes->len; i++) {
2424 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2425 g_node_append_data (root, remote_tmp);
2426 }
2427 fu_util_print_tree (root, title);
2428
2429 return TRUE;
2430}
2431
Richard Hughes196c6c62020-05-11 19:42:47 +01002432static gboolean
2433fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2434{
Richard Hughes5c82b942020-09-14 12:24:06 +01002435 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002436 g_autoptr(FuSecurityAttrs) attrs = NULL;
2437 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002438 g_autofree gchar *str = NULL;
2439
2440 /* not ready yet */
2441 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2442 g_set_error_literal (error,
2443 FWUPD_ERROR,
2444 FWUPD_ERROR_NOT_SUPPORTED,
2445 "The HSI specification is not yet complete. "
2446 "To ignore this warning, use --force");
2447 return FALSE;
2448 }
2449
Richard Hughesc7d870a2020-12-10 10:05:35 +00002450 if (!fu_util_start_engine (priv,
2451 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2452 FU_ENGINE_LOAD_FLAG_HWINFO |
2453 FU_ENGINE_LOAD_FLAG_REMOTES,
2454 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002455 return FALSE;
2456
2457 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2458 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2459 fu_engine_get_host_security_id (priv->engine));
2460
Richard Hughes5c82b942020-09-14 12:24:06 +01002461 /* show or hide different elements */
2462 if (priv->show_all) {
2463 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2464 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2465 }
2466
Richard Hughes196c6c62020-05-11 19:42:47 +01002467 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002468 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002469 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002470 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002471 g_print ("%s\n", str);
2472 return TRUE;
2473}
2474
Richard Hughesa83deb42020-08-12 12:45:36 +01002475static FuVolume *
2476fu_util_prompt_for_volume (GError **error)
2477{
2478 FuVolume *volume;
2479 guint idx;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002480 gboolean is_fallback = FALSE;
Richard Hughesa83deb42020-08-12 12:45:36 +01002481 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002482 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2483 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002484
2485 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002486 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2487 if (volumes == NULL) {
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002488 is_fallback = TRUE;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002489 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2490 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2491 if (volumes == NULL) {
2492 g_prefix_error (error, "%s: ", error_local->message);
2493 return NULL;
2494 }
2495 }
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002496 /* on fallback: only add internal vfat partitions */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002497 for (guint i = 0; i < volumes->len; i++) {
2498 FuVolume *vol = g_ptr_array_index (volumes, i);
2499 g_autofree gchar *type = fu_volume_get_id_type (vol);
2500 if (type == NULL)
2501 continue;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002502 if (is_fallback && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002503 continue;
2504 if (g_strcmp0 (type, "vfat") == 0)
2505 g_ptr_array_add (volumes_vfat, vol);
2506 }
2507 if (volumes_vfat->len == 1) {
2508 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002509 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002510 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2511 return g_object_ref (volume);
2512 }
2513
2514 /* TRANSLATORS: get interactive prompt */
2515 g_print ("%s\n", _("Choose a volume:"));
2516 /* TRANSLATORS: this is to abort the interactive prompt */
2517 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002518 for (guint i = 0; i < volumes_vfat->len; i++) {
2519 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002520 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2521 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002522 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002523 if (idx == 0) {
2524 g_set_error_literal (error,
2525 FWUPD_ERROR,
2526 FWUPD_ERROR_NOTHING_TO_DO,
2527 "Request canceled");
2528 return NULL;
2529 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002530 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002531 return g_object_ref (volume);
2532
2533}
2534
2535static gboolean
2536fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2537{
2538 g_autoptr(FuVolume) volume = NULL;
2539 volume = fu_util_prompt_for_volume (error);
2540 if (volume == NULL)
2541 return FALSE;
2542 return fu_volume_mount (volume, error);
2543}
2544
2545static gboolean
2546fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2547{
2548 g_autoptr(FuVolume) volume = NULL;
2549 volume = fu_util_prompt_for_volume (error);
2550 if (volume == NULL)
2551 return FALSE;
2552 return fu_volume_unmount (volume, error);
2553}
2554
2555static gboolean
2556fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2557{
2558 g_autofree gchar *mount_point = NULL;
2559 g_autoptr(FuDeviceLocker) locker = NULL;
2560 g_autoptr(FuVolume) volume = NULL;
2561 g_autoptr(GPtrArray) files = NULL;
2562
2563 volume = fu_util_prompt_for_volume (error);
2564 if (volume == NULL)
2565 return FALSE;
2566 locker = fu_volume_locker (volume, error);
2567 if (locker == NULL)
2568 return FALSE;
2569 mount_point = fu_volume_get_mount_point (volume);
2570 files = fu_common_get_files_recursive (mount_point, error);
2571 if (files == NULL)
2572 return FALSE;
2573 for (guint i = 0; i < files->len; i++) {
2574 const gchar *fn = g_ptr_array_index (files, i);
2575 g_print ("%s\n", fn);
2576 }
2577 return TRUE;
2578}
2579
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002580static gboolean
2581_g_str_equal0 (gconstpointer str1, gconstpointer str2)
2582{
2583 return g_strcmp0 (str1, str2) == 0;
2584}
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002585
2586static gboolean
2587fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2588{
2589 const gchar *branch;
2590 g_autoptr(FwupdRelease) rel = NULL;
2591 g_autoptr(GPtrArray) rels = NULL;
2592 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2593 g_autoptr(FuDevice) dev = NULL;
2594
2595 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002596 if (!fu_util_start_engine (priv,
2597 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2598 FU_ENGINE_LOAD_FLAG_HWINFO |
2599 FU_ENGINE_LOAD_FLAG_REMOTES,
2600 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002601 return FALSE;
2602
2603 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002604 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00002605 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002606 if (g_strv_length (values) == 1)
2607 dev = fu_util_get_device (priv, values[1], error);
2608 else
2609 dev = fu_util_prompt_for_device (priv, NULL, error);
2610 if (dev == NULL)
2611 return FALSE;
2612 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2613 g_set_error_literal (error,
2614 FWUPD_ERROR,
2615 FWUPD_ERROR_NOT_SUPPORTED,
2616 "Multiple branches not available");
2617 return FALSE;
2618 }
2619
2620 /* get all releases, including the alternate branch versions */
2621 rels = fu_engine_get_releases (priv->engine,
2622 priv->request,
2623 fu_device_get_id (dev),
2624 error);
2625 if (rels == NULL)
2626 return FALSE;
2627
2628 /* get all the unique branches */
2629 for (guint i = 0; i < rels->len; i++) {
2630 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002631 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002632#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002633 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002634 _g_str_equal0, NULL))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002635 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002636#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002637 g_ptr_array_add (branches, g_strdup (branch_tmp));
2638 }
2639
2640 /* branch name is optional */
2641 if (g_strv_length (values) > 1) {
2642 branch = values[1];
2643 } else if (branches->len == 1) {
2644 branch = g_ptr_array_index (branches, 0);
2645 } else {
2646 guint idx;
2647
2648 /* TRANSLATORS: get interactive prompt, where branch is the
2649 * supplier of the firmware, e.g. "non-free" or "free" */
2650 g_print ("%s\n", _("Choose a branch:"));
2651 /* TRANSLATORS: this is to abort the interactive prompt */
2652 g_print ("0.\t%s\n", _("Cancel"));
2653 for (guint i = 0; i < branches->len; i++) {
2654 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002655 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002656 }
2657 idx = fu_util_prompt_for_number (branches->len);
2658 if (idx == 0) {
2659 g_set_error_literal (error,
2660 FWUPD_ERROR,
2661 FWUPD_ERROR_NOTHING_TO_DO,
2662 "Request canceled");
2663 return FALSE;
2664 }
2665 branch = g_ptr_array_index (branches, idx - 1);
2666 }
2667
2668 /* sanity check */
2669 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2670 g_set_error (error,
2671 FWUPD_ERROR,
2672 FWUPD_ERROR_NOT_SUPPORTED,
2673 "Device %s is already on branch %s",
2674 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002675 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002676 return FALSE;
2677 }
2678
2679 /* the releases are ordered by version */
2680 for (guint j = 0; j < rels->len; j++) {
2681 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2682 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2683 rel = g_object_ref (rel_tmp);
2684 break;
2685 }
2686 }
2687 if (rel == NULL) {
2688 g_set_error (error,
2689 FWUPD_ERROR,
2690 FWUPD_ERROR_NOT_SUPPORTED,
2691 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002692 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002693 return FALSE;
2694 }
2695
2696 /* we're switching branch */
2697 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2698 return FALSE;
2699
2700 /* update the console if composite devices are also updated */
2701 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2702 g_signal_connect (priv->engine, "device-changed",
2703 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2704 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2705 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2706 if (!fu_util_install_release (priv, rel, error))
2707 return FALSE;
2708 fu_util_display_current_message (priv);
2709
2710 /* we don't want to ask anything */
2711 if (priv->no_reboot_check) {
2712 g_debug ("skipping reboot check");
2713 return TRUE;
2714 }
2715
2716 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2717}
2718
Richard Hughesb5976832018-05-18 10:02:09 +01002719int
2720main (int argc, char *argv[])
2721{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002722 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002723 gboolean allow_older = FALSE;
2724 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002725 gboolean force = FALSE;
2726 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002727 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002728 gboolean ignore_checksum = FALSE;
2729 gboolean ignore_power = FALSE;
2730 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002731 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002732 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002733 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2734 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002735 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002736 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002737 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002738 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002739 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2740 /* TRANSLATORS: command line option */
2741 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002742 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2743 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002744 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002745 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2746 /* TRANSLATORS: command line option */
2747 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002748 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2749 /* TRANSLATORS: command line option */
2750 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002751 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2752 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002753 _("Force the action by relaxing some runtime checks"), NULL },
2754 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2755 /* TRANSLATORS: command line option */
2756 _("Ignore firmware checksum failures"), NULL },
2757 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2758 /* TRANSLATORS: command line option */
2759 _("Ignore firmware hardware mismatch failures"), NULL },
2760 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2761 /* TRANSLATORS: command line option */
2762 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002763 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2764 /* TRANSLATORS: command line option */
2765 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002766 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2767 /* TRANSLATORS: command line option */
2768 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002769 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2770 /* TRANSLATORS: command line option */
2771 _("Show all results"), NULL },
2772 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002773 /* TRANSLATORS: command line option */
2774 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002775 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002776 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002777 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002778 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2779 /* TRANSLATORS: command line option */
2780 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002781 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002782 /* TRANSLATORS: command line option */
2783 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002784 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002785 /* TRANSLATORS: command line option */
2786 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002787 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2788 /* TRANSLATORS: command line option */
2789 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002790 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2791 /* TRANSLATORS: command line option */
2792 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002793 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2794 /* TRANSLATORS: command line option */
2795 _("Filter with a set of device flags using a ~ prefix to "
2796 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002797 { NULL}
2798 };
2799
Richard Hughes429f72b2020-01-16 12:18:19 +00002800#ifdef _WIN32
2801 /* workaround Windows setting the codepage to 1252 */
2802 g_setenv ("LANG", "C.UTF-8", FALSE);
2803#endif
2804
Richard Hughesb5976832018-05-18 10:02:09 +01002805 setlocale (LC_ALL, "");
2806
Richard Hughes668ee212019-11-22 09:17:46 +00002807 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002808 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2809 textdomain (GETTEXT_PACKAGE);
2810
Richard Hughes01c0bad2019-11-22 09:08:51 +00002811#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002812 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002813 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002814 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002815 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002816#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002817
2818 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002819 priv->main_ctx = g_main_context_new ();
2820 priv->loop = g_main_loop_new (priv->main_ctx, FALSE);
Richard Hughesb5976832018-05-18 10:02:09 +01002821 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002822 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002823
2824 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002825 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002826 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002827 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2828 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002829 /* TRANSLATORS: command description */
2830 _("Build firmware using a sandbox"),
2831 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002832 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002833 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002834 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2835 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002836 /* TRANSLATORS: command description */
2837 _("Dump SMBIOS data from a file"),
2838 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002839 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002840 "get-plugins",
2841 NULL,
2842 /* TRANSLATORS: command description */
2843 _("Get all enabled plugins registered with the system"),
2844 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002845 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002846 "get-details",
2847 NULL,
2848 /* TRANSLATORS: command description */
2849 _("Gets details about a firmware file"),
2850 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002851 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002852 "get-history",
2853 NULL,
2854 /* TRANSLATORS: command description */
2855 _("Show history of firmware updates"),
2856 fu_util_get_history);
2857 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002858 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002859 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2860 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002861 /* TRANSLATORS: command description */
2862 _("Gets the list of updates for connected hardware"),
2863 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002864 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002865 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002866 NULL,
2867 /* TRANSLATORS: command description */
2868 _("Get all devices that support firmware updates"),
2869 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002870 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002871 "get-device-flags",
2872 NULL,
2873 /* TRANSLATORS: command description */
2874 _("Get all device flags supported by fwupd"),
2875 fu_util_get_device_flags);
2876 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002877 "watch",
2878 NULL,
2879 /* TRANSLATORS: command description */
Piotr Drąg472fa592018-06-06 14:53:48 +02002880 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002881 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002882 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002883 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002884 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2885 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002886 /* TRANSLATORS: command description */
2887 _("Install a firmware blob on a device"),
2888 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002889 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002890 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002891 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2892 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002893 /* TRANSLATORS: command description */
2894 _("Install a firmware file on this hardware"),
2895 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002896 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002897 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002898 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2899 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002900 /* TRANSLATORS: command description */
2901 _("Reinstall firmware on a device"),
2902 fu_util_reinstall);
2903 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002904 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002905 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2906 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002907 /* TRANSLATORS: command description */
2908 _("Attach to firmware mode"),
2909 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002910 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002911 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002912 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2913 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002914 /* TRANSLATORS: command description */
2915 _("Detach to bootloader mode"),
2916 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002917 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002918 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002919 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2920 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002921 /* TRANSLATORS: command description */
2922 _("Unbind current driver"),
2923 fu_util_unbind_driver);
2924 fu_util_cmd_array_add (cmd_array,
2925 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002926 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2927 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002928 /* TRANSLATORS: command description */
2929 _("Bind new kernel driver"),
2930 fu_util_bind_driver);
2931 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002932 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002933 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2934 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002935 /* TRANSLATORS: command description */
2936 _("Activate pending devices"),
2937 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002938 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002939 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002940 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
Richard Hughesf6751cd2021-01-15 16:48:25 +00002941 _("[SMBIOS-FILE|HWIDS-FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002942 /* TRANSLATORS: command description */
2943 _("Return all the hardware IDs for the machine"),
2944 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002945 fu_util_cmd_array_add (cmd_array,
Richard Hughesf6751cd2021-01-15 16:48:25 +00002946 "export-hwids",
2947 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2948 _("HWIDS-FILE"),
2949 /* TRANSLATORS: command description */
2950 _("Save a file that allows generation of hardware IDs"),
2951 fu_util_export_hwids);
2952 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002953 "monitor",
2954 NULL,
2955 /* TRANSLATORS: command description */
2956 _("Monitor the daemon for events"),
2957 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002958 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002959 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002960 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2961 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002962 /* TRANSLATORS: command description */
2963 _("Update all devices that match local metadata"),
2964 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002965 fu_util_cmd_array_add (cmd_array,
2966 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002967 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2968 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002969 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002970 C_("command-description",
2971 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002972 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002973 fu_util_cmd_array_add (cmd_array,
2974 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002975 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2976 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002977 /* TRANSLATORS: command description */
2978 _("Update the stored metadata with current contents"),
2979 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002980 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002981 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002982 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2983 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002984 /* TRANSLATORS: command description */
2985 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002986 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002987 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002988 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002989 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2990 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002991 /* TRANSLATORS: command description */
2992 _("Convert a firmware file"),
2993 fu_util_firmware_convert);
2994 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002995 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002996 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2997 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002998 /* TRANSLATORS: command description */
2999 _("Build a firmware file"),
3000 fu_util_firmware_build);
3001 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003002 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003003 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3004 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01003005 /* TRANSLATORS: command description */
3006 _("Parse and show details about a firmware file"),
3007 fu_util_firmware_parse);
3008 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01003009 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003010 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3011 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01003012 /* TRANSLATORS: command description */
3013 _("Extract a firmware blob to images"),
3014 fu_util_firmware_extract);
3015 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003016 "get-firmware-types",
3017 NULL,
3018 /* TRANSLATORS: command description */
3019 _("List the available firmware types"),
3020 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00003021 fu_util_cmd_array_add (cmd_array,
3022 "get-remotes",
3023 NULL,
3024 /* TRANSLATORS: command description */
3025 _("Gets the configured remotes"),
3026 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00003027 fu_util_cmd_array_add (cmd_array,
3028 "refresh",
3029 NULL,
3030 /* TRANSLATORS: command description */
3031 _("Refresh metadata from remote server"),
3032 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01003033 fu_util_cmd_array_add (cmd_array,
3034 "security",
3035 NULL,
3036 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003037 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003038 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01003039 fu_util_cmd_array_add (cmd_array,
3040 "esp-mount",
3041 NULL,
3042 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003043 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003044 fu_util_esp_mount);
3045 fu_util_cmd_array_add (cmd_array,
3046 "esp-unmount",
3047 NULL,
3048 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003049 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003050 fu_util_esp_unmount);
3051 fu_util_cmd_array_add (cmd_array,
3052 "esp-list",
3053 NULL,
3054 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003055 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003056 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003057 fu_util_cmd_array_add (cmd_array,
3058 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003059 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3060 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003061 /* TRANSLATORS: command description */
3062 _("Switch the firmware branch on the device"),
3063 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01003064
3065 /* do stuff on ctrl+c */
3066 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003067#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003068 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3069 SIGINT, fu_util_sigint_cb,
3070 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003071#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003072 g_signal_connect (priv->cancellable, "cancelled",
3073 G_CALLBACK (fu_util_cancelled_cb), priv);
3074
3075 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003076 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003077
Mario Limonciello3f243a92019-01-21 22:05:23 -06003078 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003079 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003080 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003081 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003082 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003083 } else {
3084 /* set our implemented feature set */
3085 fu_engine_request_set_feature_flags (priv->request,
3086 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003087 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003088 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003089 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003090
Richard Hughesb5976832018-05-18 10:02:09 +01003091 /* get a list of the commands */
3092 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003093 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003094 g_option_context_set_summary (priv->context, cmd_descriptions);
3095 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003096 /* TRANSLATORS: CLI description */
3097 _("This tool allows an administrator to use the fwupd plugins "
3098 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003099
3100 /* TRANSLATORS: program name */
3101 g_set_application_name (_("Firmware Utility"));
3102 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003103 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003104 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3105 if (!ret) {
3106 /* TRANSLATORS: the user didn't read the man page */
3107 g_print ("%s: %s\n", _("Failed to parse arguments"),
3108 error->message);
3109 return EXIT_FAILURE;
3110 }
3111
Richard Hughes0e46b222019-09-05 12:13:35 +01003112 /* allow disabling SSL strict mode for broken corporate proxies */
3113 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003114 g_autofree gchar *fmt = NULL;
3115 /* TRANSLATORS: this is a prefix on the console */
3116 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003117 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003118 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3119 "to do this automatically in the future "
3120 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003121 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3122 }
3123
Richard Hughes747f5702019-08-06 14:27:26 +01003124 /* parse filter flags */
3125 if (filter != NULL) {
3126 if (!fu_util_parse_filter_flags (filter,
3127 &priv->filter_include,
3128 &priv->filter_exclude,
3129 &error)) {
3130 /* TRANSLATORS: the user didn't read the man page */
3131 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3132 error->message);
3133 return EXIT_FAILURE;
3134 }
3135 }
3136
3137
Richard Hughes460226a2018-05-21 20:56:21 +01003138 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003139 if (allow_reinstall)
3140 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3141 if (allow_older)
3142 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003143 if (allow_branch_switch)
3144 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3145 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003146 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003147 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3148 }
3149 if (ignore_checksum)
3150 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3151 if (ignore_vid_pid)
3152 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3153 if (ignore_power)
3154 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003155
Richard Hughes98ca9932018-05-18 10:24:07 +01003156 /* load engine */
3157 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3158 g_signal_connect (priv->engine, "device-added",
3159 G_CALLBACK (fu_main_engine_device_added_cb),
3160 priv);
3161 g_signal_connect (priv->engine, "device-removed",
3162 G_CALLBACK (fu_main_engine_device_removed_cb),
3163 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003164 g_signal_connect (priv->engine, "status-changed",
3165 G_CALLBACK (fu_main_engine_status_changed_cb),
3166 priv);
3167 g_signal_connect (priv->engine, "percentage-changed",
3168 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3169 priv);
3170
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003171 /* just show versions and exit */
3172 if (version) {
3173 g_autofree gchar *version_str = fu_util_get_versions ();
3174 g_print ("%s\n", version_str);
3175 return EXIT_SUCCESS;
3176 }
3177
Richard Hughes85226fd2020-06-30 14:43:48 +01003178 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003179 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3180 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3181
Richard Hughesb5976832018-05-18 10:02:09 +01003182 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003183 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003184 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003185 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003186 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003187 /* TRANSLATORS: error message explaining command to run to how to get help */
3188 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3189 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3190 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003191 return EXIT_NOTHING_TO_DO;
3192 }
Richard Hughesb5976832018-05-18 10:02:09 +01003193 return EXIT_FAILURE;
3194 }
3195
3196 /* success */
3197 return EXIT_SUCCESS;
3198}