blob: d652e0aaca0e1f3450afb5912f200a479eae93b9 [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
Richard Hughese65d28d2021-04-15 21:49:10 +010023#include "fu-cabinet.h"
Richard Hughesb333e002021-04-01 10:40:02 +010024#include "fu-context-private.h"
Mario Limonciello7a3df4b2019-01-31 10:27:22 -060025#include "fu-device-private.h"
Richard Hughes98ca9932018-05-18 10:24:07 +010026#include "fu-engine.h"
Mario Limonciello96a0dd52019-02-25 13:50:03 -060027#include "fu-history.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010028#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010029#include "fu-progressbar.h"
Richard Hughesf58ac732020-05-12 15:23:44 +010030#include "fu-security-attrs-private.h"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060031#include "fu-smbios-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010032#include "fu-util-common.h"
Richard Hughesb333e002021-04-01 10:40:02 +010033#include "fu-hwids.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050034#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060035#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060036#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010037
Richard Hughes3d005222019-05-17 14:02:41 +010038#ifdef HAVE_SYSTEMD
39#include "fu-systemd.h"
40#endif
41
Richard Hughesb5976832018-05-18 10:02:09 +010042/* custom return code */
43#define EXIT_NOTHING_TO_DO 2
44
Mario Limonciello3f243a92019-01-21 22:05:23 -060045typedef enum {
46 FU_UTIL_OPERATION_UNKNOWN,
47 FU_UTIL_OPERATION_UPDATE,
48 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000049 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060050 FU_UTIL_OPERATION_LAST
51} FuUtilOperation;
52
Richard Hughesc77e1112019-03-01 10:16:26 +000053struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010054 GCancellable *cancellable;
Richard Hughes6ed25f52021-01-10 19:27:33 +000055 GMainContext *main_ctx;
Richard Hughesb5976832018-05-18 10:02:09 +010056 GMainLoop *loop;
57 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010058 FuEngine *engine;
Richard Hughesdf89cd52020-06-26 20:25:18 +010059 FuEngineRequest *request;
Richard Hughesb5976832018-05-18 10:02:09 +010060 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060061 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050062 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000063 gboolean prepare_blob;
64 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060065 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010066 FwupdInstallFlags flags;
Richard Hughes5c82b942020-09-14 12:24:06 +010067 gboolean show_all;
Richard Hughes0e46b222019-09-05 12:13:35 +010068 gboolean disable_ssl_strict;
Richard Hughes9cf5f8f2021-04-14 20:03:55 +010069 gint lock_fd;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050070 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060071 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050072 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060073 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060074 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010075 FwupdDeviceFlags filter_include;
76 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000077};
Richard Hughesb5976832018-05-18 10:02:09 +010078
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050079static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060080fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
81{
82 g_autoptr(JsonBuilder) builder = NULL;
83 g_autoptr(JsonGenerator) json_generator = NULL;
84 g_autoptr(JsonNode) json_root = NULL;
85 g_autoptr(GPtrArray) devices = NULL;
86 g_autofree gchar *state = NULL;
87 g_autofree gchar *dirname = NULL;
88 g_autofree gchar *filename = NULL;
89
90 if (!priv->enable_json_state)
91 return TRUE;
92
93 devices = fu_engine_get_devices (priv->engine, error);
94 if (devices == NULL)
95 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000096 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060097
98 /* create header */
99 builder = json_builder_new ();
100 json_builder_begin_object (builder);
101
102 /* add each device */
103 json_builder_set_member_name (builder, "Devices");
104 json_builder_begin_array (builder);
105 for (guint i = 0; i < devices->len; i++) {
106 FwupdDevice *dev = g_ptr_array_index (devices, i);
107 json_builder_begin_object (builder);
108 fwupd_device_to_json (dev, builder);
109 json_builder_end_object (builder);
110 }
111 json_builder_end_array (builder);
112 json_builder_end_object (builder);
113
114 /* export as a string */
115 json_root = json_builder_get_root (builder);
116 json_generator = json_generator_new ();
117 json_generator_set_pretty (json_generator, TRUE);
118 json_generator_set_root (json_generator, json_root);
119 state = json_generator_to_data (json_generator, NULL);
120 if (state == NULL)
121 return FALSE;
122 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
123 filename = g_build_filename (dirname, "state.json", NULL);
124 return g_file_set_contents (filename, state, -1, error);
125}
126
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100127static void
128fu_util_show_plugin_warnings (FuUtilPrivate *priv)
129{
130 FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE;
131 GPtrArray *plugins;
132
133 /* get a superset so we do not show the same message more than once */
134 plugins = fu_engine_get_plugins (priv->engine);
135 for (guint i = 0; i < plugins->len; i++) {
136 FwupdPlugin *plugin = g_ptr_array_index (plugins, i);
137 if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING))
138 continue;
139 flags |= fwupd_plugin_get_flags (plugin);
140 }
141
142 /* never show these, they're way too generic */
143 flags &= ~FWUPD_PLUGIN_FLAG_DISABLED;
144 flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE;
Richard Hughesd94286b2021-03-01 21:12:18 +0000145 flags &= ~FWUPD_PLUGIN_FLAG_REQUIRE_HWID;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100146
147 /* print */
148 for (guint i = 0; i < 64; i++) {
Richard Hughes1df9b822020-10-30 15:18:45 +0000149 FwupdPluginFlags flag = (guint64) 1 << i;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100150 const gchar *tmp;
151 g_autofree gchar *fmt = NULL;
Richard Hughes1df9b822020-10-30 15:18:45 +0000152 g_autofree gchar *url= NULL;
153 g_autoptr(GString) str = g_string_new (NULL);
154 if ((flags & flag) == 0)
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100155 continue;
156 tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i);
157 if (tmp == NULL)
158 continue;
159 /* TRANSLATORS: this is a prefix on the console */
160 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes1df9b822020-10-30 15:18:45 +0000161 g_string_append_printf (str, "%s %s\n", fmt, tmp);
162
163 url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
164 fwupd_plugin_flag_to_string (flag));
165 g_string_append (str, " ");
166 /* TRANSLATORS: %s is a link to a website */
167 g_string_append_printf (str, _("See %s for more information."), url);
168 g_string_append (str, "\n");
169 g_printerr ("%s", str->str);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100170 }
171}
172
Mario Limonciello3143bad2019-02-27 07:31:00 -0600173static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000174fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600175{
Richard Hughesd92ccca2019-05-20 11:28:31 +0100176#ifdef HAVE_SYSTEMD
Richard Hughes3a0ee302020-12-14 14:22:18 +0000177 g_autoptr(GError) error_local = NULL;
Richard Hughes3d005222019-05-17 14:02:41 +0100178 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500179 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100180#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000181 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000182 return FALSE;
183 if (fu_engine_get_tainted (priv->engine)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100184 g_autofree gchar *fmt = NULL;
185
186 /* TRANSLATORS: this is a prefix on the console */
187 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
188 g_printerr ("%s This tool has loaded 3rd party code and "
189 "is no longer supported by the upstream developers!\n",
190 fmt);
Richard Hughesf425d292019-01-18 17:57:39 +0000191 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100192 fu_util_show_plugin_warnings (priv);
Mario Limonciellobd60de12020-11-11 13:09:43 -0600193 fu_util_show_unsupported_warn ();
Richard Hughesf425d292019-01-18 17:57:39 +0000194 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500195}
196
Richard Hughesb5976832018-05-18 10:02:09 +0100197static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500198fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
199{
200 g_autofree gchar *path = g_path_get_dirname (value);
201 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
202 g_prefix_error (error,
203 "Unable to access %s. You may need to copy %s to %s: ",
204 path, value, g_getenv ("HOME"));
205 }
206}
207
208static void
Richard Hughesb5976832018-05-18 10:02:09 +0100209fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
210{
211 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
212 /* TRANSLATORS: this is when a device ctrl+c's a watch */
213 g_print ("%s\n", _("Cancelled"));
214 g_main_loop_quit (priv->loop);
215}
216
217static gboolean
218fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
219{
220 g_autofree gchar *tmp = NULL;
221 g_autoptr(FuSmbios) smbios = NULL;
222 if (g_strv_length (values) < 1) {
223 g_set_error_literal (error,
224 FWUPD_ERROR,
225 FWUPD_ERROR_INVALID_ARGS,
226 "Invalid arguments");
227 return FALSE;
228 }
229 smbios = fu_smbios_new ();
230 if (!fu_smbios_setup_from_file (smbios, values[0], error))
231 return FALSE;
232 tmp = fu_smbios_to_string (smbios);
233 g_print ("%s\n", tmp);
234 return TRUE;
235}
236
Richard Hughes9e5675e2019-11-22 09:35:03 +0000237#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100238static gboolean
239fu_util_sigint_cb (gpointer user_data)
240{
241 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
242 g_debug ("Handling SIGINT");
243 g_cancellable_cancel (priv->cancellable);
244 return FALSE;
245}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000246#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100247
248static void
249fu_util_private_free (FuUtilPrivate *priv)
250{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500251 if (priv->current_device != NULL)
252 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100253 if (priv->engine != NULL)
254 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100255 if (priv->request != NULL)
256 g_object_unref (priv->request);
Richard Hughes6ed25f52021-01-10 19:27:33 +0000257 if (priv->main_ctx != NULL)
258 g_main_context_unref (priv->main_ctx);
Richard Hughesb5976832018-05-18 10:02:09 +0100259 if (priv->loop != NULL)
260 g_main_loop_unref (priv->loop);
261 if (priv->cancellable != NULL)
262 g_object_unref (priv->cancellable);
263 if (priv->progressbar != NULL)
264 g_object_unref (priv->progressbar);
265 if (priv->context != NULL)
266 g_option_context_free (priv->context);
Richard Hughes9cf5f8f2021-04-14 20:03:55 +0100267 if (priv->lock_fd != 0)
268 g_close (priv->lock_fd, NULL);
Mario Limonciello32241f42019-01-24 10:12:41 -0600269 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100270 g_free (priv);
271}
272
273#pragma clang diagnostic push
274#pragma clang diagnostic ignored "-Wunused-function"
275G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
276#pragma clang diagnostic pop
277
Richard Hughes98ca9932018-05-18 10:24:07 +0100278
279static void
280fu_main_engine_device_added_cb (FuEngine *engine,
281 FuDevice *device,
282 FuUtilPrivate *priv)
283{
284 g_autofree gchar *tmp = fu_device_to_string (device);
285 g_debug ("ADDED:\n%s", tmp);
286}
287
288static void
289fu_main_engine_device_removed_cb (FuEngine *engine,
290 FuDevice *device,
291 FuUtilPrivate *priv)
292{
293 g_autofree gchar *tmp = fu_device_to_string (device);
294 g_debug ("REMOVED:\n%s", tmp);
295}
296
297static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100298fu_main_engine_status_changed_cb (FuEngine *engine,
299 FwupdStatus status,
300 FuUtilPrivate *priv)
301{
302 fu_progressbar_update (priv->progressbar, status, 0);
303}
304
305static void
306fu_main_engine_percentage_changed_cb (FuEngine *engine,
307 guint percentage,
308 FuUtilPrivate *priv)
309{
310 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
311}
312
313static gboolean
314fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
315{
Richard Hughesc7d870a2020-12-10 10:05:35 +0000316 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100317 return FALSE;
318 g_main_loop_run (priv->loop);
319 return TRUE;
320}
321
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100322static gint
323fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
324{
325 return fu_plugin_name_compare (*item1, *item2);
326}
327
328static gboolean
329fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
330{
331 GPtrArray *plugins;
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100332
333 /* load engine */
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100334 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100335 return FALSE;
336
337 /* print */
338 plugins = fu_engine_get_plugins (priv->engine);
339 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
340 for (guint i = 0; i < plugins->len; i++) {
341 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100342 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
343 g_print ("%s\n", str);
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100344 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100345 if (plugins->len == 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100346 /* TRANSLATORS: nothing found */
347 g_print ("%s\n", _("No plugins found"));
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100348 }
349
350 return TRUE;
351}
352
Richard Hughes98ca9932018-05-18 10:24:07 +0100353static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100354fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
355{
356 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
357 if (!fwupd_device_has_flag (dev, priv->filter_include))
358 return FALSE;
359 }
360 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
361 if (fwupd_device_has_flag (dev, priv->filter_exclude))
362 return FALSE;
363 }
364 return TRUE;
365}
366
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500367static gchar *
368fu_util_get_tree_title (FuUtilPrivate *priv)
369{
370 return g_strdup (fu_engine_get_host_product (priv->engine));
371}
372
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600373static FuDevice *
374fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
375{
376 FuDevice *dev;
377 guint idx;
378 g_autoptr(GPtrArray) devices = NULL;
379 g_autoptr(GPtrArray) devices_filtered = NULL;
380
381 /* get devices from daemon */
382 if (devices_opt != NULL) {
383 devices = g_ptr_array_ref (devices_opt);
384 } else {
385 devices = fu_engine_get_devices (priv->engine, error);
386 if (devices == NULL)
387 return NULL;
388 }
389 fwupd_device_array_ensure_parents (devices);
390
391 /* filter results */
392 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
393 for (guint i = 0; i < devices->len; i++) {
394 dev = g_ptr_array_index (devices, i);
395 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
396 continue;
397 g_ptr_array_add (devices_filtered, g_object_ref (dev));
398 }
399
400 /* nothing */
401 if (devices_filtered->len == 0) {
402 g_set_error_literal (error,
403 FWUPD_ERROR,
404 FWUPD_ERROR_NOTHING_TO_DO,
405 "No supported devices");
406 return NULL;
407 }
408
409 /* exactly one */
410 if (devices_filtered->len == 1) {
411 dev = g_ptr_array_index (devices_filtered, 0);
412 /* TRANSLATORS: Device has been chosen by the daemon for the user */
413 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
414 return g_object_ref (dev);
415 }
416
417 /* TRANSLATORS: get interactive prompt */
418 g_print ("%s\n", _("Choose a device:"));
419 /* TRANSLATORS: this is to abort the interactive prompt */
420 g_print ("0.\t%s\n", _("Cancel"));
421 for (guint i = 0; i < devices_filtered->len; i++) {
422 dev = g_ptr_array_index (devices_filtered, i);
423 g_print ("%u.\t%s (%s)\n",
424 i + 1,
425 fu_device_get_id (dev),
426 fu_device_get_name (dev));
427 }
428 idx = fu_util_prompt_for_number (devices_filtered->len);
429 if (idx == 0) {
430 g_set_error_literal (error,
431 FWUPD_ERROR,
432 FWUPD_ERROR_NOTHING_TO_DO,
433 "Request canceled");
434 return NULL;
435 }
436 dev = g_ptr_array_index (devices_filtered, idx - 1);
437 return g_object_ref (dev);
438}
439
440static FuDevice *
441fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
442{
443 if (fwupd_guid_is_valid (id)) {
444 g_autoptr(GPtrArray) devices = NULL;
445 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
446 if (devices == NULL)
447 return NULL;
448 return fu_util_prompt_for_device (priv, devices, error);
449 }
450
451 /* did this look like a GUID? */
452 for (guint i = 0; id[i] != '\0'; i++) {
453 if (id[i] == '-') {
454 g_set_error_literal (error,
455 FWUPD_ERROR,
456 FWUPD_ERROR_INVALID_ARGS,
457 "Invalid arguments");
458 return NULL;
459 }
460 }
461 return fu_engine_get_device (priv->engine, id, error);
462}
463
Richard Hughes747f5702019-08-06 14:27:26 +0100464static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600465fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
466{
467 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500468 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600469 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500470 gboolean no_updates_header = FALSE;
471 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600472
473 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000474 if (!fu_util_start_engine (priv,
475 FU_ENGINE_LOAD_FLAG_COLDPLUG |
476 FU_ENGINE_LOAD_FLAG_HWINFO |
477 FU_ENGINE_LOAD_FLAG_REMOTES,
478 error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600479 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600480 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600481
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600482 /* parse arguments */
483 if (g_strv_length (values) == 0) {
484 devices = fu_engine_get_devices (priv->engine, error);
485 if (devices == NULL)
486 return FALSE;
487 } else if (g_strv_length (values) == 1) {
488 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600489 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600490 if (device == NULL)
491 return FALSE;
492 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
493 g_ptr_array_add (devices, device);
494 } else {
495 g_set_error_literal (error,
496 FWUPD_ERROR,
497 FWUPD_ERROR_INVALID_ARGS,
498 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600499 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600500 }
501
Richard Hughes0ef47202020-01-06 13:59:09 +0000502 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500503 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600504 for (guint i = 0; i < devices->len; i++) {
505 FwupdDevice *dev = g_ptr_array_index (devices, i);
506 g_autoptr(GPtrArray) rels = NULL;
507 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500508 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600509
Richard Hughes747f5702019-08-06 14:27:26 +0100510 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600511 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600512 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600513 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500514 if (!no_updates_header) {
515 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
516 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
517 no_updates_header = TRUE;
518 }
519 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600520 continue;
521 }
Richard Hughes747f5702019-08-06 14:27:26 +0100522 if (!fu_util_filter_device (priv, dev))
523 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600524
525 /* get the releases for this device and filter for validity */
526 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100527 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600528 fwupd_device_get_id (dev),
529 &error_local);
530 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500531 if (!latest_header) {
532 /* TRANSLATORS: message letting the user know no device upgrade available */
533 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
534 latest_header = TRUE;
535 }
536 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600537 /* discard the actual reason from user, but leave for debugging */
538 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600539 continue;
540 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500541 child = g_node_append_data (root, dev);
542
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600543 for (guint j = 0; j < rels->len; j++) {
544 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500545 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600546 }
547 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600548 /* save the device state for other applications to see */
549 if (!fu_util_save_current_state (priv, error))
550 return FALSE;
551
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500552 /* updates */
553 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
554 g_set_error_literal (error,
555 FWUPD_ERROR,
556 FWUPD_ERROR_NOTHING_TO_DO,
557 "No updates available for remaining devices");
558 return FALSE;
559 }
560
561 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600562 return TRUE;
563}
564
565static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500566fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
567{
568 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500569 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600570 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500571 gint fd;
572
573 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000574 if (!fu_util_start_engine (priv,
575 FU_ENGINE_LOAD_FLAG_COLDPLUG |
576 FU_ENGINE_LOAD_FLAG_HWINFO |
577 FU_ENGINE_LOAD_FLAG_REMOTES,
578 error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500579 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600580 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500581
582 /* check args */
583 if (g_strv_length (values) != 1) {
584 g_set_error_literal (error,
585 FWUPD_ERROR,
586 FWUPD_ERROR_INVALID_ARGS,
587 "Invalid arguments");
588 return FALSE;
589 }
590
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600591 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100592 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600593
Mario Limonciello716ab272018-05-29 12:34:37 -0500594 /* open file */
595 fd = open (values[0], O_RDONLY);
596 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500597 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500598 g_set_error (error,
599 FWUPD_ERROR,
600 FWUPD_ERROR_INVALID_FILE,
601 "failed to open %s",
602 values[0]);
603 return FALSE;
604 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100605 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500606 close (fd);
607
608 if (array == NULL)
609 return FALSE;
610 for (guint i = 0; i < array->len; i++) {
611 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600612 FwupdRelease *rel;
613 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100614 if (!fu_util_filter_device (priv, dev))
615 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600616 child = g_node_append_data (root, dev);
617 rel = fwupd_device_get_release_default (dev);
618 if (rel != NULL)
619 g_node_append_data (child, rel);
620
Mario Limonciello716ab272018-05-29 12:34:37 -0500621 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500622 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500623
Mario Limonciello716ab272018-05-29 12:34:37 -0500624 return TRUE;
625}
626
627static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100628fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
629{
630 g_autoptr(GString) str = g_string_new (NULL);
631
632 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
633 const gchar *tmp = fwupd_device_flag_to_string (i);
634 if (tmp == NULL)
635 break;
636 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
637 g_string_append (str, " ");
638 g_string_append (str, tmp);
639 g_string_append (str, " ~");
640 g_string_append (str, tmp);
641 }
642 g_print ("%s\n", str->str);
643
644 return TRUE;
645}
646
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500647static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100648fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500649{
650 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100651 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100652 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
653 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100654 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500655 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500656 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100657 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500658 GNode *child = g_node_append_data (root, dev_tmp);
659 fu_util_build_device_tree (priv, child, devs, dev_tmp);
660 }
661 }
662}
663
664static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100665fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500666{
667 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600668 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500669 g_autoptr(GPtrArray) devs = NULL;
670
671 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000672 if (!fu_util_start_engine (priv,
673 FU_ENGINE_LOAD_FLAG_COLDPLUG |
674 FU_ENGINE_LOAD_FLAG_HWINFO |
675 FU_ENGINE_LOAD_FLAG_REMOTES,
676 error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500677 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600678 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500679
Mario Limonciello76196652021-01-13 22:53:26 -0600680 /* get devices and build tree */
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500681 devs = fu_engine_get_devices (priv->engine, error);
682 if (devs == NULL)
683 return FALSE;
Mario Limonciello76196652021-01-13 22:53:26 -0600684 if (devs->len > 0) {
685 fwupd_device_array_ensure_parents (devs);
686 fu_util_build_device_tree (priv, root, devs, NULL);
687 }
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500688
689 /* print */
Mario Limonciello76196652021-01-13 22:53:26 -0600690 if (g_node_n_children (root) == 0) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500691 /* TRANSLATORS: nothing attached that can be upgraded */
692 g_print ("%s\n", _("No hardware detected with firmware update capability"));
693 return TRUE;
694 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500695 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500696
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100697 /* save the device state for other applications to see */
698 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500699}
700
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500701static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600702fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500703 FwupdDevice *device,
704 FuUtilPrivate *priv)
705{
706 g_autofree gchar *str = NULL;
707
Richard Hughes809abea2019-03-23 11:06:18 +0000708 /* allowed to set whenever the device has changed */
709 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
710 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
711 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
712 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
713
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500714 /* same as last time, so ignore */
715 if (priv->current_device != NULL &&
716 fwupd_device_compare (priv->current_device, device) == 0)
717 return;
718
Richard Hughesee562b52020-04-07 14:32:52 +0100719 /* ignore indirect devices that might have changed */
720 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
721 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
722 g_debug ("ignoring %s with status %s",
723 fwupd_device_get_name (device),
724 fwupd_status_to_string (fwupd_device_get_status (device)));
725 return;
726 }
727
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500728 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600729 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
730 /* TRANSLATORS: %1 is a device name */
731 str = g_strdup_printf (_("Updating %s…"),
732 fwupd_device_get_name (device));
733 fu_progressbar_set_title (priv->progressbar, str);
734 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
735 /* TRANSLATORS: %1 is a device name */
736 str = g_strdup_printf (_("Installing on %s…"),
737 fwupd_device_get_name (device));
738 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000739 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
740 /* TRANSLATORS: %1 is a device name */
741 str = g_strdup_printf (_("Reading from %s…"),
742 fwupd_device_get_name (device));
743 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600744 } else {
745 g_warning ("no FuUtilOperation set");
746 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500747 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600748
Mario Limonciello32241f42019-01-24 10:12:41 -0600749 if (priv->current_message == NULL) {
750 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
751 if (tmp != NULL)
752 priv->current_message = g_strdup (tmp);
753 }
754}
755
756static void
757fu_util_display_current_message (FuUtilPrivate *priv)
758{
759 if (priv->current_message == NULL)
760 return;
761 g_print ("%s\n", priv->current_message);
762 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500763}
764
Richard Hughes98ca9932018-05-18 10:24:07 +0100765static gboolean
766fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
767{
768 g_autoptr(FuDevice) device = NULL;
769 g_autoptr(GBytes) blob_fw = NULL;
770
771 /* invalid args */
772 if (g_strv_length (values) == 0) {
773 g_set_error_literal (error,
774 FWUPD_ERROR,
775 FWUPD_ERROR_INVALID_ARGS,
776 "Invalid arguments");
777 return FALSE;
778 }
779
780 /* parse blob */
781 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500782 if (blob_fw == NULL) {
783 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100784 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500785 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100786
787 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000788 if (!fu_util_start_engine (priv,
789 FU_ENGINE_LOAD_FLAG_COLDPLUG |
790 FU_ENGINE_LOAD_FLAG_HWINFO |
791 FU_ENGINE_LOAD_FLAG_REMOTES,
792 error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100793 return FALSE;
794
795 /* get device */
796 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100797 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100798 if (device == NULL)
799 return FALSE;
800 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100801 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100802 if (device == NULL)
803 return FALSE;
804 }
805
Mario Limonciello3f243a92019-01-21 22:05:23 -0600806 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500807 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600808 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500809
Richard Hughes98ca9932018-05-18 10:24:07 +0100810 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000811 if (priv->prepare_blob) {
812 g_autoptr(GPtrArray) devices = NULL;
813 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
814 g_ptr_array_add (devices, g_object_ref (device));
815 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
816 g_prefix_error (error, "failed to prepare composite action: ");
817 return FALSE;
818 }
819 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100820 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000821 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600822 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000823 if (priv->cleanup_blob) {
824 g_autoptr(FuDevice) device_new = NULL;
825 g_autoptr(GError) error_local = NULL;
826
827 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100828 device_new = fu_util_get_device (priv,
829 fu_device_get_id (device),
830 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000831 if (device_new == NULL) {
832 g_debug ("failed to find new device: %s",
833 error_local->message);
834 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600835 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000836 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
837 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
838 g_prefix_error (error, "failed to cleanup composite action: ");
839 return FALSE;
840 }
841 }
842 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600843
Mario Limonciello32241f42019-01-24 10:12:41 -0600844 fu_util_display_current_message (priv);
845
Mario Limonciello3f243a92019-01-21 22:05:23 -0600846 /* success */
847 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100848}
849
Richard Hughesa58510b2019-10-30 10:03:12 +0000850static gboolean
Richard Hughese65d28d2021-04-15 21:49:10 +0100851fu_util_firmware_sign (FuUtilPrivate *priv, gchar **values, GError **error)
852{
853 g_autoptr(FuCabinet) cabinet = fu_cabinet_new ();
854 g_autoptr(GBytes) archive_blob_new = NULL;
855 g_autoptr(GBytes) archive_blob_old = NULL;
856 g_autoptr(GBytes) cert = NULL;
857 g_autoptr(GBytes) privkey = NULL;
858
859 /* invalid args */
860 if (g_strv_length (values) != 3) {
861 g_set_error_literal (error,
862 FWUPD_ERROR,
863 FWUPD_ERROR_INVALID_ARGS,
864 "Invalid arguments, expected firmware.cab "
865 "certificate.pem privatekey.pfx");
866 return FALSE;
867 }
868
869 /* load arguments */
870 archive_blob_old = fu_common_get_contents_bytes (values[0], error);
871 if (archive_blob_old == NULL)
872 return FALSE;
873 cert = fu_common_get_contents_bytes (values[1], error);
874 if (cert == NULL)
875 return FALSE;
876 privkey = fu_common_get_contents_bytes (values[2], error);
877 if (privkey == NULL)
878 return FALSE;
879
880 /* load, sign, export */
881 if (!fu_cabinet_parse (cabinet, archive_blob_old,
882 FU_CABINET_PARSE_FLAG_NONE,
883 error))
884 return FALSE;
885 if (!fu_cabinet_sign (cabinet, cert, privkey,
886 FU_CABINET_SIGN_FLAG_NONE,
887 error))
888 return FALSE;
889 archive_blob_new = fu_cabinet_export (cabinet,
890 FU_CABINET_EXPORT_FLAG_NONE,
891 error);
892 if (archive_blob_new == NULL)
893 return FALSE;
894 return fu_common_set_contents_bytes (values[0], archive_blob_new, error);
895}
896
897static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100898fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000899{
900 g_autoptr(FuDevice) device = NULL;
901 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
902 g_autoptr(GBytes) blob_fw = NULL;
903
904 /* invalid args */
905 if (g_strv_length (values) == 0) {
906 g_set_error_literal (error,
907 FWUPD_ERROR,
908 FWUPD_ERROR_INVALID_ARGS,
909 "Invalid arguments");
910 return FALSE;
911 }
912
913 /* file already exists */
914 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
915 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
916 g_set_error_literal (error,
917 FWUPD_ERROR,
918 FWUPD_ERROR_INVALID_ARGS,
919 "Filename already exists");
920 return FALSE;
921 }
922
Richard Hughes02792c02019-11-01 14:21:20 +0000923 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000924 * avoid failing at the end of a potentially lengthy operation */
925 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
926 return FALSE;
927
928 /* load engine */
Richard Hughesda7b8982021-01-26 21:37:11 +0000929 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughesa58510b2019-10-30 10:03:12 +0000930 return FALSE;
931
932 /* get device */
933 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100934 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000935 if (device == NULL)
936 return FALSE;
937 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100938 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000939 if (device == NULL)
940 return FALSE;
941 }
942 priv->current_operation = FU_UTIL_OPERATION_READ;
943 g_signal_connect (priv->engine, "device-changed",
944 G_CALLBACK (fu_util_update_device_changed_cb), priv);
945
946 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100947 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000948 if (blob_fw == NULL)
949 return FALSE;
950 return fu_common_set_contents_bytes (values[0], blob_fw, error);
951}
952
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100953static gint
954fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
955{
956 FuInstallTask *task1 = *((FuInstallTask **) a);
957 FuInstallTask *task2 = *((FuInstallTask **) b);
958 return fu_install_task_compare (task1, task2);
959}
960
961static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100962fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
963{
Filipe Laínse0914272019-09-20 10:04:43 +0100964 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100965 { "curl", uri, "--output", fn, NULL },
966 { NULL } };
967 for (guint i = 0; argv[i][0] != NULL; i++) {
968 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100969 g_autofree gchar *fn_tmp = NULL;
970 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
971 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100972 g_debug ("%s", error_local->message);
973 continue;
974 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000975 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100976 }
977 g_set_error_literal (error,
978 FWUPD_ERROR,
979 FWUPD_ERROR_NOT_FOUND,
980 "no supported out-of-process downloaders found");
981 return FALSE;
982}
983
984static gchar *
985fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
986{
987 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100988
989 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000990 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
991 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000992 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100993 return g_strdup (perhapsfn);
994
995 /* download the firmware to a cachedir */
996 filename = fu_util_get_user_cache_path (perhapsfn);
997 if (!fu_common_mkdir_parent (filename, error))
998 return NULL;
999 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
1000 return NULL;
1001 return g_steal_pointer (&filename);
1002}
1003
1004static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001005fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
1006{
Richard Hughes3d178be2018-08-30 11:14:24 +01001007 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001008 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001009 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001010 g_autoptr(GPtrArray) devices_possible = NULL;
1011 g_autoptr(GPtrArray) errors = NULL;
1012 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +01001013 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001014
Mario Limonciello8949e892018-05-25 08:03:06 -05001015 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001016 if (!fu_util_start_engine (priv,
1017 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1018 FU_ENGINE_LOAD_FLAG_HWINFO |
1019 FU_ENGINE_LOAD_FLAG_REMOTES,
1020 error))
Mario Limonciello8949e892018-05-25 08:03:06 -05001021 return FALSE;
1022
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001023 /* handle both forms */
1024 if (g_strv_length (values) == 1) {
1025 devices_possible = fu_engine_get_devices (priv->engine, error);
1026 if (devices_possible == NULL)
1027 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001028 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001029 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001030 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001031 if (device == NULL)
1032 return FALSE;
1033 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1034 g_ptr_array_add (devices_possible, device);
1035 } else {
1036 g_set_error_literal (error,
1037 FWUPD_ERROR,
1038 FWUPD_ERROR_INVALID_ARGS,
1039 "Invalid arguments");
1040 return FALSE;
1041 }
1042
Richard Hughes3d178be2018-08-30 11:14:24 +01001043 /* download if required */
1044 filename = fu_util_download_if_required (priv, values[0], error);
1045 if (filename == NULL)
1046 return FALSE;
1047
Richard Hughes481aa2a2018-09-18 20:51:46 +01001048 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +01001049 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -05001050 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +01001051 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001052 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -05001053 }
Richard Hughes481aa2a2018-09-18 20:51:46 +01001054 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
1055 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001056 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -06001057 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001058 if (components == NULL)
1059 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001060
Richard Hughes481aa2a2018-09-18 20:51:46 +01001061 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001062 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
1063 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001064 for (guint i = 0; i < components->len; i++) {
1065 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001066
1067 /* do any devices pass the requirements */
1068 for (guint j = 0; j < devices_possible->len; j++) {
1069 FuDevice *device = g_ptr_array_index (devices_possible, j);
1070 g_autoptr(FuInstallTask) task = NULL;
1071 g_autoptr(GError) error_local = NULL;
1072
1073 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001074 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001075 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001076 priv->request,
1077 task,
1078 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001079 &error_local)) {
1080 g_debug ("first pass requirement on %s:%s failed: %s",
1081 fu_device_get_id (device),
1082 xb_node_query_text (component, "id", NULL),
1083 error_local->message);
1084 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1085 continue;
1086 }
1087
1088 /* make a second pass using possibly updated version format now */
1089 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1090 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001091 priv->request,
1092 task,
1093 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001094 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001095 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001096 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001097 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001098 error_local->message);
1099 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1100 continue;
1101 }
1102
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001103 /* if component should have an update message from CAB */
1104 fu_device_incorporate_from_component (device, component);
1105
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001106 /* success */
1107 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1108 }
1109 }
1110
1111 /* order the install tasks by the device priority */
1112 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1113
1114 /* nothing suitable */
1115 if (install_tasks->len == 0) {
1116 GError *error_tmp = fu_common_error_array_get_best (errors);
1117 g_propagate_error (error, error_tmp);
1118 return FALSE;
1119 }
1120
Mario Limonciello3f243a92019-01-21 22:05:23 -06001121 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001122 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001123 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001124
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001125 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001126 if (!fu_engine_install_tasks (priv->engine,
1127 priv->request,
1128 install_tasks,
1129 blob_cab,
1130 priv->flags,
1131 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001132 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001133
Mario Limonciello32241f42019-01-24 10:12:41 -06001134 fu_util_display_current_message (priv);
1135
Mario Limonciello3f243a92019-01-21 22:05:23 -06001136 /* we don't want to ask anything */
1137 if (priv->no_reboot_check) {
1138 g_debug ("skipping reboot check");
1139 return TRUE;
1140 }
1141
Mario Limonciello3143bad2019-02-27 07:31:00 -06001142 /* save the device state for other applications to see */
1143 if (!fu_util_save_current_state (priv, error))
1144 return FALSE;
1145
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001146 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001147 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001148}
1149
Richard Hughes98ca9932018-05-18 10:24:07 +01001150static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001151fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001152{
Mario Limonciellofd734852019-08-01 16:41:42 -05001153 FwupdRemote *remote;
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001154 GPtrArray *locations;
Mario Limonciellofd734852019-08-01 16:41:42 -05001155 const gchar *remote_id;
1156 const gchar *uri_tmp;
1157 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001158
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001159 /* get the default release only until other parts of fwupd can cope */
1160 locations = fwupd_release_get_locations (rel);
1161 if (locations->len == 0) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001162 g_set_error_literal (error,
1163 FWUPD_ERROR,
1164 FWUPD_ERROR_INVALID_FILE,
1165 "release missing URI");
1166 return FALSE;
1167 }
Richard Hughesb8dfacc2021-01-25 20:20:59 +00001168 uri_tmp = g_ptr_array_index (locations, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001169 remote_id = fwupd_release_get_remote_id (rel);
1170 if (remote_id == NULL) {
1171 g_set_error (error,
1172 FWUPD_ERROR,
1173 FWUPD_ERROR_INVALID_FILE,
1174 "failed to find remote for %s",
1175 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001176 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001177 }
1178
1179 remote = fu_engine_get_remote_by_id (priv->engine,
1180 remote_id,
1181 error);
1182 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001183 return FALSE;
1184
Mario Limonciellofd734852019-08-01 16:41:42 -05001185 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001186 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001187 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1188 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001189 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1190 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1191 argv[0] = g_build_filename (path, uri_tmp, NULL);
1192 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1193 argv[0] = g_strdup (uri_tmp + 7);
1194 /* web remote, fu_util_install will download file */
1195 } else {
1196 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1197 }
1198 return fu_util_install (priv, argv, error);
1199}
1200
1201static gboolean
1202fu_util_update_all (FuUtilPrivate *priv, GError **error)
1203{
1204 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001205 gboolean no_updates_header = FALSE;
1206 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001207
Mario Limonciello46aaee82019-01-10 12:58:00 -06001208 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001209 if (devices == NULL)
1210 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001211 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001212 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001213 for (guint i = 0; i < devices->len; i++) {
1214 FwupdDevice *dev = g_ptr_array_index (devices, i);
1215 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001216 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001217 g_autoptr(GPtrArray) rels = NULL;
1218 g_autoptr(GError) error_local = NULL;
1219
1220 if (!fu_util_is_interesting_device (dev))
1221 continue;
1222 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001223 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001224 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001225 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001226 if (!no_updates_header) {
1227 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1228 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1229 no_updates_header = TRUE;
1230 }
1231 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001232 continue;
1233 }
Richard Hughes747f5702019-08-06 14:27:26 +01001234 if (!fu_util_filter_device (priv, dev))
1235 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001236
1237 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001238 rels = fu_engine_get_upgrades (priv->engine,
1239 priv->request,
1240 device_id,
1241 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001242 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001243 if (!latest_header) {
1244 /* TRANSLATORS: message letting the user know no device upgrade available */
1245 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1246 latest_header = TRUE;
1247 }
1248 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001249 /* discard the actual reason from user, but leave for debugging */
1250 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001251 continue;
1252 }
1253
Mario Limonciello98b95162019-10-30 09:20:43 -05001254 if (!priv->no_safety_check) {
1255 if (!fu_util_prompt_warning (dev,
1256 fu_util_get_tree_title (priv),
1257 error))
1258 return FALSE;
1259 }
1260
Mario Limonciello46aaee82019-01-10 12:58:00 -06001261 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001262 if (!fu_util_install_release (priv, rel, &error_local)) {
1263 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001264 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001265 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001266 fu_util_display_current_message (priv);
1267 }
1268 return TRUE;
1269}
1270
1271static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001272fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001273{
1274 FwupdRelease *rel;
1275 g_autoptr(FuDevice) dev = NULL;
1276 g_autoptr(GPtrArray) rels = NULL;
1277
Mario Limonciello9917bb42020-04-20 13:41:27 -05001278 /* do not allow a partial device-id, lookup GUIDs */
1279 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001280 if (dev == NULL)
1281 return FALSE;
1282
1283 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001284 rels = fu_engine_get_upgrades (priv->engine,
1285 priv->request,
1286 fu_device_get_id (dev),
1287 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001288 if (rels == NULL)
1289 return FALSE;
1290 rel = g_ptr_array_index (rels, 0);
1291 if (!fu_util_install_release (priv, rel, error))
1292 return FALSE;
1293 fu_util_display_current_message (priv);
1294
1295 return TRUE;
1296}
1297
1298static gboolean
1299fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1300{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001301 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1302 g_set_error_literal (error,
1303 FWUPD_ERROR,
1304 FWUPD_ERROR_INVALID_ARGS,
1305 "--allow-older is not supported for this command");
1306 return FALSE;
1307 }
1308
1309 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1310 g_set_error_literal (error,
1311 FWUPD_ERROR,
1312 FWUPD_ERROR_INVALID_ARGS,
1313 "--allow-reinstall is not supported for this command");
1314 return FALSE;
1315 }
1316
Mario Limonciellofd734852019-08-01 16:41:42 -05001317 if (g_strv_length (values) > 1) {
1318 g_set_error_literal (error,
1319 FWUPD_ERROR,
1320 FWUPD_ERROR_INVALID_ARGS,
1321 "Invalid arguments");
1322 return FALSE;
1323 }
1324
Richard Hughesc7d870a2020-12-10 10:05:35 +00001325 if (!fu_util_start_engine (priv,
1326 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1327 FU_ENGINE_LOAD_FLAG_HWINFO |
1328 FU_ENGINE_LOAD_FLAG_REMOTES,
1329 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001330 return FALSE;
1331
1332 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1333 g_signal_connect (priv->engine, "device-changed",
1334 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1335
1336 if (g_strv_length (values) == 1) {
1337 if (!fu_util_update_by_id (priv, values[0], error))
1338 return FALSE;
1339 } else {
1340 if (!fu_util_update_all (priv, error))
1341 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001342 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001343
1344 /* we don't want to ask anything */
1345 if (priv->no_reboot_check) {
1346 g_debug ("skipping reboot check");
1347 return TRUE;
1348 }
1349
Mario Limonciello3143bad2019-02-27 07:31:00 -06001350 /* save the device state for other applications to see */
1351 if (!fu_util_save_current_state (priv, error))
1352 return FALSE;
1353
Mario Limonciello3f243a92019-01-21 22:05:23 -06001354 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001355}
1356
1357static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001358fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1359{
1360 g_autoptr(FwupdRelease) rel = NULL;
1361 g_autoptr(GPtrArray) rels = NULL;
1362 g_autoptr(FuDevice) dev = NULL;
1363
1364 if (g_strv_length (values) != 1) {
1365 g_set_error_literal (error,
1366 FWUPD_ERROR,
1367 FWUPD_ERROR_INVALID_ARGS,
1368 "Invalid arguments");
1369 return FALSE;
1370 }
1371
Richard Hughesc7d870a2020-12-10 10:05:35 +00001372 if (!fu_util_start_engine (priv,
1373 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1374 FU_ENGINE_LOAD_FLAG_HWINFO |
1375 FU_ENGINE_LOAD_FLAG_REMOTES,
1376 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001377 return FALSE;
1378
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001379 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001380 if (dev == NULL)
1381 return FALSE;
1382
1383 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001384 rels = fu_engine_get_releases_for_device (priv->engine,
1385 priv->request,
1386 dev,
1387 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001388 if (rels == NULL)
1389 return FALSE;
1390
1391 for (guint j = 0; j < rels->len; j++) {
1392 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1393 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1394 fu_device_get_version (dev),
1395 fu_device_get_version_format (dev)) == 0) {
1396 rel = g_object_ref (rel_tmp);
1397 break;
1398 }
1399 }
1400 if (rel == NULL) {
1401 g_set_error (error,
1402 FWUPD_ERROR,
1403 FWUPD_ERROR_NOT_SUPPORTED,
1404 "Unable to locate release for %s version %s",
1405 fu_device_get_name (dev),
1406 fu_device_get_version (dev));
1407 return FALSE;
1408 }
1409
1410 /* update the console if composite devices are also updated */
1411 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1412 g_signal_connect (priv->engine, "device-changed",
1413 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1414 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1415 if (!fu_util_install_release (priv, rel, error))
1416 return FALSE;
1417 fu_util_display_current_message (priv);
1418
1419 /* we don't want to ask anything */
1420 if (priv->no_reboot_check) {
1421 g_debug ("skipping reboot check");
1422 return TRUE;
1423 }
1424
1425 /* save the device state for other applications to see */
1426 if (!fu_util_save_current_state (priv, error))
1427 return FALSE;
1428
1429 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1430}
1431
1432static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001433fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1434{
1435 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001436 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001437
1438 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001439 if (!fu_util_start_engine (priv,
1440 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1441 FU_ENGINE_LOAD_FLAG_HWINFO |
1442 FU_ENGINE_LOAD_FLAG_REMOTES,
1443 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001444 return FALSE;
1445
Richard Hughes98ca9932018-05-18 10:24:07 +01001446 /* get device */
1447 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001448 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001449 if (device == NULL)
1450 return FALSE;
1451 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001452 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001453 if (device == NULL)
1454 return FALSE;
1455 }
1456
1457 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001458 locker = fu_device_locker_new (device, error);
1459 if (locker == NULL)
1460 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001461 return fu_device_detach (device, error);
1462}
1463
1464static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001465fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1466{
1467 g_autoptr(FuDevice) device = NULL;
1468 g_autoptr(FuDeviceLocker) locker = NULL;
1469
1470 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001471 if (!fu_util_start_engine (priv,
1472 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1473 FU_ENGINE_LOAD_FLAG_HWINFO |
1474 FU_ENGINE_LOAD_FLAG_REMOTES,
1475 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001476 return FALSE;
1477
1478 /* get device */
1479 if (g_strv_length (values) == 1) {
1480 device = fu_util_get_device (priv, values[0], error);
1481 } else {
1482 device = fu_util_prompt_for_device (priv, NULL, error);
1483 }
1484 if (device == NULL)
1485 return FALSE;
1486
1487 /* run vfunc */
1488 locker = fu_device_locker_new (device, error);
1489 if (locker == NULL)
1490 return FALSE;
1491 return fu_device_unbind_driver (device, error);
1492}
1493
1494static gboolean
1495fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1496{
1497 g_autoptr(FuDevice) device = NULL;
1498 g_autoptr(FuDeviceLocker) locker = NULL;
1499
1500 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001501 if (!fu_util_start_engine (priv,
1502 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1503 FU_ENGINE_LOAD_FLAG_HWINFO |
1504 FU_ENGINE_LOAD_FLAG_REMOTES,
1505 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001506 return FALSE;
1507
1508 /* get device */
1509 if (g_strv_length (values) == 3) {
1510 device = fu_util_get_device (priv, values[2], error);
1511 if (device == NULL)
1512 return FALSE;
1513 } else if (g_strv_length (values) == 2) {
1514 device = fu_util_prompt_for_device (priv, NULL, error);
1515 if (device == NULL)
1516 return FALSE;
1517 } else {
1518 g_set_error_literal (error,
1519 FWUPD_ERROR,
1520 FWUPD_ERROR_INVALID_ARGS,
1521 "Invalid arguments");
1522 return FALSE;
1523 }
1524
1525 /* run vfunc */
1526 locker = fu_device_locker_new (device, error);
1527 if (locker == NULL)
1528 return FALSE;
1529 return fu_device_bind_driver (device, values[0], values[1], error);
1530}
1531
1532static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001533fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1534{
1535 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001536 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001537
1538 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001539 if (!fu_util_start_engine (priv,
1540 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1541 FU_ENGINE_LOAD_FLAG_HWINFO |
1542 FU_ENGINE_LOAD_FLAG_REMOTES,
1543 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001544 return FALSE;
1545
Richard Hughes98ca9932018-05-18 10:24:07 +01001546 /* get device */
1547 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001548 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001549 if (device == NULL)
1550 return FALSE;
1551 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001552 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001553 if (device == NULL)
1554 return FALSE;
1555 }
1556
1557 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001558 locker = fu_device_locker_new (device, error);
1559 if (locker == NULL)
1560 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001561 return fu_device_attach (device, error);
1562}
1563
Richard Hughes1d894f12018-08-31 13:05:51 +01001564static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001565fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001566{
1567 gboolean has_pending = FALSE;
1568 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001569 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1570 if (devices == NULL)
1571 return FALSE;
1572
1573 /* only start up the plugins needed */
1574 for (guint i = 0; i < devices->len; i++) {
1575 FuDevice *dev = g_ptr_array_index (devices, i);
1576 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1577 fu_engine_add_plugin_filter (priv->engine,
1578 fu_device_get_plugin (dev));
1579 has_pending = TRUE;
1580 }
1581 }
1582
1583 if (!has_pending) {
1584 g_set_error_literal (error,
1585 FWUPD_ERROR,
1586 FWUPD_ERROR_NOTHING_TO_DO,
1587 "No devices to activate");
1588 return FALSE;
1589 }
1590
1591 return TRUE;
1592}
1593
1594static gboolean
1595fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1596{
1597 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001598 g_autoptr(GPtrArray) devices = NULL;
1599
1600 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001601 if (!fu_util_check_activation_needed (priv, error))
1602 return FALSE;
1603
1604 /* load engine */
Mario Limoncielloc4f43202021-03-10 16:07:48 -06001605 if (!fu_util_start_engine (priv,
1606 FU_ENGINE_LOAD_FLAG_READONLY |
1607 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1608 FU_ENGINE_LOAD_FLAG_REMOTES |
1609 FU_ENGINE_LOAD_FLAG_HWINFO,
1610 error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001611 return FALSE;
1612
1613 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001614 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001615 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001616 if (devices == NULL)
1617 return FALSE;
1618 } else if (g_strv_length (values) == 1) {
1619 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001620 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001621 if (device == NULL)
1622 return FALSE;
1623 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1624 g_ptr_array_add (devices, device);
1625 } else {
1626 g_set_error_literal (error,
1627 FWUPD_ERROR,
1628 FWUPD_ERROR_INVALID_ARGS,
1629 "Invalid arguments");
1630 return FALSE;
1631 }
1632
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001633 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001634 /* order by device priority */
1635 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001636 for (guint i = 0; i < devices->len; i++) {
1637 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001638 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1639 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001640 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1641 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001642 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001643 /* TRANSLATORS: shown when shutting down to switch to the new version */
1644 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1645 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1646 return FALSE;
1647 }
1648
Mario Limonciello02085a02020-09-11 14:59:35 -05001649 if (!has_pending) {
1650 g_set_error_literal (error,
1651 FWUPD_ERROR,
1652 FWUPD_ERROR_NOTHING_TO_DO,
1653 "No devices to activate");
1654 return FALSE;
1655 }
1656
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001657 return TRUE;
1658}
1659
1660static gboolean
Richard Hughesf6751cd2021-01-15 16:48:25 +00001661fu_util_export_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1662{
1663 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1664 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1665 g_autoptr(GKeyFile) kf = g_key_file_new ();
1666 g_autoptr(GPtrArray) hwid_keys = NULL;
1667
1668 /* check args */
1669 if (g_strv_length (values) != 1) {
1670 g_set_error_literal (error,
1671 FWUPD_ERROR,
1672 FWUPD_ERROR_INVALID_ARGS,
1673 "Invalid arguments, expected HWIDS-FILE");
1674 return FALSE;
1675 }
1676
1677 /* setup default hwids */
1678 if (!fu_smbios_setup (smbios, error))
1679 return FALSE;
1680 if (!fu_hwids_setup (hwids, smbios, error))
1681 return FALSE;
1682
1683 /* save all keys */
1684 hwid_keys = fu_hwids_get_keys (hwids);
1685 for (guint i = 0; i < hwid_keys->len; i++) {
1686 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1687 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1688 g_key_file_set_string (kf, "HwIds", hwid_key, value);
1689 }
1690
1691 /* success */
1692 return g_key_file_save_to_file (kf, values[0], error);
1693}
1694
1695static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001696fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1697{
Richard Hughesf6751cd2021-01-15 16:48:25 +00001698 g_autoptr(FuSmbios) smbios = NULL;
Richard Hughes1d894f12018-08-31 13:05:51 +01001699 g_autoptr(FuHwids) hwids = fu_hwids_new ();
Richard Hughesf6751cd2021-01-15 16:48:25 +00001700 g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys (hwids);
Richard Hughes1d894f12018-08-31 13:05:51 +01001701
1702 /* read DMI data */
1703 if (g_strv_length (values) == 0) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001704 smbios = fu_smbios_new ();
Richard Hughes1d894f12018-08-31 13:05:51 +01001705 if (!fu_smbios_setup (smbios, error))
1706 return FALSE;
1707 } else if (g_strv_length (values) == 1) {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001708 /* a keyfile with overrides */
1709 g_autoptr(GKeyFile) kf = g_key_file_new ();
1710 if (g_key_file_load_from_file (kf, values[0], G_KEY_FILE_NONE, NULL)) {
1711 for (guint i = 0; i < hwid_keys->len; i++) {
1712 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1713 g_autofree gchar *tmp = NULL;
1714 tmp = g_key_file_get_string (kf, "HwIds", hwid_key, NULL);
1715 fu_hwids_add_smbios_override (hwids, hwid_key, tmp);
1716 }
1717 /* a DMI blob */
1718 } else {
1719 smbios = fu_smbios_new ();
1720 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1721 return FALSE;
1722 }
Richard Hughes1d894f12018-08-31 13:05:51 +01001723 } else {
1724 g_set_error_literal (error,
1725 FWUPD_ERROR,
1726 FWUPD_ERROR_INVALID_ARGS,
1727 "Invalid arguments");
1728 return FALSE;
1729 }
1730 if (!fu_hwids_setup (hwids, smbios, error))
1731 return FALSE;
1732
1733 /* show debug output */
1734 g_print ("Computer Information\n");
1735 g_print ("--------------------\n");
Richard Hughesf6751cd2021-01-15 16:48:25 +00001736 for (guint i = 0; i < hwid_keys->len; i++) {
1737 const gchar *hwid_key = g_ptr_array_index (hwid_keys, i);
1738 const gchar *value = fu_hwids_get_value (hwids, hwid_key);
1739 if (value == NULL)
Richard Hughes1d894f12018-08-31 13:05:51 +01001740 continue;
Richard Hughesf6751cd2021-01-15 16:48:25 +00001741 if (g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1742 g_strcmp0 (hwid_key, FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1743 guint64 val = g_ascii_strtoull (value, NULL, 16);
1744 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_key, val);
Richard Hughes1d894f12018-08-31 13:05:51 +01001745 } else {
Richard Hughesf6751cd2021-01-15 16:48:25 +00001746 g_print ("%s: %s\n", hwid_key, value);
Richard Hughes1d894f12018-08-31 13:05:51 +01001747 }
1748 }
1749
1750 /* show GUIDs */
1751 g_print ("\nHardware IDs\n");
1752 g_print ("------------\n");
1753 for (guint i = 0; i < 15; i++) {
1754 const gchar *keys = NULL;
1755 g_autofree gchar *guid = NULL;
1756 g_autofree gchar *key = NULL;
1757 g_autofree gchar *keys_str = NULL;
1758 g_auto(GStrv) keysv = NULL;
1759 g_autoptr(GError) error_local = NULL;
1760
1761 /* get the GUID */
1762 key = g_strdup_printf ("HardwareID-%u", i);
1763 keys = fu_hwids_get_replace_keys (hwids, key);
1764 guid = fu_hwids_get_guid (hwids, key, &error_local);
1765 if (guid == NULL) {
1766 g_print ("%s\n", error_local->message);
1767 continue;
1768 }
1769
1770 /* show what makes up the GUID */
1771 keysv = g_strsplit (keys, "&", -1);
1772 keys_str = g_strjoinv (" + ", keysv);
1773 g_print ("{%s} <- %s\n", guid, keys_str);
1774 }
1775
1776 return TRUE;
1777}
1778
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001779static gboolean
1780fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1781{
1782 const gchar *script_fn = "startup.sh";
1783 const gchar *output_fn = "firmware.bin";
1784 g_autoptr(GBytes) archive_blob = NULL;
1785 g_autoptr(GBytes) firmware_blob = NULL;
1786 if (g_strv_length (values) < 2) {
1787 g_set_error_literal (error,
1788 FWUPD_ERROR,
1789 FWUPD_ERROR_INVALID_ARGS,
1790 "Invalid arguments");
1791 return FALSE;
1792 }
1793 archive_blob = fu_common_get_contents_bytes (values[0], error);
1794 if (archive_blob == NULL)
1795 return FALSE;
1796 if (g_strv_length (values) > 2)
1797 script_fn = values[2];
1798 if (g_strv_length (values) > 3)
1799 output_fn = values[3];
1800 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1801 if (firmware_blob == NULL)
1802 return FALSE;
1803 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1804}
1805
Richard Hughes3d607622019-03-07 16:59:27 +00001806static gboolean
1807fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1808{
1809 g_autofree gchar *sig = NULL;
1810
1811 /* check args */
1812 if (g_strv_length (values) != 1) {
1813 g_set_error_literal (error,
1814 FWUPD_ERROR,
1815 FWUPD_ERROR_INVALID_ARGS,
1816 "Invalid arguments: value expected");
1817 return FALSE;
1818 }
1819
1820 /* start engine */
1821 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1822 return FALSE;
1823 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001824 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1825 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001826 if (sig == NULL)
1827 return FALSE;
1828 g_print ("%s\n", sig);
1829 return TRUE;
1830}
1831
Mario Limonciello62f84862018-10-18 13:15:23 -05001832static void
1833fu_util_device_added_cb (FwupdClient *client,
1834 FwupdDevice *device,
1835 gpointer user_data)
1836{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001837 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001838 /* TRANSLATORS: this is when a device is hotplugged */
1839 g_print ("%s\n%s", _("Device added:"), tmp);
1840}
1841
1842static void
1843fu_util_device_removed_cb (FwupdClient *client,
1844 FwupdDevice *device,
1845 gpointer user_data)
1846{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001847 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001848 /* TRANSLATORS: this is when a device is hotplugged */
1849 g_print ("%s\n%s", _("Device removed:"), tmp);
1850}
1851
1852static void
1853fu_util_device_changed_cb (FwupdClient *client,
1854 FwupdDevice *device,
1855 gpointer user_data)
1856{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001857 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001858 /* TRANSLATORS: this is when a device has been updated */
1859 g_print ("%s\n%s", _("Device changed:"), tmp);
1860}
1861
1862static void
1863fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1864{
1865 /* TRANSLATORS: this is when the daemon state changes */
1866 g_print ("%s\n", _("Changed"));
1867}
1868
1869static gboolean
1870fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1871{
1872 g_autoptr(FwupdClient) client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00001873 fwupd_client_set_main_context (client, priv->main_ctx);
Mario Limonciello62f84862018-10-18 13:15:23 -05001874
1875 /* get all the devices */
1876 if (!fwupd_client_connect (client, priv->cancellable, error))
1877 return FALSE;
1878
1879 /* watch for any hotplugged device */
1880 g_signal_connect (client, "changed",
1881 G_CALLBACK (fu_util_changed_cb), priv);
1882 g_signal_connect (client, "device-added",
1883 G_CALLBACK (fu_util_device_added_cb), priv);
1884 g_signal_connect (client, "device-removed",
1885 G_CALLBACK (fu_util_device_removed_cb), priv);
1886 g_signal_connect (client, "device-changed",
1887 G_CALLBACK (fu_util_device_changed_cb), priv);
1888 g_signal_connect (priv->cancellable, "cancelled",
1889 G_CALLBACK (fu_util_cancelled_cb), priv);
1890 g_main_loop_run (priv->loop);
1891 return TRUE;
1892}
1893
Richard Hughes15684492019-03-15 16:27:50 +00001894static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001895fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1896{
1897 g_autoptr(GPtrArray) firmware_types = NULL;
1898
1899 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001900 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001901 return FALSE;
1902
Richard Hughesb333e002021-04-01 10:40:02 +01001903 firmware_types = fu_context_get_firmware_gtype_ids (fu_engine_get_context (priv->engine));
Richard Hughes95c98a92019-10-22 16:03:15 +01001904 for (guint i = 0; i < firmware_types->len; i++) {
1905 const gchar *id = g_ptr_array_index (firmware_types, i);
1906 g_print ("%s\n", id);
1907 }
1908 if (firmware_types->len == 0) {
1909 /* TRANSLATORS: nothing found */
1910 g_print ("%s\n", _("No firmware IDs found"));
1911 return TRUE;
1912 }
1913
1914 return TRUE;
1915}
1916
1917static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001918fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001919{
1920 g_autoptr(GPtrArray) firmware_types = NULL;
1921 guint idx;
Richard Hughesb333e002021-04-01 10:40:02 +01001922 firmware_types = fu_context_get_firmware_gtype_ids (fu_engine_get_context (priv->engine));
Richard Hughes95c98a92019-10-22 16:03:15 +01001923
1924 /* TRANSLATORS: get interactive prompt */
1925 g_print ("%s\n", _("Choose a firmware type:"));
1926 /* TRANSLATORS: this is to abort the interactive prompt */
1927 g_print ("0.\t%s\n", _("Cancel"));
1928 for (guint i = 0; i < firmware_types->len; i++) {
1929 const gchar *id = g_ptr_array_index (firmware_types, i);
1930 g_print ("%u.\t%s\n", i + 1, id);
1931 }
1932 idx = fu_util_prompt_for_number (firmware_types->len);
1933 if (idx == 0) {
1934 g_set_error_literal (error,
1935 FWUPD_ERROR,
1936 FWUPD_ERROR_NOTHING_TO_DO,
1937 "Request canceled");
1938 return NULL;
1939 }
1940
1941 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1942}
1943
1944static gboolean
1945fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1946{
1947 GType gtype;
1948 g_autoptr(GBytes) blob = NULL;
1949 g_autoptr(FuFirmware) firmware = NULL;
1950 g_autofree gchar *firmware_type = NULL;
1951 g_autofree gchar *str = NULL;
1952
1953 /* check args */
1954 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1955 g_set_error_literal (error,
1956 FWUPD_ERROR,
1957 FWUPD_ERROR_INVALID_ARGS,
1958 "Invalid arguments: filename required");
1959 return FALSE;
1960 }
1961
1962 if (g_strv_length (values) == 2)
1963 firmware_type = g_strdup (values[1]);
1964
1965 /* load file */
1966 blob = fu_common_get_contents_bytes (values[0], error);
1967 if (blob == NULL)
1968 return FALSE;
1969
1970 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001971 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001972 return FALSE;
1973
1974 /* find the GType to use */
1975 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001976 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001977 if (firmware_type == NULL)
1978 return FALSE;
Richard Hughesb333e002021-04-01 10:40:02 +01001979 gtype = fu_context_get_firmware_gtype_by_id (fu_engine_get_context (priv->engine), firmware_type);
Richard Hughes95c98a92019-10-22 16:03:15 +01001980 if (gtype == G_TYPE_INVALID) {
1981 g_set_error (error,
1982 G_IO_ERROR,
1983 G_IO_ERROR_NOT_FOUND,
1984 "GType %s not supported", firmware_type);
1985 return FALSE;
1986 }
1987 firmware = g_object_new (gtype, NULL);
1988 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1989 return FALSE;
1990 str = fu_firmware_to_string (firmware);
1991 g_print ("%s", str);
1992 return TRUE;
1993}
1994
Richard Hughesdd653442020-09-22 10:23:52 +01001995static gboolean
Richard Hughes52441f22021-03-12 21:21:10 +00001996fu_util_firmware_export (FuUtilPrivate *priv, gchar **values, GError **error)
1997{
1998 FuFirmwareExportFlags flags = FU_FIRMWARE_EXPORT_FLAG_NONE;
1999 GType gtype;
2000 g_autoptr(GBytes) blob = NULL;
2001 g_autoptr(FuFirmware) firmware = NULL;
2002 g_autofree gchar *firmware_type = NULL;
2003 g_autofree gchar *str = NULL;
2004
2005 /* check args */
2006 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
2007 g_set_error_literal (error,
2008 FWUPD_ERROR,
2009 FWUPD_ERROR_INVALID_ARGS,
2010 "Invalid arguments: filename required");
2011 return FALSE;
2012 }
2013
2014 if (g_strv_length (values) == 2)
2015 firmware_type = g_strdup (values[1]);
2016
2017 /* load file */
2018 blob = fu_common_get_contents_bytes (values[0], error);
2019 if (blob == NULL)
2020 return FALSE;
2021
2022 /* load engine */
2023 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
2024 return FALSE;
2025
2026 /* find the GType to use */
2027 if (firmware_type == NULL)
2028 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
2029 if (firmware_type == NULL)
2030 return FALSE;
Richard Hughesb333e002021-04-01 10:40:02 +01002031 gtype = fu_context_get_firmware_gtype_by_id (fu_engine_get_context (priv->engine), firmware_type);
Richard Hughes52441f22021-03-12 21:21:10 +00002032 if (gtype == G_TYPE_INVALID) {
2033 g_set_error (error,
2034 G_IO_ERROR,
2035 G_IO_ERROR_NOT_FOUND,
2036 "GType %s not supported", firmware_type);
2037 return FALSE;
2038 }
2039 firmware = g_object_new (gtype, NULL);
2040 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
2041 return FALSE;
2042 if (priv->show_all)
2043 flags |= FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG;
2044 str = fu_firmware_export_to_xml (firmware, flags, error);
2045 if (str == NULL)
2046 return FALSE;
2047 g_print ("%s", str);
2048 return TRUE;
2049}
2050
2051static gboolean
Richard Hughesdd653442020-09-22 10:23:52 +01002052fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
2053{
2054 GType gtype;
2055 g_autofree gchar *firmware_type = NULL;
2056 g_autofree gchar *str = NULL;
2057 g_autoptr(FuFirmware) firmware = NULL;
2058 g_autoptr(GBytes) blob = NULL;
2059 g_autoptr(GPtrArray) images = NULL;
2060
2061 /* check args */
2062 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
2063 g_set_error_literal (error,
2064 FWUPD_ERROR,
2065 FWUPD_ERROR_INVALID_ARGS,
2066 "Invalid arguments: filename required");
2067 return FALSE;
2068 }
2069 if (g_strv_length (values) == 2)
2070 firmware_type = g_strdup (values[1]);
2071
2072 /* load file */
2073 blob = fu_common_get_contents_bytes (values[0], error);
2074 if (blob == NULL)
2075 return FALSE;
2076
2077 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002078 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01002079 return FALSE;
2080
2081 /* find the GType to use */
2082 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002083 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01002084 if (firmware_type == NULL)
2085 return FALSE;
Richard Hughesb333e002021-04-01 10:40:02 +01002086 gtype = fu_context_get_firmware_gtype_by_id (fu_engine_get_context (priv->engine), firmware_type);
Richard Hughesdd653442020-09-22 10:23:52 +01002087 if (gtype == G_TYPE_INVALID) {
2088 g_set_error (error,
2089 G_IO_ERROR,
2090 G_IO_ERROR_NOT_FOUND,
2091 "GType %s not supported", firmware_type);
2092 return FALSE;
2093 }
2094 firmware = g_object_new (gtype, NULL);
2095 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
2096 return FALSE;
2097 str = fu_firmware_to_string (firmware);
2098 g_print ("%s", str);
2099 images = fu_firmware_get_images (firmware);
2100 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00002101 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesdd653442020-09-22 10:23:52 +01002102 g_autofree gchar *fn = NULL;
2103 g_autoptr(GBytes) blob_img = NULL;
2104
Richard Hughes88dd7c42020-09-22 16:54:40 +01002105 /* get raw image without generated header, footer or crc */
Richard Hughes1981c632021-03-09 09:44:12 +00002106 blob_img = fu_firmware_get_bytes (img, error);
2107 if (blob_img == NULL)
2108 return FALSE;
2109 if (g_bytes_get_size (blob_img) == 0)
Richard Hughes88dd7c42020-09-22 16:54:40 +01002110 continue;
2111
Richard Hughesdd653442020-09-22 10:23:52 +01002112 /* use suitable filename */
Richard Hughes1981c632021-03-09 09:44:12 +00002113 if (fu_firmware_get_filename (img) != NULL) {
2114 fn = g_strdup (fu_firmware_get_filename (img));
2115 } else if (fu_firmware_get_id (img) != NULL) {
2116 fn = g_strdup_printf ("id-%s.fw", fu_firmware_get_id (img));
2117 } else if (fu_firmware_get_idx (img) != 0x0) {
2118 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_get_idx (img));
Richard Hughesdd653442020-09-22 10:23:52 +01002119 } else {
2120 fn = g_strdup_printf ("img-0x%x.fw", i);
2121 }
2122 /* TRANSLATORS: decompressing images from a container firmware */
2123 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01002124 if (!fu_common_set_contents_bytes (fn, blob_img, error))
2125 return FALSE;
2126 }
2127
2128 /* success */
2129 return TRUE;
2130}
2131
Richard Hughes0924c932020-09-22 19:07:05 +01002132static gboolean
2133fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01002134{
Richard Hughes0924c932020-09-22 19:07:05 +01002135 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01002136 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01002137 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002138 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01002139 g_autoptr(FuFirmware) firmware_dst = NULL;
2140 g_autoptr(GBytes) blob_dst = NULL;
2141 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01002142 g_autoptr(XbBuilder) builder = xb_builder_new ();
2143 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
2144 g_autoptr(XbNode) n = NULL;
2145 g_autoptr(XbSilo) silo = NULL;
2146
Richard Hughes0924c932020-09-22 19:07:05 +01002147 /* check args */
2148 if (g_strv_length (values) != 2) {
2149 g_set_error_literal (error,
2150 FWUPD_ERROR,
2151 FWUPD_ERROR_INVALID_ARGS,
2152 "Invalid arguments: filename required");
2153 return FALSE;
2154 }
2155
2156 /* load file */
2157 blob_src = fu_common_get_contents_bytes (values[0], error);
2158 if (blob_src == NULL)
2159 return FALSE;
2160
2161 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002162 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002163 return FALSE;
2164
Richard Hughes41400a82020-09-21 13:43:15 +01002165 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002166 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002167 XB_BUILDER_SOURCE_FLAG_NONE,
2168 error)) {
2169 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002170 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002171 }
2172 xb_builder_import_source (builder, source);
2173 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2174 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002175 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002176
2177 /* create FuFirmware of specific GType */
2178 n = xb_silo_query_first (silo, "firmware", error);
2179 if (n == NULL)
2180 return FALSE;
2181 tmp = xb_node_get_attr (n, "gtype");
2182 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002183 gtype = g_type_from_name (tmp);
2184 if (gtype == G_TYPE_INVALID) {
2185 g_set_error (error,
2186 G_IO_ERROR,
2187 G_IO_ERROR_NOT_FOUND,
2188 "GType %s not registered", tmp);
2189 return FALSE;
2190 }
2191 }
2192 tmp = xb_node_get_attr (n, "id");
2193 if (tmp != NULL) {
Richard Hughesb333e002021-04-01 10:40:02 +01002194 gtype = fu_context_get_firmware_gtype_by_id (fu_engine_get_context (priv->engine), tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002195 if (gtype == G_TYPE_INVALID) {
2196 g_set_error (error,
2197 G_IO_ERROR,
2198 G_IO_ERROR_NOT_FOUND,
2199 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002200 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002201 }
Richard Hughes41400a82020-09-21 13:43:15 +01002202 }
Richard Hughes0924c932020-09-22 19:07:05 +01002203 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002204 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002205 return FALSE;
2206
2207 /* write new file */
2208 blob_dst = fu_firmware_write (firmware, error);
2209 if (blob_dst == NULL)
2210 return FALSE;
2211 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2212 return FALSE;
2213
2214 /* show what we wrote */
2215 firmware_dst = g_object_new (gtype, NULL);
2216 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2217 return FALSE;
2218 str = fu_firmware_to_string (firmware_dst);
2219 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002220
2221 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002222 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002223}
2224
Richard Hughes95c98a92019-10-22 16:03:15 +01002225static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002226fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2227{
Richard Hughesb333e002021-04-01 10:40:02 +01002228 FuContext *ctx = fu_engine_get_context (priv->engine);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002229 GType gtype_dst;
2230 GType gtype_src;
2231 g_autofree gchar *firmware_type_dst = NULL;
2232 g_autofree gchar *firmware_type_src = NULL;
2233 g_autofree gchar *str_dst = NULL;
2234 g_autofree gchar *str_src = NULL;
2235 g_autoptr(FuFirmware) firmware_dst = NULL;
2236 g_autoptr(FuFirmware) firmware_src = NULL;
2237 g_autoptr(GBytes) blob_dst = NULL;
2238 g_autoptr(GBytes) blob_src = NULL;
2239 g_autoptr(GPtrArray) images = NULL;
2240
2241 /* check args */
2242 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2243 g_set_error_literal (error,
2244 FWUPD_ERROR,
2245 FWUPD_ERROR_INVALID_ARGS,
2246 "Invalid arguments: filename required");
2247 return FALSE;
2248 }
2249
2250 if (g_strv_length (values) > 2)
2251 firmware_type_src = g_strdup (values[2]);
2252 if (g_strv_length (values) > 3)
2253 firmware_type_dst = g_strdup (values[3]);
2254
2255 /* load file */
2256 blob_src = fu_common_get_contents_bytes (values[0], error);
2257 if (blob_src == NULL)
2258 return FALSE;
2259
2260 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002261 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002262 return FALSE;
2263
2264 /* find the GType to use */
2265 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002266 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002267 if (firmware_type_src == NULL)
2268 return FALSE;
2269 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002270 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002271 if (firmware_type_dst == NULL)
2272 return FALSE;
Richard Hughesb333e002021-04-01 10:40:02 +01002273 gtype_src = fu_context_get_firmware_gtype_by_id (fu_engine_get_context (priv->engine),
2274 firmware_type_src);
Richard Hughes0924c932020-09-22 19:07:05 +01002275 if (gtype_src == G_TYPE_INVALID) {
2276 g_set_error (error,
2277 G_IO_ERROR,
2278 G_IO_ERROR_NOT_FOUND,
2279 "GType %s not supported", firmware_type_src);
2280 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002281 }
Richard Hughes0924c932020-09-22 19:07:05 +01002282 firmware_src = g_object_new (gtype_src, NULL);
2283 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2284 return FALSE;
Richard Hughesb333e002021-04-01 10:40:02 +01002285 gtype_dst = fu_context_get_firmware_gtype_by_id (ctx, firmware_type_dst);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002286 if (gtype_dst == G_TYPE_INVALID) {
2287 g_set_error (error,
2288 G_IO_ERROR,
2289 G_IO_ERROR_NOT_FOUND,
2290 "GType %s not supported", firmware_type_dst);
2291 return FALSE;
2292 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002293 str_src = fu_firmware_to_string (firmware_src);
2294 g_print ("%s", str_src);
2295
2296 /* copy images */
2297 firmware_dst = g_object_new (gtype_dst, NULL);
2298 images = fu_firmware_get_images (firmware_src);
2299 for (guint i = 0; i < images->len; i++) {
Richard Hughes1981c632021-03-09 09:44:12 +00002300 FuFirmware *img = g_ptr_array_index (images, i);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002301 fu_firmware_add_image (firmware_dst, img);
2302 }
2303
2304 /* write new file */
2305 blob_dst = fu_firmware_write (firmware_dst, error);
2306 if (blob_dst == NULL)
2307 return FALSE;
2308 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2309 return FALSE;
2310 str_dst = fu_firmware_to_string (firmware_dst);
2311 g_print ("%s", str_dst);
2312
2313 /* success */
2314 return TRUE;
2315}
2316
2317static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002318fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2319{
2320 g_autofree gchar *str = NULL;
2321 g_autoptr(FuDevice) dev = NULL;
2322
2323 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002324 if (!fu_util_start_engine (priv,
2325 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2326 FU_ENGINE_LOAD_FLAG_HWINFO |
2327 FU_ENGINE_LOAD_FLAG_REMOTES,
2328 error))
Richard Hughes15684492019-03-15 16:27:50 +00002329 return FALSE;
2330
2331 /* get device */
2332 if (g_strv_length (values) == 1) {
Ricardo Cañuelo7f009672021-03-26 08:31:03 +01002333 dev = fu_util_get_device (priv, values[0], error);
Richard Hughes15684492019-03-15 16:27:50 +00002334 if (dev == NULL)
2335 return FALSE;
2336 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002337 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002338 if (dev == NULL)
2339 return FALSE;
2340 }
2341
2342 /* add checksums */
2343 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2344 return FALSE;
2345
2346 /* show checksums */
2347 str = fu_device_to_string (dev);
2348 g_print ("%s\n", str);
2349 return TRUE;
2350}
2351
Mario Limonciellofe593942019-04-03 13:48:52 -05002352static gboolean
2353fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2354{
2355 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002356 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002357 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002358
2359 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002360 if (!fu_util_start_engine (priv,
2361 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2362 FU_ENGINE_LOAD_FLAG_HWINFO |
2363 FU_ENGINE_LOAD_FLAG_REMOTES,
2364 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002365 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002366 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002367
2368 /* get all devices from the history database */
2369 devices = fu_engine_get_history (priv->engine, error);
2370 if (devices == NULL)
2371 return FALSE;
2372
2373 /* show each device */
2374 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002375 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002376 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002377 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002378 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002379 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002380
Richard Hughes747f5702019-08-06 14:27:26 +01002381 if (!fu_util_filter_device (priv, dev))
2382 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002383 child = g_node_append_data (root, dev);
2384
2385 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002386 if (rel == NULL)
2387 continue;
2388 remote = fwupd_release_get_remote_id (rel);
2389
2390 /* doesn't actually map to remote */
2391 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002392 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002393 continue;
2394 }
2395
2396 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002397 rels = fu_engine_get_releases (priv->engine,
2398 priv->request,
2399 fwupd_device_get_id (dev),
2400 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002401 if (rels == NULL)
2402 return FALSE;
2403
2404 /* map to a release in client */
2405 for (guint j = 0; j < rels->len; j++) {
2406 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2407 if (g_strcmp0 (remote,
2408 fwupd_release_get_remote_id (rel2)) != 0)
2409 continue;
2410 if (g_strcmp0 (fwupd_release_get_version (rel),
2411 fwupd_release_get_version (rel2)) != 0)
2412 continue;
2413 g_node_append_data (child, g_object_ref (rel2));
2414 rel = NULL;
2415 break;
2416 }
2417
2418 /* didn't match anything */
2419 if (rels->len == 0 || rel != NULL) {
2420 g_node_append_data (child, rel);
2421 continue;
2422 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002423
Mario Limonciellofe593942019-04-03 13:48:52 -05002424 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002425 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002426
2427 return TRUE;
2428}
2429
Richard Hughesfd7e9942020-01-17 14:09:13 +00002430static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002431fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2432{
Richard Hughesc5710d92020-06-26 11:08:25 +01002433 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002434 g_autofree gchar *fn_raw = NULL;
2435 g_autofree gchar *fn_sig = NULL;
2436 g_autoptr(GBytes) bytes_raw = NULL;
2437 g_autoptr(GBytes) bytes_sig = NULL;
2438
Richard Hughes4959baa2020-01-17 14:33:00 +00002439 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002440 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2441 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002442 g_set_error (error,
2443 FWUPD_ERROR,
2444 FWUPD_ERROR_NOTHING_TO_DO,
2445 "no metadata signature URI available for %s",
2446 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002447 return FALSE;
2448 }
2449 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002450 if (!fu_common_mkdir_parent (fn_sig, error))
2451 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002452 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002453 return FALSE;
2454 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2455 if (bytes_sig == NULL)
2456 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002457 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2458 return FALSE;
2459
2460 /* payload */
2461 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2462 if (metadata_uri == NULL) {
2463 g_set_error (error,
2464 FWUPD_ERROR,
2465 FWUPD_ERROR_NOTHING_TO_DO,
2466 "no metadata URI available for %s",
2467 fwupd_remote_get_id (remote));
2468 return FALSE;
2469 }
2470 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2471 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2472 return FALSE;
2473 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2474 if (bytes_raw == NULL)
2475 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002476
2477 /* send to daemon */
2478 g_debug ("updating %s", fwupd_remote_get_id (remote));
2479 return fu_engine_update_metadata_bytes (priv->engine,
2480 fwupd_remote_get_id (remote),
2481 bytes_raw,
2482 bytes_sig,
2483 error);
2484
2485}
2486
2487static gboolean
2488fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2489{
2490 g_autoptr(GPtrArray) remotes = NULL;
2491
2492 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002493 if (!fu_util_start_engine (priv,
2494 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2495 FU_ENGINE_LOAD_FLAG_HWINFO |
2496 FU_ENGINE_LOAD_FLAG_REMOTES,
2497 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002498 return FALSE;
2499
2500 /* download new metadata */
2501 remotes = fu_engine_get_remotes (priv->engine, error);
2502 if (remotes == NULL)
2503 return FALSE;
2504 for (guint i = 0; i < remotes->len; i++) {
2505 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2506 if (!fwupd_remote_get_enabled (remote))
2507 continue;
2508 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2509 continue;
2510 if (!fu_util_refresh_remote (priv, remote, error))
2511 return FALSE;
2512 }
2513 return TRUE;
2514}
2515
2516static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002517fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2518{
2519 g_autoptr(GNode) root = g_node_new (NULL);
2520 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002521 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002522
2523 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002524 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002525 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002526 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002527
2528 /* list remotes */
2529 remotes = fu_engine_get_remotes (priv->engine, error);
2530 if (remotes == NULL)
2531 return FALSE;
2532 if (remotes->len == 0) {
2533 g_set_error_literal (error,
2534 FWUPD_ERROR,
2535 FWUPD_ERROR_NOTHING_TO_DO,
2536 "no remotes available");
2537 return FALSE;
2538 }
2539 for (guint i = 0; i < remotes->len; i++) {
2540 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2541 g_node_append_data (root, remote_tmp);
2542 }
2543 fu_util_print_tree (root, title);
2544
2545 return TRUE;
2546}
2547
Richard Hughes196c6c62020-05-11 19:42:47 +01002548static gboolean
2549fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2550{
Richard Hughes5c82b942020-09-14 12:24:06 +01002551 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002552 g_autoptr(FuSecurityAttrs) attrs = NULL;
2553 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002554 g_autofree gchar *str = NULL;
2555
2556 /* not ready yet */
2557 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2558 g_set_error_literal (error,
2559 FWUPD_ERROR,
2560 FWUPD_ERROR_NOT_SUPPORTED,
2561 "The HSI specification is not yet complete. "
2562 "To ignore this warning, use --force");
2563 return FALSE;
2564 }
2565
Richard Hughesc7d870a2020-12-10 10:05:35 +00002566 if (!fu_util_start_engine (priv,
2567 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2568 FU_ENGINE_LOAD_FLAG_HWINFO |
2569 FU_ENGINE_LOAD_FLAG_REMOTES,
2570 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002571 return FALSE;
2572
2573 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2574 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2575 fu_engine_get_host_security_id (priv->engine));
2576
Richard Hughes5c82b942020-09-14 12:24:06 +01002577 /* show or hide different elements */
2578 if (priv->show_all) {
2579 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2580 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2581 }
2582
Richard Hughes196c6c62020-05-11 19:42:47 +01002583 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002584 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002585 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002586 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002587 g_print ("%s\n", str);
2588 return TRUE;
2589}
2590
Richard Hughesa83deb42020-08-12 12:45:36 +01002591static FuVolume *
2592fu_util_prompt_for_volume (GError **error)
2593{
2594 FuVolume *volume;
2595 guint idx;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002596 gboolean is_fallback = FALSE;
Richard Hughesa83deb42020-08-12 12:45:36 +01002597 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002598 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2599 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002600
2601 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002602 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2603 if (volumes == NULL) {
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002604 is_fallback = TRUE;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002605 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2606 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2607 if (volumes == NULL) {
2608 g_prefix_error (error, "%s: ", error_local->message);
2609 return NULL;
2610 }
2611 }
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002612 /* on fallback: only add internal vfat partitions */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002613 for (guint i = 0; i < volumes->len; i++) {
2614 FuVolume *vol = g_ptr_array_index (volumes, i);
2615 g_autofree gchar *type = fu_volume_get_id_type (vol);
2616 if (type == NULL)
2617 continue;
Đoàn Trần Công Danhe9adb952021-01-23 11:36:26 +07002618 if (is_fallback && !fu_volume_is_internal (vol))
Mario Limonciello56d816a2020-11-11 16:59:30 -06002619 continue;
2620 if (g_strcmp0 (type, "vfat") == 0)
2621 g_ptr_array_add (volumes_vfat, vol);
2622 }
2623 if (volumes_vfat->len == 1) {
2624 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002625 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002626 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2627 return g_object_ref (volume);
2628 }
2629
2630 /* TRANSLATORS: get interactive prompt */
2631 g_print ("%s\n", _("Choose a volume:"));
2632 /* TRANSLATORS: this is to abort the interactive prompt */
2633 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002634 for (guint i = 0; i < volumes_vfat->len; i++) {
2635 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002636 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2637 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002638 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002639 if (idx == 0) {
2640 g_set_error_literal (error,
2641 FWUPD_ERROR,
2642 FWUPD_ERROR_NOTHING_TO_DO,
2643 "Request canceled");
2644 return NULL;
2645 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002646 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002647 return g_object_ref (volume);
2648
2649}
2650
2651static gboolean
2652fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2653{
2654 g_autoptr(FuVolume) volume = NULL;
2655 volume = fu_util_prompt_for_volume (error);
2656 if (volume == NULL)
2657 return FALSE;
2658 return fu_volume_mount (volume, error);
2659}
2660
2661static gboolean
2662fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2663{
2664 g_autoptr(FuVolume) volume = NULL;
2665 volume = fu_util_prompt_for_volume (error);
2666 if (volume == NULL)
2667 return FALSE;
2668 return fu_volume_unmount (volume, error);
2669}
2670
2671static gboolean
2672fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2673{
2674 g_autofree gchar *mount_point = NULL;
2675 g_autoptr(FuDeviceLocker) locker = NULL;
2676 g_autoptr(FuVolume) volume = NULL;
2677 g_autoptr(GPtrArray) files = NULL;
2678
2679 volume = fu_util_prompt_for_volume (error);
2680 if (volume == NULL)
2681 return FALSE;
2682 locker = fu_volume_locker (volume, error);
2683 if (locker == NULL)
2684 return FALSE;
2685 mount_point = fu_volume_get_mount_point (volume);
2686 files = fu_common_get_files_recursive (mount_point, error);
2687 if (files == NULL)
2688 return FALSE;
2689 for (guint i = 0; i < files->len; i++) {
2690 const gchar *fn = g_ptr_array_index (files, i);
2691 g_print ("%s\n", fn);
2692 }
2693 return TRUE;
2694}
2695
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002696static gboolean
2697_g_str_equal0 (gconstpointer str1, gconstpointer str2)
2698{
2699 return g_strcmp0 (str1, str2) == 0;
2700}
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002701
2702static gboolean
2703fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2704{
2705 const gchar *branch;
2706 g_autoptr(FwupdRelease) rel = NULL;
2707 g_autoptr(GPtrArray) rels = NULL;
2708 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2709 g_autoptr(FuDevice) dev = NULL;
2710
2711 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002712 if (!fu_util_start_engine (priv,
2713 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2714 FU_ENGINE_LOAD_FLAG_HWINFO |
2715 FU_ENGINE_LOAD_FLAG_REMOTES,
2716 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002717 return FALSE;
2718
2719 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002720 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00002721 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002722 if (g_strv_length (values) == 1)
2723 dev = fu_util_get_device (priv, values[1], error);
2724 else
2725 dev = fu_util_prompt_for_device (priv, NULL, error);
2726 if (dev == NULL)
2727 return FALSE;
2728 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2729 g_set_error_literal (error,
2730 FWUPD_ERROR,
2731 FWUPD_ERROR_NOT_SUPPORTED,
2732 "Multiple branches not available");
2733 return FALSE;
2734 }
2735
2736 /* get all releases, including the alternate branch versions */
2737 rels = fu_engine_get_releases (priv->engine,
2738 priv->request,
2739 fu_device_get_id (dev),
2740 error);
2741 if (rels == NULL)
2742 return FALSE;
2743
2744 /* get all the unique branches */
2745 for (guint i = 0; i < rels->len; i++) {
2746 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002747 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002748#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002749 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002750 _g_str_equal0, NULL))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002751 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002752#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002753 g_ptr_array_add (branches, g_strdup (branch_tmp));
2754 }
2755
2756 /* branch name is optional */
2757 if (g_strv_length (values) > 1) {
2758 branch = values[1];
2759 } else if (branches->len == 1) {
2760 branch = g_ptr_array_index (branches, 0);
2761 } else {
2762 guint idx;
2763
2764 /* TRANSLATORS: get interactive prompt, where branch is the
2765 * supplier of the firmware, e.g. "non-free" or "free" */
2766 g_print ("%s\n", _("Choose a branch:"));
2767 /* TRANSLATORS: this is to abort the interactive prompt */
2768 g_print ("0.\t%s\n", _("Cancel"));
2769 for (guint i = 0; i < branches->len; i++) {
2770 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002771 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002772 }
2773 idx = fu_util_prompt_for_number (branches->len);
2774 if (idx == 0) {
2775 g_set_error_literal (error,
2776 FWUPD_ERROR,
2777 FWUPD_ERROR_NOTHING_TO_DO,
2778 "Request canceled");
2779 return FALSE;
2780 }
2781 branch = g_ptr_array_index (branches, idx - 1);
2782 }
2783
2784 /* sanity check */
2785 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2786 g_set_error (error,
2787 FWUPD_ERROR,
2788 FWUPD_ERROR_NOT_SUPPORTED,
2789 "Device %s is already on branch %s",
2790 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002791 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002792 return FALSE;
2793 }
2794
2795 /* the releases are ordered by version */
2796 for (guint j = 0; j < rels->len; j++) {
2797 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2798 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2799 rel = g_object_ref (rel_tmp);
2800 break;
2801 }
2802 }
2803 if (rel == NULL) {
2804 g_set_error (error,
2805 FWUPD_ERROR,
2806 FWUPD_ERROR_NOT_SUPPORTED,
2807 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002808 fu_util_branch_for_display (branch));
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002809 return FALSE;
2810 }
2811
2812 /* we're switching branch */
2813 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2814 return FALSE;
2815
2816 /* update the console if composite devices are also updated */
2817 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2818 g_signal_connect (priv->engine, "device-changed",
2819 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2820 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2821 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2822 if (!fu_util_install_release (priv, rel, error))
2823 return FALSE;
2824 fu_util_display_current_message (priv);
2825
2826 /* we don't want to ask anything */
2827 if (priv->no_reboot_check) {
2828 g_debug ("skipping reboot check");
2829 return TRUE;
2830 }
2831
2832 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2833}
2834
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002835static gboolean
2836fu_util_lock (FuUtilPrivate *priv, GError **error)
2837{
2838#ifdef HAVE_WRLCK
2839 struct flock lockp = {
2840 .l_type = F_WRLCK,
2841 .l_whence = SEEK_SET,
2842 };
Daniel Campelloaf56c3b2021-05-05 15:04:17 +00002843 g_autofree gchar *lockdir = NULL;
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002844 g_autofree gchar *lockfn = NULL;
2845
2846 /* open file */
Daniel Campelloaf56c3b2021-05-05 15:04:17 +00002847 lockdir = fu_common_get_path (FU_PATH_KIND_LOCKDIR);
2848 lockfn = g_build_filename (lockdir, "fwupdtool", NULL);
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002849 if (!fu_common_mkdir_parent (lockfn, error))
2850 return FALSE;
2851 priv->lock_fd = g_open (lockfn, O_RDWR | O_CREAT, S_IRWXU);
2852 if (priv->lock_fd < 0) {
2853 g_set_error (error,
2854 FWUPD_ERROR,
2855 FWUPD_ERROR_NOT_SUPPORTED,
2856 "failed to open %s",
2857 lockfn);
2858 return FALSE;
2859 }
2860
2861 /* write lock */
Norbert Kamiński58fbbc22021-04-19 12:39:47 +02002862#ifdef HAVE_OFD
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002863 if (fcntl (priv->lock_fd, F_OFD_SETLK, &lockp) < 0) {
2864 g_set_error (error,
2865 FWUPD_ERROR,
2866 FWUPD_ERROR_NOT_SUPPORTED,
2867 "another instance has locked %s",
2868 lockfn);
2869 return FALSE;
2870 }
Norbert Kamiński58fbbc22021-04-19 12:39:47 +02002871#else
2872 if (fcntl (priv->lock_fd, F_SETLK, &lockp) < 0) {
2873 g_set_error (error,
2874 FWUPD_ERROR,
2875 FWUPD_ERROR_NOT_SUPPORTED,
2876 "another instance has locked %s",
2877 lockfn);
2878 return FALSE;
2879 }
2880#endif
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002881
2882 /* success */
2883 g_debug ("locked %s", lockfn);
2884#endif
2885 return TRUE;
2886}
2887
Richard Hughesb5976832018-05-18 10:02:09 +01002888int
2889main (int argc, char *argv[])
2890{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002891 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002892 gboolean allow_older = FALSE;
2893 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002894 gboolean force = FALSE;
2895 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002896 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002897 gboolean ignore_checksum = FALSE;
2898 gboolean ignore_power = FALSE;
2899 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002900 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002901 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002902 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2903 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002904 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002905 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002906 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002907 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002908 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2909 /* TRANSLATORS: command line option */
2910 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002911 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2912 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002913 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002914 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2915 /* TRANSLATORS: command line option */
2916 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002917 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2918 /* TRANSLATORS: command line option */
2919 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002920 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2921 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002922 _("Force the action by relaxing some runtime checks"), NULL },
2923 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2924 /* TRANSLATORS: command line option */
2925 _("Ignore firmware checksum failures"), NULL },
2926 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2927 /* TRANSLATORS: command line option */
2928 _("Ignore firmware hardware mismatch failures"), NULL },
2929 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2930 /* TRANSLATORS: command line option */
2931 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002932 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2933 /* TRANSLATORS: command line option */
John Karahalisae690c52021-04-23 07:18:26 -04002934 _("Do not check or prompt for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002935 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2936 /* TRANSLATORS: command line option */
2937 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002938 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2939 /* TRANSLATORS: command line option */
2940 _("Show all results"), NULL },
2941 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002942 /* TRANSLATORS: command line option */
2943 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002944 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002945 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002946 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002947 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2948 /* TRANSLATORS: command line option */
2949 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002950 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002951 /* TRANSLATORS: command line option */
2952 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002953 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002954 /* TRANSLATORS: command line option */
2955 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002956 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2957 /* TRANSLATORS: command line option */
2958 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002959 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2960 /* TRANSLATORS: command line option */
2961 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002962 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2963 /* TRANSLATORS: command line option */
2964 _("Filter with a set of device flags using a ~ prefix to "
2965 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002966 { NULL}
2967 };
2968
Richard Hughes429f72b2020-01-16 12:18:19 +00002969#ifdef _WIN32
2970 /* workaround Windows setting the codepage to 1252 */
2971 g_setenv ("LANG", "C.UTF-8", FALSE);
2972#endif
2973
Richard Hughesb5976832018-05-18 10:02:09 +01002974 setlocale (LC_ALL, "");
2975
Richard Hughes668ee212019-11-22 09:17:46 +00002976 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002977 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2978 textdomain (GETTEXT_PACKAGE);
2979
Richard Hughes9cf5f8f2021-04-14 20:03:55 +01002980 /* ensure single instance */
2981 if (!fu_util_lock (priv, &error)) {
2982 /* TRANSLATORS: another fwupdtool instance is already running */
2983 g_print ("%s: %s\n", _("Failed to lock"), error->message);
2984 return EXIT_FAILURE;
2985 }
2986
Richard Hughes01c0bad2019-11-22 09:08:51 +00002987#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002988 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002989 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002990 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002991 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002992#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002993
2994 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002995 priv->main_ctx = g_main_context_new ();
2996 priv->loop = g_main_loop_new (priv->main_ctx, FALSE);
Richard Hughesb5976832018-05-18 10:02:09 +01002997 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002998 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002999
3000 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00003001 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05003002 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003003 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3004 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05003005 /* TRANSLATORS: command description */
3006 _("Build firmware using a sandbox"),
3007 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00003008 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01003009 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003010 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3011 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01003012 /* TRANSLATORS: command description */
3013 _("Dump SMBIOS data from a file"),
3014 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00003015 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01003016 "get-plugins",
3017 NULL,
3018 /* TRANSLATORS: command description */
3019 _("Get all enabled plugins registered with the system"),
3020 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00003021 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05003022 "get-details",
3023 NULL,
3024 /* TRANSLATORS: command description */
3025 _("Gets details about a firmware file"),
3026 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00003027 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05003028 "get-history",
3029 NULL,
3030 /* TRANSLATORS: command description */
3031 _("Show history of firmware updates"),
3032 fu_util_get_history);
3033 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05003034 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003035 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3036 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06003037 /* TRANSLATORS: command description */
3038 _("Gets the list of updates for connected hardware"),
3039 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00003040 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01003041 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01003042 NULL,
3043 /* TRANSLATORS: command description */
3044 _("Get all devices that support firmware updates"),
3045 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00003046 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01003047 "get-device-flags",
3048 NULL,
3049 /* TRANSLATORS: command description */
3050 _("Get all device flags supported by fwupd"),
3051 fu_util_get_device_flags);
3052 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01003053 "watch",
3054 NULL,
3055 /* TRANSLATORS: command description */
Piotr Drąg472fa592018-06-06 14:53:48 +02003056 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01003057 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00003058 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01003059 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003060 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3061 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01003062 /* TRANSLATORS: command description */
3063 _("Install a firmware blob on a device"),
3064 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00003065 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01003066 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003067 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3068 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01003069 /* TRANSLATORS: command description */
3070 _("Install a firmware file on this hardware"),
3071 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00003072 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01003073 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003074 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3075 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01003076 /* TRANSLATORS: command description */
3077 _("Reinstall firmware on a device"),
3078 fu_util_reinstall);
3079 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01003080 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003081 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3082 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01003083 /* TRANSLATORS: command description */
3084 _("Attach to firmware mode"),
3085 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00003086 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01003087 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003088 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3089 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01003090 /* TRANSLATORS: command description */
3091 _("Detach to bootloader mode"),
3092 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00003093 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01003094 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003095 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3096 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01003097 /* TRANSLATORS: command description */
3098 _("Unbind current driver"),
3099 fu_util_unbind_driver);
3100 fu_util_cmd_array_add (cmd_array,
3101 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003102 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3103 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01003104 /* TRANSLATORS: command description */
3105 _("Bind new kernel driver"),
3106 fu_util_bind_driver);
3107 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06003108 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003109 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3110 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06003111 /* TRANSLATORS: command description */
3112 _("Activate pending devices"),
3113 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00003114 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01003115 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003116 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
Richard Hughesf6751cd2021-01-15 16:48:25 +00003117 _("[SMBIOS-FILE|HWIDS-FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01003118 /* TRANSLATORS: command description */
3119 _("Return all the hardware IDs for the machine"),
3120 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00003121 fu_util_cmd_array_add (cmd_array,
Richard Hughesf6751cd2021-01-15 16:48:25 +00003122 "export-hwids",
3123 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3124 _("HWIDS-FILE"),
3125 /* TRANSLATORS: command description */
3126 _("Save a file that allows generation of hardware IDs"),
3127 fu_util_export_hwids);
3128 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05003129 "monitor",
3130 NULL,
3131 /* TRANSLATORS: command description */
3132 _("Monitor the daemon for events"),
3133 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00003134 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05003135 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003136 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3137 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06003138 /* TRANSLATORS: command description */
3139 _("Update all devices that match local metadata"),
3140 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00003141 fu_util_cmd_array_add (cmd_array,
3142 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003143 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3144 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00003145 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00003146 C_("command-description",
3147 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00003148 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00003149 fu_util_cmd_array_add (cmd_array,
3150 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003151 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3152 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00003153 /* TRANSLATORS: command description */
3154 _("Update the stored metadata with current contents"),
3155 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01003156 fu_util_cmd_array_add (cmd_array,
Richard Hughese65d28d2021-04-15 21:49:10 +01003157 "firmware-sign",
3158 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3159 _("FILENAME CERTIFICATE PRIVATE-KEY"),
3160 /* TRANSLATORS: command description */
3161 _("Sign a firmware with a new key"),
3162 fu_util_firmware_sign);
3163 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01003164 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003165 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3166 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00003167 /* TRANSLATORS: command description */
3168 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01003169 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00003170 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00003171 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003172 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3173 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00003174 /* TRANSLATORS: command description */
3175 _("Convert a firmware file"),
3176 fu_util_firmware_convert);
3177 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01003178 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003179 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3180 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01003181 /* TRANSLATORS: command description */
3182 _("Build a firmware file"),
3183 fu_util_firmware_build);
3184 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003185 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003186 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3187 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01003188 /* TRANSLATORS: command description */
3189 _("Parse and show details about a firmware file"),
3190 fu_util_firmware_parse);
3191 fu_util_cmd_array_add (cmd_array,
Richard Hughes52441f22021-03-12 21:21:10 +00003192 "firmware-export",
3193 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3194 _("FILENAME [FIRMWARE-TYPE]"),
3195 /* TRANSLATORS: command description */
3196 _("Export a firmware file structure to XML"),
3197 fu_util_firmware_export);
3198 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01003199 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003200 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3201 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01003202 /* TRANSLATORS: command description */
3203 _("Extract a firmware blob to images"),
3204 fu_util_firmware_extract);
3205 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01003206 "get-firmware-types",
3207 NULL,
3208 /* TRANSLATORS: command description */
3209 _("List the available firmware types"),
3210 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00003211 fu_util_cmd_array_add (cmd_array,
3212 "get-remotes",
3213 NULL,
3214 /* TRANSLATORS: command description */
3215 _("Gets the configured remotes"),
3216 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00003217 fu_util_cmd_array_add (cmd_array,
3218 "refresh",
3219 NULL,
3220 /* TRANSLATORS: command description */
3221 _("Refresh metadata from remote server"),
3222 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01003223 fu_util_cmd_array_add (cmd_array,
3224 "security",
3225 NULL,
3226 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003227 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003228 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01003229 fu_util_cmd_array_add (cmd_array,
3230 "esp-mount",
3231 NULL,
3232 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003233 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003234 fu_util_esp_mount);
3235 fu_util_cmd_array_add (cmd_array,
3236 "esp-unmount",
3237 NULL,
3238 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003239 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003240 fu_util_esp_unmount);
3241 fu_util_cmd_array_add (cmd_array,
3242 "esp-list",
3243 NULL,
3244 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003245 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01003246 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003247 fu_util_cmd_array_add (cmd_array,
3248 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003249 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3250 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003251 /* TRANSLATORS: command description */
3252 _("Switch the firmware branch on the device"),
3253 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01003254
3255 /* do stuff on ctrl+c */
3256 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003257#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003258 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3259 SIGINT, fu_util_sigint_cb,
3260 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003261#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003262 g_signal_connect (priv->cancellable, "cancelled",
3263 G_CALLBACK (fu_util_cancelled_cb), priv);
3264
3265 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003266 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003267
Mario Limonciello3f243a92019-01-21 22:05:23 -06003268 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003269 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003270 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003271 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003272 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003273 } else {
3274 /* set our implemented feature set */
3275 fu_engine_request_set_feature_flags (priv->request,
3276 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003277 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003278 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003279 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003280
Richard Hughesb5976832018-05-18 10:02:09 +01003281 /* get a list of the commands */
3282 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003283 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003284 g_option_context_set_summary (priv->context, cmd_descriptions);
3285 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003286 /* TRANSLATORS: CLI description */
3287 _("This tool allows an administrator to use the fwupd plugins "
3288 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003289
3290 /* TRANSLATORS: program name */
3291 g_set_application_name (_("Firmware Utility"));
3292 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003293 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003294 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3295 if (!ret) {
3296 /* TRANSLATORS: the user didn't read the man page */
3297 g_print ("%s: %s\n", _("Failed to parse arguments"),
3298 error->message);
3299 return EXIT_FAILURE;
3300 }
3301
Richard Hughes0e46b222019-09-05 12:13:35 +01003302 /* allow disabling SSL strict mode for broken corporate proxies */
3303 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003304 g_autofree gchar *fmt = NULL;
3305 /* TRANSLATORS: this is a prefix on the console */
3306 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003307 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003308 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3309 "to do this automatically in the future "
3310 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003311 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3312 }
3313
Richard Hughes747f5702019-08-06 14:27:26 +01003314 /* parse filter flags */
3315 if (filter != NULL) {
3316 if (!fu_util_parse_filter_flags (filter,
3317 &priv->filter_include,
3318 &priv->filter_exclude,
3319 &error)) {
3320 /* TRANSLATORS: the user didn't read the man page */
3321 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3322 error->message);
3323 return EXIT_FAILURE;
3324 }
3325 }
3326
3327
Richard Hughes460226a2018-05-21 20:56:21 +01003328 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003329 if (allow_reinstall)
3330 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3331 if (allow_older)
3332 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003333 if (allow_branch_switch)
3334 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3335 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003336 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003337 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3338 }
3339 if (ignore_checksum)
3340 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3341 if (ignore_vid_pid)
3342 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3343 if (ignore_power)
3344 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003345
Richard Hughes98ca9932018-05-18 10:24:07 +01003346 /* load engine */
3347 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3348 g_signal_connect (priv->engine, "device-added",
3349 G_CALLBACK (fu_main_engine_device_added_cb),
3350 priv);
3351 g_signal_connect (priv->engine, "device-removed",
3352 G_CALLBACK (fu_main_engine_device_removed_cb),
3353 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003354 g_signal_connect (priv->engine, "status-changed",
3355 G_CALLBACK (fu_main_engine_status_changed_cb),
3356 priv);
3357 g_signal_connect (priv->engine, "percentage-changed",
3358 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3359 priv);
3360
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003361 /* just show versions and exit */
3362 if (version) {
3363 g_autofree gchar *version_str = fu_util_get_versions ();
3364 g_print ("%s\n", version_str);
3365 return EXIT_SUCCESS;
3366 }
3367
Richard Hughes85226fd2020-06-30 14:43:48 +01003368 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003369 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3370 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3371
Richard Hughesb5976832018-05-18 10:02:09 +01003372 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003373 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003374 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003375 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003376 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003377 /* TRANSLATORS: error message explaining command to run to how to get help */
3378 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3379 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3380 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003381 return EXIT_NOTHING_TO_DO;
3382 }
Richard Hughesb5976832018-05-18 10:02:09 +01003383 return EXIT_FAILURE;
3384 }
3385
3386 /* success */
3387 return EXIT_SUCCESS;
3388}