blob: b97eae9ced1bd3368c9023669ba8969a2bfe8e41 [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 */
Richard Hughesda7b8982021-01-26 21:37:11 +0000875 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughesa58510b2019-10-30 10:03:12 +0000876 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;
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001100 GPtrArray *locations;
Mario Limonciellofd734852019-08-01 16:41:42 -05001101 const gchar *remote_id;
1102 const gchar *uri_tmp;
1103 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001104
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001105 /* get the default release only until other parts of fwupd can cope */
1106 locations = fwupd_release_get_locations (rel);
1107 if (locations->len == 0) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001108 g_set_error_literal (error,
1109 FWUPD_ERROR,
1110 FWUPD_ERROR_INVALID_FILE,
1111 "release missing URI");
1112 return FALSE;
1113 }
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001114 uri_tmp = g_ptr_array_index (locations, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001115 remote_id = fwupd_release_get_remote_id (rel);
1116 if (remote_id == NULL) {
1117 g_set_error (error,
1118 FWUPD_ERROR,
1119 FWUPD_ERROR_INVALID_FILE,
1120 "failed to find remote for %s",
1121 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001122 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001123 }
1124
1125 remote = fu_engine_get_remote_by_id (priv->engine,
1126 remote_id,
1127 error);
1128 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001129 return FALSE;
1130
Mario Limonciellofd734852019-08-01 16:41:42 -05001131 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001132 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001133 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1134 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001135 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1136 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1137 argv[0] = g_build_filename (path, uri_tmp, NULL);
1138 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1139 argv[0] = g_strdup (uri_tmp + 7);
1140 /* web remote, fu_util_install will download file */
1141 } else {
1142 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1143 }
1144 return fu_util_install (priv, argv, error);
1145}
1146
1147static gboolean
1148fu_util_update_all (FuUtilPrivate *priv, GError **error)
1149{
1150 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001151 gboolean no_updates_header = FALSE;
1152 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001153
Mario Limonciello46aaee82019-01-10 12:58:00 -06001154 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001155 if (devices == NULL)
1156 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001157 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001158 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001159 for (guint i = 0; i < devices->len; i++) {
1160 FwupdDevice *dev = g_ptr_array_index (devices, i);
1161 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001162 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001163 g_autoptr(GPtrArray) rels = NULL;
1164 g_autoptr(GError) error_local = NULL;
1165
1166 if (!fu_util_is_interesting_device (dev))
1167 continue;
1168 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001169 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001170 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001171 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001172 if (!no_updates_header) {
1173 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1174 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1175 no_updates_header = TRUE;
1176 }
1177 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001178 continue;
1179 }
Richard Hughes747f5702019-08-06 14:27:26 +01001180 if (!fu_util_filter_device (priv, dev))
1181 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001182
1183 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001184 rels = fu_engine_get_upgrades (priv->engine,
1185 priv->request,
1186 device_id,
1187 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001188 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001189 if (!latest_header) {
1190 /* TRANSLATORS: message letting the user know no device upgrade available */
1191 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1192 latest_header = TRUE;
1193 }
1194 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001195 /* discard the actual reason from user, but leave for debugging */
1196 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001197 continue;
1198 }
1199
Mario Limonciello98b95162019-10-30 09:20:43 -05001200 if (!priv->no_safety_check) {
1201 if (!fu_util_prompt_warning (dev,
1202 fu_util_get_tree_title (priv),
1203 error))
1204 return FALSE;
1205 }
1206
Mario Limonciello46aaee82019-01-10 12:58:00 -06001207 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001208 if (!fu_util_install_release (priv, rel, &error_local)) {
1209 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001210 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001211 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001212 fu_util_display_current_message (priv);
1213 }
1214 return TRUE;
1215}
1216
1217static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001218fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001219{
1220 FwupdRelease *rel;
1221 g_autoptr(FuDevice) dev = NULL;
1222 g_autoptr(GPtrArray) rels = NULL;
1223
Mario Limonciello9917bb42020-04-20 13:41:27 -05001224 /* do not allow a partial device-id, lookup GUIDs */
1225 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001226 if (dev == NULL)
1227 return FALSE;
1228
1229 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001230 rels = fu_engine_get_upgrades (priv->engine,
1231 priv->request,
1232 fu_device_get_id (dev),
1233 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001234 if (rels == NULL)
1235 return FALSE;
1236 rel = g_ptr_array_index (rels, 0);
1237 if (!fu_util_install_release (priv, rel, error))
1238 return FALSE;
1239 fu_util_display_current_message (priv);
1240
1241 return TRUE;
1242}
1243
1244static gboolean
1245fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1246{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001247 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1248 g_set_error_literal (error,
1249 FWUPD_ERROR,
1250 FWUPD_ERROR_INVALID_ARGS,
1251 "--allow-older is not supported for this command");
1252 return FALSE;
1253 }
1254
1255 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1256 g_set_error_literal (error,
1257 FWUPD_ERROR,
1258 FWUPD_ERROR_INVALID_ARGS,
1259 "--allow-reinstall is not supported for this command");
1260 return FALSE;
1261 }
1262
Mario Limonciellofd734852019-08-01 16:41:42 -05001263 if (g_strv_length (values) > 1) {
1264 g_set_error_literal (error,
1265 FWUPD_ERROR,
1266 FWUPD_ERROR_INVALID_ARGS,
1267 "Invalid arguments");
1268 return FALSE;
1269 }
1270
Richard Hughesc7d870a2020-12-10 10:05:35 +00001271 if (!fu_util_start_engine (priv,
1272 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1273 FU_ENGINE_LOAD_FLAG_HWINFO |
1274 FU_ENGINE_LOAD_FLAG_REMOTES,
1275 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001276 return FALSE;
1277
1278 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1279 g_signal_connect (priv->engine, "device-changed",
1280 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1281
1282 if (g_strv_length (values) == 1) {
1283 if (!fu_util_update_by_id (priv, values[0], error))
1284 return FALSE;
1285 } else {
1286 if (!fu_util_update_all (priv, error))
1287 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001288 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001289
1290 /* we don't want to ask anything */
1291 if (priv->no_reboot_check) {
1292 g_debug ("skipping reboot check");
1293 return TRUE;
1294 }
1295
Mario Limonciello3143bad2019-02-27 07:31:00 -06001296 /* save the device state for other applications to see */
1297 if (!fu_util_save_current_state (priv, error))
1298 return FALSE;
1299
Mario Limonciello3f243a92019-01-21 22:05:23 -06001300 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001301}
1302
1303static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001304fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1305{
1306 g_autoptr(FwupdRelease) rel = NULL;
1307 g_autoptr(GPtrArray) rels = NULL;
1308 g_autoptr(FuDevice) dev = NULL;
1309
1310 if (g_strv_length (values) != 1) {
1311 g_set_error_literal (error,
1312 FWUPD_ERROR,
1313 FWUPD_ERROR_INVALID_ARGS,
1314 "Invalid arguments");
1315 return FALSE;
1316 }
1317
Richard Hughesc7d870a2020-12-10 10:05:35 +00001318 if (!fu_util_start_engine (priv,
1319 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1320 FU_ENGINE_LOAD_FLAG_HWINFO |
1321 FU_ENGINE_LOAD_FLAG_REMOTES,
1322 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001323 return FALSE;
1324
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001325 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001326 if (dev == NULL)
1327 return FALSE;
1328
1329 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001330 rels = fu_engine_get_releases_for_device (priv->engine,
1331 priv->request,
1332 dev,
1333 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001334 if (rels == NULL)
1335 return FALSE;
1336
1337 for (guint j = 0; j < rels->len; j++) {
1338 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1339 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1340 fu_device_get_version (dev),
1341 fu_device_get_version_format (dev)) == 0) {
1342 rel = g_object_ref (rel_tmp);
1343 break;
1344 }
1345 }
1346 if (rel == NULL) {
1347 g_set_error (error,
1348 FWUPD_ERROR,
1349 FWUPD_ERROR_NOT_SUPPORTED,
1350 "Unable to locate release for %s version %s",
1351 fu_device_get_name (dev),
1352 fu_device_get_version (dev));
1353 return FALSE;
1354 }
1355
1356 /* update the console if composite devices are also updated */
1357 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1358 g_signal_connect (priv->engine, "device-changed",
1359 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1360 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1361 if (!fu_util_install_release (priv, rel, error))
1362 return FALSE;
1363 fu_util_display_current_message (priv);
1364
1365 /* we don't want to ask anything */
1366 if (priv->no_reboot_check) {
1367 g_debug ("skipping reboot check");
1368 return TRUE;
1369 }
1370
1371 /* save the device state for other applications to see */
1372 if (!fu_util_save_current_state (priv, error))
1373 return FALSE;
1374
1375 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1376}
1377
1378static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001379fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1380{
1381 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001382 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001383
1384 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001385 if (!fu_util_start_engine (priv,
1386 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1387 FU_ENGINE_LOAD_FLAG_HWINFO |
1388 FU_ENGINE_LOAD_FLAG_REMOTES,
1389 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001390 return FALSE;
1391
Richard Hughes98ca9932018-05-18 10:24:07 +01001392 /* get device */
1393 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001394 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001395 if (device == NULL)
1396 return FALSE;
1397 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001398 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001399 if (device == NULL)
1400 return FALSE;
1401 }
1402
1403 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001404 locker = fu_device_locker_new (device, error);
1405 if (locker == NULL)
1406 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001407 return fu_device_detach (device, error);
1408}
1409
1410static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001411fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1412{
1413 g_autoptr(FuDevice) device = NULL;
1414 g_autoptr(FuDeviceLocker) locker = NULL;
1415
1416 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001417 if (!fu_util_start_engine (priv,
1418 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1419 FU_ENGINE_LOAD_FLAG_HWINFO |
1420 FU_ENGINE_LOAD_FLAG_REMOTES,
1421 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001422 return FALSE;
1423
1424 /* get device */
1425 if (g_strv_length (values) == 1) {
1426 device = fu_util_get_device (priv, values[0], error);
1427 } else {
1428 device = fu_util_prompt_for_device (priv, NULL, error);
1429 }
1430 if (device == NULL)
1431 return FALSE;
1432
1433 /* run vfunc */
1434 locker = fu_device_locker_new (device, error);
1435 if (locker == NULL)
1436 return FALSE;
1437 return fu_device_unbind_driver (device, error);
1438}
1439
1440static gboolean
1441fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1442{
1443 g_autoptr(FuDevice) device = NULL;
1444 g_autoptr(FuDeviceLocker) locker = NULL;
1445
1446 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001447 if (!fu_util_start_engine (priv,
1448 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1449 FU_ENGINE_LOAD_FLAG_HWINFO |
1450 FU_ENGINE_LOAD_FLAG_REMOTES,
1451 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001452 return FALSE;
1453
1454 /* get device */
1455 if (g_strv_length (values) == 3) {
1456 device = fu_util_get_device (priv, values[2], error);
1457 if (device == NULL)
1458 return FALSE;
1459 } else if (g_strv_length (values) == 2) {
1460 device = fu_util_prompt_for_device (priv, NULL, error);
1461 if (device == NULL)
1462 return FALSE;
1463 } else {
1464 g_set_error_literal (error,
1465 FWUPD_ERROR,
1466 FWUPD_ERROR_INVALID_ARGS,
1467 "Invalid arguments");
1468 return FALSE;
1469 }
1470
1471 /* run vfunc */
1472 locker = fu_device_locker_new (device, error);
1473 if (locker == NULL)
1474 return FALSE;
1475 return fu_device_bind_driver (device, values[0], values[1], error);
1476}
1477
1478static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001479fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1480{
1481 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001482 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001483
1484 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001485 if (!fu_util_start_engine (priv,
1486 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1487 FU_ENGINE_LOAD_FLAG_HWINFO |
1488 FU_ENGINE_LOAD_FLAG_REMOTES,
1489 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001490 return FALSE;
1491
Richard Hughes98ca9932018-05-18 10:24:07 +01001492 /* get device */
1493 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001494 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001495 if (device == NULL)
1496 return FALSE;
1497 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001498 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001499 if (device == NULL)
1500 return FALSE;
1501 }
1502
1503 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001504 locker = fu_device_locker_new (device, error);
1505 if (locker == NULL)
1506 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001507 return fu_device_attach (device, error);
1508}
1509
Richard Hughes1d894f12018-08-31 13:05:51 +01001510static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001511fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001512{
1513 gboolean has_pending = FALSE;
1514 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001515 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1516 if (devices == NULL)
1517 return FALSE;
1518
1519 /* only start up the plugins needed */
1520 for (guint i = 0; i < devices->len; i++) {
1521 FuDevice *dev = g_ptr_array_index (devices, i);
1522 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1523 fu_engine_add_plugin_filter (priv->engine,
1524 fu_device_get_plugin (dev));
1525 has_pending = TRUE;
1526 }
1527 }
1528
1529 if (!has_pending) {
1530 g_set_error_literal (error,
1531 FWUPD_ERROR,
1532 FWUPD_ERROR_NOTHING_TO_DO,
1533 "No devices to activate");
1534 return FALSE;
1535 }
1536
1537 return TRUE;
1538}
1539
1540static gboolean
1541fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1542{
1543 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001544 g_autoptr(GPtrArray) devices = NULL;
1545
1546 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001547 if (!fu_util_check_activation_needed (priv, error))
1548 return FALSE;
1549
1550 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001551 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY, error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001552 return FALSE;
1553
1554 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001555 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001556 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001557 if (devices == NULL)
1558 return FALSE;
1559 } else if (g_strv_length (values) == 1) {
1560 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001561 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001562 if (device == NULL)
1563 return FALSE;
1564 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1565 g_ptr_array_add (devices, device);
1566 } else {
1567 g_set_error_literal (error,
1568 FWUPD_ERROR,
1569 FWUPD_ERROR_INVALID_ARGS,
1570 "Invalid arguments");
1571 return FALSE;
1572 }
1573
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001574 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001575 /* order by device priority */
1576 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001577 for (guint i = 0; i < devices->len; i++) {
1578 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001579 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1580 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001581 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1582 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001583 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001584 /* TRANSLATORS: shown when shutting down to switch to the new version */
1585 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1586 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1587 return FALSE;
1588 }
1589
Mario Limonciello02085a02020-09-11 14:59:35 -05001590 if (!has_pending) {
1591 g_set_error_literal (error,
1592 FWUPD_ERROR,
1593 FWUPD_ERROR_NOTHING_TO_DO,
1594 "No devices to activate");
1595 return FALSE;
1596 }
1597
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001598 return TRUE;
1599}
1600
1601static gboolean
Richard Hughesf6751cd2021-01-15 16:48:25 +00001602fu_util_export_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1603{
1604 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1605 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1606 g_autoptr(GKeyFile) kf = g_key_file_new ();
1607 g_autoptr(GPtrArray) hwid_keys = NULL;
1608
1609 /* check args */
1610 if (g_strv_length (values) != 1) {
1611 g_set_error_literal (error,
1612 FWUPD_ERROR,
1613 FWUPD_ERROR_INVALID_ARGS,
1614 "Invalid arguments, expected HWIDS-FILE");
1615 return FALSE;
1616 }
1617
1618 /* setup default hwids */
1619 if (!fu_smbios_setup (smbios, error))
1620 return FALSE;
1621 if (!fu_hwids_setup (hwids, smbios, error))
1622 return FALSE;
1623
1624 /* save all keys */
1625 hwid_keys = fu_hwids_get_keys (hwids);
1626 for (guint i = 0; i < hwid_keys->len; i++) {
1627 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1628 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1629 g_key_file_set_string (kf, "HwIds", hwid_key, value);
1630 }
1631
1632 /* success */
1633 return g_key_file_save_to_file (kf, values[0], error);
1634}
1635
1636static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001637fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1638{
Richard Hughesf6751cd2021-01-15 16:48:25 +00001639 g_autoptr(FuSmbios) smbios = NULL;
Richard Hughes1d894f12018-08-31 13:05:51 +01001640 g_autoptr(FuHwids) hwids = fu_hwids_new ();
Richard Hughesf6751cd2021-01-15 16:48:25 +00001641 g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys (hwids);
Richard Hughes1d894f12018-08-31 13:05:51 +01001642
1643 /* read DMI data */
1644 if (g_strv_length (values) == 0) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001645 smbios = fu_smbios_new ();
Richard Hughes1d894f12018-08-31 13:05:51 +01001646 if (!fu_smbios_setup (smbios, error))
1647 return FALSE;
1648 } else if (g_strv_length (values) == 1) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001649 /* a keyfile with overrides */
1650 g_autoptr(GKeyFile) kf = g_key_file_new ();
1651 if (g_key_file_load_from_file (kf, values[0], G_KEY_FILE_NONE, NULL)) {
1652 for (guint i = 0; i < hwid_keys->len; i++) {
1653 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1654 g_autofree gchar *tmp = NULL;
1655 tmp = g_key_file_get_string (kf, "HwIds", hwid_key, NULL);
1656 fu_hwids_add_smbios_override (hwids, hwid_key, tmp);
1657 }
1658 /* a DMI blob */
1659 } else {
1660 smbios = fu_smbios_new ();
1661 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1662 return FALSE;
1663 }
Richard Hughes1d894f12018-08-31 13:05:51 +01001664 } else {
1665 g_set_error_literal (error,
1666 FWUPD_ERROR,
1667 FWUPD_ERROR_INVALID_ARGS,
1668 "Invalid arguments");
1669 return FALSE;
1670 }
1671 if (!fu_hwids_setup (hwids, smbios, error))
1672 return FALSE;
1673
1674 /* show debug output */
1675 g_print ("Computer Information\n");
1676 g_print ("--------------------\n");
Richard Hughesf6751cd2021-01-15 16:48:25 +00001677 for (guint i = 0; i < hwid_keys->len; i++) {
1678 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1679 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1680 if (value == NULL)
Richard Hughes1d894f12018-08-31 13:05:51 +01001681 continue;
Richard Hughesf6751cd2021-01-15 16:48:25 +00001682 if (g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1683 g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1684 guint64 val = g_ascii_strtoull (value, NULL, 16);
1685 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
Richard Hughes1d894f12018-08-31 13:05:51 +01001686 } else {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001687 g_print ("%s: %s\n", hwid_key, value);
Richard Hughes1d894f12018-08-31 13:05:51 +01001688 }
1689 }
1690
1691 /* show GUIDs */
1692 g_print ("\nHardware IDs\n");
1693 g_print ("------------\n");
1694 for (guint i = 0; i < 15; i++) {
1695 const gchar *keys = NULL;
1696 g_autofree gchar *guid = NULL;
1697 g_autofree gchar *key = NULL;
1698 g_autofree gchar *keys_str = NULL;
1699 g_auto(GStrv) keysv = NULL;
1700 g_autoptr(GError) error_local = NULL;
1701
1702 /* get the GUID */
1703 key = g_strdup_printf ("HardwareID-%u", i);
1704 keys = fu_hwids_get_replace_keys (hwids, key);
1705 guid = fu_hwids_get_guid (hwids, key, &error_local);
1706 if (guid == NULL) {
1707 g_print ("%s\n", error_local->message);
1708 continue;
1709 }
1710
1711 /* show what makes up the GUID */
1712 keysv = g_strsplit (keys, "&", -1);
1713 keys_str = g_strjoinv (" + ", keysv);
1714 g_print ("{%s} <- %s\n", guid, keys_str);
1715 }
1716
1717 return TRUE;
1718}
1719
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001720static gboolean
1721fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1722{
1723 const gchar *script_fn = "startup.sh";
1724 const gchar *output_fn = "firmware.bin";
1725 g_autoptr(GBytes) archive_blob = NULL;
1726 g_autoptr(GBytes) firmware_blob = NULL;
1727 if (g_strv_length (values) < 2) {
1728 g_set_error_literal (error,
1729 FWUPD_ERROR,
1730 FWUPD_ERROR_INVALID_ARGS,
1731 "Invalid arguments");
1732 return FALSE;
1733 }
1734 archive_blob = fu_common_get_contents_bytes (values[0], error);
1735 if (archive_blob == NULL)
1736 return FALSE;
1737 if (g_strv_length (values) > 2)
1738 script_fn = values[2];
1739 if (g_strv_length (values) > 3)
1740 output_fn = values[3];
1741 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1742 if (firmware_blob == NULL)
1743 return FALSE;
1744 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1745}
1746
Richard Hughes3d607622019-03-07 16:59:27 +00001747static gboolean
1748fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1749{
1750 g_autofree gchar *sig = NULL;
1751
1752 /* check args */
1753 if (g_strv_length (values) != 1) {
1754 g_set_error_literal (error,
1755 FWUPD_ERROR,
1756 FWUPD_ERROR_INVALID_ARGS,
1757 "Invalid arguments: value expected");
1758 return FALSE;
1759 }
1760
1761 /* start engine */
1762 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1763 return FALSE;
1764 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001765 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1766 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001767 if (sig == NULL)
1768 return FALSE;
1769 g_print ("%s\n", sig);
1770 return TRUE;
1771}
1772
Mario Limonciello62f84862018-10-18 13:15:23 -05001773static void
1774fu_util_device_added_cb (FwupdClient *client,
1775 FwupdDevice *device,
1776 gpointer user_data)
1777{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001778 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001779 /* TRANSLATORS: this is when a device is hotplugged */
1780 g_print ("%s\n%s", _("Device added:"), tmp);
1781}
1782
1783static void
1784fu_util_device_removed_cb (FwupdClient *client,
1785 FwupdDevice *device,
1786 gpointer user_data)
1787{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001788 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001789 /* TRANSLATORS: this is when a device is hotplugged */
1790 g_print ("%s\n%s", _("Device removed:"), tmp);
1791}
1792
1793static void
1794fu_util_device_changed_cb (FwupdClient *client,
1795 FwupdDevice *device,
1796 gpointer user_data)
1797{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001798 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001799 /* TRANSLATORS: this is when a device has been updated */
1800 g_print ("%s\n%s", _("Device changed:"), tmp);
1801}
1802
1803static void
1804fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1805{
1806 /* TRANSLATORS: this is when the daemon state changes */
1807 g_print ("%s\n", _("Changed"));
1808}
1809
1810static gboolean
1811fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1812{
1813 g_autoptr(FwupdClient) client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00001814 fwupd_client_set_main_context (client, priv->main_ctx);
Mario Limonciello62f84862018-10-18 13:15:23 -05001815
1816 /* get all the devices */
1817 if (!fwupd_client_connect (client, priv->cancellable, error))
1818 return FALSE;
1819
1820 /* watch for any hotplugged device */
1821 g_signal_connect (client, "changed",
1822 G_CALLBACK (fu_util_changed_cb), priv);
1823 g_signal_connect (client, "device-added",
1824 G_CALLBACK (fu_util_device_added_cb), priv);
1825 g_signal_connect (client, "device-removed",
1826 G_CALLBACK (fu_util_device_removed_cb), priv);
1827 g_signal_connect (client, "device-changed",
1828 G_CALLBACK (fu_util_device_changed_cb), priv);
1829 g_signal_connect (priv->cancellable, "cancelled",
1830 G_CALLBACK (fu_util_cancelled_cb), priv);
1831 g_main_loop_run (priv->loop);
1832 return TRUE;
1833}
1834
Richard Hughes15684492019-03-15 16:27:50 +00001835static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001836fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1837{
1838 g_autoptr(GPtrArray) firmware_types = NULL;
1839
1840 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001841 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001842 return FALSE;
1843
1844 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1845 for (guint i = 0; i < firmware_types->len; i++) {
1846 const gchar *id = g_ptr_array_index (firmware_types, i);
1847 g_print ("%s\n", id);
1848 }
1849 if (firmware_types->len == 0) {
1850 /* TRANSLATORS: nothing found */
1851 g_print ("%s\n", _("No firmware IDs found"));
1852 return TRUE;
1853 }
1854
1855 return TRUE;
1856}
1857
1858static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001859fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001860{
1861 g_autoptr(GPtrArray) firmware_types = NULL;
1862 guint idx;
1863 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1864
1865 /* TRANSLATORS: get interactive prompt */
1866 g_print ("%s\n", _("Choose a firmware type:"));
1867 /* TRANSLATORS: this is to abort the interactive prompt */
1868 g_print ("0.\t%s\n", _("Cancel"));
1869 for (guint i = 0; i < firmware_types->len; i++) {
1870 const gchar *id = g_ptr_array_index (firmware_types, i);
1871 g_print ("%u.\t%s\n", i + 1, id);
1872 }
1873 idx = fu_util_prompt_for_number (firmware_types->len);
1874 if (idx == 0) {
1875 g_set_error_literal (error,
1876 FWUPD_ERROR,
1877 FWUPD_ERROR_NOTHING_TO_DO,
1878 "Request canceled");
1879 return NULL;
1880 }
1881
1882 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1883}
1884
1885static gboolean
1886fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1887{
1888 GType gtype;
1889 g_autoptr(GBytes) blob = NULL;
1890 g_autoptr(FuFirmware) firmware = NULL;
1891 g_autofree gchar *firmware_type = NULL;
1892 g_autofree gchar *str = NULL;
1893
1894 /* check args */
1895 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1896 g_set_error_literal (error,
1897 FWUPD_ERROR,
1898 FWUPD_ERROR_INVALID_ARGS,
1899 "Invalid arguments: filename required");
1900 return FALSE;
1901 }
1902
1903 if (g_strv_length (values) == 2)
1904 firmware_type = g_strdup (values[1]);
1905
1906 /* load file */
1907 blob = fu_common_get_contents_bytes (values[0], error);
1908 if (blob == NULL)
1909 return FALSE;
1910
1911 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001912 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001913 return FALSE;
1914
1915 /* find the GType to use */
1916 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001917 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001918 if (firmware_type == NULL)
1919 return FALSE;
1920 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1921 if (gtype == G_TYPE_INVALID) {
1922 g_set_error (error,
1923 G_IO_ERROR,
1924 G_IO_ERROR_NOT_FOUND,
1925 "GType %s not supported", firmware_type);
1926 return FALSE;
1927 }
1928 firmware = g_object_new (gtype, NULL);
1929 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1930 return FALSE;
1931 str = fu_firmware_to_string (firmware);
1932 g_print ("%s", str);
1933 return TRUE;
1934}
1935
Richard Hughesdd653442020-09-22 10:23:52 +01001936static gboolean
1937fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1938{
1939 GType gtype;
1940 g_autofree gchar *firmware_type = NULL;
1941 g_autofree gchar *str = NULL;
1942 g_autoptr(FuFirmware) firmware = NULL;
1943 g_autoptr(GBytes) blob = NULL;
1944 g_autoptr(GPtrArray) images = NULL;
1945
1946 /* check args */
1947 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1948 g_set_error_literal (error,
1949 FWUPD_ERROR,
1950 FWUPD_ERROR_INVALID_ARGS,
1951 "Invalid arguments: filename required");
1952 return FALSE;
1953 }
1954 if (g_strv_length (values) == 2)
1955 firmware_type = g_strdup (values[1]);
1956
1957 /* load file */
1958 blob = fu_common_get_contents_bytes (values[0], error);
1959 if (blob == NULL)
1960 return FALSE;
1961
1962 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001963 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01001964 return FALSE;
1965
1966 /* find the GType to use */
1967 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001968 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001969 if (firmware_type == NULL)
1970 return FALSE;
1971 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1972 if (gtype == G_TYPE_INVALID) {
1973 g_set_error (error,
1974 G_IO_ERROR,
1975 G_IO_ERROR_NOT_FOUND,
1976 "GType %s not supported", firmware_type);
1977 return FALSE;
1978 }
1979 firmware = g_object_new (gtype, NULL);
1980 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1981 return FALSE;
1982 str = fu_firmware_to_string (firmware);
1983 g_print ("%s", str);
1984 images = fu_firmware_get_images (firmware);
1985 for (guint i = 0; i < images->len; i++) {
1986 FuFirmwareImage *img = g_ptr_array_index (images, i);
1987 g_autofree gchar *fn = NULL;
1988 g_autoptr(GBytes) blob_img = NULL;
1989
Richard Hughes88dd7c42020-09-22 16:54:40 +01001990 /* get raw image without generated header, footer or crc */
1991 blob_img = fu_firmware_image_get_bytes (img);
1992 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1993 continue;
1994
Richard Hughesdd653442020-09-22 10:23:52 +01001995 /* use suitable filename */
1996 if (fu_firmware_image_get_filename (img) != NULL) {
1997 fn = g_strdup (fu_firmware_image_get_filename (img));
1998 } else if (fu_firmware_image_get_id (img) != NULL) {
1999 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
2000 } else if (fu_firmware_image_get_idx (img) != 0x0) {
2001 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
2002 } else {
2003 fn = g_strdup_printf ("img-0x%x.fw", i);
2004 }
2005 /* TRANSLATORS: decompressing images from a container firmware */
2006 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01002007 if (!fu_common_set_contents_bytes (fn, blob_img, error))
2008 return FALSE;
2009 }
2010
2011 /* success */
2012 return TRUE;
2013}
2014
Richard Hughes0924c932020-09-22 19:07:05 +01002015static gboolean
2016fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01002017{
Richard Hughes0924c932020-09-22 19:07:05 +01002018 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01002019 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01002020 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002021 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01002022 g_autoptr(FuFirmware) firmware_dst = NULL;
2023 g_autoptr(GBytes) blob_dst = NULL;
2024 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002025 g_autoptr(XbBuilder) builder = xb_builder_new ();
2026 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2027 g_autoptr(XbNode) n = NULL;
2028 g_autoptr(XbSilo) silo = NULL;
2029
Richard Hughes0924c932020-09-22 19:07:05 +01002030 /* check args */
2031 if (g_strv_length (values) != 2) {
2032 g_set_error_literal (error,
2033 FWUPD_ERROR,
2034 FWUPD_ERROR_INVALID_ARGS,
2035 "Invalid arguments: filename required");
2036 return FALSE;
2037 }
2038
2039 /* load file */
2040 blob_src = fu_common_get_contents_bytes (values[0], error);
2041 if (blob_src == NULL)
2042 return FALSE;
2043
2044 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002045 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002046 return FALSE;
2047
Richard Hughes41400a82020-09-21 13:43:15 +01002048 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002049 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002050 XB_BUILDER_SOURCE_FLAG_NONE,
2051 error)) {
2052 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002053 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002054 }
2055 xb_builder_import_source (builder, source);
2056 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2057 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002058 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002059
2060 /* create FuFirmware of specific GType */
2061 n = xb_silo_query_first (silo, "firmware", error);
2062 if (n == NULL)
2063 return FALSE;
2064 tmp = xb_node_get_attr (n, "gtype");
2065 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002066 gtype = g_type_from_name (tmp);
2067 if (gtype == G_TYPE_INVALID) {
2068 g_set_error (error,
2069 G_IO_ERROR,
2070 G_IO_ERROR_NOT_FOUND,
2071 "GType %s not registered", tmp);
2072 return FALSE;
2073 }
2074 }
2075 tmp = xb_node_get_attr (n, "id");
2076 if (tmp != NULL) {
2077 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002078 if (gtype == G_TYPE_INVALID) {
2079 g_set_error (error,
2080 G_IO_ERROR,
2081 G_IO_ERROR_NOT_FOUND,
2082 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002083 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002084 }
Richard Hughes41400a82020-09-21 13:43:15 +01002085 }
Richard Hughes0924c932020-09-22 19:07:05 +01002086 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002087 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002088 return FALSE;
2089
2090 /* write new file */
2091 blob_dst = fu_firmware_write (firmware, error);
2092 if (blob_dst == NULL)
2093 return FALSE;
2094 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2095 return FALSE;
2096
2097 /* show what we wrote */
2098 firmware_dst = g_object_new (gtype, NULL);
2099 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2100 return FALSE;
2101 str = fu_firmware_to_string (firmware_dst);
2102 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002103
2104 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002105 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002106}
2107
Richard Hughes95c98a92019-10-22 16:03:15 +01002108static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002109fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2110{
2111 GType gtype_dst;
2112 GType gtype_src;
2113 g_autofree gchar *firmware_type_dst = NULL;
2114 g_autofree gchar *firmware_type_src = NULL;
2115 g_autofree gchar *str_dst = NULL;
2116 g_autofree gchar *str_src = NULL;
2117 g_autoptr(FuFirmware) firmware_dst = NULL;
2118 g_autoptr(FuFirmware) firmware_src = NULL;
2119 g_autoptr(GBytes) blob_dst = NULL;
2120 g_autoptr(GBytes) blob_src = NULL;
2121 g_autoptr(GPtrArray) images = NULL;
2122
2123 /* check args */
2124 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2125 g_set_error_literal (error,
2126 FWUPD_ERROR,
2127 FWUPD_ERROR_INVALID_ARGS,
2128 "Invalid arguments: filename required");
2129 return FALSE;
2130 }
2131
2132 if (g_strv_length (values) > 2)
2133 firmware_type_src = g_strdup (values[2]);
2134 if (g_strv_length (values) > 3)
2135 firmware_type_dst = g_strdup (values[3]);
2136
2137 /* load file */
2138 blob_src = fu_common_get_contents_bytes (values[0], error);
2139 if (blob_src == NULL)
2140 return FALSE;
2141
2142 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002143 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002144 return FALSE;
2145
2146 /* find the GType to use */
2147 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002148 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002149 if (firmware_type_src == NULL)
2150 return FALSE;
2151 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002152 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002153 if (firmware_type_dst == NULL)
2154 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002155 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2156 if (gtype_src == G_TYPE_INVALID) {
2157 g_set_error (error,
2158 G_IO_ERROR,
2159 G_IO_ERROR_NOT_FOUND,
2160 "GType %s not supported", firmware_type_src);
2161 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002162 }
Richard Hughes0924c932020-09-22 19:07:05 +01002163 firmware_src = g_object_new (gtype_src, NULL);
2164 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2165 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002166 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2167 if (gtype_dst == G_TYPE_INVALID) {
2168 g_set_error (error,
2169 G_IO_ERROR,
2170 G_IO_ERROR_NOT_FOUND,
2171 "GType %s not supported", firmware_type_dst);
2172 return FALSE;
2173 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002174 str_src = fu_firmware_to_string (firmware_src);
2175 g_print ("%s", str_src);
2176
2177 /* copy images */
2178 firmware_dst = g_object_new (gtype_dst, NULL);
2179 images = fu_firmware_get_images (firmware_src);
2180 for (guint i = 0; i < images->len; i++) {
2181 FuFirmwareImage *img = g_ptr_array_index (images, i);
2182 fu_firmware_add_image (firmware_dst, img);
2183 }
2184
2185 /* write new file */
2186 blob_dst = fu_firmware_write (firmware_dst, error);
2187 if (blob_dst == NULL)
2188 return FALSE;
2189 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2190 return FALSE;
2191 str_dst = fu_firmware_to_string (firmware_dst);
2192 g_print ("%s", str_dst);
2193
2194 /* success */
2195 return TRUE;
2196}
2197
2198static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002199fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2200{
2201 g_autofree gchar *str = NULL;
2202 g_autoptr(FuDevice) dev = NULL;
2203
2204 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002205 if (!fu_util_start_engine (priv,
2206 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2207 FU_ENGINE_LOAD_FLAG_HWINFO |
2208 FU_ENGINE_LOAD_FLAG_REMOTES,
2209 error))
Richard Hughes15684492019-03-15 16:27:50 +00002210 return FALSE;
2211
2212 /* get device */
2213 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002214 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002215 if (dev == NULL)
2216 return FALSE;
2217 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002218 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002219 if (dev == NULL)
2220 return FALSE;
2221 }
2222
2223 /* add checksums */
2224 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2225 return FALSE;
2226
2227 /* show checksums */
2228 str = fu_device_to_string (dev);
2229 g_print ("%s\n", str);
2230 return TRUE;
2231}
2232
Mario Limonciellofe593942019-04-03 13:48:52 -05002233static gboolean
2234fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2235{
2236 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002237 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002238 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002239
2240 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002241 if (!fu_util_start_engine (priv,
2242 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2243 FU_ENGINE_LOAD_FLAG_HWINFO |
2244 FU_ENGINE_LOAD_FLAG_REMOTES,
2245 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002246 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002247 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002248
2249 /* get all devices from the history database */
2250 devices = fu_engine_get_history (priv->engine, error);
2251 if (devices == NULL)
2252 return FALSE;
2253
2254 /* show each device */
2255 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002256 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002257 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002258 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002259 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002260 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002261
Richard Hughes747f5702019-08-06 14:27:26 +01002262 if (!fu_util_filter_device (priv, dev))
2263 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002264 child = g_node_append_data (root, dev);
2265
2266 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002267 if (rel == NULL)
2268 continue;
2269 remote = fwupd_release_get_remote_id (rel);
2270
2271 /* doesn't actually map to remote */
2272 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002273 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002274 continue;
2275 }
2276
2277 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002278 rels = fu_engine_get_releases (priv->engine,
2279 priv->request,
2280 fwupd_device_get_id (dev),
2281 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002282 if (rels == NULL)
2283 return FALSE;
2284
2285 /* map to a release in client */
2286 for (guint j = 0; j < rels->len; j++) {
2287 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2288 if (g_strcmp0 (remote,
2289 fwupd_release_get_remote_id (rel2)) != 0)
2290 continue;
2291 if (g_strcmp0 (fwupd_release_get_version (rel),
2292 fwupd_release_get_version (rel2)) != 0)
2293 continue;
2294 g_node_append_data (child, g_object_ref (rel2));
2295 rel = NULL;
2296 break;
2297 }
2298
2299 /* didn't match anything */
2300 if (rels->len == 0 || rel != NULL) {
2301 g_node_append_data (child, rel);
2302 continue;
2303 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002304
Mario Limonciellofe593942019-04-03 13:48:52 -05002305 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002306 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002307
2308 return TRUE;
2309}
2310
Richard Hughesfd7e9942020-01-17 14:09:13 +00002311static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002312fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2313{
Richard Hughesc5710d92020-06-26 11:08:25 +01002314 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002315 g_autofree gchar *fn_raw = NULL;
2316 g_autofree gchar *fn_sig = NULL;
2317 g_autoptr(GBytes) bytes_raw = NULL;
2318 g_autoptr(GBytes) bytes_sig = NULL;
2319
Richard Hughes4959baa2020-01-17 14:33:00 +00002320 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002321 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2322 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002323 g_set_error (error,
2324 FWUPD_ERROR,
2325 FWUPD_ERROR_NOTHING_TO_DO,
2326 "no metadata signature URI available for %s",
2327 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002328 return FALSE;
2329 }
2330 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002331 if (!fu_common_mkdir_parent (fn_sig, error))
2332 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002333 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002334 return FALSE;
2335 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2336 if (bytes_sig == NULL)
2337 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002338 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2339 return FALSE;
2340
2341 /* payload */
2342 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2343 if (metadata_uri == NULL) {
2344 g_set_error (error,
2345 FWUPD_ERROR,
2346 FWUPD_ERROR_NOTHING_TO_DO,
2347 "no metadata URI available for %s",
2348 fwupd_remote_get_id (remote));
2349 return FALSE;
2350 }
2351 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2352 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2353 return FALSE;
2354 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2355 if (bytes_raw == NULL)
2356 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002357
2358 /* send to daemon */
2359 g_debug ("updating %s", fwupd_remote_get_id (remote));
2360 return fu_engine_update_metadata_bytes (priv->engine,
2361 fwupd_remote_get_id (remote),
2362 bytes_raw,
2363 bytes_sig,
2364 error);
2365
2366}
2367
2368static gboolean
2369fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2370{
2371 g_autoptr(GPtrArray) remotes = NULL;
2372
2373 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002374 if (!fu_util_start_engine (priv,
2375 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2376 FU_ENGINE_LOAD_FLAG_HWINFO |
2377 FU_ENGINE_LOAD_FLAG_REMOTES,
2378 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002379 return FALSE;
2380
2381 /* download new metadata */
2382 remotes = fu_engine_get_remotes (priv->engine, error);
2383 if (remotes == NULL)
2384 return FALSE;
2385 for (guint i = 0; i < remotes->len; i++) {
2386 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2387 if (!fwupd_remote_get_enabled (remote))
2388 continue;
2389 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2390 continue;
2391 if (!fu_util_refresh_remote (priv, remote, error))
2392 return FALSE;
2393 }
2394 return TRUE;
2395}
2396
2397static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002398fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2399{
2400 g_autoptr(GNode) root = g_node_new (NULL);
2401 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002402 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002403
2404 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002405 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002406 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002407 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002408
2409 /* list remotes */
2410 remotes = fu_engine_get_remotes (priv->engine, error);
2411 if (remotes == NULL)
2412 return FALSE;
2413 if (remotes->len == 0) {
2414 g_set_error_literal (error,
2415 FWUPD_ERROR,
2416 FWUPD_ERROR_NOTHING_TO_DO,
2417 "no remotes available");
2418 return FALSE;
2419 }
2420 for (guint i = 0; i < remotes->len; i++) {
2421 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2422 g_node_append_data (root, remote_tmp);
2423 }
2424 fu_util_print_tree (root, title);
2425
2426 return TRUE;
2427}
2428
Richard Hughes196c6c62020-05-11 19:42:47 +01002429static gboolean
2430fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2431{
Richard Hughes5c82b942020-09-14 12:24:06 +01002432 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002433 g_autoptr(FuSecurityAttrs) attrs = NULL;
2434 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002435 g_autofree gchar *str = NULL;
2436
2437 /* not ready yet */
2438 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2439 g_set_error_literal (error,
2440 FWUPD_ERROR,
2441 FWUPD_ERROR_NOT_SUPPORTED,
2442 "The HSI specification is not yet complete. "
2443 "To ignore this warning, use --force");
2444 return FALSE;
2445 }
2446
Richard Hughesc7d870a2020-12-10 10:05:35 +00002447 if (!fu_util_start_engine (priv,
2448 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2449 FU_ENGINE_LOAD_FLAG_HWINFO |
2450 FU_ENGINE_LOAD_FLAG_REMOTES,
2451 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002452 return FALSE;
2453
2454 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2455 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2456 fu_engine_get_host_security_id (priv->engine));
2457
Richard Hughes5c82b942020-09-14 12:24:06 +01002458 /* show or hide different elements */
2459 if (priv->show_all) {
2460 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2461 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2462 }
2463
Richard Hughes196c6c62020-05-11 19:42:47 +01002464 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002465 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002466 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002467 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002468 g_print ("%s\n", str);
2469 return TRUE;
2470}
2471
Richard Hughesa83deb42020-08-12 12:45:36 +01002472static FuVolume *
2473fu_util_prompt_for_volume (GError **error)
2474{
2475 FuVolume *volume;
2476 guint idx;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002477 gboolean is_fallback = FALSE;
Richard Hughesa83deb42020-08-12 12:45:36 +01002478 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002479 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2480 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002481
2482 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002483 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2484 if (volumes == NULL) {
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002485 is_fallback = TRUE;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002486 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2487 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2488 if (volumes == NULL) {
2489 g_prefix_error (error, "%s: ", error_local->message);
2490 return NULL;
2491 }
2492 }
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002493 /* on fallback: only add internal vfat partitions */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002494 for (guint i = 0; i < volumes->len; i++) {
2495 FuVolume *vol = g_ptr_array_index (volumes, i);
2496 g_autofree gchar *type = fu_volume_get_id_type (vol);
2497 if (type == NULL)
2498 continue;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002499 if (is_fallback && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002500 continue;
2501 if (g_strcmp0 (type, "vfat") == 0)
2502 g_ptr_array_add (volumes_vfat, vol);
2503 }
2504 if (volumes_vfat->len == 1) {
2505 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002506 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002507 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2508 return g_object_ref (volume);
2509 }
2510
2511 /* TRANSLATORS: get interactive prompt */
2512 g_print ("%s\n", _("Choose a volume:"));
2513 /* TRANSLATORS: this is to abort the interactive prompt */
2514 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002515 for (guint i = 0; i < volumes_vfat->len; i++) {
2516 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002517 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2518 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002519 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002520 if (idx == 0) {
2521 g_set_error_literal (error,
2522 FWUPD_ERROR,
2523 FWUPD_ERROR_NOTHING_TO_DO,
2524 "Request canceled");
2525 return NULL;
2526 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002527 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002528 return g_object_ref (volume);
2529
2530}
2531
2532static gboolean
2533fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2534{
2535 g_autoptr(FuVolume) volume = NULL;
2536 volume = fu_util_prompt_for_volume (error);
2537 if (volume == NULL)
2538 return FALSE;
2539 return fu_volume_mount (volume, error);
2540}
2541
2542static gboolean
2543fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2544{
2545 g_autoptr(FuVolume) volume = NULL;
2546 volume = fu_util_prompt_for_volume (error);
2547 if (volume == NULL)
2548 return FALSE;
2549 return fu_volume_unmount (volume, error);
2550}
2551
2552static gboolean
2553fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2554{
2555 g_autofree gchar *mount_point = NULL;
2556 g_autoptr(FuDeviceLocker) locker = NULL;
2557 g_autoptr(FuVolume) volume = NULL;
2558 g_autoptr(GPtrArray) files = NULL;
2559
2560 volume = fu_util_prompt_for_volume (error);
2561 if (volume == NULL)
2562 return FALSE;
2563 locker = fu_volume_locker (volume, error);
2564 if (locker == NULL)
2565 return FALSE;
2566 mount_point = fu_volume_get_mount_point (volume);
2567 files = fu_common_get_files_recursive (mount_point, error);
2568 if (files == NULL)
2569 return FALSE;
2570 for (guint i = 0; i < files->len; i++) {
2571 const gchar *fn = g_ptr_array_index (files, i);
2572 g_print ("%s\n", fn);
2573 }
2574 return TRUE;
2575}
2576
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002577static gboolean
2578_g_str_equal0 (gconstpointer str1, gconstpointer str2)
2579{
2580 return g_strcmp0 (str1, str2) == 0;
2581}
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002582
2583static gboolean
2584fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2585{
2586 const gchar *branch;
2587 g_autoptr(FwupdRelease) rel = NULL;
2588 g_autoptr(GPtrArray) rels = NULL;
2589 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2590 g_autoptr(FuDevice) dev = NULL;
2591
2592 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002593 if (!fu_util_start_engine (priv,
2594 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2595 FU_ENGINE_LOAD_FLAG_HWINFO |
2596 FU_ENGINE_LOAD_FLAG_REMOTES,
2597 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002598 return FALSE;
2599
2600 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002601 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00002602 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002603 if (g_strv_length (values) == 1)
2604 dev = fu_util_get_device (priv, values[1], error);
2605 else
2606 dev = fu_util_prompt_for_device (priv, NULL, error);
2607 if (dev == NULL)
2608 return FALSE;
2609 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2610 g_set_error_literal (error,
2611 FWUPD_ERROR,
2612 FWUPD_ERROR_NOT_SUPPORTED,
2613 "Multiple branches not available");
2614 return FALSE;
2615 }
2616
2617 /* get all releases, including the alternate branch versions */
2618 rels = fu_engine_get_releases (priv->engine,
2619 priv->request,
2620 fu_device_get_id (dev),
2621 error);
2622 if (rels == NULL)
2623 return FALSE;
2624
2625 /* get all the unique branches */
2626 for (guint i = 0; i < rels->len; i++) {
2627 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002628 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002629#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002630 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002631 _g_str_equal0, NULL))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002632 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002633#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002634 g_ptr_array_add (branches, g_strdup (branch_tmp));
2635 }
2636
2637 /* branch name is optional */
2638 if (g_strv_length (values) > 1) {
2639 branch = values[1];
2640 } else if (branches->len == 1) {
2641 branch = g_ptr_array_index (branches, 0);
2642 } else {
2643 guint idx;
2644
2645 /* TRANSLATORS: get interactive prompt, where branch is the
2646 * supplier of the firmware, e.g. "non-free" or "free" */
2647 g_print ("%s\n", _("Choose a branch:"));
2648 /* TRANSLATORS: this is to abort the interactive prompt */
2649 g_print ("0.\t%s\n", _("Cancel"));
2650 for (guint i = 0; i < branches->len; i++) {
2651 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002652 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002653 }
2654 idx = fu_util_prompt_for_number (branches->len);
2655 if (idx == 0) {
2656 g_set_error_literal (error,
2657 FWUPD_ERROR,
2658 FWUPD_ERROR_NOTHING_TO_DO,
2659 "Request canceled");
2660 return FALSE;
2661 }
2662 branch = g_ptr_array_index (branches, idx - 1);
2663 }
2664
2665 /* sanity check */
2666 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2667 g_set_error (error,
2668 FWUPD_ERROR,
2669 FWUPD_ERROR_NOT_SUPPORTED,
2670 "Device %s is already on branch %s",
2671 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002672 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002673 return FALSE;
2674 }
2675
2676 /* the releases are ordered by version */
2677 for (guint j = 0; j < rels->len; j++) {
2678 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2679 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2680 rel = g_object_ref (rel_tmp);
2681 break;
2682 }
2683 }
2684 if (rel == NULL) {
2685 g_set_error (error,
2686 FWUPD_ERROR,
2687 FWUPD_ERROR_NOT_SUPPORTED,
2688 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002689 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002690 return FALSE;
2691 }
2692
2693 /* we're switching branch */
2694 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2695 return FALSE;
2696
2697 /* update the console if composite devices are also updated */
2698 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2699 g_signal_connect (priv->engine, "device-changed",
2700 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2701 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2702 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2703 if (!fu_util_install_release (priv, rel, error))
2704 return FALSE;
2705 fu_util_display_current_message (priv);
2706
2707 /* we don't want to ask anything */
2708 if (priv->no_reboot_check) {
2709 g_debug ("skipping reboot check");
2710 return TRUE;
2711 }
2712
2713 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2714}
2715
Richard Hughesb5976832018-05-18 10:02:09 +01002716int
2717main (int argc, char *argv[])
2718{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002719 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002720 gboolean allow_older = FALSE;
2721 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002722 gboolean force = FALSE;
2723 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002724 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002725 gboolean ignore_checksum = FALSE;
2726 gboolean ignore_power = FALSE;
2727 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002728 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002729 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002730 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2731 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002732 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002733 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002734 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002735 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002736 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2737 /* TRANSLATORS: command line option */
2738 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002739 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2740 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002741 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002742 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2743 /* TRANSLATORS: command line option */
2744 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002745 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2746 /* TRANSLATORS: command line option */
2747 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002748 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2749 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002750 _("Force the action by relaxing some runtime checks"), NULL },
2751 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2752 /* TRANSLATORS: command line option */
2753 _("Ignore firmware checksum failures"), NULL },
2754 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2755 /* TRANSLATORS: command line option */
2756 _("Ignore firmware hardware mismatch failures"), NULL },
2757 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2758 /* TRANSLATORS: command line option */
2759 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002760 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2761 /* TRANSLATORS: command line option */
2762 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002763 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2764 /* TRANSLATORS: command line option */
2765 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002766 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2767 /* TRANSLATORS: command line option */
2768 _("Show all results"), NULL },
2769 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002770 /* TRANSLATORS: command line option */
2771 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002772 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002773 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002774 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002775 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2776 /* TRANSLATORS: command line option */
2777 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002778 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002779 /* TRANSLATORS: command line option */
2780 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002781 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002782 /* TRANSLATORS: command line option */
2783 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002784 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2785 /* TRANSLATORS: command line option */
2786 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002787 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2788 /* TRANSLATORS: command line option */
2789 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002790 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2791 /* TRANSLATORS: command line option */
2792 _("Filter with a set of device flags using a ~ prefix to "
2793 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002794 { NULL}
2795 };
2796
Richard Hughes429f72b2020-01-16 12:18:19 +00002797#ifdef _WIN32
2798 /* workaround Windows setting the codepage to 1252 */
2799 g_setenv ("LANG", "C.UTF-8", FALSE);
2800#endif
2801
Richard Hughesb5976832018-05-18 10:02:09 +01002802 setlocale (LC_ALL, "");
2803
Richard Hughes668ee212019-11-22 09:17:46 +00002804 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002805 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2806 textdomain (GETTEXT_PACKAGE);
2807
Richard Hughes01c0bad2019-11-22 09:08:51 +00002808#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002809 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002810 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002811 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002812 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002813#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002814
2815 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002816 priv->main_ctx = g_main_context_new ();
2817 priv->loop = g_main_loop_new (priv->main_ctx, FALSE);
Richard Hughesb5976832018-05-18 10:02:09 +01002818 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002819 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002820
2821 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002822 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002823 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002824 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2825 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002826 /* TRANSLATORS: command description */
2827 _("Build firmware using a sandbox"),
2828 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002829 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002830 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002831 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2832 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002833 /* TRANSLATORS: command description */
2834 _("Dump SMBIOS data from a file"),
2835 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002836 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002837 "get-plugins",
2838 NULL,
2839 /* TRANSLATORS: command description */
2840 _("Get all enabled plugins registered with the system"),
2841 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002842 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002843 "get-details",
2844 NULL,
2845 /* TRANSLATORS: command description */
2846 _("Gets details about a firmware file"),
2847 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002848 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002849 "get-history",
2850 NULL,
2851 /* TRANSLATORS: command description */
2852 _("Show history of firmware updates"),
2853 fu_util_get_history);
2854 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002855 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002856 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2857 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002858 /* TRANSLATORS: command description */
2859 _("Gets the list of updates for connected hardware"),
2860 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002861 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002862 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002863 NULL,
2864 /* TRANSLATORS: command description */
2865 _("Get all devices that support firmware updates"),
2866 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002867 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002868 "get-device-flags",
2869 NULL,
2870 /* TRANSLATORS: command description */
2871 _("Get all device flags supported by fwupd"),
2872 fu_util_get_device_flags);
2873 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002874 "watch",
2875 NULL,
2876 /* TRANSLATORS: command description */
Piotr Drąg472fa592018-06-06 14:53:48 +02002877 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002878 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002879 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002880 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002881 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2882 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002883 /* TRANSLATORS: command description */
2884 _("Install a firmware blob on a device"),
2885 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002886 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002887 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002888 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2889 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002890 /* TRANSLATORS: command description */
2891 _("Install a firmware file on this hardware"),
2892 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002893 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002894 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002895 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2896 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002897 /* TRANSLATORS: command description */
2898 _("Reinstall firmware on a device"),
2899 fu_util_reinstall);
2900 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002901 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002902 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2903 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002904 /* TRANSLATORS: command description */
2905 _("Attach to firmware mode"),
2906 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002907 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002908 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002909 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2910 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002911 /* TRANSLATORS: command description */
2912 _("Detach to bootloader mode"),
2913 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002914 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002915 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002916 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2917 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002918 /* TRANSLATORS: command description */
2919 _("Unbind current driver"),
2920 fu_util_unbind_driver);
2921 fu_util_cmd_array_add (cmd_array,
2922 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002923 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2924 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002925 /* TRANSLATORS: command description */
2926 _("Bind new kernel driver"),
2927 fu_util_bind_driver);
2928 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002929 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002930 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2931 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002932 /* TRANSLATORS: command description */
2933 _("Activate pending devices"),
2934 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002935 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002936 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002937 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
Richard Hughesf6751cd2021-01-15 16:48:25 +00002938 _("[SMBIOS-FILE|HWIDS-FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002939 /* TRANSLATORS: command description */
2940 _("Return all the hardware IDs for the machine"),
2941 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002942 fu_util_cmd_array_add (cmd_array,
Richard Hughesf6751cd2021-01-15 16:48:25 +00002943 "export-hwids",
2944 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2945 _("HWIDS-FILE"),
2946 /* TRANSLATORS: command description */
2947 _("Save a file that allows generation of hardware IDs"),
2948 fu_util_export_hwids);
2949 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002950 "monitor",
2951 NULL,
2952 /* TRANSLATORS: command description */
2953 _("Monitor the daemon for events"),
2954 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002955 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002956 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002957 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2958 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002959 /* TRANSLATORS: command description */
2960 _("Update all devices that match local metadata"),
2961 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002962 fu_util_cmd_array_add (cmd_array,
2963 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002964 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2965 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002966 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002967 C_("command-description",
2968 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002969 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002970 fu_util_cmd_array_add (cmd_array,
2971 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002972 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2973 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002974 /* TRANSLATORS: command description */
2975 _("Update the stored metadata with current contents"),
2976 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002977 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002978 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002979 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2980 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002981 /* TRANSLATORS: command description */
2982 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002983 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002984 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002985 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002986 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2987 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002988 /* TRANSLATORS: command description */
2989 _("Convert a firmware file"),
2990 fu_util_firmware_convert);
2991 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002992 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002993 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2994 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002995 /* TRANSLATORS: command description */
2996 _("Build a firmware file"),
2997 fu_util_firmware_build);
2998 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002999 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003000 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3001 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01003002 /* TRANSLATORS: command description */
3003 _("Parse and show details about a firmware file"),
3004 fu_util_firmware_parse);
3005 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01003006 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003007 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3008 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01003009 /* TRANSLATORS: command description */
3010 _("Extract a firmware blob to images"),
3011 fu_util_firmware_extract);
3012 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003013 "get-firmware-types",
3014 NULL,
3015 /* TRANSLATORS: command description */
3016 _("List the available firmware types"),
3017 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00003018 fu_util_cmd_array_add (cmd_array,
3019 "get-remotes",
3020 NULL,
3021 /* TRANSLATORS: command description */
3022 _("Gets the configured remotes"),
3023 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00003024 fu_util_cmd_array_add (cmd_array,
3025 "refresh",
3026 NULL,
3027 /* TRANSLATORS: command description */
3028 _("Refresh metadata from remote server"),
3029 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01003030 fu_util_cmd_array_add (cmd_array,
3031 "security",
3032 NULL,
3033 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003034 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003035 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01003036 fu_util_cmd_array_add (cmd_array,
3037 "esp-mount",
3038 NULL,
3039 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003040 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003041 fu_util_esp_mount);
3042 fu_util_cmd_array_add (cmd_array,
3043 "esp-unmount",
3044 NULL,
3045 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003046 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003047 fu_util_esp_unmount);
3048 fu_util_cmd_array_add (cmd_array,
3049 "esp-list",
3050 NULL,
3051 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003052 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003053 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003054 fu_util_cmd_array_add (cmd_array,
3055 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003056 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3057 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003058 /* TRANSLATORS: command description */
3059 _("Switch the firmware branch on the device"),
3060 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01003061
3062 /* do stuff on ctrl+c */
3063 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003064#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003065 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3066 SIGINT, fu_util_sigint_cb,
3067 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003068#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003069 g_signal_connect (priv->cancellable, "cancelled",
3070 G_CALLBACK (fu_util_cancelled_cb), priv);
3071
3072 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003073 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003074
Mario Limonciello3f243a92019-01-21 22:05:23 -06003075 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003076 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003077 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003078 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003079 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003080 } else {
3081 /* set our implemented feature set */
3082 fu_engine_request_set_feature_flags (priv->request,
3083 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003084 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003085 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003086 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003087
Richard Hughesb5976832018-05-18 10:02:09 +01003088 /* get a list of the commands */
3089 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003090 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003091 g_option_context_set_summary (priv->context, cmd_descriptions);
3092 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003093 /* TRANSLATORS: CLI description */
3094 _("This tool allows an administrator to use the fwupd plugins "
3095 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003096
3097 /* TRANSLATORS: program name */
3098 g_set_application_name (_("Firmware Utility"));
3099 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003100 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003101 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3102 if (!ret) {
3103 /* TRANSLATORS: the user didn't read the man page */
3104 g_print ("%s: %s\n", _("Failed to parse arguments"),
3105 error->message);
3106 return EXIT_FAILURE;
3107 }
3108
Richard Hughes0e46b222019-09-05 12:13:35 +01003109 /* allow disabling SSL strict mode for broken corporate proxies */
3110 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003111 g_autofree gchar *fmt = NULL;
3112 /* TRANSLATORS: this is a prefix on the console */
3113 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003114 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003115 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3116 "to do this automatically in the future "
3117 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003118 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3119 }
3120
Richard Hughes747f5702019-08-06 14:27:26 +01003121 /* parse filter flags */
3122 if (filter != NULL) {
3123 if (!fu_util_parse_filter_flags (filter,
3124 &priv->filter_include,
3125 &priv->filter_exclude,
3126 &error)) {
3127 /* TRANSLATORS: the user didn't read the man page */
3128 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3129 error->message);
3130 return EXIT_FAILURE;
3131 }
3132 }
3133
3134
Richard Hughes460226a2018-05-21 20:56:21 +01003135 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003136 if (allow_reinstall)
3137 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3138 if (allow_older)
3139 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003140 if (allow_branch_switch)
3141 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3142 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003143 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003144 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3145 }
3146 if (ignore_checksum)
3147 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3148 if (ignore_vid_pid)
3149 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3150 if (ignore_power)
3151 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003152
Richard Hughes98ca9932018-05-18 10:24:07 +01003153 /* load engine */
3154 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3155 g_signal_connect (priv->engine, "device-added",
3156 G_CALLBACK (fu_main_engine_device_added_cb),
3157 priv);
3158 g_signal_connect (priv->engine, "device-removed",
3159 G_CALLBACK (fu_main_engine_device_removed_cb),
3160 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003161 g_signal_connect (priv->engine, "status-changed",
3162 G_CALLBACK (fu_main_engine_status_changed_cb),
3163 priv);
3164 g_signal_connect (priv->engine, "percentage-changed",
3165 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3166 priv);
3167
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003168 /* just show versions and exit */
3169 if (version) {
3170 g_autofree gchar *version_str = fu_util_get_versions ();
3171 g_print ("%s\n", version_str);
3172 return EXIT_SUCCESS;
3173 }
3174
Richard Hughes85226fd2020-06-30 14:43:48 +01003175 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003176 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3177 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3178
Richard Hughesb5976832018-05-18 10:02:09 +01003179 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003180 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003181 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003182 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003183 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003184 /* TRANSLATORS: error message explaining command to run to how to get help */
3185 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3186 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3187 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003188 return EXIT_NOTHING_TO_DO;
3189 }
Richard Hughesb5976832018-05-18 10:02:09 +01003190 return EXIT_FAILURE;
3191 }
3192
3193 /* success */
3194 return EXIT_SUCCESS;
3195}