blob: 4d803a408220068c04d95be82f2fcf1ca177d8ca [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughesb5976832018-05-18 10:02:09 +01002 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
3 *
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 Hughes3d178be2018-08-30 11:14:24 +010021#include <libsoup/soup.h>
Richard Hughesd5aab652020-02-25 12:47:50 +000022#include <jcat.h>
Richard Hughesb5976832018-05-18 10:02:09 +010023
Mario Limonciello7a3df4b2019-01-31 10:27:22 -060024#include "fu-device-private.h"
Richard Hughes98ca9932018-05-18 10:24:07 +010025#include "fu-engine.h"
Mario Limonciello96a0dd52019-02-25 13:50:03 -060026#include "fu-history.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010027#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010028#include "fu-progressbar.h"
Richard Hughesf58ac732020-05-12 15:23:44 +010029#include "fu-security-attrs-private.h"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060030#include "fu-smbios-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010031#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050032#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060033#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060034#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010035
Richard Hughes3d005222019-05-17 14:02:41 +010036#ifdef HAVE_SYSTEMD
37#include "fu-systemd.h"
38#endif
39
Richard Hughesb5976832018-05-18 10:02:09 +010040/* custom return code */
41#define EXIT_NOTHING_TO_DO 2
42
Mario Limonciello3f243a92019-01-21 22:05:23 -060043typedef enum {
44 FU_UTIL_OPERATION_UNKNOWN,
45 FU_UTIL_OPERATION_UPDATE,
46 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000047 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060048 FU_UTIL_OPERATION_LAST
49} FuUtilOperation;
50
Richard Hughesc77e1112019-03-01 10:16:26 +000051struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010052 GCancellable *cancellable;
53 GMainLoop *loop;
54 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010055 FuEngine *engine;
Richard Hughesdf89cd52020-06-26 20:25:18 +010056 FuEngineRequest *request;
Richard Hughesb5976832018-05-18 10:02:09 +010057 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060058 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050059 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000060 gboolean prepare_blob;
61 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060062 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010063 FwupdInstallFlags flags;
Richard Hughes5c82b942020-09-14 12:24:06 +010064 gboolean show_all;
Richard Hughes0e46b222019-09-05 12:13:35 +010065 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050066 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060067 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050068 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060069 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060070 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010071 FwupdDeviceFlags filter_include;
72 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000073};
Richard Hughesb5976832018-05-18 10:02:09 +010074
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050075static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060076fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
77{
78 g_autoptr(JsonBuilder) builder = NULL;
79 g_autoptr(JsonGenerator) json_generator = NULL;
80 g_autoptr(JsonNode) json_root = NULL;
81 g_autoptr(GPtrArray) devices = NULL;
82 g_autofree gchar *state = NULL;
83 g_autofree gchar *dirname = NULL;
84 g_autofree gchar *filename = NULL;
85
86 if (!priv->enable_json_state)
87 return TRUE;
88
89 devices = fu_engine_get_devices (priv->engine, error);
90 if (devices == NULL)
91 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000092 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060093
94 /* create header */
95 builder = json_builder_new ();
96 json_builder_begin_object (builder);
97
98 /* add each device */
99 json_builder_set_member_name (builder, "Devices");
100 json_builder_begin_array (builder);
101 for (guint i = 0; i < devices->len; i++) {
102 FwupdDevice *dev = g_ptr_array_index (devices, i);
103 json_builder_begin_object (builder);
104 fwupd_device_to_json (dev, builder);
105 json_builder_end_object (builder);
106 }
107 json_builder_end_array (builder);
108 json_builder_end_object (builder);
109
110 /* export as a string */
111 json_root = json_builder_get_root (builder);
112 json_generator = json_generator_new ();
113 json_generator_set_pretty (json_generator, TRUE);
114 json_generator_set_root (json_generator, json_root);
115 state = json_generator_to_data (json_generator, NULL);
116 if (state == NULL)
117 return FALSE;
118 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
119 filename = g_build_filename (dirname, "state.json", NULL);
120 return g_file_set_contents (filename, state, -1, error);
121}
122
123static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000124fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600125{
126 g_autoptr(GError) error_local = NULL;
127
Richard Hughesd92ccca2019-05-20 11:28:31 +0100128#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100129 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500130 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100131#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000132 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000133 return FALSE;
134 if (fu_engine_get_tainted (priv->engine)) {
135 g_printerr ("WARNING: This tool has loaded 3rd party code and "
136 "is no longer supported by the upstream developers!\n");
137 }
138 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500139}
140
Richard Hughesb5976832018-05-18 10:02:09 +0100141static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500142fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
143{
144 g_autofree gchar *path = g_path_get_dirname (value);
145 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
146 g_prefix_error (error,
147 "Unable to access %s. You may need to copy %s to %s: ",
148 path, value, g_getenv ("HOME"));
149 }
150}
151
152static void
Richard Hughesb5976832018-05-18 10:02:09 +0100153fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
154{
155 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
156 /* TRANSLATORS: this is when a device ctrl+c's a watch */
157 g_print ("%s\n", _("Cancelled"));
158 g_main_loop_quit (priv->loop);
159}
160
161static gboolean
162fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
163{
164 g_autofree gchar *tmp = NULL;
165 g_autoptr(FuSmbios) smbios = NULL;
166 if (g_strv_length (values) < 1) {
167 g_set_error_literal (error,
168 FWUPD_ERROR,
169 FWUPD_ERROR_INVALID_ARGS,
170 "Invalid arguments");
171 return FALSE;
172 }
173 smbios = fu_smbios_new ();
174 if (!fu_smbios_setup_from_file (smbios, values[0], error))
175 return FALSE;
176 tmp = fu_smbios_to_string (smbios);
177 g_print ("%s\n", tmp);
178 return TRUE;
179}
180
Richard Hughes9e5675e2019-11-22 09:35:03 +0000181#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100182static gboolean
183fu_util_sigint_cb (gpointer user_data)
184{
185 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
186 g_debug ("Handling SIGINT");
187 g_cancellable_cancel (priv->cancellable);
188 return FALSE;
189}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000190#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100191
192static void
193fu_util_private_free (FuUtilPrivate *priv)
194{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500195 if (priv->current_device != NULL)
196 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100197 if (priv->engine != NULL)
198 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100199 if (priv->request != NULL)
200 g_object_unref (priv->request);
Richard Hughesb5976832018-05-18 10:02:09 +0100201 if (priv->loop != NULL)
202 g_main_loop_unref (priv->loop);
203 if (priv->cancellable != NULL)
204 g_object_unref (priv->cancellable);
205 if (priv->progressbar != NULL)
206 g_object_unref (priv->progressbar);
207 if (priv->context != NULL)
208 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600209 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100210 g_free (priv);
211}
212
213#pragma clang diagnostic push
214#pragma clang diagnostic ignored "-Wunused-function"
215G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
216#pragma clang diagnostic pop
217
Richard Hughes98ca9932018-05-18 10:24:07 +0100218
219static void
220fu_main_engine_device_added_cb (FuEngine *engine,
221 FuDevice *device,
222 FuUtilPrivate *priv)
223{
224 g_autofree gchar *tmp = fu_device_to_string (device);
225 g_debug ("ADDED:\n%s", tmp);
226}
227
228static void
229fu_main_engine_device_removed_cb (FuEngine *engine,
230 FuDevice *device,
231 FuUtilPrivate *priv)
232{
233 g_autofree gchar *tmp = fu_device_to_string (device);
234 g_debug ("REMOVED:\n%s", tmp);
235}
236
237static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100238fu_main_engine_status_changed_cb (FuEngine *engine,
239 FwupdStatus status,
240 FuUtilPrivate *priv)
241{
242 fu_progressbar_update (priv->progressbar, status, 0);
243}
244
245static void
246fu_main_engine_percentage_changed_cb (FuEngine *engine,
247 guint percentage,
248 FuUtilPrivate *priv)
249{
250 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
251}
252
253static gboolean
254fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
255{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000256 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100257 return FALSE;
258 g_main_loop_run (priv->loop);
259 return TRUE;
260}
261
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100262static gint
263fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
264{
265 return fu_plugin_name_compare (*item1, *item2);
266}
267
268static gboolean
269fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
270{
271 GPtrArray *plugins;
272 guint cnt = 0;
273
274 /* load engine */
275 if (!fu_engine_load_plugins (priv->engine, error))
276 return FALSE;
277
278 /* print */
279 plugins = fu_engine_get_plugins (priv->engine);
280 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
281 for (guint i = 0; i < plugins->len; i++) {
282 FuPlugin *plugin = g_ptr_array_index (plugins, i);
283 if (!fu_plugin_get_enabled (plugin))
284 continue;
285 g_print ("%s\n", fu_plugin_get_name (plugin));
286 cnt++;
287 }
288 if (cnt == 0) {
289 /* TRANSLATORS: nothing found */
290 g_print ("%s\n", _("No plugins found"));
291 return TRUE;
292 }
293
294 return TRUE;
295}
296
Richard Hughes98ca9932018-05-18 10:24:07 +0100297static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100298fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
299{
300 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
301 if (!fwupd_device_has_flag (dev, priv->filter_include))
302 return FALSE;
303 }
304 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
305 if (fwupd_device_has_flag (dev, priv->filter_exclude))
306 return FALSE;
307 }
308 return TRUE;
309}
310
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500311static gchar *
312fu_util_get_tree_title (FuUtilPrivate *priv)
313{
314 return g_strdup (fu_engine_get_host_product (priv->engine));
315}
316
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600317static FuDevice *
318fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
319{
320 FuDevice *dev;
321 guint idx;
322 g_autoptr(GPtrArray) devices = NULL;
323 g_autoptr(GPtrArray) devices_filtered = NULL;
324
325 /* get devices from daemon */
326 if (devices_opt != NULL) {
327 devices = g_ptr_array_ref (devices_opt);
328 } else {
329 devices = fu_engine_get_devices (priv->engine, error);
330 if (devices == NULL)
331 return NULL;
332 }
333 fwupd_device_array_ensure_parents (devices);
334
335 /* filter results */
336 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
337 for (guint i = 0; i < devices->len; i++) {
338 dev = g_ptr_array_index (devices, i);
339 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
340 continue;
341 g_ptr_array_add (devices_filtered, g_object_ref (dev));
342 }
343
344 /* nothing */
345 if (devices_filtered->len == 0) {
346 g_set_error_literal (error,
347 FWUPD_ERROR,
348 FWUPD_ERROR_NOTHING_TO_DO,
349 "No supported devices");
350 return NULL;
351 }
352
353 /* exactly one */
354 if (devices_filtered->len == 1) {
355 dev = g_ptr_array_index (devices_filtered, 0);
356 /* TRANSLATORS: Device has been chosen by the daemon for the user */
357 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
358 return g_object_ref (dev);
359 }
360
361 /* TRANSLATORS: get interactive prompt */
362 g_print ("%s\n", _("Choose a device:"));
363 /* TRANSLATORS: this is to abort the interactive prompt */
364 g_print ("0.\t%s\n", _("Cancel"));
365 for (guint i = 0; i < devices_filtered->len; i++) {
366 dev = g_ptr_array_index (devices_filtered, i);
367 g_print ("%u.\t%s (%s)\n",
368 i + 1,
369 fu_device_get_id (dev),
370 fu_device_get_name (dev));
371 }
372 idx = fu_util_prompt_for_number (devices_filtered->len);
373 if (idx == 0) {
374 g_set_error_literal (error,
375 FWUPD_ERROR,
376 FWUPD_ERROR_NOTHING_TO_DO,
377 "Request canceled");
378 return NULL;
379 }
380 dev = g_ptr_array_index (devices_filtered, idx - 1);
381 return g_object_ref (dev);
382}
383
384static FuDevice *
385fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
386{
387 if (fwupd_guid_is_valid (id)) {
388 g_autoptr(GPtrArray) devices = NULL;
389 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
390 if (devices == NULL)
391 return NULL;
392 return fu_util_prompt_for_device (priv, devices, error);
393 }
394
395 /* did this look like a GUID? */
396 for (guint i = 0; id[i] != '\0'; i++) {
397 if (id[i] == '-') {
398 g_set_error_literal (error,
399 FWUPD_ERROR,
400 FWUPD_ERROR_INVALID_ARGS,
401 "Invalid arguments");
402 return NULL;
403 }
404 }
405 return fu_engine_get_device (priv->engine, id, error);
406}
407
Richard Hughes747f5702019-08-06 14:27:26 +0100408static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600409fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
410{
411 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500412 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600413 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500414 gboolean no_updates_header = FALSE;
415 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600416
417 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000418 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600419 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600420 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600421
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600422 /* parse arguments */
423 if (g_strv_length (values) == 0) {
424 devices = fu_engine_get_devices (priv->engine, error);
425 if (devices == NULL)
426 return FALSE;
427 } else if (g_strv_length (values) == 1) {
428 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600429 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600430 if (device == NULL)
431 return FALSE;
432 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
433 g_ptr_array_add (devices, device);
434 } else {
435 g_set_error_literal (error,
436 FWUPD_ERROR,
437 FWUPD_ERROR_INVALID_ARGS,
438 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600439 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600440 }
441
Richard Hughes0ef47202020-01-06 13:59:09 +0000442 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500443 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600444 for (guint i = 0; i < devices->len; i++) {
445 FwupdDevice *dev = g_ptr_array_index (devices, i);
446 g_autoptr(GPtrArray) rels = NULL;
447 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500448 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600449
Richard Hughes747f5702019-08-06 14:27:26 +0100450 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600451 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600452 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600453 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500454 if (!no_updates_header) {
455 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
456 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
457 no_updates_header = TRUE;
458 }
459 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600460 continue;
461 }
Richard Hughes747f5702019-08-06 14:27:26 +0100462 if (!fu_util_filter_device (priv, dev))
463 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600464
465 /* get the releases for this device and filter for validity */
466 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100467 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600468 fwupd_device_get_id (dev),
469 &error_local);
470 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500471 if (!latest_header) {
472 /* TRANSLATORS: message letting the user know no device upgrade available */
473 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
474 latest_header = TRUE;
475 }
476 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600477 /* discard the actual reason from user, but leave for debugging */
478 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600479 continue;
480 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500481 child = g_node_append_data (root, dev);
482
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600483 for (guint j = 0; j < rels->len; j++) {
484 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500485 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600486 }
487 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600488 /* save the device state for other applications to see */
489 if (!fu_util_save_current_state (priv, error))
490 return FALSE;
491
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500492 /* updates */
493 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
494 g_set_error_literal (error,
495 FWUPD_ERROR,
496 FWUPD_ERROR_NOTHING_TO_DO,
497 "No updates available for remaining devices");
498 return FALSE;
499 }
500
501 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600502 return TRUE;
503}
504
505static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500506fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
507{
508 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500509 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600510 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500511 gint fd;
512
513 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000514 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500515 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600516 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500517
518 /* check args */
519 if (g_strv_length (values) != 1) {
520 g_set_error_literal (error,
521 FWUPD_ERROR,
522 FWUPD_ERROR_INVALID_ARGS,
523 "Invalid arguments");
524 return FALSE;
525 }
526
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600527 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100528 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600529
Mario Limonciello716ab272018-05-29 12:34:37 -0500530 /* open file */
531 fd = open (values[0], O_RDONLY);
532 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500533 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500534 g_set_error (error,
535 FWUPD_ERROR,
536 FWUPD_ERROR_INVALID_FILE,
537 "failed to open %s",
538 values[0]);
539 return FALSE;
540 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100541 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500542 close (fd);
543
544 if (array == NULL)
545 return FALSE;
546 for (guint i = 0; i < array->len; i++) {
547 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600548 FwupdRelease *rel;
549 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100550 if (!fu_util_filter_device (priv, dev))
551 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600552 child = g_node_append_data (root, dev);
553 rel = fwupd_device_get_release_default (dev);
554 if (rel != NULL)
555 g_node_append_data (child, rel);
556
Mario Limonciello716ab272018-05-29 12:34:37 -0500557 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500558 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500559
Mario Limonciello716ab272018-05-29 12:34:37 -0500560 return TRUE;
561}
562
563static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100564fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
565{
566 g_autoptr(GString) str = g_string_new (NULL);
567
568 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
569 const gchar *tmp = fwupd_device_flag_to_string (i);
570 if (tmp == NULL)
571 break;
572 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
573 g_string_append (str, " ");
574 g_string_append (str, tmp);
575 g_string_append (str, " ~");
576 g_string_append (str, tmp);
577 }
578 g_print ("%s\n", str->str);
579
580 return TRUE;
581}
582
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500583static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100584fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500585{
586 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100587 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100588 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
589 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100590 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500591 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500592 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100593 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500594 GNode *child = g_node_append_data (root, dev_tmp);
595 fu_util_build_device_tree (priv, child, devs, dev_tmp);
596 }
597 }
598}
599
600static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100601fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500602{
603 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600604 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500605 g_autoptr(GPtrArray) devs = NULL;
606
607 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000608 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500609 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600610 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500611
612 /* print */
613 devs = fu_engine_get_devices (priv->engine, error);
614 if (devs == NULL)
615 return FALSE;
616
617 /* print */
618 if (devs->len == 0) {
619 /* TRANSLATORS: nothing attached that can be upgraded */
620 g_print ("%s\n", _("No hardware detected with firmware update capability"));
621 return TRUE;
622 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000623 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500624 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500625 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500626
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100627 /* save the device state for other applications to see */
628 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500629}
630
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500631static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600632fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500633 FwupdDevice *device,
634 FuUtilPrivate *priv)
635{
636 g_autofree gchar *str = NULL;
637
Richard Hughes809abea2019-03-23 11:06:18 +0000638 /* allowed to set whenever the device has changed */
639 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
640 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
641 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
642 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
643
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500644 /* same as last time, so ignore */
645 if (priv->current_device != NULL &&
646 fwupd_device_compare (priv->current_device, device) == 0)
647 return;
648
Richard Hughesee562b52020-04-07 14:32:52 +0100649 /* ignore indirect devices that might have changed */
650 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
651 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
652 g_debug ("ignoring %s with status %s",
653 fwupd_device_get_name (device),
654 fwupd_status_to_string (fwupd_device_get_status (device)));
655 return;
656 }
657
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500658 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600659 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
660 /* TRANSLATORS: %1 is a device name */
661 str = g_strdup_printf (_("Updating %s…"),
662 fwupd_device_get_name (device));
663 fu_progressbar_set_title (priv->progressbar, str);
664 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
665 /* TRANSLATORS: %1 is a device name */
666 str = g_strdup_printf (_("Installing on %s…"),
667 fwupd_device_get_name (device));
668 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000669 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
670 /* TRANSLATORS: %1 is a device name */
671 str = g_strdup_printf (_("Reading from %s…"),
672 fwupd_device_get_name (device));
673 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600674 } else {
675 g_warning ("no FuUtilOperation set");
676 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500677 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600678
Mario Limonciello32241f42019-01-24 10:12:41 -0600679 if (priv->current_message == NULL) {
680 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
681 if (tmp != NULL)
682 priv->current_message = g_strdup (tmp);
683 }
684}
685
686static void
687fu_util_display_current_message (FuUtilPrivate *priv)
688{
689 if (priv->current_message == NULL)
690 return;
691 g_print ("%s\n", priv->current_message);
692 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500693}
694
Richard Hughes98ca9932018-05-18 10:24:07 +0100695static gboolean
696fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
697{
698 g_autoptr(FuDevice) device = NULL;
699 g_autoptr(GBytes) blob_fw = NULL;
700
701 /* invalid args */
702 if (g_strv_length (values) == 0) {
703 g_set_error_literal (error,
704 FWUPD_ERROR,
705 FWUPD_ERROR_INVALID_ARGS,
706 "Invalid arguments");
707 return FALSE;
708 }
709
710 /* parse blob */
711 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500712 if (blob_fw == NULL) {
713 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100714 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500715 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100716
717 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000718 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100719 return FALSE;
720
721 /* get device */
722 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100723 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100724 if (device == NULL)
725 return FALSE;
726 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100727 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100728 if (device == NULL)
729 return FALSE;
730 }
731
Mario Limonciello3f243a92019-01-21 22:05:23 -0600732 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500733 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600734 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500735
Richard Hughes98ca9932018-05-18 10:24:07 +0100736 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000737 if (priv->prepare_blob) {
738 g_autoptr(GPtrArray) devices = NULL;
739 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
740 g_ptr_array_add (devices, g_object_ref (device));
741 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
742 g_prefix_error (error, "failed to prepare composite action: ");
743 return FALSE;
744 }
745 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100746 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000747 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600748 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000749 if (priv->cleanup_blob) {
750 g_autoptr(FuDevice) device_new = NULL;
751 g_autoptr(GError) error_local = NULL;
752
753 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100754 device_new = fu_util_get_device (priv,
755 fu_device_get_id (device),
756 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000757 if (device_new == NULL) {
758 g_debug ("failed to find new device: %s",
759 error_local->message);
760 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600761 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000762 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
763 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
764 g_prefix_error (error, "failed to cleanup composite action: ");
765 return FALSE;
766 }
767 }
768 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600769
Mario Limonciello32241f42019-01-24 10:12:41 -0600770 fu_util_display_current_message (priv);
771
Mario Limonciello3f243a92019-01-21 22:05:23 -0600772 /* success */
773 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100774}
775
Richard Hughesa58510b2019-10-30 10:03:12 +0000776static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100777fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000778{
779 g_autoptr(FuDevice) device = NULL;
780 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
781 g_autoptr(GBytes) blob_fw = NULL;
782
783 /* invalid args */
784 if (g_strv_length (values) == 0) {
785 g_set_error_literal (error,
786 FWUPD_ERROR,
787 FWUPD_ERROR_INVALID_ARGS,
788 "Invalid arguments");
789 return FALSE;
790 }
791
792 /* file already exists */
793 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
794 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
795 g_set_error_literal (error,
796 FWUPD_ERROR,
797 FWUPD_ERROR_INVALID_ARGS,
798 "Filename already exists");
799 return FALSE;
800 }
801
Richard Hughes02792c02019-11-01 14:21:20 +0000802 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000803 * avoid failing at the end of a potentially lengthy operation */
804 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
805 return FALSE;
806
807 /* load engine */
808 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
809 return FALSE;
810
811 /* get device */
812 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100813 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000814 if (device == NULL)
815 return FALSE;
816 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100817 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000818 if (device == NULL)
819 return FALSE;
820 }
821 priv->current_operation = FU_UTIL_OPERATION_READ;
822 g_signal_connect (priv->engine, "device-changed",
823 G_CALLBACK (fu_util_update_device_changed_cb), priv);
824
825 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100826 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000827 if (blob_fw == NULL)
828 return FALSE;
829 return fu_common_set_contents_bytes (values[0], blob_fw, error);
830}
831
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100832static gint
833fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
834{
835 FuInstallTask *task1 = *((FuInstallTask **) a);
836 FuInstallTask *task2 = *((FuInstallTask **) b);
837 return fu_install_task_compare (task1, task2);
838}
839
840static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100841fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
842{
Filipe Laínse0914272019-09-20 10:04:43 +0100843 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100844 { "curl", uri, "--output", fn, NULL },
845 { NULL } };
846 for (guint i = 0; argv[i][0] != NULL; i++) {
847 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100848 g_autofree gchar *fn_tmp = NULL;
849 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
850 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100851 g_debug ("%s", error_local->message);
852 continue;
853 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000854 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100855 }
856 g_set_error_literal (error,
857 FWUPD_ERROR,
858 FWUPD_ERROR_NOT_FOUND,
859 "no supported out-of-process downloaders found");
860 return FALSE;
861}
862
863static gchar *
864fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
865{
866 g_autofree gchar *filename = NULL;
867 g_autoptr(SoupURI) uri = NULL;
868
869 /* a local file */
870 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000871 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
872 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100873 if (uri == NULL)
874 return g_strdup (perhapsfn);
875
876 /* download the firmware to a cachedir */
877 filename = fu_util_get_user_cache_path (perhapsfn);
878 if (!fu_common_mkdir_parent (filename, error))
879 return NULL;
880 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
881 return NULL;
882 return g_steal_pointer (&filename);
883}
884
885static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100886fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
887{
Richard Hughes3d178be2018-08-30 11:14:24 +0100888 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100889 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100890 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100891 g_autoptr(GPtrArray) devices_possible = NULL;
892 g_autoptr(GPtrArray) errors = NULL;
893 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100894 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100895
Mario Limonciello8949e892018-05-25 08:03:06 -0500896 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000897 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500898 return FALSE;
899
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100900 /* handle both forms */
901 if (g_strv_length (values) == 1) {
902 devices_possible = fu_engine_get_devices (priv->engine, error);
903 if (devices_possible == NULL)
904 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000905 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100906 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100907 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100908 if (device == NULL)
909 return FALSE;
910 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
911 g_ptr_array_add (devices_possible, device);
912 } else {
913 g_set_error_literal (error,
914 FWUPD_ERROR,
915 FWUPD_ERROR_INVALID_ARGS,
916 "Invalid arguments");
917 return FALSE;
918 }
919
Richard Hughes3d178be2018-08-30 11:14:24 +0100920 /* download if required */
921 filename = fu_util_download_if_required (priv, values[0], error);
922 if (filename == NULL)
923 return FALSE;
924
Richard Hughes481aa2a2018-09-18 20:51:46 +0100925 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100926 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500927 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100928 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100929 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500930 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100931 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
932 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100933 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600934 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100935 if (components == NULL)
936 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100937
Richard Hughes481aa2a2018-09-18 20:51:46 +0100938 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100939 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
940 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100941 for (guint i = 0; i < components->len; i++) {
942 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100943
944 /* do any devices pass the requirements */
945 for (guint j = 0; j < devices_possible->len; j++) {
946 FuDevice *device = g_ptr_array_index (devices_possible, j);
947 g_autoptr(FuInstallTask) task = NULL;
948 g_autoptr(GError) error_local = NULL;
949
950 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100951 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100952 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100953 priv->request,
954 task,
955 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500956 &error_local)) {
957 g_debug ("first pass requirement on %s:%s failed: %s",
958 fu_device_get_id (device),
959 xb_node_query_text (component, "id", NULL),
960 error_local->message);
961 g_ptr_array_add (errors, g_steal_pointer (&error_local));
962 continue;
963 }
964
965 /* make a second pass using possibly updated version format now */
966 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
967 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100968 priv->request,
969 task,
970 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100971 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500972 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100973 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100974 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100975 error_local->message);
976 g_ptr_array_add (errors, g_steal_pointer (&error_local));
977 continue;
978 }
979
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600980 /* if component should have an update message from CAB */
981 fu_device_incorporate_from_component (device, component);
982
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100983 /* success */
984 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
985 }
986 }
987
988 /* order the install tasks by the device priority */
989 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
990
991 /* nothing suitable */
992 if (install_tasks->len == 0) {
993 GError *error_tmp = fu_common_error_array_get_best (errors);
994 g_propagate_error (error, error_tmp);
995 return FALSE;
996 }
997
Mario Limonciello3f243a92019-01-21 22:05:23 -0600998 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500999 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001000 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001001
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001002 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001003 if (!fu_engine_install_tasks (priv->engine,
1004 priv->request,
1005 install_tasks,
1006 blob_cab,
1007 priv->flags,
1008 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001009 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001010
Mario Limonciello32241f42019-01-24 10:12:41 -06001011 fu_util_display_current_message (priv);
1012
Mario Limonciello3f243a92019-01-21 22:05:23 -06001013 /* we don't want to ask anything */
1014 if (priv->no_reboot_check) {
1015 g_debug ("skipping reboot check");
1016 return TRUE;
1017 }
1018
Mario Limonciello3143bad2019-02-27 07:31:00 -06001019 /* save the device state for other applications to see */
1020 if (!fu_util_save_current_state (priv, error))
1021 return FALSE;
1022
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001023 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001024 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001025}
1026
Richard Hughes98ca9932018-05-18 10:24:07 +01001027static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001028fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001029{
Mario Limonciellofd734852019-08-01 16:41:42 -05001030 FwupdRemote *remote;
1031 const gchar *remote_id;
1032 const gchar *uri_tmp;
1033 g_auto(GStrv) argv = NULL;
Daniel Campello722f5322020-08-12 11:27:38 -06001034 g_autoptr(SoupURI) uri = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001035
Mario Limonciellofd734852019-08-01 16:41:42 -05001036 uri_tmp = fwupd_release_get_uri (rel);
1037 if (uri_tmp == NULL) {
1038 g_set_error_literal (error,
1039 FWUPD_ERROR,
1040 FWUPD_ERROR_INVALID_FILE,
1041 "release missing URI");
1042 return FALSE;
1043 }
1044 remote_id = fwupd_release_get_remote_id (rel);
1045 if (remote_id == NULL) {
1046 g_set_error (error,
1047 FWUPD_ERROR,
1048 FWUPD_ERROR_INVALID_FILE,
1049 "failed to find remote for %s",
1050 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001051 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001052 }
1053
1054 remote = fu_engine_get_remote_by_id (priv->engine,
1055 remote_id,
1056 error);
1057 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001058 return FALSE;
1059
Mario Limonciellofd734852019-08-01 16:41:42 -05001060 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001061 /* local remotes may have the firmware already */
1062 uri = soup_uri_new (uri_tmp);
1063 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001064 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1065 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1066 argv[0] = g_build_filename (path, uri_tmp, NULL);
1067 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1068 argv[0] = g_strdup (uri_tmp + 7);
1069 /* web remote, fu_util_install will download file */
1070 } else {
1071 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1072 }
1073 return fu_util_install (priv, argv, error);
1074}
1075
1076static gboolean
1077fu_util_update_all (FuUtilPrivate *priv, GError **error)
1078{
1079 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001080 gboolean no_updates_header = FALSE;
1081 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001082
Mario Limonciello46aaee82019-01-10 12:58:00 -06001083 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001084 if (devices == NULL)
1085 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001086 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001087 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001088 for (guint i = 0; i < devices->len; i++) {
1089 FwupdDevice *dev = g_ptr_array_index (devices, i);
1090 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001091 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001092 g_autoptr(GPtrArray) rels = NULL;
1093 g_autoptr(GError) error_local = NULL;
1094
1095 if (!fu_util_is_interesting_device (dev))
1096 continue;
1097 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001098 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001099 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001100 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001101 if (!no_updates_header) {
1102 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1103 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1104 no_updates_header = TRUE;
1105 }
1106 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001107 continue;
1108 }
Richard Hughes747f5702019-08-06 14:27:26 +01001109 if (!fu_util_filter_device (priv, dev))
1110 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001111
1112 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001113 rels = fu_engine_get_upgrades (priv->engine,
1114 priv->request,
1115 device_id,
1116 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001117 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001118 if (!latest_header) {
1119 /* TRANSLATORS: message letting the user know no device upgrade available */
1120 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1121 latest_header = TRUE;
1122 }
1123 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001124 /* discard the actual reason from user, but leave for debugging */
1125 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001126 continue;
1127 }
1128
Mario Limonciello98b95162019-10-30 09:20:43 -05001129 if (!priv->no_safety_check) {
1130 if (!fu_util_prompt_warning (dev,
1131 fu_util_get_tree_title (priv),
1132 error))
1133 return FALSE;
1134 }
1135
Mario Limonciello46aaee82019-01-10 12:58:00 -06001136 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001137 if (!fu_util_install_release (priv, rel, &error_local)) {
1138 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001139 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001140 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001141 fu_util_display_current_message (priv);
1142 }
1143 return TRUE;
1144}
1145
1146static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001147fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001148{
1149 FwupdRelease *rel;
1150 g_autoptr(FuDevice) dev = NULL;
1151 g_autoptr(GPtrArray) rels = NULL;
1152
Mario Limonciello9917bb42020-04-20 13:41:27 -05001153 /* do not allow a partial device-id, lookup GUIDs */
1154 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001155 if (dev == NULL)
1156 return FALSE;
1157
1158 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001159 rels = fu_engine_get_upgrades (priv->engine,
1160 priv->request,
1161 fu_device_get_id (dev),
1162 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001163 if (rels == NULL)
1164 return FALSE;
1165 rel = g_ptr_array_index (rels, 0);
1166 if (!fu_util_install_release (priv, rel, error))
1167 return FALSE;
1168 fu_util_display_current_message (priv);
1169
1170 return TRUE;
1171}
1172
1173static gboolean
1174fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1175{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001176 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1177 g_set_error_literal (error,
1178 FWUPD_ERROR,
1179 FWUPD_ERROR_INVALID_ARGS,
1180 "--allow-older is not supported for this command");
1181 return FALSE;
1182 }
1183
1184 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1185 g_set_error_literal (error,
1186 FWUPD_ERROR,
1187 FWUPD_ERROR_INVALID_ARGS,
1188 "--allow-reinstall is not supported for this command");
1189 return FALSE;
1190 }
1191
Mario Limonciellofd734852019-08-01 16:41:42 -05001192 if (g_strv_length (values) > 1) {
1193 g_set_error_literal (error,
1194 FWUPD_ERROR,
1195 FWUPD_ERROR_INVALID_ARGS,
1196 "Invalid arguments");
1197 return FALSE;
1198 }
1199
1200 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1201 return FALSE;
1202
1203 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1204 g_signal_connect (priv->engine, "device-changed",
1205 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1206
1207 if (g_strv_length (values) == 1) {
1208 if (!fu_util_update_by_id (priv, values[0], error))
1209 return FALSE;
1210 } else {
1211 if (!fu_util_update_all (priv, error))
1212 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001213 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001214
1215 /* we don't want to ask anything */
1216 if (priv->no_reboot_check) {
1217 g_debug ("skipping reboot check");
1218 return TRUE;
1219 }
1220
Mario Limonciello3143bad2019-02-27 07:31:00 -06001221 /* save the device state for other applications to see */
1222 if (!fu_util_save_current_state (priv, error))
1223 return FALSE;
1224
Mario Limonciello3f243a92019-01-21 22:05:23 -06001225 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001226}
1227
1228static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001229fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1230{
1231 g_autoptr(FwupdRelease) rel = NULL;
1232 g_autoptr(GPtrArray) rels = NULL;
1233 g_autoptr(FuDevice) dev = NULL;
1234
1235 if (g_strv_length (values) != 1) {
1236 g_set_error_literal (error,
1237 FWUPD_ERROR,
1238 FWUPD_ERROR_INVALID_ARGS,
1239 "Invalid arguments");
1240 return FALSE;
1241 }
1242
1243 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1244 return FALSE;
1245
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001246 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001247 if (dev == NULL)
1248 return FALSE;
1249
1250 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001251 rels = fu_engine_get_releases_for_device (priv->engine,
1252 priv->request,
1253 dev,
1254 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001255 if (rels == NULL)
1256 return FALSE;
1257
1258 for (guint j = 0; j < rels->len; j++) {
1259 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1260 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1261 fu_device_get_version (dev),
1262 fu_device_get_version_format (dev)) == 0) {
1263 rel = g_object_ref (rel_tmp);
1264 break;
1265 }
1266 }
1267 if (rel == NULL) {
1268 g_set_error (error,
1269 FWUPD_ERROR,
1270 FWUPD_ERROR_NOT_SUPPORTED,
1271 "Unable to locate release for %s version %s",
1272 fu_device_get_name (dev),
1273 fu_device_get_version (dev));
1274 return FALSE;
1275 }
1276
1277 /* update the console if composite devices are also updated */
1278 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1279 g_signal_connect (priv->engine, "device-changed",
1280 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1281 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1282 if (!fu_util_install_release (priv, rel, error))
1283 return FALSE;
1284 fu_util_display_current_message (priv);
1285
1286 /* we don't want to ask anything */
1287 if (priv->no_reboot_check) {
1288 g_debug ("skipping reboot check");
1289 return TRUE;
1290 }
1291
1292 /* save the device state for other applications to see */
1293 if (!fu_util_save_current_state (priv, error))
1294 return FALSE;
1295
1296 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1297}
1298
1299static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001300fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1301{
1302 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001303 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001304
1305 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001306 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001307 return FALSE;
1308
Richard Hughes98ca9932018-05-18 10:24:07 +01001309 /* get device */
1310 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001311 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001312 if (device == NULL)
1313 return FALSE;
1314 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001315 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001316 if (device == NULL)
1317 return FALSE;
1318 }
1319
1320 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001321 locker = fu_device_locker_new (device, error);
1322 if (locker == NULL)
1323 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001324 return fu_device_detach (device, error);
1325}
1326
1327static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001328fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1329{
1330 g_autoptr(FuDevice) device = NULL;
1331 g_autoptr(FuDeviceLocker) locker = NULL;
1332
1333 /* load engine */
1334 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1335 return FALSE;
1336
1337 /* get device */
1338 if (g_strv_length (values) == 1) {
1339 device = fu_util_get_device (priv, values[0], error);
1340 } else {
1341 device = fu_util_prompt_for_device (priv, NULL, error);
1342 }
1343 if (device == NULL)
1344 return FALSE;
1345
1346 /* run vfunc */
1347 locker = fu_device_locker_new (device, error);
1348 if (locker == NULL)
1349 return FALSE;
1350 return fu_device_unbind_driver (device, error);
1351}
1352
1353static gboolean
1354fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1355{
1356 g_autoptr(FuDevice) device = NULL;
1357 g_autoptr(FuDeviceLocker) locker = NULL;
1358
1359 /* load engine */
1360 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1361 return FALSE;
1362
1363 /* get device */
1364 if (g_strv_length (values) == 3) {
1365 device = fu_util_get_device (priv, values[2], error);
1366 if (device == NULL)
1367 return FALSE;
1368 } else if (g_strv_length (values) == 2) {
1369 device = fu_util_prompt_for_device (priv, NULL, error);
1370 if (device == NULL)
1371 return FALSE;
1372 } else {
1373 g_set_error_literal (error,
1374 FWUPD_ERROR,
1375 FWUPD_ERROR_INVALID_ARGS,
1376 "Invalid arguments");
1377 return FALSE;
1378 }
1379
1380 /* run vfunc */
1381 locker = fu_device_locker_new (device, error);
1382 if (locker == NULL)
1383 return FALSE;
1384 return fu_device_bind_driver (device, values[0], values[1], error);
1385}
1386
1387static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001388fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1389{
1390 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001391 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001392
1393 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001394 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001395 return FALSE;
1396
Richard Hughes98ca9932018-05-18 10:24:07 +01001397 /* get device */
1398 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001399 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001400 if (device == NULL)
1401 return FALSE;
1402 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001403 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001404 if (device == NULL)
1405 return FALSE;
1406 }
1407
1408 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001409 locker = fu_device_locker_new (device, error);
1410 if (locker == NULL)
1411 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001412 return fu_device_attach (device, error);
1413}
1414
Richard Hughes1d894f12018-08-31 13:05:51 +01001415static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001416fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001417{
1418 gboolean has_pending = FALSE;
1419 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001420 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1421 if (devices == NULL)
1422 return FALSE;
1423
1424 /* only start up the plugins needed */
1425 for (guint i = 0; i < devices->len; i++) {
1426 FuDevice *dev = g_ptr_array_index (devices, i);
1427 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1428 fu_engine_add_plugin_filter (priv->engine,
1429 fu_device_get_plugin (dev));
1430 has_pending = TRUE;
1431 }
1432 }
1433
1434 if (!has_pending) {
1435 g_set_error_literal (error,
1436 FWUPD_ERROR,
1437 FWUPD_ERROR_NOTHING_TO_DO,
1438 "No devices to activate");
1439 return FALSE;
1440 }
1441
1442 return TRUE;
1443}
1444
1445static gboolean
1446fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1447{
1448 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001449 g_autoptr(GPtrArray) devices = NULL;
1450
1451 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001452 if (!fu_util_check_activation_needed (priv, error))
1453 return FALSE;
1454
1455 /* load engine */
1456 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
1457 return FALSE;
1458
1459 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001460 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001461 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001462 if (devices == NULL)
1463 return FALSE;
1464 } else if (g_strv_length (values) == 1) {
1465 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001466 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001467 if (device == NULL)
1468 return FALSE;
1469 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1470 g_ptr_array_add (devices, device);
1471 } else {
1472 g_set_error_literal (error,
1473 FWUPD_ERROR,
1474 FWUPD_ERROR_INVALID_ARGS,
1475 "Invalid arguments");
1476 return FALSE;
1477 }
1478
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001479 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001480 /* order by device priority */
1481 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001482 for (guint i = 0; i < devices->len; i++) {
1483 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001484 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1485 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001486 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1487 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001488 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001489 /* TRANSLATORS: shown when shutting down to switch to the new version */
1490 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1491 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1492 return FALSE;
1493 }
1494
Mario Limonciello02085a02020-09-11 14:59:35 -05001495 if (!has_pending) {
1496 g_set_error_literal (error,
1497 FWUPD_ERROR,
1498 FWUPD_ERROR_NOTHING_TO_DO,
1499 "No devices to activate");
1500 return FALSE;
1501 }
1502
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001503 return TRUE;
1504}
1505
1506static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001507fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1508{
1509 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1510 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1511 const gchar *hwid_keys[] = {
1512 FU_HWIDS_KEY_BIOS_VENDOR,
1513 FU_HWIDS_KEY_BIOS_VERSION,
1514 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1515 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1516 FU_HWIDS_KEY_MANUFACTURER,
1517 FU_HWIDS_KEY_FAMILY,
1518 FU_HWIDS_KEY_PRODUCT_NAME,
1519 FU_HWIDS_KEY_PRODUCT_SKU,
1520 FU_HWIDS_KEY_ENCLOSURE_KIND,
1521 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1522 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1523 NULL };
1524
1525 /* read DMI data */
1526 if (g_strv_length (values) == 0) {
1527 if (!fu_smbios_setup (smbios, error))
1528 return FALSE;
1529 } else if (g_strv_length (values) == 1) {
1530 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1531 return FALSE;
1532 } else {
1533 g_set_error_literal (error,
1534 FWUPD_ERROR,
1535 FWUPD_ERROR_INVALID_ARGS,
1536 "Invalid arguments");
1537 return FALSE;
1538 }
1539 if (!fu_hwids_setup (hwids, smbios, error))
1540 return FALSE;
1541
1542 /* show debug output */
1543 g_print ("Computer Information\n");
1544 g_print ("--------------------\n");
1545 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1546 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1547 if (tmp == NULL)
1548 continue;
1549 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1550 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1551 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1552 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1553 } else {
1554 g_print ("%s: %s\n", hwid_keys[i], tmp);
1555 }
1556 }
1557
1558 /* show GUIDs */
1559 g_print ("\nHardware IDs\n");
1560 g_print ("------------\n");
1561 for (guint i = 0; i < 15; i++) {
1562 const gchar *keys = NULL;
1563 g_autofree gchar *guid = NULL;
1564 g_autofree gchar *key = NULL;
1565 g_autofree gchar *keys_str = NULL;
1566 g_auto(GStrv) keysv = NULL;
1567 g_autoptr(GError) error_local = NULL;
1568
1569 /* get the GUID */
1570 key = g_strdup_printf ("HardwareID-%u", i);
1571 keys = fu_hwids_get_replace_keys (hwids, key);
1572 guid = fu_hwids_get_guid (hwids, key, &error_local);
1573 if (guid == NULL) {
1574 g_print ("%s\n", error_local->message);
1575 continue;
1576 }
1577
1578 /* show what makes up the GUID */
1579 keysv = g_strsplit (keys, "&", -1);
1580 keys_str = g_strjoinv (" + ", keysv);
1581 g_print ("{%s} <- %s\n", guid, keys_str);
1582 }
1583
1584 return TRUE;
1585}
1586
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001587static gboolean
1588fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1589{
1590 const gchar *script_fn = "startup.sh";
1591 const gchar *output_fn = "firmware.bin";
1592 g_autoptr(GBytes) archive_blob = NULL;
1593 g_autoptr(GBytes) firmware_blob = NULL;
1594 if (g_strv_length (values) < 2) {
1595 g_set_error_literal (error,
1596 FWUPD_ERROR,
1597 FWUPD_ERROR_INVALID_ARGS,
1598 "Invalid arguments");
1599 return FALSE;
1600 }
1601 archive_blob = fu_common_get_contents_bytes (values[0], error);
1602 if (archive_blob == NULL)
1603 return FALSE;
1604 if (g_strv_length (values) > 2)
1605 script_fn = values[2];
1606 if (g_strv_length (values) > 3)
1607 output_fn = values[3];
1608 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1609 if (firmware_blob == NULL)
1610 return FALSE;
1611 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1612}
1613
Richard Hughes3d607622019-03-07 16:59:27 +00001614static gboolean
1615fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1616{
1617 g_autofree gchar *sig = NULL;
1618
1619 /* check args */
1620 if (g_strv_length (values) != 1) {
1621 g_set_error_literal (error,
1622 FWUPD_ERROR,
1623 FWUPD_ERROR_INVALID_ARGS,
1624 "Invalid arguments: value expected");
1625 return FALSE;
1626 }
1627
1628 /* start engine */
1629 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1630 return FALSE;
1631 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001632 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1633 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001634 if (sig == NULL)
1635 return FALSE;
1636 g_print ("%s\n", sig);
1637 return TRUE;
1638}
1639
Mario Limonciello62f84862018-10-18 13:15:23 -05001640static void
1641fu_util_device_added_cb (FwupdClient *client,
1642 FwupdDevice *device,
1643 gpointer user_data)
1644{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001645 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001646 /* TRANSLATORS: this is when a device is hotplugged */
1647 g_print ("%s\n%s", _("Device added:"), tmp);
1648}
1649
1650static void
1651fu_util_device_removed_cb (FwupdClient *client,
1652 FwupdDevice *device,
1653 gpointer user_data)
1654{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001655 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001656 /* TRANSLATORS: this is when a device is hotplugged */
1657 g_print ("%s\n%s", _("Device removed:"), tmp);
1658}
1659
1660static void
1661fu_util_device_changed_cb (FwupdClient *client,
1662 FwupdDevice *device,
1663 gpointer user_data)
1664{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001665 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001666 /* TRANSLATORS: this is when a device has been updated */
1667 g_print ("%s\n%s", _("Device changed:"), tmp);
1668}
1669
1670static void
1671fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1672{
1673 /* TRANSLATORS: this is when the daemon state changes */
1674 g_print ("%s\n", _("Changed"));
1675}
1676
1677static gboolean
1678fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1679{
1680 g_autoptr(FwupdClient) client = fwupd_client_new ();
1681
1682 /* get all the devices */
1683 if (!fwupd_client_connect (client, priv->cancellable, error))
1684 return FALSE;
1685
1686 /* watch for any hotplugged device */
1687 g_signal_connect (client, "changed",
1688 G_CALLBACK (fu_util_changed_cb), priv);
1689 g_signal_connect (client, "device-added",
1690 G_CALLBACK (fu_util_device_added_cb), priv);
1691 g_signal_connect (client, "device-removed",
1692 G_CALLBACK (fu_util_device_removed_cb), priv);
1693 g_signal_connect (client, "device-changed",
1694 G_CALLBACK (fu_util_device_changed_cb), priv);
1695 g_signal_connect (priv->cancellable, "cancelled",
1696 G_CALLBACK (fu_util_cancelled_cb), priv);
1697 g_main_loop_run (priv->loop);
1698 return TRUE;
1699}
1700
Richard Hughes15684492019-03-15 16:27:50 +00001701static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001702fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1703{
1704 g_autoptr(GPtrArray) firmware_types = NULL;
1705
1706 /* load engine */
1707 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1708 return FALSE;
1709
1710 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1711 for (guint i = 0; i < firmware_types->len; i++) {
1712 const gchar *id = g_ptr_array_index (firmware_types, i);
1713 g_print ("%s\n", id);
1714 }
1715 if (firmware_types->len == 0) {
1716 /* TRANSLATORS: nothing found */
1717 g_print ("%s\n", _("No firmware IDs found"));
1718 return TRUE;
1719 }
1720
1721 return TRUE;
1722}
1723
1724static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001725fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001726{
1727 g_autoptr(GPtrArray) firmware_types = NULL;
1728 guint idx;
1729 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1730
1731 /* TRANSLATORS: get interactive prompt */
1732 g_print ("%s\n", _("Choose a firmware type:"));
1733 /* TRANSLATORS: this is to abort the interactive prompt */
1734 g_print ("0.\t%s\n", _("Cancel"));
1735 for (guint i = 0; i < firmware_types->len; i++) {
1736 const gchar *id = g_ptr_array_index (firmware_types, i);
1737 g_print ("%u.\t%s\n", i + 1, id);
1738 }
1739 idx = fu_util_prompt_for_number (firmware_types->len);
1740 if (idx == 0) {
1741 g_set_error_literal (error,
1742 FWUPD_ERROR,
1743 FWUPD_ERROR_NOTHING_TO_DO,
1744 "Request canceled");
1745 return NULL;
1746 }
1747
1748 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1749}
1750
1751static gboolean
1752fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1753{
1754 GType gtype;
1755 g_autoptr(GBytes) blob = NULL;
1756 g_autoptr(FuFirmware) firmware = NULL;
1757 g_autofree gchar *firmware_type = NULL;
1758 g_autofree gchar *str = NULL;
1759
1760 /* check args */
1761 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1762 g_set_error_literal (error,
1763 FWUPD_ERROR,
1764 FWUPD_ERROR_INVALID_ARGS,
1765 "Invalid arguments: filename required");
1766 return FALSE;
1767 }
1768
1769 if (g_strv_length (values) == 2)
1770 firmware_type = g_strdup (values[1]);
1771
1772 /* load file */
1773 blob = fu_common_get_contents_bytes (values[0], error);
1774 if (blob == NULL)
1775 return FALSE;
1776
1777 /* load engine */
1778 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1779 return FALSE;
1780
1781 /* find the GType to use */
1782 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001783 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001784 if (firmware_type == NULL)
1785 return FALSE;
1786 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1787 if (gtype == G_TYPE_INVALID) {
1788 g_set_error (error,
1789 G_IO_ERROR,
1790 G_IO_ERROR_NOT_FOUND,
1791 "GType %s not supported", firmware_type);
1792 return FALSE;
1793 }
1794 firmware = g_object_new (gtype, NULL);
1795 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1796 return FALSE;
1797 str = fu_firmware_to_string (firmware);
1798 g_print ("%s", str);
1799 return TRUE;
1800}
1801
Richard Hughesdd653442020-09-22 10:23:52 +01001802static gboolean
1803fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1804{
1805 GType gtype;
1806 g_autofree gchar *firmware_type = NULL;
1807 g_autofree gchar *str = NULL;
1808 g_autoptr(FuFirmware) firmware = NULL;
1809 g_autoptr(GBytes) blob = NULL;
1810 g_autoptr(GPtrArray) images = NULL;
1811
1812 /* check args */
1813 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1814 g_set_error_literal (error,
1815 FWUPD_ERROR,
1816 FWUPD_ERROR_INVALID_ARGS,
1817 "Invalid arguments: filename required");
1818 return FALSE;
1819 }
1820 if (g_strv_length (values) == 2)
1821 firmware_type = g_strdup (values[1]);
1822
1823 /* load file */
1824 blob = fu_common_get_contents_bytes (values[0], error);
1825 if (blob == NULL)
1826 return FALSE;
1827
1828 /* load engine */
1829 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1830 return FALSE;
1831
1832 /* find the GType to use */
1833 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001834 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001835 if (firmware_type == NULL)
1836 return FALSE;
1837 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1838 if (gtype == G_TYPE_INVALID) {
1839 g_set_error (error,
1840 G_IO_ERROR,
1841 G_IO_ERROR_NOT_FOUND,
1842 "GType %s not supported", firmware_type);
1843 return FALSE;
1844 }
1845 firmware = g_object_new (gtype, NULL);
1846 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1847 return FALSE;
1848 str = fu_firmware_to_string (firmware);
1849 g_print ("%s", str);
1850 images = fu_firmware_get_images (firmware);
1851 for (guint i = 0; i < images->len; i++) {
1852 FuFirmwareImage *img = g_ptr_array_index (images, i);
1853 g_autofree gchar *fn = NULL;
1854 g_autoptr(GBytes) blob_img = NULL;
1855
Richard Hughes88dd7c42020-09-22 16:54:40 +01001856 /* get raw image without generated header, footer or crc */
1857 blob_img = fu_firmware_image_get_bytes (img);
1858 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1859 continue;
1860
Richard Hughesdd653442020-09-22 10:23:52 +01001861 /* use suitable filename */
1862 if (fu_firmware_image_get_filename (img) != NULL) {
1863 fn = g_strdup (fu_firmware_image_get_filename (img));
1864 } else if (fu_firmware_image_get_id (img) != NULL) {
1865 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1866 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1867 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1868 } else {
1869 fn = g_strdup_printf ("img-0x%x.fw", i);
1870 }
1871 /* TRANSLATORS: decompressing images from a container firmware */
1872 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01001873 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1874 return FALSE;
1875 }
1876
1877 /* success */
1878 return TRUE;
1879}
1880
Richard Hughes0924c932020-09-22 19:07:05 +01001881static gboolean
1882fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01001883{
Richard Hughes0924c932020-09-22 19:07:05 +01001884 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01001885 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01001886 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001887 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01001888 g_autoptr(FuFirmware) firmware_dst = NULL;
1889 g_autoptr(GBytes) blob_dst = NULL;
1890 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001891 g_autoptr(XbBuilder) builder = xb_builder_new ();
1892 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1893 g_autoptr(XbNode) n = NULL;
1894 g_autoptr(XbSilo) silo = NULL;
1895
Richard Hughes0924c932020-09-22 19:07:05 +01001896 /* check args */
1897 if (g_strv_length (values) != 2) {
1898 g_set_error_literal (error,
1899 FWUPD_ERROR,
1900 FWUPD_ERROR_INVALID_ARGS,
1901 "Invalid arguments: filename required");
1902 return FALSE;
1903 }
1904
1905 /* load file */
1906 blob_src = fu_common_get_contents_bytes (values[0], error);
1907 if (blob_src == NULL)
1908 return FALSE;
1909
1910 /* load engine */
1911 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1912 return FALSE;
1913
Richard Hughes41400a82020-09-21 13:43:15 +01001914 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01001915 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01001916 XB_BUILDER_SOURCE_FLAG_NONE,
1917 error)) {
1918 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01001919 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001920 }
1921 xb_builder_import_source (builder, source);
1922 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
1923 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001924 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001925
1926 /* create FuFirmware of specific GType */
1927 n = xb_silo_query_first (silo, "firmware", error);
1928 if (n == NULL)
1929 return FALSE;
1930 tmp = xb_node_get_attr (n, "gtype");
1931 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01001932 gtype = g_type_from_name (tmp);
1933 if (gtype == G_TYPE_INVALID) {
1934 g_set_error (error,
1935 G_IO_ERROR,
1936 G_IO_ERROR_NOT_FOUND,
1937 "GType %s not registered", tmp);
1938 return FALSE;
1939 }
1940 }
1941 tmp = xb_node_get_attr (n, "id");
1942 if (tmp != NULL) {
1943 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01001944 if (gtype == G_TYPE_INVALID) {
1945 g_set_error (error,
1946 G_IO_ERROR,
1947 G_IO_ERROR_NOT_FOUND,
1948 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01001949 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001950 }
Richard Hughes41400a82020-09-21 13:43:15 +01001951 }
Richard Hughes0924c932020-09-22 19:07:05 +01001952 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01001953 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01001954 return FALSE;
1955
1956 /* write new file */
1957 blob_dst = fu_firmware_write (firmware, error);
1958 if (blob_dst == NULL)
1959 return FALSE;
1960 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
1961 return FALSE;
1962
1963 /* show what we wrote */
1964 firmware_dst = g_object_new (gtype, NULL);
1965 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
1966 return FALSE;
1967 str = fu_firmware_to_string (firmware_dst);
1968 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01001969
1970 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01001971 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01001972}
1973
Richard Hughes95c98a92019-10-22 16:03:15 +01001974static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00001975fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
1976{
1977 GType gtype_dst;
1978 GType gtype_src;
1979 g_autofree gchar *firmware_type_dst = NULL;
1980 g_autofree gchar *firmware_type_src = NULL;
1981 g_autofree gchar *str_dst = NULL;
1982 g_autofree gchar *str_src = NULL;
1983 g_autoptr(FuFirmware) firmware_dst = NULL;
1984 g_autoptr(FuFirmware) firmware_src = NULL;
1985 g_autoptr(GBytes) blob_dst = NULL;
1986 g_autoptr(GBytes) blob_src = NULL;
1987 g_autoptr(GPtrArray) images = NULL;
1988
1989 /* check args */
1990 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
1991 g_set_error_literal (error,
1992 FWUPD_ERROR,
1993 FWUPD_ERROR_INVALID_ARGS,
1994 "Invalid arguments: filename required");
1995 return FALSE;
1996 }
1997
1998 if (g_strv_length (values) > 2)
1999 firmware_type_src = g_strdup (values[2]);
2000 if (g_strv_length (values) > 3)
2001 firmware_type_dst = g_strdup (values[3]);
2002
2003 /* load file */
2004 blob_src = fu_common_get_contents_bytes (values[0], error);
2005 if (blob_src == NULL)
2006 return FALSE;
2007
2008 /* load engine */
2009 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
2010 return FALSE;
2011
2012 /* find the GType to use */
2013 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002014 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002015 if (firmware_type_src == NULL)
2016 return FALSE;
2017 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002018 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002019 if (firmware_type_dst == NULL)
2020 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002021 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2022 if (gtype_src == G_TYPE_INVALID) {
2023 g_set_error (error,
2024 G_IO_ERROR,
2025 G_IO_ERROR_NOT_FOUND,
2026 "GType %s not supported", firmware_type_src);
2027 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002028 }
Richard Hughes0924c932020-09-22 19:07:05 +01002029 firmware_src = g_object_new (gtype_src, NULL);
2030 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2031 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002032 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2033 if (gtype_dst == G_TYPE_INVALID) {
2034 g_set_error (error,
2035 G_IO_ERROR,
2036 G_IO_ERROR_NOT_FOUND,
2037 "GType %s not supported", firmware_type_dst);
2038 return FALSE;
2039 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002040 str_src = fu_firmware_to_string (firmware_src);
2041 g_print ("%s", str_src);
2042
2043 /* copy images */
2044 firmware_dst = g_object_new (gtype_dst, NULL);
2045 images = fu_firmware_get_images (firmware_src);
2046 for (guint i = 0; i < images->len; i++) {
2047 FuFirmwareImage *img = g_ptr_array_index (images, i);
2048 fu_firmware_add_image (firmware_dst, img);
2049 }
2050
2051 /* write new file */
2052 blob_dst = fu_firmware_write (firmware_dst, error);
2053 if (blob_dst == NULL)
2054 return FALSE;
2055 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2056 return FALSE;
2057 str_dst = fu_firmware_to_string (firmware_dst);
2058 g_print ("%s", str_dst);
2059
2060 /* success */
2061 return TRUE;
2062}
2063
2064static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002065fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2066{
2067 g_autofree gchar *str = NULL;
2068 g_autoptr(FuDevice) dev = NULL;
2069
2070 /* load engine */
2071 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2072 return FALSE;
2073
2074 /* get device */
2075 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002076 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002077 if (dev == NULL)
2078 return FALSE;
2079 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002080 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002081 if (dev == NULL)
2082 return FALSE;
2083 }
2084
2085 /* add checksums */
2086 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2087 return FALSE;
2088
2089 /* show checksums */
2090 str = fu_device_to_string (dev);
2091 g_print ("%s\n", str);
2092 return TRUE;
2093}
2094
Mario Limonciellofe593942019-04-03 13:48:52 -05002095static gboolean
2096fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2097{
2098 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002099 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002100 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002101
2102 /* load engine */
2103 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2104 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002105 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002106
2107 /* get all devices from the history database */
2108 devices = fu_engine_get_history (priv->engine, error);
2109 if (devices == NULL)
2110 return FALSE;
2111
2112 /* show each device */
2113 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002114 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002115 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002116 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002117 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002118 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002119
Richard Hughes747f5702019-08-06 14:27:26 +01002120 if (!fu_util_filter_device (priv, dev))
2121 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002122 child = g_node_append_data (root, dev);
2123
2124 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002125 if (rel == NULL)
2126 continue;
2127 remote = fwupd_release_get_remote_id (rel);
2128
2129 /* doesn't actually map to remote */
2130 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002131 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002132 continue;
2133 }
2134
2135 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002136 rels = fu_engine_get_releases (priv->engine,
2137 priv->request,
2138 fwupd_device_get_id (dev),
2139 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002140 if (rels == NULL)
2141 return FALSE;
2142
2143 /* map to a release in client */
2144 for (guint j = 0; j < rels->len; j++) {
2145 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2146 if (g_strcmp0 (remote,
2147 fwupd_release_get_remote_id (rel2)) != 0)
2148 continue;
2149 if (g_strcmp0 (fwupd_release_get_version (rel),
2150 fwupd_release_get_version (rel2)) != 0)
2151 continue;
2152 g_node_append_data (child, g_object_ref (rel2));
2153 rel = NULL;
2154 break;
2155 }
2156
2157 /* didn't match anything */
2158 if (rels->len == 0 || rel != NULL) {
2159 g_node_append_data (child, rel);
2160 continue;
2161 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002162
Mario Limonciellofe593942019-04-03 13:48:52 -05002163 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002164 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002165
2166 return TRUE;
2167}
2168
Richard Hughesfd7e9942020-01-17 14:09:13 +00002169static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002170fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2171{
Richard Hughesc5710d92020-06-26 11:08:25 +01002172 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002173 g_autofree gchar *fn_raw = NULL;
2174 g_autofree gchar *fn_sig = NULL;
2175 g_autoptr(GBytes) bytes_raw = NULL;
2176 g_autoptr(GBytes) bytes_sig = NULL;
2177
2178 /* payload */
Richard Hughesc5710d92020-06-26 11:08:25 +01002179 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2180 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002181 g_set_error (error,
2182 FWUPD_ERROR,
2183 FWUPD_ERROR_NOTHING_TO_DO,
2184 "no metadata URI available for %s",
2185 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002186 return FALSE;
2187 }
2188 fn_raw = fu_util_get_user_cache_path (metadata_uri);
Richard Hughes4959baa2020-01-17 14:33:00 +00002189 if (!fu_common_mkdir_parent (fn_raw, error))
2190 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002191 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002192 return FALSE;
2193 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2194 if (bytes_raw == NULL)
2195 return FALSE;
2196
2197 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002198 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2199 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002200 g_set_error (error,
2201 FWUPD_ERROR,
2202 FWUPD_ERROR_NOTHING_TO_DO,
2203 "no metadata signature URI available for %s",
2204 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002205 return FALSE;
2206 }
2207 fn_sig = fu_util_get_user_cache_path (metadata_uri);
2208 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002209 return FALSE;
2210 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2211 if (bytes_sig == NULL)
2212 return FALSE;
2213
2214 /* send to daemon */
2215 g_debug ("updating %s", fwupd_remote_get_id (remote));
2216 return fu_engine_update_metadata_bytes (priv->engine,
2217 fwupd_remote_get_id (remote),
2218 bytes_raw,
2219 bytes_sig,
2220 error);
2221
2222}
2223
2224static gboolean
2225fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2226{
2227 g_autoptr(GPtrArray) remotes = NULL;
2228
2229 /* load engine */
2230 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2231 return FALSE;
2232
2233 /* download new metadata */
2234 remotes = fu_engine_get_remotes (priv->engine, error);
2235 if (remotes == NULL)
2236 return FALSE;
2237 for (guint i = 0; i < remotes->len; i++) {
2238 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2239 if (!fwupd_remote_get_enabled (remote))
2240 continue;
2241 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2242 continue;
2243 if (!fu_util_refresh_remote (priv, remote, error))
2244 return FALSE;
2245 }
2246 return TRUE;
2247}
2248
2249static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002250fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2251{
2252 g_autoptr(GNode) root = g_node_new (NULL);
2253 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002254 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002255
2256 /* load engine */
2257 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2258 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002259 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002260
2261 /* list remotes */
2262 remotes = fu_engine_get_remotes (priv->engine, error);
2263 if (remotes == NULL)
2264 return FALSE;
2265 if (remotes->len == 0) {
2266 g_set_error_literal (error,
2267 FWUPD_ERROR,
2268 FWUPD_ERROR_NOTHING_TO_DO,
2269 "no remotes available");
2270 return FALSE;
2271 }
2272 for (guint i = 0; i < remotes->len; i++) {
2273 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2274 g_node_append_data (root, remote_tmp);
2275 }
2276 fu_util_print_tree (root, title);
2277
2278 return TRUE;
2279}
2280
Richard Hughes196c6c62020-05-11 19:42:47 +01002281static gboolean
2282fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2283{
Richard Hughes5c82b942020-09-14 12:24:06 +01002284 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002285 g_autoptr(FuSecurityAttrs) attrs = NULL;
2286 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002287 g_autofree gchar *str = NULL;
2288
2289 /* not ready yet */
2290 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2291 g_set_error_literal (error,
2292 FWUPD_ERROR,
2293 FWUPD_ERROR_NOT_SUPPORTED,
2294 "The HSI specification is not yet complete. "
2295 "To ignore this warning, use --force");
2296 return FALSE;
2297 }
2298
2299 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2300 return FALSE;
2301
2302 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2303 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2304 fu_engine_get_host_security_id (priv->engine));
2305
Richard Hughes5c82b942020-09-14 12:24:06 +01002306 /* show or hide different elements */
2307 if (priv->show_all) {
2308 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2309 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2310 }
2311
Richard Hughes196c6c62020-05-11 19:42:47 +01002312 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002313 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002314 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002315 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002316 g_print ("%s\n", str);
2317 return TRUE;
2318}
2319
Richard Hughesa83deb42020-08-12 12:45:36 +01002320static FuVolume *
2321fu_util_prompt_for_volume (GError **error)
2322{
2323 FuVolume *volume;
2324 guint idx;
2325 g_autoptr(GPtrArray) volumes = NULL;
2326
2327 /* exactly one */
2328 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
Jochen Sprickerhof25b78a62020-09-10 23:57:02 +02002329 if (volumes == NULL)
2330 return NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002331 if (volumes->len == 1) {
2332 volume = g_ptr_array_index (volumes, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002333 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002334 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2335 return g_object_ref (volume);
2336 }
2337
2338 /* TRANSLATORS: get interactive prompt */
2339 g_print ("%s\n", _("Choose a volume:"));
2340 /* TRANSLATORS: this is to abort the interactive prompt */
2341 g_print ("0.\t%s\n", _("Cancel"));
2342 for (guint i = 0; i < volumes->len; i++) {
2343 volume = g_ptr_array_index (volumes, i);
2344 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2345 }
2346 idx = fu_util_prompt_for_number (volumes->len);
2347 if (idx == 0) {
2348 g_set_error_literal (error,
2349 FWUPD_ERROR,
2350 FWUPD_ERROR_NOTHING_TO_DO,
2351 "Request canceled");
2352 return NULL;
2353 }
2354 volume = g_ptr_array_index (volumes, idx - 1);
2355 return g_object_ref (volume);
2356
2357}
2358
2359static gboolean
2360fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2361{
2362 g_autoptr(FuVolume) volume = NULL;
2363 volume = fu_util_prompt_for_volume (error);
2364 if (volume == NULL)
2365 return FALSE;
2366 return fu_volume_mount (volume, error);
2367}
2368
2369static gboolean
2370fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2371{
2372 g_autoptr(FuVolume) volume = NULL;
2373 volume = fu_util_prompt_for_volume (error);
2374 if (volume == NULL)
2375 return FALSE;
2376 return fu_volume_unmount (volume, error);
2377}
2378
2379static gboolean
2380fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2381{
2382 g_autofree gchar *mount_point = NULL;
2383 g_autoptr(FuDeviceLocker) locker = NULL;
2384 g_autoptr(FuVolume) volume = NULL;
2385 g_autoptr(GPtrArray) files = NULL;
2386
2387 volume = fu_util_prompt_for_volume (error);
2388 if (volume == NULL)
2389 return FALSE;
2390 locker = fu_volume_locker (volume, error);
2391 if (locker == NULL)
2392 return FALSE;
2393 mount_point = fu_volume_get_mount_point (volume);
2394 files = fu_common_get_files_recursive (mount_point, error);
2395 if (files == NULL)
2396 return FALSE;
2397 for (guint i = 0; i < files->len; i++) {
2398 const gchar *fn = g_ptr_array_index (files, i);
2399 g_print ("%s\n", fn);
2400 }
2401 return TRUE;
2402}
2403
Richard Hughesb5976832018-05-18 10:02:09 +01002404int
2405main (int argc, char *argv[])
2406{
Richard Hughes460226a2018-05-21 20:56:21 +01002407 gboolean allow_older = FALSE;
2408 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002409 gboolean force = FALSE;
2410 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002411 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002412 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002413 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002414 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2415 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002416 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002417 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002418 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002419 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002420 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2421 /* TRANSLATORS: command line option */
2422 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002423 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2424 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002425 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002426 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2427 /* TRANSLATORS: command line option */
2428 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002429 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2430 /* TRANSLATORS: command line option */
2431 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002432 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2433 /* TRANSLATORS: command line option */
2434 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002435 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2436 /* TRANSLATORS: command line option */
2437 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002438 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2439 /* TRANSLATORS: command line option */
2440 _("Show all results"), NULL },
2441 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002442 /* TRANSLATORS: command line option */
2443 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002444 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002445 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002446 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002447 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2448 /* TRANSLATORS: command line option */
2449 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002450 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002451 /* TRANSLATORS: command line option */
2452 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002453 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002454 /* TRANSLATORS: command line option */
2455 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002456 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2457 /* TRANSLATORS: command line option */
2458 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002459 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2460 /* TRANSLATORS: command line option */
2461 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002462 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2463 /* TRANSLATORS: command line option */
2464 _("Filter with a set of device flags using a ~ prefix to "
2465 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002466 { NULL}
2467 };
2468
Richard Hughes429f72b2020-01-16 12:18:19 +00002469#ifdef _WIN32
2470 /* workaround Windows setting the codepage to 1252 */
2471 g_setenv ("LANG", "C.UTF-8", FALSE);
2472#endif
2473
Richard Hughesb5976832018-05-18 10:02:09 +01002474 setlocale (LC_ALL, "");
2475
Richard Hughes668ee212019-11-22 09:17:46 +00002476 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002477 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2478 textdomain (GETTEXT_PACKAGE);
2479
Richard Hughes01c0bad2019-11-22 09:08:51 +00002480#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002481 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002482 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002483 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002484 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002485#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002486
2487 /* create helper object */
2488 priv->loop = g_main_loop_new (NULL, FALSE);
2489 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002490 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002491
2492 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002493 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002494 "build-firmware",
2495 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
2496 /* TRANSLATORS: command description */
2497 _("Build firmware using a sandbox"),
2498 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002499 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002500 "smbios-dump",
2501 "FILE",
2502 /* TRANSLATORS: command description */
2503 _("Dump SMBIOS data from a file"),
2504 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002505 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002506 "get-plugins",
2507 NULL,
2508 /* TRANSLATORS: command description */
2509 _("Get all enabled plugins registered with the system"),
2510 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002511 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002512 "get-details",
2513 NULL,
2514 /* TRANSLATORS: command description */
2515 _("Gets details about a firmware file"),
2516 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002517 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002518 "get-history",
2519 NULL,
2520 /* TRANSLATORS: command description */
2521 _("Show history of firmware updates"),
2522 fu_util_get_history);
2523 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002524 "get-updates,get-upgrades",
Daniel Campello9fbd7fe2020-09-28 14:53:39 -06002525 "[DEVICE-ID|GUID]",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002526 /* TRANSLATORS: command description */
2527 _("Gets the list of updates for connected hardware"),
2528 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002529 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002530 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002531 NULL,
2532 /* TRANSLATORS: command description */
2533 _("Get all devices that support firmware updates"),
2534 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002535 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002536 "get-device-flags",
2537 NULL,
2538 /* TRANSLATORS: command description */
2539 _("Get all device flags supported by fwupd"),
2540 fu_util_get_device_flags);
2541 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002542 "watch",
2543 NULL,
2544 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002545 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002546 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002547 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002548 "install-blob",
2549 "FILENAME DEVICE-ID",
2550 /* TRANSLATORS: command description */
2551 _("Install a firmware blob on a device"),
2552 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002553 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002554 "install",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002555 "FILE [DEVICE-ID|GUID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002556 /* TRANSLATORS: command description */
2557 _("Install a firmware file on this hardware"),
2558 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002559 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002560 "reinstall",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002561 "DEVICE-ID|GUID",
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002562 /* TRANSLATORS: command description */
2563 _("Reinstall firmware on a device"),
2564 fu_util_reinstall);
2565 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002566 "attach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002567 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002568 /* TRANSLATORS: command description */
2569 _("Attach to firmware mode"),
2570 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002571 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002572 "detach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002573 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002574 /* TRANSLATORS: command description */
2575 _("Detach to bootloader mode"),
2576 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002577 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002578 "unbind-driver",
2579 "[DEVICE-ID|GUID]",
2580 /* TRANSLATORS: command description */
2581 _("Unbind current driver"),
2582 fu_util_unbind_driver);
2583 fu_util_cmd_array_add (cmd_array,
2584 "bind-driver",
2585 "subsystem driver [DEVICE-ID|GUID]",
2586 /* TRANSLATORS: command description */
2587 _("Bind new kernel driver"),
2588 fu_util_bind_driver);
2589 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002590 "activate",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002591 "[DEVICE-ID|GUID]",
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002592 /* TRANSLATORS: command description */
2593 _("Activate pending devices"),
2594 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002595 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002596 "hwids",
2597 "[FILE]",
2598 /* TRANSLATORS: command description */
2599 _("Return all the hardware IDs for the machine"),
2600 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002601 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002602 "monitor",
2603 NULL,
2604 /* TRANSLATORS: command description */
2605 _("Monitor the daemon for events"),
2606 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002607 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002608 "update,upgrade",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002609 "[DEVICE-ID|GUID]",
Mario Limonciello46aaee82019-01-10 12:58:00 -06002610 /* TRANSLATORS: command description */
2611 _("Update all devices that match local metadata"),
2612 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002613 fu_util_cmd_array_add (cmd_array,
2614 "self-sign",
2615 "TEXT",
2616 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002617 C_("command-description",
2618 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002619 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002620 fu_util_cmd_array_add (cmd_array,
2621 "verify-update",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002622 "[DEVICE-ID|GUID]",
Richard Hughes15684492019-03-15 16:27:50 +00002623 /* TRANSLATORS: command description */
2624 _("Update the stored metadata with current contents"),
2625 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002626 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002627 "firmware-dump",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002628 "FILENAME [DEVICE-ID|GUID]",
Richard Hughesa58510b2019-10-30 10:03:12 +00002629 /* TRANSLATORS: command description */
2630 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002631 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002632 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002633 "firmware-convert",
2634 "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]",
2635 /* TRANSLATORS: command description */
2636 _("Convert a firmware file"),
2637 fu_util_firmware_convert);
2638 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002639 "firmware-build",
2640 "BUILDER-XML FILENAME-DST",
2641 /* TRANSLATORS: command description */
2642 _("Build a firmware file"),
2643 fu_util_firmware_build);
2644 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002645 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06002646 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01002647 /* TRANSLATORS: command description */
2648 _("Parse and show details about a firmware file"),
2649 fu_util_firmware_parse);
2650 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002651 "firmware-extract",
2652 "FILENAME [FIRMWARE-TYPE]",
2653 /* TRANSLATORS: command description */
2654 _("Extract a firmware blob to images"),
2655 fu_util_firmware_extract);
2656 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002657 "get-firmware-types",
2658 NULL,
2659 /* TRANSLATORS: command description */
2660 _("List the available firmware types"),
2661 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002662 fu_util_cmd_array_add (cmd_array,
2663 "get-remotes",
2664 NULL,
2665 /* TRANSLATORS: command description */
2666 _("Gets the configured remotes"),
2667 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002668 fu_util_cmd_array_add (cmd_array,
2669 "refresh",
2670 NULL,
2671 /* TRANSLATORS: command description */
2672 _("Refresh metadata from remote server"),
2673 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002674 fu_util_cmd_array_add (cmd_array,
2675 "security",
2676 NULL,
2677 /* TRANSLATORS: command description */
2678 _("Gets the host security attributes."),
2679 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002680 fu_util_cmd_array_add (cmd_array,
2681 "esp-mount",
2682 NULL,
2683 /* TRANSLATORS: command description */
2684 _("Mounts the ESP."),
2685 fu_util_esp_mount);
2686 fu_util_cmd_array_add (cmd_array,
2687 "esp-unmount",
2688 NULL,
2689 /* TRANSLATORS: command description */
2690 _("Unmounts the ESP."),
2691 fu_util_esp_unmount);
2692 fu_util_cmd_array_add (cmd_array,
2693 "esp-list",
2694 NULL,
2695 /* TRANSLATORS: command description */
2696 _("Lists files on the ESP."),
2697 fu_util_esp_list);
Richard Hughesb5976832018-05-18 10:02:09 +01002698
2699 /* do stuff on ctrl+c */
2700 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002701#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002702 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2703 SIGINT, fu_util_sigint_cb,
2704 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002705#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002706 g_signal_connect (priv->cancellable, "cancelled",
2707 G_CALLBACK (fu_util_cancelled_cb), priv);
2708
2709 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002710 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002711
Mario Limonciello3f243a92019-01-21 22:05:23 -06002712 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002713 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002714 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002715 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002716 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01002717 } else {
2718 /* set our implemented feature set */
2719 fu_engine_request_set_feature_flags (priv->request,
2720 FWUPD_FEATURE_FLAG_DETACH_ACTION |
2721 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002722 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002723
Richard Hughesb5976832018-05-18 10:02:09 +01002724 /* get a list of the commands */
2725 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002726 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002727 g_option_context_set_summary (priv->context, cmd_descriptions);
2728 g_option_context_set_description (priv->context,
2729 "This tool allows an administrator to use the fwupd plugins "
2730 "without being installed on the host system.");
2731
2732 /* TRANSLATORS: program name */
2733 g_set_application_name (_("Firmware Utility"));
2734 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002735 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002736 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2737 if (!ret) {
2738 /* TRANSLATORS: the user didn't read the man page */
2739 g_print ("%s: %s\n", _("Failed to parse arguments"),
2740 error->message);
2741 return EXIT_FAILURE;
2742 }
2743
Richard Hughes0e46b222019-09-05 12:13:35 +01002744 /* allow disabling SSL strict mode for broken corporate proxies */
2745 if (priv->disable_ssl_strict) {
2746 /* TRANSLATORS: try to help */
2747 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2748 "to do this automatically in the future "
2749 "export DISABLE_SSL_STRICT in your environment"));
2750 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2751 }
2752
Richard Hughes747f5702019-08-06 14:27:26 +01002753 /* parse filter flags */
2754 if (filter != NULL) {
2755 if (!fu_util_parse_filter_flags (filter,
2756 &priv->filter_include,
2757 &priv->filter_exclude,
2758 &error)) {
2759 /* TRANSLATORS: the user didn't read the man page */
2760 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2761 error->message);
2762 return EXIT_FAILURE;
2763 }
2764 }
2765
2766
Richard Hughes460226a2018-05-21 20:56:21 +01002767 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002768 if (allow_reinstall)
2769 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2770 if (allow_older)
2771 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2772 if (force)
2773 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2774
Richard Hughes98ca9932018-05-18 10:24:07 +01002775 /* load engine */
2776 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2777 g_signal_connect (priv->engine, "device-added",
2778 G_CALLBACK (fu_main_engine_device_added_cb),
2779 priv);
2780 g_signal_connect (priv->engine, "device-removed",
2781 G_CALLBACK (fu_main_engine_device_removed_cb),
2782 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002783 g_signal_connect (priv->engine, "status-changed",
2784 G_CALLBACK (fu_main_engine_status_changed_cb),
2785 priv);
2786 g_signal_connect (priv->engine, "percentage-changed",
2787 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2788 priv);
2789
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002790 /* just show versions and exit */
2791 if (version) {
2792 g_autofree gchar *version_str = fu_util_get_versions ();
2793 g_print ("%s\n", version_str);
2794 return EXIT_SUCCESS;
2795 }
2796
Richard Hughes85226fd2020-06-30 14:43:48 +01002797 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002798 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2799 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2800
Richard Hughesb5976832018-05-18 10:02:09 +01002801 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002802 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002803 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05002804 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002805 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05002806 /* TRANSLATORS: error message explaining command to run to how to get help */
2807 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
2808 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2809 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002810 return EXIT_NOTHING_TO_DO;
2811 }
Richard Hughesb5976832018-05-18 10:02:09 +01002812 return EXIT_FAILURE;
2813 }
2814
2815 /* success */
2816 return EXIT_SUCCESS;
2817}