blob: c6a3ea10d4c2020d2d78deabf7947ec7f21f3338 [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;
141
142 /* print */
143 for (guint i = 0; i < 64; i++) {
Richard Hughes1df9b822020-10-30 15:18:45 +0000144 FwupdPluginFlags flag = (guint64) 1 << i;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100145 const gchar *tmp;
146 g_autofree gchar *fmt = NULL;
Richard Hughes1df9b822020-10-30 15:18:45 +0000147 g_autofree gchar *url= NULL;
148 g_autoptr(GString) str = g_string_new (NULL);
149 if ((flags & flag) == 0)
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100150 continue;
151 tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i);
152 if (tmp == NULL)
153 continue;
154 /* TRANSLATORS: this is a prefix on the console */
155 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes1df9b822020-10-30 15:18:45 +0000156 g_string_append_printf (str, "%s %s\n", fmt, tmp);
157
158 url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
159 fwupd_plugin_flag_to_string (flag));
160 g_string_append (str, " ");
161 /* TRANSLATORS: %s is a link to a website */
162 g_string_append_printf (str, _("See %s for more information."), url);
163 g_string_append (str, "\n");
164 g_printerr ("%s", str->str);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100165 }
166}
167
Mario Limonciello3143bad2019-02-27 07:31:00 -0600168static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000169fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600170{
Richard Hughesd92ccca2019-05-20 11:28:31 +0100171#ifdef HAVE_SYSTEMD
Richard Hughes3a0ee302020-12-14 14:22:18 +0000172 g_autoptr(GError) error_local = NULL;
Richard Hughes3d005222019-05-17 14:02:41 +0100173 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500174 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100175#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000176 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000177 return FALSE;
178 if (fu_engine_get_tainted (priv->engine)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100179 g_autofree gchar *fmt = NULL;
180
181 /* TRANSLATORS: this is a prefix on the console */
182 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
183 g_printerr ("%s This tool has loaded 3rd party code and "
184 "is no longer supported by the upstream developers!\n",
185 fmt);
Richard Hughesf425d292019-01-18 17:57:39 +0000186 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100187 fu_util_show_plugin_warnings (priv);
Mario Limonciellobd60de12020-11-11 13:09:43 -0600188 fu_util_show_unsupported_warn ();
Richard Hughesf425d292019-01-18 17:57:39 +0000189 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500190}
191
Richard Hughesb5976832018-05-18 10:02:09 +0100192static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500193fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
194{
195 g_autofree gchar *path = g_path_get_dirname (value);
196 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
197 g_prefix_error (error,
198 "Unable to access %s. You may need to copy %s to %s: ",
199 path, value, g_getenv ("HOME"));
200 }
201}
202
203static void
Richard Hughesb5976832018-05-18 10:02:09 +0100204fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
205{
206 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
207 /* TRANSLATORS: this is when a device ctrl+c's a watch */
208 g_print ("%s\n", _("Cancelled"));
209 g_main_loop_quit (priv->loop);
210}
211
212static gboolean
213fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
214{
215 g_autofree gchar *tmp = NULL;
216 g_autoptr(FuSmbios) smbios = NULL;
217 if (g_strv_length (values) < 1) {
218 g_set_error_literal (error,
219 FWUPD_ERROR,
220 FWUPD_ERROR_INVALID_ARGS,
221 "Invalid arguments");
222 return FALSE;
223 }
224 smbios = fu_smbios_new ();
225 if (!fu_smbios_setup_from_file (smbios, values[0], error))
226 return FALSE;
227 tmp = fu_smbios_to_string (smbios);
228 g_print ("%s\n", tmp);
229 return TRUE;
230}
231
Richard Hughes9e5675e2019-11-22 09:35:03 +0000232#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100233static gboolean
234fu_util_sigint_cb (gpointer user_data)
235{
236 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
237 g_debug ("Handling SIGINT");
238 g_cancellable_cancel (priv->cancellable);
239 return FALSE;
240}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000241#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100242
243static void
244fu_util_private_free (FuUtilPrivate *priv)
245{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500246 if (priv->current_device != NULL)
247 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100248 if (priv->engine != NULL)
249 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100250 if (priv->request != NULL)
251 g_object_unref (priv->request);
Richard Hughes6ed25f52021-01-10 19:27:33 +0000252 if (priv->main_ctx != NULL)
253 g_main_context_unref (priv->main_ctx);
Richard Hughesb5976832018-05-18 10:02:09 +0100254 if (priv->loop != NULL)
255 g_main_loop_unref (priv->loop);
256 if (priv->cancellable != NULL)
257 g_object_unref (priv->cancellable);
258 if (priv->progressbar != NULL)
259 g_object_unref (priv->progressbar);
260 if (priv->context != NULL)
261 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600262 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100263 g_free (priv);
264}
265
266#pragma clang diagnostic push
267#pragma clang diagnostic ignored "-Wunused-function"
268G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
269#pragma clang diagnostic pop
270
Richard Hughes98ca9932018-05-18 10:24:07 +0100271
272static void
273fu_main_engine_device_added_cb (FuEngine *engine,
274 FuDevice *device,
275 FuUtilPrivate *priv)
276{
277 g_autofree gchar *tmp = fu_device_to_string (device);
278 g_debug ("ADDED:\n%s", tmp);
279}
280
281static void
282fu_main_engine_device_removed_cb (FuEngine *engine,
283 FuDevice *device,
284 FuUtilPrivate *priv)
285{
286 g_autofree gchar *tmp = fu_device_to_string (device);
287 g_debug ("REMOVED:\n%s", tmp);
288}
289
290static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100291fu_main_engine_status_changed_cb (FuEngine *engine,
292 FwupdStatus status,
293 FuUtilPrivate *priv)
294{
295 fu_progressbar_update (priv->progressbar, status, 0);
296}
297
298static void
299fu_main_engine_percentage_changed_cb (FuEngine *engine,
300 guint percentage,
301 FuUtilPrivate *priv)
302{
303 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
304}
305
306static gboolean
307fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
308{
Richard Hughesc7d870a2020-12-10 10:05:35 +0000309 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100310 return FALSE;
311 g_main_loop_run (priv->loop);
312 return TRUE;
313}
314
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100315static gint
316fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
317{
318 return fu_plugin_name_compare (*item1, *item2);
319}
320
321static gboolean
322fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
323{
324 GPtrArray *plugins;
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100325
326 /* load engine */
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100327 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100328 return FALSE;
329
330 /* print */
331 plugins = fu_engine_get_plugins (priv->engine);
332 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
333 for (guint i = 0; i < plugins->len; i++) {
334 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100335 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
336 g_print ("%s\n", str);
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100337 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100338 if (plugins->len == 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100339 /* TRANSLATORS: nothing found */
340 g_print ("%s\n", _("No plugins found"));
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100341 }
342
343 return TRUE;
344}
345
Richard Hughes98ca9932018-05-18 10:24:07 +0100346static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100347fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
348{
349 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
350 if (!fwupd_device_has_flag (dev, priv->filter_include))
351 return FALSE;
352 }
353 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
354 if (fwupd_device_has_flag (dev, priv->filter_exclude))
355 return FALSE;
356 }
357 return TRUE;
358}
359
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500360static gchar *
361fu_util_get_tree_title (FuUtilPrivate *priv)
362{
363 return g_strdup (fu_engine_get_host_product (priv->engine));
364}
365
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600366static FuDevice *
367fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
368{
369 FuDevice *dev;
370 guint idx;
371 g_autoptr(GPtrArray) devices = NULL;
372 g_autoptr(GPtrArray) devices_filtered = NULL;
373
374 /* get devices from daemon */
375 if (devices_opt != NULL) {
376 devices = g_ptr_array_ref (devices_opt);
377 } else {
378 devices = fu_engine_get_devices (priv->engine, error);
379 if (devices == NULL)
380 return NULL;
381 }
382 fwupd_device_array_ensure_parents (devices);
383
384 /* filter results */
385 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
386 for (guint i = 0; i < devices->len; i++) {
387 dev = g_ptr_array_index (devices, i);
388 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
389 continue;
390 g_ptr_array_add (devices_filtered, g_object_ref (dev));
391 }
392
393 /* nothing */
394 if (devices_filtered->len == 0) {
395 g_set_error_literal (error,
396 FWUPD_ERROR,
397 FWUPD_ERROR_NOTHING_TO_DO,
398 "No supported devices");
399 return NULL;
400 }
401
402 /* exactly one */
403 if (devices_filtered->len == 1) {
404 dev = g_ptr_array_index (devices_filtered, 0);
405 /* TRANSLATORS: Device has been chosen by the daemon for the user */
406 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
407 return g_object_ref (dev);
408 }
409
410 /* TRANSLATORS: get interactive prompt */
411 g_print ("%s\n", _("Choose a device:"));
412 /* TRANSLATORS: this is to abort the interactive prompt */
413 g_print ("0.\t%s\n", _("Cancel"));
414 for (guint i = 0; i < devices_filtered->len; i++) {
415 dev = g_ptr_array_index (devices_filtered, i);
416 g_print ("%u.\t%s (%s)\n",
417 i + 1,
418 fu_device_get_id (dev),
419 fu_device_get_name (dev));
420 }
421 idx = fu_util_prompt_for_number (devices_filtered->len);
422 if (idx == 0) {
423 g_set_error_literal (error,
424 FWUPD_ERROR,
425 FWUPD_ERROR_NOTHING_TO_DO,
426 "Request canceled");
427 return NULL;
428 }
429 dev = g_ptr_array_index (devices_filtered, idx - 1);
430 return g_object_ref (dev);
431}
432
433static FuDevice *
434fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
435{
436 if (fwupd_guid_is_valid (id)) {
437 g_autoptr(GPtrArray) devices = NULL;
438 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
439 if (devices == NULL)
440 return NULL;
441 return fu_util_prompt_for_device (priv, devices, error);
442 }
443
444 /* did this look like a GUID? */
445 for (guint i = 0; id[i] != '\0'; i++) {
446 if (id[i] == '-') {
447 g_set_error_literal (error,
448 FWUPD_ERROR,
449 FWUPD_ERROR_INVALID_ARGS,
450 "Invalid arguments");
451 return NULL;
452 }
453 }
454 return fu_engine_get_device (priv->engine, id, error);
455}
456
Richard Hughes747f5702019-08-06 14:27:26 +0100457static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600458fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
459{
460 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500461 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600462 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500463 gboolean no_updates_header = FALSE;
464 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600465
466 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000467 if (!fu_util_start_engine (priv,
468 FU_ENGINE_LOAD_FLAG_COLDPLUG |
469 FU_ENGINE_LOAD_FLAG_HWINFO |
470 FU_ENGINE_LOAD_FLAG_REMOTES,
471 error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600472 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600473 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600474
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600475 /* parse arguments */
476 if (g_strv_length (values) == 0) {
477 devices = fu_engine_get_devices (priv->engine, error);
478 if (devices == NULL)
479 return FALSE;
480 } else if (g_strv_length (values) == 1) {
481 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600482 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600483 if (device == NULL)
484 return FALSE;
485 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
486 g_ptr_array_add (devices, device);
487 } else {
488 g_set_error_literal (error,
489 FWUPD_ERROR,
490 FWUPD_ERROR_INVALID_ARGS,
491 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600492 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600493 }
494
Richard Hughes0ef47202020-01-06 13:59:09 +0000495 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500496 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600497 for (guint i = 0; i < devices->len; i++) {
498 FwupdDevice *dev = g_ptr_array_index (devices, i);
499 g_autoptr(GPtrArray) rels = NULL;
500 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500501 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600502
Richard Hughes747f5702019-08-06 14:27:26 +0100503 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600504 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600505 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600506 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500507 if (!no_updates_header) {
508 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
509 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
510 no_updates_header = TRUE;
511 }
512 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600513 continue;
514 }
Richard Hughes747f5702019-08-06 14:27:26 +0100515 if (!fu_util_filter_device (priv, dev))
516 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600517
518 /* get the releases for this device and filter for validity */
519 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100520 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600521 fwupd_device_get_id (dev),
522 &error_local);
523 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500524 if (!latest_header) {
525 /* TRANSLATORS: message letting the user know no device upgrade available */
526 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
527 latest_header = TRUE;
528 }
529 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600530 /* discard the actual reason from user, but leave for debugging */
531 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600532 continue;
533 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500534 child = g_node_append_data (root, dev);
535
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600536 for (guint j = 0; j < rels->len; j++) {
537 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500538 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600539 }
540 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600541 /* save the device state for other applications to see */
542 if (!fu_util_save_current_state (priv, error))
543 return FALSE;
544
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500545 /* updates */
546 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
547 g_set_error_literal (error,
548 FWUPD_ERROR,
549 FWUPD_ERROR_NOTHING_TO_DO,
550 "No updates available for remaining devices");
551 return FALSE;
552 }
553
554 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600555 return TRUE;
556}
557
558static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500559fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
560{
561 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500562 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600563 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500564 gint fd;
565
566 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000567 if (!fu_util_start_engine (priv,
568 FU_ENGINE_LOAD_FLAG_COLDPLUG |
569 FU_ENGINE_LOAD_FLAG_HWINFO |
570 FU_ENGINE_LOAD_FLAG_REMOTES,
571 error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500572 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600573 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500574
575 /* check args */
576 if (g_strv_length (values) != 1) {
577 g_set_error_literal (error,
578 FWUPD_ERROR,
579 FWUPD_ERROR_INVALID_ARGS,
580 "Invalid arguments");
581 return FALSE;
582 }
583
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600584 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100585 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600586
Mario Limonciello716ab272018-05-29 12:34:37 -0500587 /* open file */
588 fd = open (values[0], O_RDONLY);
589 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500590 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500591 g_set_error (error,
592 FWUPD_ERROR,
593 FWUPD_ERROR_INVALID_FILE,
594 "failed to open %s",
595 values[0]);
596 return FALSE;
597 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100598 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500599 close (fd);
600
601 if (array == NULL)
602 return FALSE;
603 for (guint i = 0; i < array->len; i++) {
604 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600605 FwupdRelease *rel;
606 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100607 if (!fu_util_filter_device (priv, dev))
608 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600609 child = g_node_append_data (root, dev);
610 rel = fwupd_device_get_release_default (dev);
611 if (rel != NULL)
612 g_node_append_data (child, rel);
613
Mario Limonciello716ab272018-05-29 12:34:37 -0500614 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500615 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500616
Mario Limonciello716ab272018-05-29 12:34:37 -0500617 return TRUE;
618}
619
620static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100621fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
622{
623 g_autoptr(GString) str = g_string_new (NULL);
624
625 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
626 const gchar *tmp = fwupd_device_flag_to_string (i);
627 if (tmp == NULL)
628 break;
629 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
630 g_string_append (str, " ");
631 g_string_append (str, tmp);
632 g_string_append (str, " ~");
633 g_string_append (str, tmp);
634 }
635 g_print ("%s\n", str->str);
636
637 return TRUE;
638}
639
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500640static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100641fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500642{
643 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100644 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100645 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
646 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100647 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500648 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500649 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100650 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500651 GNode *child = g_node_append_data (root, dev_tmp);
652 fu_util_build_device_tree (priv, child, devs, dev_tmp);
653 }
654 }
655}
656
657static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100658fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500659{
660 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600661 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500662 g_autoptr(GPtrArray) devs = NULL;
663
664 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000665 if (!fu_util_start_engine (priv,
666 FU_ENGINE_LOAD_FLAG_COLDPLUG |
667 FU_ENGINE_LOAD_FLAG_HWINFO |
668 FU_ENGINE_LOAD_FLAG_REMOTES,
669 error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500670 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600671 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500672
Mario Limonciello76196652021-01-13 22:53:26 -0600673 /* get devices and build tree */
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500674 devs = fu_engine_get_devices (priv->engine, error);
675 if (devs == NULL)
676 return FALSE;
Mario Limonciello76196652021-01-13 22:53:26 -0600677 if (devs->len > 0) {
678 fwupd_device_array_ensure_parents (devs);
679 fu_util_build_device_tree (priv, root, devs, NULL);
680 }
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500681
682 /* print */
Mario Limonciello76196652021-01-13 22:53:26 -0600683 if (g_node_n_children (root) == 0) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500684 /* TRANSLATORS: nothing attached that can be upgraded */
685 g_print ("%s\n", _("No hardware detected with firmware update capability"));
686 return TRUE;
687 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500688 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500689
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100690 /* save the device state for other applications to see */
691 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500692}
693
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500694static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600695fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500696 FwupdDevice *device,
697 FuUtilPrivate *priv)
698{
699 g_autofree gchar *str = NULL;
700
Richard Hughes809abea2019-03-23 11:06:18 +0000701 /* allowed to set whenever the device has changed */
702 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
703 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
704 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
705 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
706
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500707 /* same as last time, so ignore */
708 if (priv->current_device != NULL &&
709 fwupd_device_compare (priv->current_device, device) == 0)
710 return;
711
Richard Hughesee562b52020-04-07 14:32:52 +0100712 /* ignore indirect devices that might have changed */
713 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
714 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
715 g_debug ("ignoring %s with status %s",
716 fwupd_device_get_name (device),
717 fwupd_status_to_string (fwupd_device_get_status (device)));
718 return;
719 }
720
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500721 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600722 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
723 /* TRANSLATORS: %1 is a device name */
724 str = g_strdup_printf (_("Updating %s…"),
725 fwupd_device_get_name (device));
726 fu_progressbar_set_title (priv->progressbar, str);
727 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
728 /* TRANSLATORS: %1 is a device name */
729 str = g_strdup_printf (_("Installing on %s…"),
730 fwupd_device_get_name (device));
731 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000732 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
733 /* TRANSLATORS: %1 is a device name */
734 str = g_strdup_printf (_("Reading from %s…"),
735 fwupd_device_get_name (device));
736 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600737 } else {
738 g_warning ("no FuUtilOperation set");
739 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500740 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600741
Mario Limonciello32241f42019-01-24 10:12:41 -0600742 if (priv->current_message == NULL) {
743 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
744 if (tmp != NULL)
745 priv->current_message = g_strdup (tmp);
746 }
747}
748
749static void
750fu_util_display_current_message (FuUtilPrivate *priv)
751{
752 if (priv->current_message == NULL)
753 return;
754 g_print ("%s\n", priv->current_message);
755 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500756}
757
Richard Hughes98ca9932018-05-18 10:24:07 +0100758static gboolean
759fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
760{
761 g_autoptr(FuDevice) device = NULL;
762 g_autoptr(GBytes) blob_fw = NULL;
763
764 /* invalid args */
765 if (g_strv_length (values) == 0) {
766 g_set_error_literal (error,
767 FWUPD_ERROR,
768 FWUPD_ERROR_INVALID_ARGS,
769 "Invalid arguments");
770 return FALSE;
771 }
772
773 /* parse blob */
774 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500775 if (blob_fw == NULL) {
776 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100777 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500778 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100779
780 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000781 if (!fu_util_start_engine (priv,
782 FU_ENGINE_LOAD_FLAG_COLDPLUG |
783 FU_ENGINE_LOAD_FLAG_HWINFO |
784 FU_ENGINE_LOAD_FLAG_REMOTES,
785 error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100786 return FALSE;
787
788 /* get device */
789 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100790 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100791 if (device == NULL)
792 return FALSE;
793 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100794 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100795 if (device == NULL)
796 return FALSE;
797 }
798
Mario Limonciello3f243a92019-01-21 22:05:23 -0600799 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500800 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600801 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500802
Richard Hughes98ca9932018-05-18 10:24:07 +0100803 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000804 if (priv->prepare_blob) {
805 g_autoptr(GPtrArray) devices = NULL;
806 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
807 g_ptr_array_add (devices, g_object_ref (device));
808 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
809 g_prefix_error (error, "failed to prepare composite action: ");
810 return FALSE;
811 }
812 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100813 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000814 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600815 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000816 if (priv->cleanup_blob) {
817 g_autoptr(FuDevice) device_new = NULL;
818 g_autoptr(GError) error_local = NULL;
819
820 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100821 device_new = fu_util_get_device (priv,
822 fu_device_get_id (device),
823 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000824 if (device_new == NULL) {
825 g_debug ("failed to find new device: %s",
826 error_local->message);
827 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600828 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000829 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
830 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
831 g_prefix_error (error, "failed to cleanup composite action: ");
832 return FALSE;
833 }
834 }
835 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600836
Mario Limonciello32241f42019-01-24 10:12:41 -0600837 fu_util_display_current_message (priv);
838
Mario Limonciello3f243a92019-01-21 22:05:23 -0600839 /* success */
840 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100841}
842
Richard Hughesa58510b2019-10-30 10:03:12 +0000843static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100844fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000845{
846 g_autoptr(FuDevice) device = NULL;
847 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
848 g_autoptr(GBytes) blob_fw = NULL;
849
850 /* invalid args */
851 if (g_strv_length (values) == 0) {
852 g_set_error_literal (error,
853 FWUPD_ERROR,
854 FWUPD_ERROR_INVALID_ARGS,
855 "Invalid arguments");
856 return FALSE;
857 }
858
859 /* file already exists */
860 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
861 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
862 g_set_error_literal (error,
863 FWUPD_ERROR,
864 FWUPD_ERROR_INVALID_ARGS,
865 "Filename already exists");
866 return FALSE;
867 }
868
Richard Hughes02792c02019-11-01 14:21:20 +0000869 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000870 * avoid failing at the end of a potentially lengthy operation */
871 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
872 return FALSE;
873
874 /* load engine */
875 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
876 return FALSE;
877
878 /* get device */
879 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100880 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000881 if (device == NULL)
882 return FALSE;
883 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100884 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000885 if (device == NULL)
886 return FALSE;
887 }
888 priv->current_operation = FU_UTIL_OPERATION_READ;
889 g_signal_connect (priv->engine, "device-changed",
890 G_CALLBACK (fu_util_update_device_changed_cb), priv);
891
892 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100893 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000894 if (blob_fw == NULL)
895 return FALSE;
896 return fu_common_set_contents_bytes (values[0], blob_fw, error);
897}
898
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100899static gint
900fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
901{
902 FuInstallTask *task1 = *((FuInstallTask **) a);
903 FuInstallTask *task2 = *((FuInstallTask **) b);
904 return fu_install_task_compare (task1, task2);
905}
906
907static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100908fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
909{
Filipe Laínse0914272019-09-20 10:04:43 +0100910 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100911 { "curl", uri, "--output", fn, NULL },
912 { NULL } };
913 for (guint i = 0; argv[i][0] != NULL; i++) {
914 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100915 g_autofree gchar *fn_tmp = NULL;
916 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
917 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100918 g_debug ("%s", error_local->message);
919 continue;
920 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000921 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100922 }
923 g_set_error_literal (error,
924 FWUPD_ERROR,
925 FWUPD_ERROR_NOT_FOUND,
926 "no supported out-of-process downloaders found");
927 return FALSE;
928}
929
930static gchar *
931fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
932{
933 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100934
935 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000936 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
937 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000938 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100939 return g_strdup (perhapsfn);
940
941 /* download the firmware to a cachedir */
942 filename = fu_util_get_user_cache_path (perhapsfn);
943 if (!fu_common_mkdir_parent (filename, error))
944 return NULL;
945 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
946 return NULL;
947 return g_steal_pointer (&filename);
948}
949
950static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100951fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
952{
Richard Hughes3d178be2018-08-30 11:14:24 +0100953 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100954 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100955 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100956 g_autoptr(GPtrArray) devices_possible = NULL;
957 g_autoptr(GPtrArray) errors = NULL;
958 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100959 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100960
Mario Limonciello8949e892018-05-25 08:03:06 -0500961 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000962 if (!fu_util_start_engine (priv,
963 FU_ENGINE_LOAD_FLAG_COLDPLUG |
964 FU_ENGINE_LOAD_FLAG_HWINFO |
965 FU_ENGINE_LOAD_FLAG_REMOTES,
966 error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500967 return FALSE;
968
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100969 /* handle both forms */
970 if (g_strv_length (values) == 1) {
971 devices_possible = fu_engine_get_devices (priv->engine, error);
972 if (devices_possible == NULL)
973 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000974 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100975 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100976 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100977 if (device == NULL)
978 return FALSE;
979 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
980 g_ptr_array_add (devices_possible, device);
981 } else {
982 g_set_error_literal (error,
983 FWUPD_ERROR,
984 FWUPD_ERROR_INVALID_ARGS,
985 "Invalid arguments");
986 return FALSE;
987 }
988
Richard Hughes3d178be2018-08-30 11:14:24 +0100989 /* download if required */
990 filename = fu_util_download_if_required (priv, values[0], error);
991 if (filename == NULL)
992 return FALSE;
993
Richard Hughes481aa2a2018-09-18 20:51:46 +0100994 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100995 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500996 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100997 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100998 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500999 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001000 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
1001 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001002 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -06001003 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001004 if (components == NULL)
1005 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001006
Richard Hughes481aa2a2018-09-18 20:51:46 +01001007 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001008 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
1009 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001010 for (guint i = 0; i < components->len; i++) {
1011 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001012
1013 /* do any devices pass the requirements */
1014 for (guint j = 0; j < devices_possible->len; j++) {
1015 FuDevice *device = g_ptr_array_index (devices_possible, j);
1016 g_autoptr(FuInstallTask) task = NULL;
1017 g_autoptr(GError) error_local = NULL;
1018
1019 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001020 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001021 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001022 priv->request,
1023 task,
1024 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001025 &error_local)) {
1026 g_debug ("first pass requirement on %s:%s failed: %s",
1027 fu_device_get_id (device),
1028 xb_node_query_text (component, "id", NULL),
1029 error_local->message);
1030 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1031 continue;
1032 }
1033
1034 /* make a second pass using possibly updated version format now */
1035 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1036 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001037 priv->request,
1038 task,
1039 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001040 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001041 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001042 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001043 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001044 error_local->message);
1045 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1046 continue;
1047 }
1048
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001049 /* if component should have an update message from CAB */
1050 fu_device_incorporate_from_component (device, component);
1051
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001052 /* success */
1053 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1054 }
1055 }
1056
1057 /* order the install tasks by the device priority */
1058 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1059
1060 /* nothing suitable */
1061 if (install_tasks->len == 0) {
1062 GError *error_tmp = fu_common_error_array_get_best (errors);
1063 g_propagate_error (error, error_tmp);
1064 return FALSE;
1065 }
1066
Mario Limonciello3f243a92019-01-21 22:05:23 -06001067 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001068 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001069 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001070
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001071 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001072 if (!fu_engine_install_tasks (priv->engine,
1073 priv->request,
1074 install_tasks,
1075 blob_cab,
1076 priv->flags,
1077 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001078 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001079
Mario Limonciello32241f42019-01-24 10:12:41 -06001080 fu_util_display_current_message (priv);
1081
Mario Limonciello3f243a92019-01-21 22:05:23 -06001082 /* we don't want to ask anything */
1083 if (priv->no_reboot_check) {
1084 g_debug ("skipping reboot check");
1085 return TRUE;
1086 }
1087
Mario Limonciello3143bad2019-02-27 07:31:00 -06001088 /* save the device state for other applications to see */
1089 if (!fu_util_save_current_state (priv, error))
1090 return FALSE;
1091
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001092 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001093 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001094}
1095
Richard Hughes98ca9932018-05-18 10:24:07 +01001096static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001097fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001098{
Mario Limonciellofd734852019-08-01 16:41:42 -05001099 FwupdRemote *remote;
1100 const gchar *remote_id;
1101 const gchar *uri_tmp;
1102 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001103
Mario Limonciellofd734852019-08-01 16:41:42 -05001104 uri_tmp = fwupd_release_get_uri (rel);
1105 if (uri_tmp == NULL) {
1106 g_set_error_literal (error,
1107 FWUPD_ERROR,
1108 FWUPD_ERROR_INVALID_FILE,
1109 "release missing URI");
1110 return FALSE;
1111 }
1112 remote_id = fwupd_release_get_remote_id (rel);
1113 if (remote_id == NULL) {
1114 g_set_error (error,
1115 FWUPD_ERROR,
1116 FWUPD_ERROR_INVALID_FILE,
1117 "failed to find remote for %s",
1118 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001119 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001120 }
1121
1122 remote = fu_engine_get_remote_by_id (priv->engine,
1123 remote_id,
1124 error);
1125 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001126 return FALSE;
1127
Mario Limonciellofd734852019-08-01 16:41:42 -05001128 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001129 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001130 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1131 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001132 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1133 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1134 argv[0] = g_build_filename (path, uri_tmp, NULL);
1135 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1136 argv[0] = g_strdup (uri_tmp + 7);
1137 /* web remote, fu_util_install will download file */
1138 } else {
1139 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1140 }
1141 return fu_util_install (priv, argv, error);
1142}
1143
1144static gboolean
1145fu_util_update_all (FuUtilPrivate *priv, GError **error)
1146{
1147 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001148 gboolean no_updates_header = FALSE;
1149 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001150
Mario Limonciello46aaee82019-01-10 12:58:00 -06001151 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001152 if (devices == NULL)
1153 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001154 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001155 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001156 for (guint i = 0; i < devices->len; i++) {
1157 FwupdDevice *dev = g_ptr_array_index (devices, i);
1158 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001159 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001160 g_autoptr(GPtrArray) rels = NULL;
1161 g_autoptr(GError) error_local = NULL;
1162
1163 if (!fu_util_is_interesting_device (dev))
1164 continue;
1165 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001166 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001167 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001168 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001169 if (!no_updates_header) {
1170 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1171 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1172 no_updates_header = TRUE;
1173 }
1174 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001175 continue;
1176 }
Richard Hughes747f5702019-08-06 14:27:26 +01001177 if (!fu_util_filter_device (priv, dev))
1178 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001179
1180 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001181 rels = fu_engine_get_upgrades (priv->engine,
1182 priv->request,
1183 device_id,
1184 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001185 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001186 if (!latest_header) {
1187 /* TRANSLATORS: message letting the user know no device upgrade available */
1188 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1189 latest_header = TRUE;
1190 }
1191 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001192 /* discard the actual reason from user, but leave for debugging */
1193 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001194 continue;
1195 }
1196
Mario Limonciello98b95162019-10-30 09:20:43 -05001197 if (!priv->no_safety_check) {
1198 if (!fu_util_prompt_warning (dev,
1199 fu_util_get_tree_title (priv),
1200 error))
1201 return FALSE;
1202 }
1203
Mario Limonciello46aaee82019-01-10 12:58:00 -06001204 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001205 if (!fu_util_install_release (priv, rel, &error_local)) {
1206 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001207 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001208 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001209 fu_util_display_current_message (priv);
1210 }
1211 return TRUE;
1212}
1213
1214static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001215fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001216{
1217 FwupdRelease *rel;
1218 g_autoptr(FuDevice) dev = NULL;
1219 g_autoptr(GPtrArray) rels = NULL;
1220
Mario Limonciello9917bb42020-04-20 13:41:27 -05001221 /* do not allow a partial device-id, lookup GUIDs */
1222 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001223 if (dev == NULL)
1224 return FALSE;
1225
1226 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001227 rels = fu_engine_get_upgrades (priv->engine,
1228 priv->request,
1229 fu_device_get_id (dev),
1230 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001231 if (rels == NULL)
1232 return FALSE;
1233 rel = g_ptr_array_index (rels, 0);
1234 if (!fu_util_install_release (priv, rel, error))
1235 return FALSE;
1236 fu_util_display_current_message (priv);
1237
1238 return TRUE;
1239}
1240
1241static gboolean
1242fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1243{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001244 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1245 g_set_error_literal (error,
1246 FWUPD_ERROR,
1247 FWUPD_ERROR_INVALID_ARGS,
1248 "--allow-older is not supported for this command");
1249 return FALSE;
1250 }
1251
1252 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1253 g_set_error_literal (error,
1254 FWUPD_ERROR,
1255 FWUPD_ERROR_INVALID_ARGS,
1256 "--allow-reinstall is not supported for this command");
1257 return FALSE;
1258 }
1259
Mario Limonciellofd734852019-08-01 16:41:42 -05001260 if (g_strv_length (values) > 1) {
1261 g_set_error_literal (error,
1262 FWUPD_ERROR,
1263 FWUPD_ERROR_INVALID_ARGS,
1264 "Invalid arguments");
1265 return FALSE;
1266 }
1267
Richard Hughesc7d870a2020-12-10 10:05:35 +00001268 if (!fu_util_start_engine (priv,
1269 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1270 FU_ENGINE_LOAD_FLAG_HWINFO |
1271 FU_ENGINE_LOAD_FLAG_REMOTES,
1272 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001273 return FALSE;
1274
1275 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1276 g_signal_connect (priv->engine, "device-changed",
1277 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1278
1279 if (g_strv_length (values) == 1) {
1280 if (!fu_util_update_by_id (priv, values[0], error))
1281 return FALSE;
1282 } else {
1283 if (!fu_util_update_all (priv, error))
1284 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001285 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001286
1287 /* we don't want to ask anything */
1288 if (priv->no_reboot_check) {
1289 g_debug ("skipping reboot check");
1290 return TRUE;
1291 }
1292
Mario Limonciello3143bad2019-02-27 07:31:00 -06001293 /* save the device state for other applications to see */
1294 if (!fu_util_save_current_state (priv, error))
1295 return FALSE;
1296
Mario Limonciello3f243a92019-01-21 22:05:23 -06001297 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001298}
1299
1300static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001301fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1302{
1303 g_autoptr(FwupdRelease) rel = NULL;
1304 g_autoptr(GPtrArray) rels = NULL;
1305 g_autoptr(FuDevice) dev = NULL;
1306
1307 if (g_strv_length (values) != 1) {
1308 g_set_error_literal (error,
1309 FWUPD_ERROR,
1310 FWUPD_ERROR_INVALID_ARGS,
1311 "Invalid arguments");
1312 return FALSE;
1313 }
1314
Richard Hughesc7d870a2020-12-10 10:05:35 +00001315 if (!fu_util_start_engine (priv,
1316 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1317 FU_ENGINE_LOAD_FLAG_HWINFO |
1318 FU_ENGINE_LOAD_FLAG_REMOTES,
1319 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001320 return FALSE;
1321
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001322 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001323 if (dev == NULL)
1324 return FALSE;
1325
1326 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001327 rels = fu_engine_get_releases_for_device (priv->engine,
1328 priv->request,
1329 dev,
1330 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001331 if (rels == NULL)
1332 return FALSE;
1333
1334 for (guint j = 0; j < rels->len; j++) {
1335 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1336 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1337 fu_device_get_version (dev),
1338 fu_device_get_version_format (dev)) == 0) {
1339 rel = g_object_ref (rel_tmp);
1340 break;
1341 }
1342 }
1343 if (rel == NULL) {
1344 g_set_error (error,
1345 FWUPD_ERROR,
1346 FWUPD_ERROR_NOT_SUPPORTED,
1347 "Unable to locate release for %s version %s",
1348 fu_device_get_name (dev),
1349 fu_device_get_version (dev));
1350 return FALSE;
1351 }
1352
1353 /* update the console if composite devices are also updated */
1354 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1355 g_signal_connect (priv->engine, "device-changed",
1356 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1357 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1358 if (!fu_util_install_release (priv, rel, error))
1359 return FALSE;
1360 fu_util_display_current_message (priv);
1361
1362 /* we don't want to ask anything */
1363 if (priv->no_reboot_check) {
1364 g_debug ("skipping reboot check");
1365 return TRUE;
1366 }
1367
1368 /* save the device state for other applications to see */
1369 if (!fu_util_save_current_state (priv, error))
1370 return FALSE;
1371
1372 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1373}
1374
1375static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001376fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1377{
1378 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001379 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001380
1381 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001382 if (!fu_util_start_engine (priv,
1383 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1384 FU_ENGINE_LOAD_FLAG_HWINFO |
1385 FU_ENGINE_LOAD_FLAG_REMOTES,
1386 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001387 return FALSE;
1388
Richard Hughes98ca9932018-05-18 10:24:07 +01001389 /* get device */
1390 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001391 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001392 if (device == NULL)
1393 return FALSE;
1394 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001395 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001396 if (device == NULL)
1397 return FALSE;
1398 }
1399
1400 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001401 locker = fu_device_locker_new (device, error);
1402 if (locker == NULL)
1403 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001404 return fu_device_detach (device, error);
1405}
1406
1407static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001408fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1409{
1410 g_autoptr(FuDevice) device = NULL;
1411 g_autoptr(FuDeviceLocker) locker = NULL;
1412
1413 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001414 if (!fu_util_start_engine (priv,
1415 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1416 FU_ENGINE_LOAD_FLAG_HWINFO |
1417 FU_ENGINE_LOAD_FLAG_REMOTES,
1418 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001419 return FALSE;
1420
1421 /* get device */
1422 if (g_strv_length (values) == 1) {
1423 device = fu_util_get_device (priv, values[0], error);
1424 } else {
1425 device = fu_util_prompt_for_device (priv, NULL, error);
1426 }
1427 if (device == NULL)
1428 return FALSE;
1429
1430 /* run vfunc */
1431 locker = fu_device_locker_new (device, error);
1432 if (locker == NULL)
1433 return FALSE;
1434 return fu_device_unbind_driver (device, error);
1435}
1436
1437static gboolean
1438fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1439{
1440 g_autoptr(FuDevice) device = NULL;
1441 g_autoptr(FuDeviceLocker) locker = NULL;
1442
1443 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001444 if (!fu_util_start_engine (priv,
1445 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1446 FU_ENGINE_LOAD_FLAG_HWINFO |
1447 FU_ENGINE_LOAD_FLAG_REMOTES,
1448 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001449 return FALSE;
1450
1451 /* get device */
1452 if (g_strv_length (values) == 3) {
1453 device = fu_util_get_device (priv, values[2], error);
1454 if (device == NULL)
1455 return FALSE;
1456 } else if (g_strv_length (values) == 2) {
1457 device = fu_util_prompt_for_device (priv, NULL, error);
1458 if (device == NULL)
1459 return FALSE;
1460 } else {
1461 g_set_error_literal (error,
1462 FWUPD_ERROR,
1463 FWUPD_ERROR_INVALID_ARGS,
1464 "Invalid arguments");
1465 return FALSE;
1466 }
1467
1468 /* run vfunc */
1469 locker = fu_device_locker_new (device, error);
1470 if (locker == NULL)
1471 return FALSE;
1472 return fu_device_bind_driver (device, values[0], values[1], error);
1473}
1474
1475static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001476fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1477{
1478 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001479 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001480
1481 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001482 if (!fu_util_start_engine (priv,
1483 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1484 FU_ENGINE_LOAD_FLAG_HWINFO |
1485 FU_ENGINE_LOAD_FLAG_REMOTES,
1486 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001487 return FALSE;
1488
Richard Hughes98ca9932018-05-18 10:24:07 +01001489 /* get device */
1490 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001491 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001492 if (device == NULL)
1493 return FALSE;
1494 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001495 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001496 if (device == NULL)
1497 return FALSE;
1498 }
1499
1500 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001501 locker = fu_device_locker_new (device, error);
1502 if (locker == NULL)
1503 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001504 return fu_device_attach (device, error);
1505}
1506
Richard Hughes1d894f12018-08-31 13:05:51 +01001507static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001508fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001509{
1510 gboolean has_pending = FALSE;
1511 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001512 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1513 if (devices == NULL)
1514 return FALSE;
1515
1516 /* only start up the plugins needed */
1517 for (guint i = 0; i < devices->len; i++) {
1518 FuDevice *dev = g_ptr_array_index (devices, i);
1519 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1520 fu_engine_add_plugin_filter (priv->engine,
1521 fu_device_get_plugin (dev));
1522 has_pending = TRUE;
1523 }
1524 }
1525
1526 if (!has_pending) {
1527 g_set_error_literal (error,
1528 FWUPD_ERROR,
1529 FWUPD_ERROR_NOTHING_TO_DO,
1530 "No devices to activate");
1531 return FALSE;
1532 }
1533
1534 return TRUE;
1535}
1536
1537static gboolean
1538fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1539{
1540 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001541 g_autoptr(GPtrArray) devices = NULL;
1542
1543 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001544 if (!fu_util_check_activation_needed (priv, error))
1545 return FALSE;
1546
1547 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001548 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY, error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001549 return FALSE;
1550
1551 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001552 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001553 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001554 if (devices == NULL)
1555 return FALSE;
1556 } else if (g_strv_length (values) == 1) {
1557 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001558 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001559 if (device == NULL)
1560 return FALSE;
1561 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1562 g_ptr_array_add (devices, device);
1563 } else {
1564 g_set_error_literal (error,
1565 FWUPD_ERROR,
1566 FWUPD_ERROR_INVALID_ARGS,
1567 "Invalid arguments");
1568 return FALSE;
1569 }
1570
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001571 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001572 /* order by device priority */
1573 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001574 for (guint i = 0; i < devices->len; i++) {
1575 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001576 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1577 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001578 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1579 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001580 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001581 /* TRANSLATORS: shown when shutting down to switch to the new version */
1582 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1583 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1584 return FALSE;
1585 }
1586
Mario Limonciello02085a02020-09-11 14:59:35 -05001587 if (!has_pending) {
1588 g_set_error_literal (error,
1589 FWUPD_ERROR,
1590 FWUPD_ERROR_NOTHING_TO_DO,
1591 "No devices to activate");
1592 return FALSE;
1593 }
1594
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001595 return TRUE;
1596}
1597
1598static gboolean
Richard Hughesf6751cd2021-01-15 16:48:25 +00001599fu_util_export_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1600{
1601 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1602 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1603 g_autoptr(GKeyFile) kf = g_key_file_new ();
1604 g_autoptr(GPtrArray) hwid_keys = NULL;
1605
1606 /* check args */
1607 if (g_strv_length (values) != 1) {
1608 g_set_error_literal (error,
1609 FWUPD_ERROR,
1610 FWUPD_ERROR_INVALID_ARGS,
1611 "Invalid arguments, expected HWIDS-FILE");
1612 return FALSE;
1613 }
1614
1615 /* setup default hwids */
1616 if (!fu_smbios_setup (smbios, error))
1617 return FALSE;
1618 if (!fu_hwids_setup (hwids, smbios, error))
1619 return FALSE;
1620
1621 /* save all keys */
1622 hwid_keys = fu_hwids_get_keys (hwids);
1623 for (guint i = 0; i < hwid_keys->len; i++) {
1624 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1625 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1626 g_key_file_set_string (kf, "HwIds", hwid_key, value);
1627 }
1628
1629 /* success */
1630 return g_key_file_save_to_file (kf, values[0], error);
1631}
1632
1633static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001634fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1635{
Richard Hughesf6751cd2021-01-15 16:48:25 +00001636 g_autoptr(FuSmbios) smbios = NULL;
Richard Hughes1d894f12018-08-31 13:05:51 +01001637 g_autoptr(FuHwids) hwids = fu_hwids_new ();
Richard Hughesf6751cd2021-01-15 16:48:25 +00001638 g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys (hwids);
Richard Hughes1d894f12018-08-31 13:05:51 +01001639
1640 /* read DMI data */
1641 if (g_strv_length (values) == 0) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001642 smbios = fu_smbios_new ();
Richard Hughes1d894f12018-08-31 13:05:51 +01001643 if (!fu_smbios_setup (smbios, error))
1644 return FALSE;
1645 } else if (g_strv_length (values) == 1) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001646 /* a keyfile with overrides */
1647 g_autoptr(GKeyFile) kf = g_key_file_new ();
1648 if (g_key_file_load_from_file (kf, values[0], G_KEY_FILE_NONE, NULL)) {
1649 for (guint i = 0; i < hwid_keys->len; i++) {
1650 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1651 g_autofree gchar *tmp = NULL;
1652 tmp = g_key_file_get_string (kf, "HwIds", hwid_key, NULL);
1653 fu_hwids_add_smbios_override (hwids, hwid_key, tmp);
1654 }
1655 /* a DMI blob */
1656 } else {
1657 smbios = fu_smbios_new ();
1658 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1659 return FALSE;
1660 }
Richard Hughes1d894f12018-08-31 13:05:51 +01001661 } else {
1662 g_set_error_literal (error,
1663 FWUPD_ERROR,
1664 FWUPD_ERROR_INVALID_ARGS,
1665 "Invalid arguments");
1666 return FALSE;
1667 }
1668 if (!fu_hwids_setup (hwids, smbios, error))
1669 return FALSE;
1670
1671 /* show debug output */
1672 g_print ("Computer Information\n");
1673 g_print ("--------------------\n");
Richard Hughesf6751cd2021-01-15 16:48:25 +00001674 for (guint i = 0; i < hwid_keys->len; i++) {
1675 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1676 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1677 if (value == NULL)
Richard Hughes1d894f12018-08-31 13:05:51 +01001678 continue;
Richard Hughesf6751cd2021-01-15 16:48:25 +00001679 if (g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1680 g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1681 guint64 val = g_ascii_strtoull (value, NULL, 16);
1682 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
Richard Hughes1d894f12018-08-31 13:05:51 +01001683 } else {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001684 g_print ("%s: %s\n", hwid_key, value);
Richard Hughes1d894f12018-08-31 13:05:51 +01001685 }
1686 }
1687
1688 /* show GUIDs */
1689 g_print ("\nHardware IDs\n");
1690 g_print ("------------\n");
1691 for (guint i = 0; i < 15; i++) {
1692 const gchar *keys = NULL;
1693 g_autofree gchar *guid = NULL;
1694 g_autofree gchar *key = NULL;
1695 g_autofree gchar *keys_str = NULL;
1696 g_auto(GStrv) keysv = NULL;
1697 g_autoptr(GError) error_local = NULL;
1698
1699 /* get the GUID */
1700 key = g_strdup_printf ("HardwareID-%u", i);
1701 keys = fu_hwids_get_replace_keys (hwids, key);
1702 guid = fu_hwids_get_guid (hwids, key, &error_local);
1703 if (guid == NULL) {
1704 g_print ("%s\n", error_local->message);
1705 continue;
1706 }
1707
1708 /* show what makes up the GUID */
1709 keysv = g_strsplit (keys, "&", -1);
1710 keys_str = g_strjoinv (" + ", keysv);
1711 g_print ("{%s} <- %s\n", guid, keys_str);
1712 }
1713
1714 return TRUE;
1715}
1716
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001717static gboolean
1718fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1719{
1720 const gchar *script_fn = "startup.sh";
1721 const gchar *output_fn = "firmware.bin";
1722 g_autoptr(GBytes) archive_blob = NULL;
1723 g_autoptr(GBytes) firmware_blob = NULL;
1724 if (g_strv_length (values) < 2) {
1725 g_set_error_literal (error,
1726 FWUPD_ERROR,
1727 FWUPD_ERROR_INVALID_ARGS,
1728 "Invalid arguments");
1729 return FALSE;
1730 }
1731 archive_blob = fu_common_get_contents_bytes (values[0], error);
1732 if (archive_blob == NULL)
1733 return FALSE;
1734 if (g_strv_length (values) > 2)
1735 script_fn = values[2];
1736 if (g_strv_length (values) > 3)
1737 output_fn = values[3];
1738 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1739 if (firmware_blob == NULL)
1740 return FALSE;
1741 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1742}
1743
Richard Hughes3d607622019-03-07 16:59:27 +00001744static gboolean
1745fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1746{
1747 g_autofree gchar *sig = NULL;
1748
1749 /* check args */
1750 if (g_strv_length (values) != 1) {
1751 g_set_error_literal (error,
1752 FWUPD_ERROR,
1753 FWUPD_ERROR_INVALID_ARGS,
1754 "Invalid arguments: value expected");
1755 return FALSE;
1756 }
1757
1758 /* start engine */
1759 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1760 return FALSE;
1761 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001762 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1763 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001764 if (sig == NULL)
1765 return FALSE;
1766 g_print ("%s\n", sig);
1767 return TRUE;
1768}
1769
Mario Limonciello62f84862018-10-18 13:15:23 -05001770static void
1771fu_util_device_added_cb (FwupdClient *client,
1772 FwupdDevice *device,
1773 gpointer user_data)
1774{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001775 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001776 /* TRANSLATORS: this is when a device is hotplugged */
1777 g_print ("%s\n%s", _("Device added:"), tmp);
1778}
1779
1780static void
1781fu_util_device_removed_cb (FwupdClient *client,
1782 FwupdDevice *device,
1783 gpointer user_data)
1784{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001785 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001786 /* TRANSLATORS: this is when a device is hotplugged */
1787 g_print ("%s\n%s", _("Device removed:"), tmp);
1788}
1789
1790static void
1791fu_util_device_changed_cb (FwupdClient *client,
1792 FwupdDevice *device,
1793 gpointer user_data)
1794{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001795 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001796 /* TRANSLATORS: this is when a device has been updated */
1797 g_print ("%s\n%s", _("Device changed:"), tmp);
1798}
1799
1800static void
1801fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1802{
1803 /* TRANSLATORS: this is when the daemon state changes */
1804 g_print ("%s\n", _("Changed"));
1805}
1806
1807static gboolean
1808fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1809{
1810 g_autoptr(FwupdClient) client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00001811 fwupd_client_set_main_context (client, priv->main_ctx);
Mario Limonciello62f84862018-10-18 13:15:23 -05001812
1813 /* get all the devices */
1814 if (!fwupd_client_connect (client, priv->cancellable, error))
1815 return FALSE;
1816
1817 /* watch for any hotplugged device */
1818 g_signal_connect (client, "changed",
1819 G_CALLBACK (fu_util_changed_cb), priv);
1820 g_signal_connect (client, "device-added",
1821 G_CALLBACK (fu_util_device_added_cb), priv);
1822 g_signal_connect (client, "device-removed",
1823 G_CALLBACK (fu_util_device_removed_cb), priv);
1824 g_signal_connect (client, "device-changed",
1825 G_CALLBACK (fu_util_device_changed_cb), priv);
1826 g_signal_connect (priv->cancellable, "cancelled",
1827 G_CALLBACK (fu_util_cancelled_cb), priv);
1828 g_main_loop_run (priv->loop);
1829 return TRUE;
1830}
1831
Richard Hughes15684492019-03-15 16:27:50 +00001832static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001833fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1834{
1835 g_autoptr(GPtrArray) firmware_types = NULL;
1836
1837 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001838 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001839 return FALSE;
1840
1841 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1842 for (guint i = 0; i < firmware_types->len; i++) {
1843 const gchar *id = g_ptr_array_index (firmware_types, i);
1844 g_print ("%s\n", id);
1845 }
1846 if (firmware_types->len == 0) {
1847 /* TRANSLATORS: nothing found */
1848 g_print ("%s\n", _("No firmware IDs found"));
1849 return TRUE;
1850 }
1851
1852 return TRUE;
1853}
1854
1855static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001856fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001857{
1858 g_autoptr(GPtrArray) firmware_types = NULL;
1859 guint idx;
1860 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1861
1862 /* TRANSLATORS: get interactive prompt */
1863 g_print ("%s\n", _("Choose a firmware type:"));
1864 /* TRANSLATORS: this is to abort the interactive prompt */
1865 g_print ("0.\t%s\n", _("Cancel"));
1866 for (guint i = 0; i < firmware_types->len; i++) {
1867 const gchar *id = g_ptr_array_index (firmware_types, i);
1868 g_print ("%u.\t%s\n", i + 1, id);
1869 }
1870 idx = fu_util_prompt_for_number (firmware_types->len);
1871 if (idx == 0) {
1872 g_set_error_literal (error,
1873 FWUPD_ERROR,
1874 FWUPD_ERROR_NOTHING_TO_DO,
1875 "Request canceled");
1876 return NULL;
1877 }
1878
1879 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1880}
1881
1882static gboolean
1883fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1884{
1885 GType gtype;
1886 g_autoptr(GBytes) blob = NULL;
1887 g_autoptr(FuFirmware) firmware = NULL;
1888 g_autofree gchar *firmware_type = NULL;
1889 g_autofree gchar *str = NULL;
1890
1891 /* check args */
1892 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1893 g_set_error_literal (error,
1894 FWUPD_ERROR,
1895 FWUPD_ERROR_INVALID_ARGS,
1896 "Invalid arguments: filename required");
1897 return FALSE;
1898 }
1899
1900 if (g_strv_length (values) == 2)
1901 firmware_type = g_strdup (values[1]);
1902
1903 /* load file */
1904 blob = fu_common_get_contents_bytes (values[0], error);
1905 if (blob == NULL)
1906 return FALSE;
1907
1908 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001909 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001910 return FALSE;
1911
1912 /* find the GType to use */
1913 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001914 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001915 if (firmware_type == NULL)
1916 return FALSE;
1917 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1918 if (gtype == G_TYPE_INVALID) {
1919 g_set_error (error,
1920 G_IO_ERROR,
1921 G_IO_ERROR_NOT_FOUND,
1922 "GType %s not supported", firmware_type);
1923 return FALSE;
1924 }
1925 firmware = g_object_new (gtype, NULL);
1926 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1927 return FALSE;
1928 str = fu_firmware_to_string (firmware);
1929 g_print ("%s", str);
1930 return TRUE;
1931}
1932
Richard Hughesdd653442020-09-22 10:23:52 +01001933static gboolean
1934fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1935{
1936 GType gtype;
1937 g_autofree gchar *firmware_type = NULL;
1938 g_autofree gchar *str = NULL;
1939 g_autoptr(FuFirmware) firmware = NULL;
1940 g_autoptr(GBytes) blob = NULL;
1941 g_autoptr(GPtrArray) images = NULL;
1942
1943 /* check args */
1944 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1945 g_set_error_literal (error,
1946 FWUPD_ERROR,
1947 FWUPD_ERROR_INVALID_ARGS,
1948 "Invalid arguments: filename required");
1949 return FALSE;
1950 }
1951 if (g_strv_length (values) == 2)
1952 firmware_type = g_strdup (values[1]);
1953
1954 /* load file */
1955 blob = fu_common_get_contents_bytes (values[0], error);
1956 if (blob == NULL)
1957 return FALSE;
1958
1959 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001960 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01001961 return FALSE;
1962
1963 /* find the GType to use */
1964 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001965 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001966 if (firmware_type == NULL)
1967 return FALSE;
1968 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1969 if (gtype == G_TYPE_INVALID) {
1970 g_set_error (error,
1971 G_IO_ERROR,
1972 G_IO_ERROR_NOT_FOUND,
1973 "GType %s not supported", firmware_type);
1974 return FALSE;
1975 }
1976 firmware = g_object_new (gtype, NULL);
1977 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1978 return FALSE;
1979 str = fu_firmware_to_string (firmware);
1980 g_print ("%s", str);
1981 images = fu_firmware_get_images (firmware);
1982 for (guint i = 0; i < images->len; i++) {
1983 FuFirmwareImage *img = g_ptr_array_index (images, i);
1984 g_autofree gchar *fn = NULL;
1985 g_autoptr(GBytes) blob_img = NULL;
1986
Richard Hughes88dd7c42020-09-22 16:54:40 +01001987 /* get raw image without generated header, footer or crc */
1988 blob_img = fu_firmware_image_get_bytes (img);
1989 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1990 continue;
1991
Richard Hughesdd653442020-09-22 10:23:52 +01001992 /* use suitable filename */
1993 if (fu_firmware_image_get_filename (img) != NULL) {
1994 fn = g_strdup (fu_firmware_image_get_filename (img));
1995 } else if (fu_firmware_image_get_id (img) != NULL) {
1996 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1997 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1998 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1999 } else {
2000 fn = g_strdup_printf ("img-0x%x.fw", i);
2001 }
2002 /* TRANSLATORS: decompressing images from a container firmware */
2003 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01002004 if (!fu_common_set_contents_bytes (fn, blob_img, error))
2005 return FALSE;
2006 }
2007
2008 /* success */
2009 return TRUE;
2010}
2011
Richard Hughes0924c932020-09-22 19:07:05 +01002012static gboolean
2013fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01002014{
Richard Hughes0924c932020-09-22 19:07:05 +01002015 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01002016 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01002017 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002018 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01002019 g_autoptr(FuFirmware) firmware_dst = NULL;
2020 g_autoptr(GBytes) blob_dst = NULL;
2021 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002022 g_autoptr(XbBuilder) builder = xb_builder_new ();
2023 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2024 g_autoptr(XbNode) n = NULL;
2025 g_autoptr(XbSilo) silo = NULL;
2026
Richard Hughes0924c932020-09-22 19:07:05 +01002027 /* check args */
2028 if (g_strv_length (values) != 2) {
2029 g_set_error_literal (error,
2030 FWUPD_ERROR,
2031 FWUPD_ERROR_INVALID_ARGS,
2032 "Invalid arguments: filename required");
2033 return FALSE;
2034 }
2035
2036 /* load file */
2037 blob_src = fu_common_get_contents_bytes (values[0], error);
2038 if (blob_src == NULL)
2039 return FALSE;
2040
2041 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002042 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002043 return FALSE;
2044
Richard Hughes41400a82020-09-21 13:43:15 +01002045 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002046 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002047 XB_BUILDER_SOURCE_FLAG_NONE,
2048 error)) {
2049 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002050 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002051 }
2052 xb_builder_import_source (builder, source);
2053 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2054 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002055 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002056
2057 /* create FuFirmware of specific GType */
2058 n = xb_silo_query_first (silo, "firmware", error);
2059 if (n == NULL)
2060 return FALSE;
2061 tmp = xb_node_get_attr (n, "gtype");
2062 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002063 gtype = g_type_from_name (tmp);
2064 if (gtype == G_TYPE_INVALID) {
2065 g_set_error (error,
2066 G_IO_ERROR,
2067 G_IO_ERROR_NOT_FOUND,
2068 "GType %s not registered", tmp);
2069 return FALSE;
2070 }
2071 }
2072 tmp = xb_node_get_attr (n, "id");
2073 if (tmp != NULL) {
2074 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002075 if (gtype == G_TYPE_INVALID) {
2076 g_set_error (error,
2077 G_IO_ERROR,
2078 G_IO_ERROR_NOT_FOUND,
2079 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002080 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002081 }
Richard Hughes41400a82020-09-21 13:43:15 +01002082 }
Richard Hughes0924c932020-09-22 19:07:05 +01002083 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002084 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002085 return FALSE;
2086
2087 /* write new file */
2088 blob_dst = fu_firmware_write (firmware, error);
2089 if (blob_dst == NULL)
2090 return FALSE;
2091 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2092 return FALSE;
2093
2094 /* show what we wrote */
2095 firmware_dst = g_object_new (gtype, NULL);
2096 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2097 return FALSE;
2098 str = fu_firmware_to_string (firmware_dst);
2099 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002100
2101 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002102 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002103}
2104
Richard Hughes95c98a92019-10-22 16:03:15 +01002105static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002106fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2107{
2108 GType gtype_dst;
2109 GType gtype_src;
2110 g_autofree gchar *firmware_type_dst = NULL;
2111 g_autofree gchar *firmware_type_src = NULL;
2112 g_autofree gchar *str_dst = NULL;
2113 g_autofree gchar *str_src = NULL;
2114 g_autoptr(FuFirmware) firmware_dst = NULL;
2115 g_autoptr(FuFirmware) firmware_src = NULL;
2116 g_autoptr(GBytes) blob_dst = NULL;
2117 g_autoptr(GBytes) blob_src = NULL;
2118 g_autoptr(GPtrArray) images = NULL;
2119
2120 /* check args */
2121 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2122 g_set_error_literal (error,
2123 FWUPD_ERROR,
2124 FWUPD_ERROR_INVALID_ARGS,
2125 "Invalid arguments: filename required");
2126 return FALSE;
2127 }
2128
2129 if (g_strv_length (values) > 2)
2130 firmware_type_src = g_strdup (values[2]);
2131 if (g_strv_length (values) > 3)
2132 firmware_type_dst = g_strdup (values[3]);
2133
2134 /* load file */
2135 blob_src = fu_common_get_contents_bytes (values[0], error);
2136 if (blob_src == NULL)
2137 return FALSE;
2138
2139 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002140 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002141 return FALSE;
2142
2143 /* find the GType to use */
2144 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002145 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002146 if (firmware_type_src == NULL)
2147 return FALSE;
2148 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002149 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002150 if (firmware_type_dst == NULL)
2151 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002152 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2153 if (gtype_src == G_TYPE_INVALID) {
2154 g_set_error (error,
2155 G_IO_ERROR,
2156 G_IO_ERROR_NOT_FOUND,
2157 "GType %s not supported", firmware_type_src);
2158 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002159 }
Richard Hughes0924c932020-09-22 19:07:05 +01002160 firmware_src = g_object_new (gtype_src, NULL);
2161 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2162 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002163 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2164 if (gtype_dst == G_TYPE_INVALID) {
2165 g_set_error (error,
2166 G_IO_ERROR,
2167 G_IO_ERROR_NOT_FOUND,
2168 "GType %s not supported", firmware_type_dst);
2169 return FALSE;
2170 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002171 str_src = fu_firmware_to_string (firmware_src);
2172 g_print ("%s", str_src);
2173
2174 /* copy images */
2175 firmware_dst = g_object_new (gtype_dst, NULL);
2176 images = fu_firmware_get_images (firmware_src);
2177 for (guint i = 0; i < images->len; i++) {
2178 FuFirmwareImage *img = g_ptr_array_index (images, i);
2179 fu_firmware_add_image (firmware_dst, img);
2180 }
2181
2182 /* write new file */
2183 blob_dst = fu_firmware_write (firmware_dst, error);
2184 if (blob_dst == NULL)
2185 return FALSE;
2186 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2187 return FALSE;
2188 str_dst = fu_firmware_to_string (firmware_dst);
2189 g_print ("%s", str_dst);
2190
2191 /* success */
2192 return TRUE;
2193}
2194
2195static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002196fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2197{
2198 g_autofree gchar *str = NULL;
2199 g_autoptr(FuDevice) dev = NULL;
2200
2201 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002202 if (!fu_util_start_engine (priv,
2203 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2204 FU_ENGINE_LOAD_FLAG_HWINFO |
2205 FU_ENGINE_LOAD_FLAG_REMOTES,
2206 error))
Richard Hughes15684492019-03-15 16:27:50 +00002207 return FALSE;
2208
2209 /* get device */
2210 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002211 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002212 if (dev == NULL)
2213 return FALSE;
2214 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002215 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002216 if (dev == NULL)
2217 return FALSE;
2218 }
2219
2220 /* add checksums */
2221 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2222 return FALSE;
2223
2224 /* show checksums */
2225 str = fu_device_to_string (dev);
2226 g_print ("%s\n", str);
2227 return TRUE;
2228}
2229
Mario Limonciellofe593942019-04-03 13:48:52 -05002230static gboolean
2231fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2232{
2233 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002234 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002235 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002236
2237 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002238 if (!fu_util_start_engine (priv,
2239 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2240 FU_ENGINE_LOAD_FLAG_HWINFO |
2241 FU_ENGINE_LOAD_FLAG_REMOTES,
2242 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002243 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002244 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002245
2246 /* get all devices from the history database */
2247 devices = fu_engine_get_history (priv->engine, error);
2248 if (devices == NULL)
2249 return FALSE;
2250
2251 /* show each device */
2252 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002253 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002254 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002255 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002256 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002257 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002258
Richard Hughes747f5702019-08-06 14:27:26 +01002259 if (!fu_util_filter_device (priv, dev))
2260 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002261 child = g_node_append_data (root, dev);
2262
2263 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002264 if (rel == NULL)
2265 continue;
2266 remote = fwupd_release_get_remote_id (rel);
2267
2268 /* doesn't actually map to remote */
2269 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002270 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002271 continue;
2272 }
2273
2274 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002275 rels = fu_engine_get_releases (priv->engine,
2276 priv->request,
2277 fwupd_device_get_id (dev),
2278 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002279 if (rels == NULL)
2280 return FALSE;
2281
2282 /* map to a release in client */
2283 for (guint j = 0; j < rels->len; j++) {
2284 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2285 if (g_strcmp0 (remote,
2286 fwupd_release_get_remote_id (rel2)) != 0)
2287 continue;
2288 if (g_strcmp0 (fwupd_release_get_version (rel),
2289 fwupd_release_get_version (rel2)) != 0)
2290 continue;
2291 g_node_append_data (child, g_object_ref (rel2));
2292 rel = NULL;
2293 break;
2294 }
2295
2296 /* didn't match anything */
2297 if (rels->len == 0 || rel != NULL) {
2298 g_node_append_data (child, rel);
2299 continue;
2300 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002301
Mario Limonciellofe593942019-04-03 13:48:52 -05002302 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002303 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002304
2305 return TRUE;
2306}
2307
Richard Hughesfd7e9942020-01-17 14:09:13 +00002308static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002309fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2310{
Richard Hughesc5710d92020-06-26 11:08:25 +01002311 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002312 g_autofree gchar *fn_raw = NULL;
2313 g_autofree gchar *fn_sig = NULL;
2314 g_autoptr(GBytes) bytes_raw = NULL;
2315 g_autoptr(GBytes) bytes_sig = NULL;
2316
Richard Hughes4959baa2020-01-17 14:33:00 +00002317 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002318 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2319 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002320 g_set_error (error,
2321 FWUPD_ERROR,
2322 FWUPD_ERROR_NOTHING_TO_DO,
2323 "no metadata signature URI available for %s",
2324 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002325 return FALSE;
2326 }
2327 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002328 if (!fu_common_mkdir_parent (fn_sig, error))
2329 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002330 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002331 return FALSE;
2332 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2333 if (bytes_sig == NULL)
2334 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002335 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2336 return FALSE;
2337
2338 /* payload */
2339 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2340 if (metadata_uri == NULL) {
2341 g_set_error (error,
2342 FWUPD_ERROR,
2343 FWUPD_ERROR_NOTHING_TO_DO,
2344 "no metadata URI available for %s",
2345 fwupd_remote_get_id (remote));
2346 return FALSE;
2347 }
2348 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2349 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2350 return FALSE;
2351 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2352 if (bytes_raw == NULL)
2353 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002354
2355 /* send to daemon */
2356 g_debug ("updating %s", fwupd_remote_get_id (remote));
2357 return fu_engine_update_metadata_bytes (priv->engine,
2358 fwupd_remote_get_id (remote),
2359 bytes_raw,
2360 bytes_sig,
2361 error);
2362
2363}
2364
2365static gboolean
2366fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2367{
2368 g_autoptr(GPtrArray) remotes = NULL;
2369
2370 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002371 if (!fu_util_start_engine (priv,
2372 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2373 FU_ENGINE_LOAD_FLAG_HWINFO |
2374 FU_ENGINE_LOAD_FLAG_REMOTES,
2375 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002376 return FALSE;
2377
2378 /* download new metadata */
2379 remotes = fu_engine_get_remotes (priv->engine, error);
2380 if (remotes == NULL)
2381 return FALSE;
2382 for (guint i = 0; i < remotes->len; i++) {
2383 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2384 if (!fwupd_remote_get_enabled (remote))
2385 continue;
2386 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2387 continue;
2388 if (!fu_util_refresh_remote (priv, remote, error))
2389 return FALSE;
2390 }
2391 return TRUE;
2392}
2393
2394static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002395fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2396{
2397 g_autoptr(GNode) root = g_node_new (NULL);
2398 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002399 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002400
2401 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002402 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002403 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002404 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002405
2406 /* list remotes */
2407 remotes = fu_engine_get_remotes (priv->engine, error);
2408 if (remotes == NULL)
2409 return FALSE;
2410 if (remotes->len == 0) {
2411 g_set_error_literal (error,
2412 FWUPD_ERROR,
2413 FWUPD_ERROR_NOTHING_TO_DO,
2414 "no remotes available");
2415 return FALSE;
2416 }
2417 for (guint i = 0; i < remotes->len; i++) {
2418 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2419 g_node_append_data (root, remote_tmp);
2420 }
2421 fu_util_print_tree (root, title);
2422
2423 return TRUE;
2424}
2425
Richard Hughes196c6c62020-05-11 19:42:47 +01002426static gboolean
2427fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2428{
Richard Hughes5c82b942020-09-14 12:24:06 +01002429 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002430 g_autoptr(FuSecurityAttrs) attrs = NULL;
2431 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002432 g_autofree gchar *str = NULL;
2433
2434 /* not ready yet */
2435 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2436 g_set_error_literal (error,
2437 FWUPD_ERROR,
2438 FWUPD_ERROR_NOT_SUPPORTED,
2439 "The HSI specification is not yet complete. "
2440 "To ignore this warning, use --force");
2441 return FALSE;
2442 }
2443
Richard Hughesc7d870a2020-12-10 10:05:35 +00002444 if (!fu_util_start_engine (priv,
2445 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2446 FU_ENGINE_LOAD_FLAG_HWINFO |
2447 FU_ENGINE_LOAD_FLAG_REMOTES,
2448 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002449 return FALSE;
2450
2451 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2452 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2453 fu_engine_get_host_security_id (priv->engine));
2454
Richard Hughes5c82b942020-09-14 12:24:06 +01002455 /* show or hide different elements */
2456 if (priv->show_all) {
2457 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2458 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2459 }
2460
Richard Hughes196c6c62020-05-11 19:42:47 +01002461 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002462 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002463 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002464 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002465 g_print ("%s\n", str);
2466 return TRUE;
2467}
2468
Richard Hughesa83deb42020-08-12 12:45:36 +01002469static FuVolume *
2470fu_util_prompt_for_volume (GError **error)
2471{
2472 FuVolume *volume;
2473 guint idx;
2474 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002475 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2476 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002477
2478 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002479 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2480 if (volumes == NULL) {
2481 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2482 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2483 if (volumes == NULL) {
2484 g_prefix_error (error, "%s: ", error_local->message);
2485 return NULL;
2486 }
2487 }
2488 /* only add internal vfat partitions */
2489 for (guint i = 0; i < volumes->len; i++) {
2490 FuVolume *vol = g_ptr_array_index (volumes, i);
2491 g_autofree gchar *type = fu_volume_get_id_type (vol);
2492 if (type == NULL)
2493 continue;
2494 if (!fu_volume_is_internal (vol))
2495 continue;
2496 if (g_strcmp0 (type, "vfat") == 0)
2497 g_ptr_array_add (volumes_vfat, vol);
2498 }
2499 if (volumes_vfat->len == 1) {
2500 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002501 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002502 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2503 return g_object_ref (volume);
2504 }
2505
2506 /* TRANSLATORS: get interactive prompt */
2507 g_print ("%s\n", _("Choose a volume:"));
2508 /* TRANSLATORS: this is to abort the interactive prompt */
2509 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002510 for (guint i = 0; i < volumes_vfat->len; i++) {
2511 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002512 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2513 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002514 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002515 if (idx == 0) {
2516 g_set_error_literal (error,
2517 FWUPD_ERROR,
2518 FWUPD_ERROR_NOTHING_TO_DO,
2519 "Request canceled");
2520 return NULL;
2521 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002522 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002523 return g_object_ref (volume);
2524
2525}
2526
2527static gboolean
2528fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2529{
2530 g_autoptr(FuVolume) volume = NULL;
2531 volume = fu_util_prompt_for_volume (error);
2532 if (volume == NULL)
2533 return FALSE;
2534 return fu_volume_mount (volume, error);
2535}
2536
2537static gboolean
2538fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2539{
2540 g_autoptr(FuVolume) volume = NULL;
2541 volume = fu_util_prompt_for_volume (error);
2542 if (volume == NULL)
2543 return FALSE;
2544 return fu_volume_unmount (volume, error);
2545}
2546
2547static gboolean
2548fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2549{
2550 g_autofree gchar *mount_point = NULL;
2551 g_autoptr(FuDeviceLocker) locker = NULL;
2552 g_autoptr(FuVolume) volume = NULL;
2553 g_autoptr(GPtrArray) files = NULL;
2554
2555 volume = fu_util_prompt_for_volume (error);
2556 if (volume == NULL)
2557 return FALSE;
2558 locker = fu_volume_locker (volume, error);
2559 if (locker == NULL)
2560 return FALSE;
2561 mount_point = fu_volume_get_mount_point (volume);
2562 files = fu_common_get_files_recursive (mount_point, error);
2563 if (files == NULL)
2564 return FALSE;
2565 for (guint i = 0; i < files->len; i++) {
2566 const gchar *fn = g_ptr_array_index (files, i);
2567 g_print ("%s\n", fn);
2568 }
2569 return TRUE;
2570}
2571
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002572static gboolean
2573_g_str_equal0 (gconstpointer str1, gconstpointer str2)
2574{
2575 return g_strcmp0 (str1, str2) == 0;
2576}
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002577
2578static gboolean
2579fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2580{
2581 const gchar *branch;
2582 g_autoptr(FwupdRelease) rel = NULL;
2583 g_autoptr(GPtrArray) rels = NULL;
2584 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2585 g_autoptr(FuDevice) dev = NULL;
2586
2587 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002588 if (!fu_util_start_engine (priv,
2589 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2590 FU_ENGINE_LOAD_FLAG_HWINFO |
2591 FU_ENGINE_LOAD_FLAG_REMOTES,
2592 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002593 return FALSE;
2594
2595 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002596 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00002597 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002598 if (g_strv_length (values) == 1)
2599 dev = fu_util_get_device (priv, values[1], error);
2600 else
2601 dev = fu_util_prompt_for_device (priv, NULL, error);
2602 if (dev == NULL)
2603 return FALSE;
2604 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2605 g_set_error_literal (error,
2606 FWUPD_ERROR,
2607 FWUPD_ERROR_NOT_SUPPORTED,
2608 "Multiple branches not available");
2609 return FALSE;
2610 }
2611
2612 /* get all releases, including the alternate branch versions */
2613 rels = fu_engine_get_releases (priv->engine,
2614 priv->request,
2615 fu_device_get_id (dev),
2616 error);
2617 if (rels == NULL)
2618 return FALSE;
2619
2620 /* get all the unique branches */
2621 for (guint i = 0; i < rels->len; i++) {
2622 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002623 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002624#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002625 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002626 _g_str_equal0, NULL))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002627 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002628#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002629 g_ptr_array_add (branches, g_strdup (branch_tmp));
2630 }
2631
2632 /* branch name is optional */
2633 if (g_strv_length (values) > 1) {
2634 branch = values[1];
2635 } else if (branches->len == 1) {
2636 branch = g_ptr_array_index (branches, 0);
2637 } else {
2638 guint idx;
2639
2640 /* TRANSLATORS: get interactive prompt, where branch is the
2641 * supplier of the firmware, e.g. "non-free" or "free" */
2642 g_print ("%s\n", _("Choose a branch:"));
2643 /* TRANSLATORS: this is to abort the interactive prompt */
2644 g_print ("0.\t%s\n", _("Cancel"));
2645 for (guint i = 0; i < branches->len; i++) {
2646 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002647 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002648 }
2649 idx = fu_util_prompt_for_number (branches->len);
2650 if (idx == 0) {
2651 g_set_error_literal (error,
2652 FWUPD_ERROR,
2653 FWUPD_ERROR_NOTHING_TO_DO,
2654 "Request canceled");
2655 return FALSE;
2656 }
2657 branch = g_ptr_array_index (branches, idx - 1);
2658 }
2659
2660 /* sanity check */
2661 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2662 g_set_error (error,
2663 FWUPD_ERROR,
2664 FWUPD_ERROR_NOT_SUPPORTED,
2665 "Device %s is already on branch %s",
2666 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002667 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002668 return FALSE;
2669 }
2670
2671 /* the releases are ordered by version */
2672 for (guint j = 0; j < rels->len; j++) {
2673 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2674 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2675 rel = g_object_ref (rel_tmp);
2676 break;
2677 }
2678 }
2679 if (rel == NULL) {
2680 g_set_error (error,
2681 FWUPD_ERROR,
2682 FWUPD_ERROR_NOT_SUPPORTED,
2683 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002684 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002685 return FALSE;
2686 }
2687
2688 /* we're switching branch */
2689 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2690 return FALSE;
2691
2692 /* update the console if composite devices are also updated */
2693 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2694 g_signal_connect (priv->engine, "device-changed",
2695 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2696 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2697 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2698 if (!fu_util_install_release (priv, rel, error))
2699 return FALSE;
2700 fu_util_display_current_message (priv);
2701
2702 /* we don't want to ask anything */
2703 if (priv->no_reboot_check) {
2704 g_debug ("skipping reboot check");
2705 return TRUE;
2706 }
2707
2708 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2709}
2710
Richard Hughesb5976832018-05-18 10:02:09 +01002711int
2712main (int argc, char *argv[])
2713{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002714 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002715 gboolean allow_older = FALSE;
2716 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002717 gboolean force = FALSE;
2718 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002719 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002720 gboolean ignore_checksum = FALSE;
2721 gboolean ignore_power = FALSE;
2722 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002723 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002724 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002725 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2726 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002727 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002728 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002729 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002730 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002731 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2732 /* TRANSLATORS: command line option */
2733 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002734 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2735 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002736 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002737 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2738 /* TRANSLATORS: command line option */
2739 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002740 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2741 /* TRANSLATORS: command line option */
2742 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002743 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2744 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002745 _("Force the action by relaxing some runtime checks"), NULL },
2746 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2747 /* TRANSLATORS: command line option */
2748 _("Ignore firmware checksum failures"), NULL },
2749 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2750 /* TRANSLATORS: command line option */
2751 _("Ignore firmware hardware mismatch failures"), NULL },
2752 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2753 /* TRANSLATORS: command line option */
2754 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002755 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2756 /* TRANSLATORS: command line option */
2757 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002758 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2759 /* TRANSLATORS: command line option */
2760 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002761 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2762 /* TRANSLATORS: command line option */
2763 _("Show all results"), NULL },
2764 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002765 /* TRANSLATORS: command line option */
2766 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002767 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002768 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002769 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002770 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2771 /* TRANSLATORS: command line option */
2772 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002773 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002774 /* TRANSLATORS: command line option */
2775 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002776 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002777 /* TRANSLATORS: command line option */
2778 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002779 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2780 /* TRANSLATORS: command line option */
2781 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002782 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2783 /* TRANSLATORS: command line option */
2784 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002785 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2786 /* TRANSLATORS: command line option */
2787 _("Filter with a set of device flags using a ~ prefix to "
2788 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002789 { NULL}
2790 };
2791
Richard Hughes429f72b2020-01-16 12:18:19 +00002792#ifdef _WIN32
2793 /* workaround Windows setting the codepage to 1252 */
2794 g_setenv ("LANG", "C.UTF-8", FALSE);
2795#endif
2796
Richard Hughesb5976832018-05-18 10:02:09 +01002797 setlocale (LC_ALL, "");
2798
Richard Hughes668ee212019-11-22 09:17:46 +00002799 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002800 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2801 textdomain (GETTEXT_PACKAGE);
2802
Richard Hughes01c0bad2019-11-22 09:08:51 +00002803#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002804 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002805 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002806 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002807 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002808#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002809
2810 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002811 priv->main_ctx = g_main_context_new ();
2812 priv->loop = g_main_loop_new (priv->main_ctx, FALSE);
Richard Hughesb5976832018-05-18 10:02:09 +01002813 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002814 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002815
2816 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002817 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002818 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002819 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2820 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002821 /* TRANSLATORS: command description */
2822 _("Build firmware using a sandbox"),
2823 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002824 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002825 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002826 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2827 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002828 /* TRANSLATORS: command description */
2829 _("Dump SMBIOS data from a file"),
2830 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002831 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002832 "get-plugins",
2833 NULL,
2834 /* TRANSLATORS: command description */
2835 _("Get all enabled plugins registered with the system"),
2836 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002837 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002838 "get-details",
2839 NULL,
2840 /* TRANSLATORS: command description */
2841 _("Gets details about a firmware file"),
2842 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002843 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002844 "get-history",
2845 NULL,
2846 /* TRANSLATORS: command description */
2847 _("Show history of firmware updates"),
2848 fu_util_get_history);
2849 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002850 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002851 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2852 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002853 /* TRANSLATORS: command description */
2854 _("Gets the list of updates for connected hardware"),
2855 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002856 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002857 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002858 NULL,
2859 /* TRANSLATORS: command description */
2860 _("Get all devices that support firmware updates"),
2861 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002862 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002863 "get-device-flags",
2864 NULL,
2865 /* TRANSLATORS: command description */
2866 _("Get all device flags supported by fwupd"),
2867 fu_util_get_device_flags);
2868 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002869 "watch",
2870 NULL,
2871 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002872 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002873 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002874 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002875 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002876 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2877 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002878 /* TRANSLATORS: command description */
2879 _("Install a firmware blob on a device"),
2880 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002881 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002882 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002883 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2884 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002885 /* TRANSLATORS: command description */
2886 _("Install a firmware file on this hardware"),
2887 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002888 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002889 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002890 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2891 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002892 /* TRANSLATORS: command description */
2893 _("Reinstall firmware on a device"),
2894 fu_util_reinstall);
2895 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002896 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002897 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2898 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002899 /* TRANSLATORS: command description */
2900 _("Attach to firmware mode"),
2901 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002902 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002903 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002904 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2905 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002906 /* TRANSLATORS: command description */
2907 _("Detach to bootloader mode"),
2908 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002909 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002910 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002911 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2912 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002913 /* TRANSLATORS: command description */
2914 _("Unbind current driver"),
2915 fu_util_unbind_driver);
2916 fu_util_cmd_array_add (cmd_array,
2917 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002918 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2919 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002920 /* TRANSLATORS: command description */
2921 _("Bind new kernel driver"),
2922 fu_util_bind_driver);
2923 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002924 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002925 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2926 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002927 /* TRANSLATORS: command description */
2928 _("Activate pending devices"),
2929 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002930 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002931 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002932 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
Richard Hughesf6751cd2021-01-15 16:48:25 +00002933 _("[SMBIOS-FILE|HWIDS-FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002934 /* TRANSLATORS: command description */
2935 _("Return all the hardware IDs for the machine"),
2936 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002937 fu_util_cmd_array_add (cmd_array,
Richard Hughesf6751cd2021-01-15 16:48:25 +00002938 "export-hwids",
2939 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2940 _("HWIDS-FILE"),
2941 /* TRANSLATORS: command description */
2942 _("Save a file that allows generation of hardware IDs"),
2943 fu_util_export_hwids);
2944 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002945 "monitor",
2946 NULL,
2947 /* TRANSLATORS: command description */
2948 _("Monitor the daemon for events"),
2949 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002950 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002951 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002952 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2953 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002954 /* TRANSLATORS: command description */
2955 _("Update all devices that match local metadata"),
2956 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002957 fu_util_cmd_array_add (cmd_array,
2958 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002959 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2960 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002961 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002962 C_("command-description",
2963 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002964 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002965 fu_util_cmd_array_add (cmd_array,
2966 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002967 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2968 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002969 /* TRANSLATORS: command description */
2970 _("Update the stored metadata with current contents"),
2971 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002972 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002973 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002974 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2975 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002976 /* TRANSLATORS: command description */
2977 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002978 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002979 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002980 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002981 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2982 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002983 /* TRANSLATORS: command description */
2984 _("Convert a firmware file"),
2985 fu_util_firmware_convert);
2986 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002987 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002988 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2989 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002990 /* TRANSLATORS: command description */
2991 _("Build a firmware file"),
2992 fu_util_firmware_build);
2993 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002994 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002995 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2996 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01002997 /* TRANSLATORS: command description */
2998 _("Parse and show details about a firmware file"),
2999 fu_util_firmware_parse);
3000 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01003001 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003002 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3003 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01003004 /* TRANSLATORS: command description */
3005 _("Extract a firmware blob to images"),
3006 fu_util_firmware_extract);
3007 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003008 "get-firmware-types",
3009 NULL,
3010 /* TRANSLATORS: command description */
3011 _("List the available firmware types"),
3012 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00003013 fu_util_cmd_array_add (cmd_array,
3014 "get-remotes",
3015 NULL,
3016 /* TRANSLATORS: command description */
3017 _("Gets the configured remotes"),
3018 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00003019 fu_util_cmd_array_add (cmd_array,
3020 "refresh",
3021 NULL,
3022 /* TRANSLATORS: command description */
3023 _("Refresh metadata from remote server"),
3024 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01003025 fu_util_cmd_array_add (cmd_array,
3026 "security",
3027 NULL,
3028 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003029 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003030 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01003031 fu_util_cmd_array_add (cmd_array,
3032 "esp-mount",
3033 NULL,
3034 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003035 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003036 fu_util_esp_mount);
3037 fu_util_cmd_array_add (cmd_array,
3038 "esp-unmount",
3039 NULL,
3040 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003041 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003042 fu_util_esp_unmount);
3043 fu_util_cmd_array_add (cmd_array,
3044 "esp-list",
3045 NULL,
3046 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003047 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003048 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003049 fu_util_cmd_array_add (cmd_array,
3050 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003051 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3052 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003053 /* TRANSLATORS: command description */
3054 _("Switch the firmware branch on the device"),
3055 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01003056
3057 /* do stuff on ctrl+c */
3058 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003059#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003060 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3061 SIGINT, fu_util_sigint_cb,
3062 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003063#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003064 g_signal_connect (priv->cancellable, "cancelled",
3065 G_CALLBACK (fu_util_cancelled_cb), priv);
3066
3067 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003068 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003069
Mario Limonciello3f243a92019-01-21 22:05:23 -06003070 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003071 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003072 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003073 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003074 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003075 } else {
3076 /* set our implemented feature set */
3077 fu_engine_request_set_feature_flags (priv->request,
3078 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003079 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003080 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003081 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003082
Richard Hughesb5976832018-05-18 10:02:09 +01003083 /* get a list of the commands */
3084 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003085 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003086 g_option_context_set_summary (priv->context, cmd_descriptions);
3087 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003088 /* TRANSLATORS: CLI description */
3089 _("This tool allows an administrator to use the fwupd plugins "
3090 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003091
3092 /* TRANSLATORS: program name */
3093 g_set_application_name (_("Firmware Utility"));
3094 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003095 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003096 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3097 if (!ret) {
3098 /* TRANSLATORS: the user didn't read the man page */
3099 g_print ("%s: %s\n", _("Failed to parse arguments"),
3100 error->message);
3101 return EXIT_FAILURE;
3102 }
3103
Richard Hughes0e46b222019-09-05 12:13:35 +01003104 /* allow disabling SSL strict mode for broken corporate proxies */
3105 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003106 g_autofree gchar *fmt = NULL;
3107 /* TRANSLATORS: this is a prefix on the console */
3108 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003109 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003110 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3111 "to do this automatically in the future "
3112 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003113 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3114 }
3115
Richard Hughes747f5702019-08-06 14:27:26 +01003116 /* parse filter flags */
3117 if (filter != NULL) {
3118 if (!fu_util_parse_filter_flags (filter,
3119 &priv->filter_include,
3120 &priv->filter_exclude,
3121 &error)) {
3122 /* TRANSLATORS: the user didn't read the man page */
3123 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3124 error->message);
3125 return EXIT_FAILURE;
3126 }
3127 }
3128
3129
Richard Hughes460226a2018-05-21 20:56:21 +01003130 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003131 if (allow_reinstall)
3132 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3133 if (allow_older)
3134 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003135 if (allow_branch_switch)
3136 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3137 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003138 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003139 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3140 }
3141 if (ignore_checksum)
3142 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3143 if (ignore_vid_pid)
3144 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3145 if (ignore_power)
3146 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003147
Richard Hughes98ca9932018-05-18 10:24:07 +01003148 /* load engine */
3149 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3150 g_signal_connect (priv->engine, "device-added",
3151 G_CALLBACK (fu_main_engine_device_added_cb),
3152 priv);
3153 g_signal_connect (priv->engine, "device-removed",
3154 G_CALLBACK (fu_main_engine_device_removed_cb),
3155 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003156 g_signal_connect (priv->engine, "status-changed",
3157 G_CALLBACK (fu_main_engine_status_changed_cb),
3158 priv);
3159 g_signal_connect (priv->engine, "percentage-changed",
3160 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3161 priv);
3162
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003163 /* just show versions and exit */
3164 if (version) {
3165 g_autofree gchar *version_str = fu_util_get_versions ();
3166 g_print ("%s\n", version_str);
3167 return EXIT_SUCCESS;
3168 }
3169
Richard Hughes85226fd2020-06-30 14:43:48 +01003170 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003171 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3172 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3173
Richard Hughesb5976832018-05-18 10:02:09 +01003174 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003175 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003176 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003177 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003178 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003179 /* TRANSLATORS: error message explaining command to run to how to get help */
3180 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3181 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3182 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003183 return EXIT_NOTHING_TO_DO;
3184 }
Richard Hughesb5976832018-05-18 10:02:09 +01003185 return EXIT_FAILURE;
3186 }
3187
3188 /* success */
3189 return EXIT_SUCCESS;
3190}