blob: 3148848678b6533d974987fe317e825f60ce5191 [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 Limonciello4250d9d2019-08-29 09:53:44 -0500488 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500489 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600490 /* save the device state for other applications to see */
491 if (!fu_util_save_current_state (priv, error))
492 return FALSE;
493
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600494 /* success */
495 return TRUE;
496}
497
498static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500499fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
500{
501 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500502 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600503 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500504 gint fd;
505
506 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000507 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500508 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600509 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500510
511 /* check args */
512 if (g_strv_length (values) != 1) {
513 g_set_error_literal (error,
514 FWUPD_ERROR,
515 FWUPD_ERROR_INVALID_ARGS,
516 "Invalid arguments");
517 return FALSE;
518 }
519
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600520 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100521 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600522
Mario Limonciello716ab272018-05-29 12:34:37 -0500523 /* open file */
524 fd = open (values[0], O_RDONLY);
525 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500526 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500527 g_set_error (error,
528 FWUPD_ERROR,
529 FWUPD_ERROR_INVALID_FILE,
530 "failed to open %s",
531 values[0]);
532 return FALSE;
533 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100534 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500535 close (fd);
536
537 if (array == NULL)
538 return FALSE;
539 for (guint i = 0; i < array->len; i++) {
540 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600541 FwupdRelease *rel;
542 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100543 if (!fu_util_filter_device (priv, dev))
544 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600545 child = g_node_append_data (root, dev);
546 rel = fwupd_device_get_release_default (dev);
547 if (rel != NULL)
548 g_node_append_data (child, rel);
549
Mario Limonciello716ab272018-05-29 12:34:37 -0500550 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500551 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500552
Mario Limonciello716ab272018-05-29 12:34:37 -0500553 return TRUE;
554}
555
556static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100557fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
558{
559 g_autoptr(GString) str = g_string_new (NULL);
560
561 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
562 const gchar *tmp = fwupd_device_flag_to_string (i);
563 if (tmp == NULL)
564 break;
565 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
566 g_string_append (str, " ");
567 g_string_append (str, tmp);
568 g_string_append (str, " ~");
569 g_string_append (str, tmp);
570 }
571 g_print ("%s\n", str->str);
572
573 return TRUE;
574}
575
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500576static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100577fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500578{
579 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100580 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100581 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
582 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100583 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500584 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500585 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100586 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500587 GNode *child = g_node_append_data (root, dev_tmp);
588 fu_util_build_device_tree (priv, child, devs, dev_tmp);
589 }
590 }
591}
592
593static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100594fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500595{
596 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600597 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500598 g_autoptr(GPtrArray) devs = NULL;
599
600 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000601 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500602 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600603 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500604
605 /* print */
606 devs = fu_engine_get_devices (priv->engine, error);
607 if (devs == NULL)
608 return FALSE;
609
610 /* print */
611 if (devs->len == 0) {
612 /* TRANSLATORS: nothing attached that can be upgraded */
613 g_print ("%s\n", _("No hardware detected with firmware update capability"));
614 return TRUE;
615 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000616 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500617 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500618 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500619
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100620 /* save the device state for other applications to see */
621 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500622}
623
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500624static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600625fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500626 FwupdDevice *device,
627 FuUtilPrivate *priv)
628{
629 g_autofree gchar *str = NULL;
630
Richard Hughes809abea2019-03-23 11:06:18 +0000631 /* allowed to set whenever the device has changed */
632 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
633 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
634 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
635 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
636
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500637 /* same as last time, so ignore */
638 if (priv->current_device != NULL &&
639 fwupd_device_compare (priv->current_device, device) == 0)
640 return;
641
Richard Hughesee562b52020-04-07 14:32:52 +0100642 /* ignore indirect devices that might have changed */
643 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
644 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
645 g_debug ("ignoring %s with status %s",
646 fwupd_device_get_name (device),
647 fwupd_status_to_string (fwupd_device_get_status (device)));
648 return;
649 }
650
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500651 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600652 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
653 /* TRANSLATORS: %1 is a device name */
654 str = g_strdup_printf (_("Updating %s…"),
655 fwupd_device_get_name (device));
656 fu_progressbar_set_title (priv->progressbar, str);
657 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
658 /* TRANSLATORS: %1 is a device name */
659 str = g_strdup_printf (_("Installing on %s…"),
660 fwupd_device_get_name (device));
661 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000662 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
663 /* TRANSLATORS: %1 is a device name */
664 str = g_strdup_printf (_("Reading from %s…"),
665 fwupd_device_get_name (device));
666 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600667 } else {
668 g_warning ("no FuUtilOperation set");
669 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500670 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600671
Mario Limonciello32241f42019-01-24 10:12:41 -0600672 if (priv->current_message == NULL) {
673 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
674 if (tmp != NULL)
675 priv->current_message = g_strdup (tmp);
676 }
677}
678
679static void
680fu_util_display_current_message (FuUtilPrivate *priv)
681{
682 if (priv->current_message == NULL)
683 return;
684 g_print ("%s\n", priv->current_message);
685 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500686}
687
Richard Hughes98ca9932018-05-18 10:24:07 +0100688static gboolean
689fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
690{
691 g_autoptr(FuDevice) device = NULL;
692 g_autoptr(GBytes) blob_fw = NULL;
693
694 /* invalid args */
695 if (g_strv_length (values) == 0) {
696 g_set_error_literal (error,
697 FWUPD_ERROR,
698 FWUPD_ERROR_INVALID_ARGS,
699 "Invalid arguments");
700 return FALSE;
701 }
702
703 /* parse blob */
704 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500705 if (blob_fw == NULL) {
706 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100707 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500708 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100709
710 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000711 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100712 return FALSE;
713
714 /* get device */
715 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100716 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100717 if (device == NULL)
718 return FALSE;
719 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100720 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100721 if (device == NULL)
722 return FALSE;
723 }
724
Mario Limonciello3f243a92019-01-21 22:05:23 -0600725 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500726 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600727 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500728
Richard Hughes98ca9932018-05-18 10:24:07 +0100729 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000730 if (priv->prepare_blob) {
731 g_autoptr(GPtrArray) devices = NULL;
732 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
733 g_ptr_array_add (devices, g_object_ref (device));
734 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
735 g_prefix_error (error, "failed to prepare composite action: ");
736 return FALSE;
737 }
738 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100739 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000740 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600741 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000742 if (priv->cleanup_blob) {
743 g_autoptr(FuDevice) device_new = NULL;
744 g_autoptr(GError) error_local = NULL;
745
746 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100747 device_new = fu_util_get_device (priv,
748 fu_device_get_id (device),
749 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000750 if (device_new == NULL) {
751 g_debug ("failed to find new device: %s",
752 error_local->message);
753 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600754 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000755 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
756 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
757 g_prefix_error (error, "failed to cleanup composite action: ");
758 return FALSE;
759 }
760 }
761 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600762
Mario Limonciello32241f42019-01-24 10:12:41 -0600763 fu_util_display_current_message (priv);
764
Mario Limonciello3f243a92019-01-21 22:05:23 -0600765 /* success */
766 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100767}
768
Richard Hughesa58510b2019-10-30 10:03:12 +0000769static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100770fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000771{
772 g_autoptr(FuDevice) device = NULL;
773 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
774 g_autoptr(GBytes) blob_fw = NULL;
775
776 /* invalid args */
777 if (g_strv_length (values) == 0) {
778 g_set_error_literal (error,
779 FWUPD_ERROR,
780 FWUPD_ERROR_INVALID_ARGS,
781 "Invalid arguments");
782 return FALSE;
783 }
784
785 /* file already exists */
786 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
787 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
788 g_set_error_literal (error,
789 FWUPD_ERROR,
790 FWUPD_ERROR_INVALID_ARGS,
791 "Filename already exists");
792 return FALSE;
793 }
794
Richard Hughes02792c02019-11-01 14:21:20 +0000795 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000796 * avoid failing at the end of a potentially lengthy operation */
797 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
798 return FALSE;
799
800 /* load engine */
801 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
802 return FALSE;
803
804 /* get device */
805 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100806 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000807 if (device == NULL)
808 return FALSE;
809 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100810 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000811 if (device == NULL)
812 return FALSE;
813 }
814 priv->current_operation = FU_UTIL_OPERATION_READ;
815 g_signal_connect (priv->engine, "device-changed",
816 G_CALLBACK (fu_util_update_device_changed_cb), priv);
817
818 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100819 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000820 if (blob_fw == NULL)
821 return FALSE;
822 return fu_common_set_contents_bytes (values[0], blob_fw, error);
823}
824
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100825static gint
826fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
827{
828 FuInstallTask *task1 = *((FuInstallTask **) a);
829 FuInstallTask *task2 = *((FuInstallTask **) b);
830 return fu_install_task_compare (task1, task2);
831}
832
833static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100834fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
835{
Filipe Laínse0914272019-09-20 10:04:43 +0100836 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100837 { "curl", uri, "--output", fn, NULL },
838 { NULL } };
839 for (guint i = 0; argv[i][0] != NULL; i++) {
840 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100841 g_autofree gchar *fn_tmp = NULL;
842 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
843 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100844 g_debug ("%s", error_local->message);
845 continue;
846 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000847 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100848 }
849 g_set_error_literal (error,
850 FWUPD_ERROR,
851 FWUPD_ERROR_NOT_FOUND,
852 "no supported out-of-process downloaders found");
853 return FALSE;
854}
855
856static gchar *
857fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
858{
859 g_autofree gchar *filename = NULL;
860 g_autoptr(SoupURI) uri = NULL;
861
862 /* a local file */
863 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000864 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
865 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100866 if (uri == NULL)
867 return g_strdup (perhapsfn);
868
869 /* download the firmware to a cachedir */
870 filename = fu_util_get_user_cache_path (perhapsfn);
871 if (!fu_common_mkdir_parent (filename, error))
872 return NULL;
873 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
874 return NULL;
875 return g_steal_pointer (&filename);
876}
877
878static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100879fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
880{
Richard Hughes3d178be2018-08-30 11:14:24 +0100881 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100882 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100883 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100884 g_autoptr(GPtrArray) devices_possible = NULL;
885 g_autoptr(GPtrArray) errors = NULL;
886 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100887 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100888
Mario Limonciello8949e892018-05-25 08:03:06 -0500889 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000890 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500891 return FALSE;
892
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100893 /* handle both forms */
894 if (g_strv_length (values) == 1) {
895 devices_possible = fu_engine_get_devices (priv->engine, error);
896 if (devices_possible == NULL)
897 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000898 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100899 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100900 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100901 if (device == NULL)
902 return FALSE;
903 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
904 g_ptr_array_add (devices_possible, device);
905 } else {
906 g_set_error_literal (error,
907 FWUPD_ERROR,
908 FWUPD_ERROR_INVALID_ARGS,
909 "Invalid arguments");
910 return FALSE;
911 }
912
Richard Hughes3d178be2018-08-30 11:14:24 +0100913 /* download if required */
914 filename = fu_util_download_if_required (priv, values[0], error);
915 if (filename == NULL)
916 return FALSE;
917
Richard Hughes481aa2a2018-09-18 20:51:46 +0100918 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100919 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500920 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100921 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100922 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500923 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100924 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
925 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100926 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600927 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100928 if (components == NULL)
929 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100930
Richard Hughes481aa2a2018-09-18 20:51:46 +0100931 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100932 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
933 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100934 for (guint i = 0; i < components->len; i++) {
935 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100936
937 /* do any devices pass the requirements */
938 for (guint j = 0; j < devices_possible->len; j++) {
939 FuDevice *device = g_ptr_array_index (devices_possible, j);
940 g_autoptr(FuInstallTask) task = NULL;
941 g_autoptr(GError) error_local = NULL;
942
943 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100944 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100945 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100946 priv->request,
947 task,
948 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500949 &error_local)) {
950 g_debug ("first pass requirement on %s:%s failed: %s",
951 fu_device_get_id (device),
952 xb_node_query_text (component, "id", NULL),
953 error_local->message);
954 g_ptr_array_add (errors, g_steal_pointer (&error_local));
955 continue;
956 }
957
958 /* make a second pass using possibly updated version format now */
959 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
960 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100961 priv->request,
962 task,
963 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100964 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500965 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100966 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100967 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100968 error_local->message);
969 g_ptr_array_add (errors, g_steal_pointer (&error_local));
970 continue;
971 }
972
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600973 /* if component should have an update message from CAB */
974 fu_device_incorporate_from_component (device, component);
975
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100976 /* success */
977 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
978 }
979 }
980
981 /* order the install tasks by the device priority */
982 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
983
984 /* nothing suitable */
985 if (install_tasks->len == 0) {
986 GError *error_tmp = fu_common_error_array_get_best (errors);
987 g_propagate_error (error, error_tmp);
988 return FALSE;
989 }
990
Mario Limonciello3f243a92019-01-21 22:05:23 -0600991 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500992 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600993 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500994
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100995 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +0100996 if (!fu_engine_install_tasks (priv->engine,
997 priv->request,
998 install_tasks,
999 blob_cab,
1000 priv->flags,
1001 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001002 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001003
Mario Limonciello32241f42019-01-24 10:12:41 -06001004 fu_util_display_current_message (priv);
1005
Mario Limonciello3f243a92019-01-21 22:05:23 -06001006 /* we don't want to ask anything */
1007 if (priv->no_reboot_check) {
1008 g_debug ("skipping reboot check");
1009 return TRUE;
1010 }
1011
Mario Limonciello3143bad2019-02-27 07:31:00 -06001012 /* save the device state for other applications to see */
1013 if (!fu_util_save_current_state (priv, error))
1014 return FALSE;
1015
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001016 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001017 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001018}
1019
Richard Hughes98ca9932018-05-18 10:24:07 +01001020static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001021fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001022{
Mario Limonciellofd734852019-08-01 16:41:42 -05001023 FwupdRemote *remote;
1024 const gchar *remote_id;
1025 const gchar *uri_tmp;
1026 g_auto(GStrv) argv = NULL;
Daniel Campello722f5322020-08-12 11:27:38 -06001027 g_autoptr(SoupURI) uri = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001028
Mario Limonciellofd734852019-08-01 16:41:42 -05001029 uri_tmp = fwupd_release_get_uri (rel);
1030 if (uri_tmp == NULL) {
1031 g_set_error_literal (error,
1032 FWUPD_ERROR,
1033 FWUPD_ERROR_INVALID_FILE,
1034 "release missing URI");
1035 return FALSE;
1036 }
1037 remote_id = fwupd_release_get_remote_id (rel);
1038 if (remote_id == NULL) {
1039 g_set_error (error,
1040 FWUPD_ERROR,
1041 FWUPD_ERROR_INVALID_FILE,
1042 "failed to find remote for %s",
1043 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001044 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001045 }
1046
1047 remote = fu_engine_get_remote_by_id (priv->engine,
1048 remote_id,
1049 error);
1050 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001051 return FALSE;
1052
Mario Limonciellofd734852019-08-01 16:41:42 -05001053 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001054 /* local remotes may have the firmware already */
1055 uri = soup_uri_new (uri_tmp);
1056 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001057 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1058 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1059 argv[0] = g_build_filename (path, uri_tmp, NULL);
1060 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1061 argv[0] = g_strdup (uri_tmp + 7);
1062 /* web remote, fu_util_install will download file */
1063 } else {
1064 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1065 }
1066 return fu_util_install (priv, argv, error);
1067}
1068
1069static gboolean
1070fu_util_update_all (FuUtilPrivate *priv, GError **error)
1071{
1072 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001073 gboolean no_updates_header = FALSE;
1074 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001075
Mario Limonciello46aaee82019-01-10 12:58:00 -06001076 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001077 if (devices == NULL)
1078 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001079 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001080 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001081 for (guint i = 0; i < devices->len; i++) {
1082 FwupdDevice *dev = g_ptr_array_index (devices, i);
1083 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001084 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001085 g_autoptr(GPtrArray) rels = NULL;
1086 g_autoptr(GError) error_local = NULL;
1087
1088 if (!fu_util_is_interesting_device (dev))
1089 continue;
1090 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001091 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001092 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001093 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001094 if (!no_updates_header) {
1095 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1096 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1097 no_updates_header = TRUE;
1098 }
1099 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001100 continue;
1101 }
Richard Hughes747f5702019-08-06 14:27:26 +01001102 if (!fu_util_filter_device (priv, dev))
1103 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001104
1105 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001106 rels = fu_engine_get_upgrades (priv->engine,
1107 priv->request,
1108 device_id,
1109 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001110 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001111 if (!latest_header) {
1112 /* TRANSLATORS: message letting the user know no device upgrade available */
1113 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1114 latest_header = TRUE;
1115 }
1116 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001117 /* discard the actual reason from user, but leave for debugging */
1118 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001119 continue;
1120 }
1121
Mario Limonciello98b95162019-10-30 09:20:43 -05001122 if (!priv->no_safety_check) {
1123 if (!fu_util_prompt_warning (dev,
1124 fu_util_get_tree_title (priv),
1125 error))
1126 return FALSE;
1127 }
1128
Mario Limonciello46aaee82019-01-10 12:58:00 -06001129 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001130 if (!fu_util_install_release (priv, rel, &error_local)) {
1131 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001132 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001133 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001134 fu_util_display_current_message (priv);
1135 }
1136 return TRUE;
1137}
1138
1139static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001140fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001141{
1142 FwupdRelease *rel;
1143 g_autoptr(FuDevice) dev = NULL;
1144 g_autoptr(GPtrArray) rels = NULL;
1145
Mario Limonciello9917bb42020-04-20 13:41:27 -05001146 /* do not allow a partial device-id, lookup GUIDs */
1147 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001148 if (dev == NULL)
1149 return FALSE;
1150
1151 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001152 rels = fu_engine_get_upgrades (priv->engine,
1153 priv->request,
1154 fu_device_get_id (dev),
1155 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001156 if (rels == NULL)
1157 return FALSE;
1158 rel = g_ptr_array_index (rels, 0);
1159 if (!fu_util_install_release (priv, rel, error))
1160 return FALSE;
1161 fu_util_display_current_message (priv);
1162
1163 return TRUE;
1164}
1165
1166static gboolean
1167fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1168{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001169 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1170 g_set_error_literal (error,
1171 FWUPD_ERROR,
1172 FWUPD_ERROR_INVALID_ARGS,
1173 "--allow-older is not supported for this command");
1174 return FALSE;
1175 }
1176
1177 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1178 g_set_error_literal (error,
1179 FWUPD_ERROR,
1180 FWUPD_ERROR_INVALID_ARGS,
1181 "--allow-reinstall is not supported for this command");
1182 return FALSE;
1183 }
1184
Mario Limonciellofd734852019-08-01 16:41:42 -05001185 if (g_strv_length (values) > 1) {
1186 g_set_error_literal (error,
1187 FWUPD_ERROR,
1188 FWUPD_ERROR_INVALID_ARGS,
1189 "Invalid arguments");
1190 return FALSE;
1191 }
1192
1193 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1194 return FALSE;
1195
1196 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1197 g_signal_connect (priv->engine, "device-changed",
1198 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1199
1200 if (g_strv_length (values) == 1) {
1201 if (!fu_util_update_by_id (priv, values[0], error))
1202 return FALSE;
1203 } else {
1204 if (!fu_util_update_all (priv, error))
1205 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001206 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001207
1208 /* we don't want to ask anything */
1209 if (priv->no_reboot_check) {
1210 g_debug ("skipping reboot check");
1211 return TRUE;
1212 }
1213
Mario Limonciello3143bad2019-02-27 07:31:00 -06001214 /* save the device state for other applications to see */
1215 if (!fu_util_save_current_state (priv, error))
1216 return FALSE;
1217
Mario Limonciello3f243a92019-01-21 22:05:23 -06001218 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001219}
1220
1221static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001222fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1223{
1224 g_autoptr(FwupdRelease) rel = NULL;
1225 g_autoptr(GPtrArray) rels = NULL;
1226 g_autoptr(FuDevice) dev = NULL;
1227
1228 if (g_strv_length (values) != 1) {
1229 g_set_error_literal (error,
1230 FWUPD_ERROR,
1231 FWUPD_ERROR_INVALID_ARGS,
1232 "Invalid arguments");
1233 return FALSE;
1234 }
1235
1236 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1237 return FALSE;
1238
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001239 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001240 if (dev == NULL)
1241 return FALSE;
1242
1243 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001244 rels = fu_engine_get_releases_for_device (priv->engine,
1245 priv->request,
1246 dev,
1247 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001248 if (rels == NULL)
1249 return FALSE;
1250
1251 for (guint j = 0; j < rels->len; j++) {
1252 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1253 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1254 fu_device_get_version (dev),
1255 fu_device_get_version_format (dev)) == 0) {
1256 rel = g_object_ref (rel_tmp);
1257 break;
1258 }
1259 }
1260 if (rel == NULL) {
1261 g_set_error (error,
1262 FWUPD_ERROR,
1263 FWUPD_ERROR_NOT_SUPPORTED,
1264 "Unable to locate release for %s version %s",
1265 fu_device_get_name (dev),
1266 fu_device_get_version (dev));
1267 return FALSE;
1268 }
1269
1270 /* update the console if composite devices are also updated */
1271 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1272 g_signal_connect (priv->engine, "device-changed",
1273 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1274 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1275 if (!fu_util_install_release (priv, rel, error))
1276 return FALSE;
1277 fu_util_display_current_message (priv);
1278
1279 /* we don't want to ask anything */
1280 if (priv->no_reboot_check) {
1281 g_debug ("skipping reboot check");
1282 return TRUE;
1283 }
1284
1285 /* save the device state for other applications to see */
1286 if (!fu_util_save_current_state (priv, error))
1287 return FALSE;
1288
1289 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1290}
1291
1292static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001293fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1294{
1295 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001296 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001297
1298 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001299 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001300 return FALSE;
1301
Richard Hughes98ca9932018-05-18 10:24:07 +01001302 /* get device */
1303 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001304 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001305 if (device == NULL)
1306 return FALSE;
1307 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001308 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001309 if (device == NULL)
1310 return FALSE;
1311 }
1312
1313 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001314 locker = fu_device_locker_new (device, error);
1315 if (locker == NULL)
1316 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001317 return fu_device_detach (device, error);
1318}
1319
1320static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001321fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1322{
1323 g_autoptr(FuDevice) device = NULL;
1324 g_autoptr(FuDeviceLocker) locker = NULL;
1325
1326 /* load engine */
1327 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1328 return FALSE;
1329
1330 /* get device */
1331 if (g_strv_length (values) == 1) {
1332 device = fu_util_get_device (priv, values[0], error);
1333 } else {
1334 device = fu_util_prompt_for_device (priv, NULL, error);
1335 }
1336 if (device == NULL)
1337 return FALSE;
1338
1339 /* run vfunc */
1340 locker = fu_device_locker_new (device, error);
1341 if (locker == NULL)
1342 return FALSE;
1343 return fu_device_unbind_driver (device, error);
1344}
1345
1346static gboolean
1347fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1348{
1349 g_autoptr(FuDevice) device = NULL;
1350 g_autoptr(FuDeviceLocker) locker = NULL;
1351
1352 /* load engine */
1353 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1354 return FALSE;
1355
1356 /* get device */
1357 if (g_strv_length (values) == 3) {
1358 device = fu_util_get_device (priv, values[2], error);
1359 if (device == NULL)
1360 return FALSE;
1361 } else if (g_strv_length (values) == 2) {
1362 device = fu_util_prompt_for_device (priv, NULL, error);
1363 if (device == NULL)
1364 return FALSE;
1365 } else {
1366 g_set_error_literal (error,
1367 FWUPD_ERROR,
1368 FWUPD_ERROR_INVALID_ARGS,
1369 "Invalid arguments");
1370 return FALSE;
1371 }
1372
1373 /* run vfunc */
1374 locker = fu_device_locker_new (device, error);
1375 if (locker == NULL)
1376 return FALSE;
1377 return fu_device_bind_driver (device, values[0], values[1], error);
1378}
1379
1380static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001381fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1382{
1383 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001384 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001385
1386 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001387 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001388 return FALSE;
1389
Richard Hughes98ca9932018-05-18 10:24:07 +01001390 /* get device */
1391 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001392 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001393 if (device == NULL)
1394 return FALSE;
1395 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001396 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001397 if (device == NULL)
1398 return FALSE;
1399 }
1400
1401 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001402 locker = fu_device_locker_new (device, error);
1403 if (locker == NULL)
1404 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001405 return fu_device_attach (device, error);
1406}
1407
Richard Hughes1d894f12018-08-31 13:05:51 +01001408static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001409fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001410{
1411 gboolean has_pending = FALSE;
1412 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001413 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1414 if (devices == NULL)
1415 return FALSE;
1416
1417 /* only start up the plugins needed */
1418 for (guint i = 0; i < devices->len; i++) {
1419 FuDevice *dev = g_ptr_array_index (devices, i);
1420 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1421 fu_engine_add_plugin_filter (priv->engine,
1422 fu_device_get_plugin (dev));
1423 has_pending = TRUE;
1424 }
1425 }
1426
1427 if (!has_pending) {
1428 g_set_error_literal (error,
1429 FWUPD_ERROR,
1430 FWUPD_ERROR_NOTHING_TO_DO,
1431 "No devices to activate");
1432 return FALSE;
1433 }
1434
1435 return TRUE;
1436}
1437
1438static gboolean
1439fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1440{
1441 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001442 g_autoptr(GPtrArray) devices = NULL;
1443
1444 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001445 if (!fu_util_check_activation_needed (priv, error))
1446 return FALSE;
1447
1448 /* load engine */
1449 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
1450 return FALSE;
1451
1452 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001453 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001454 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001455 if (devices == NULL)
1456 return FALSE;
1457 } else if (g_strv_length (values) == 1) {
1458 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001459 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001460 if (device == NULL)
1461 return FALSE;
1462 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1463 g_ptr_array_add (devices, device);
1464 } else {
1465 g_set_error_literal (error,
1466 FWUPD_ERROR,
1467 FWUPD_ERROR_INVALID_ARGS,
1468 "Invalid arguments");
1469 return FALSE;
1470 }
1471
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001472 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001473 /* order by device priority */
1474 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001475 for (guint i = 0; i < devices->len; i++) {
1476 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001477 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1478 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001479 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1480 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001481 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001482 /* TRANSLATORS: shown when shutting down to switch to the new version */
1483 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1484 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1485 return FALSE;
1486 }
1487
Mario Limonciello02085a02020-09-11 14:59:35 -05001488 if (!has_pending) {
1489 g_set_error_literal (error,
1490 FWUPD_ERROR,
1491 FWUPD_ERROR_NOTHING_TO_DO,
1492 "No devices to activate");
1493 return FALSE;
1494 }
1495
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001496 return TRUE;
1497}
1498
1499static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001500fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1501{
1502 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1503 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1504 const gchar *hwid_keys[] = {
1505 FU_HWIDS_KEY_BIOS_VENDOR,
1506 FU_HWIDS_KEY_BIOS_VERSION,
1507 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1508 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1509 FU_HWIDS_KEY_MANUFACTURER,
1510 FU_HWIDS_KEY_FAMILY,
1511 FU_HWIDS_KEY_PRODUCT_NAME,
1512 FU_HWIDS_KEY_PRODUCT_SKU,
1513 FU_HWIDS_KEY_ENCLOSURE_KIND,
1514 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1515 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1516 NULL };
1517
1518 /* read DMI data */
1519 if (g_strv_length (values) == 0) {
1520 if (!fu_smbios_setup (smbios, error))
1521 return FALSE;
1522 } else if (g_strv_length (values) == 1) {
1523 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1524 return FALSE;
1525 } else {
1526 g_set_error_literal (error,
1527 FWUPD_ERROR,
1528 FWUPD_ERROR_INVALID_ARGS,
1529 "Invalid arguments");
1530 return FALSE;
1531 }
1532 if (!fu_hwids_setup (hwids, smbios, error))
1533 return FALSE;
1534
1535 /* show debug output */
1536 g_print ("Computer Information\n");
1537 g_print ("--------------------\n");
1538 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1539 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1540 if (tmp == NULL)
1541 continue;
1542 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1543 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1544 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1545 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1546 } else {
1547 g_print ("%s: %s\n", hwid_keys[i], tmp);
1548 }
1549 }
1550
1551 /* show GUIDs */
1552 g_print ("\nHardware IDs\n");
1553 g_print ("------------\n");
1554 for (guint i = 0; i < 15; i++) {
1555 const gchar *keys = NULL;
1556 g_autofree gchar *guid = NULL;
1557 g_autofree gchar *key = NULL;
1558 g_autofree gchar *keys_str = NULL;
1559 g_auto(GStrv) keysv = NULL;
1560 g_autoptr(GError) error_local = NULL;
1561
1562 /* get the GUID */
1563 key = g_strdup_printf ("HardwareID-%u", i);
1564 keys = fu_hwids_get_replace_keys (hwids, key);
1565 guid = fu_hwids_get_guid (hwids, key, &error_local);
1566 if (guid == NULL) {
1567 g_print ("%s\n", error_local->message);
1568 continue;
1569 }
1570
1571 /* show what makes up the GUID */
1572 keysv = g_strsplit (keys, "&", -1);
1573 keys_str = g_strjoinv (" + ", keysv);
1574 g_print ("{%s} <- %s\n", guid, keys_str);
1575 }
1576
1577 return TRUE;
1578}
1579
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001580static gboolean
1581fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1582{
1583 const gchar *script_fn = "startup.sh";
1584 const gchar *output_fn = "firmware.bin";
1585 g_autoptr(GBytes) archive_blob = NULL;
1586 g_autoptr(GBytes) firmware_blob = NULL;
1587 if (g_strv_length (values) < 2) {
1588 g_set_error_literal (error,
1589 FWUPD_ERROR,
1590 FWUPD_ERROR_INVALID_ARGS,
1591 "Invalid arguments");
1592 return FALSE;
1593 }
1594 archive_blob = fu_common_get_contents_bytes (values[0], error);
1595 if (archive_blob == NULL)
1596 return FALSE;
1597 if (g_strv_length (values) > 2)
1598 script_fn = values[2];
1599 if (g_strv_length (values) > 3)
1600 output_fn = values[3];
1601 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1602 if (firmware_blob == NULL)
1603 return FALSE;
1604 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1605}
1606
Richard Hughes3d607622019-03-07 16:59:27 +00001607static gboolean
1608fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1609{
1610 g_autofree gchar *sig = NULL;
1611
1612 /* check args */
1613 if (g_strv_length (values) != 1) {
1614 g_set_error_literal (error,
1615 FWUPD_ERROR,
1616 FWUPD_ERROR_INVALID_ARGS,
1617 "Invalid arguments: value expected");
1618 return FALSE;
1619 }
1620
1621 /* start engine */
1622 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1623 return FALSE;
1624 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001625 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1626 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001627 if (sig == NULL)
1628 return FALSE;
1629 g_print ("%s\n", sig);
1630 return TRUE;
1631}
1632
Mario Limonciello62f84862018-10-18 13:15:23 -05001633static void
1634fu_util_device_added_cb (FwupdClient *client,
1635 FwupdDevice *device,
1636 gpointer user_data)
1637{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001638 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001639 /* TRANSLATORS: this is when a device is hotplugged */
1640 g_print ("%s\n%s", _("Device added:"), tmp);
1641}
1642
1643static void
1644fu_util_device_removed_cb (FwupdClient *client,
1645 FwupdDevice *device,
1646 gpointer user_data)
1647{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001648 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001649 /* TRANSLATORS: this is when a device is hotplugged */
1650 g_print ("%s\n%s", _("Device removed:"), tmp);
1651}
1652
1653static void
1654fu_util_device_changed_cb (FwupdClient *client,
1655 FwupdDevice *device,
1656 gpointer user_data)
1657{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001658 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001659 /* TRANSLATORS: this is when a device has been updated */
1660 g_print ("%s\n%s", _("Device changed:"), tmp);
1661}
1662
1663static void
1664fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1665{
1666 /* TRANSLATORS: this is when the daemon state changes */
1667 g_print ("%s\n", _("Changed"));
1668}
1669
1670static gboolean
1671fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1672{
1673 g_autoptr(FwupdClient) client = fwupd_client_new ();
1674
1675 /* get all the devices */
1676 if (!fwupd_client_connect (client, priv->cancellable, error))
1677 return FALSE;
1678
1679 /* watch for any hotplugged device */
1680 g_signal_connect (client, "changed",
1681 G_CALLBACK (fu_util_changed_cb), priv);
1682 g_signal_connect (client, "device-added",
1683 G_CALLBACK (fu_util_device_added_cb), priv);
1684 g_signal_connect (client, "device-removed",
1685 G_CALLBACK (fu_util_device_removed_cb), priv);
1686 g_signal_connect (client, "device-changed",
1687 G_CALLBACK (fu_util_device_changed_cb), priv);
1688 g_signal_connect (priv->cancellable, "cancelled",
1689 G_CALLBACK (fu_util_cancelled_cb), priv);
1690 g_main_loop_run (priv->loop);
1691 return TRUE;
1692}
1693
Richard Hughes15684492019-03-15 16:27:50 +00001694static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001695fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1696{
1697 g_autoptr(GPtrArray) firmware_types = NULL;
1698
1699 /* load engine */
1700 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1701 return FALSE;
1702
1703 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1704 for (guint i = 0; i < firmware_types->len; i++) {
1705 const gchar *id = g_ptr_array_index (firmware_types, i);
1706 g_print ("%s\n", id);
1707 }
1708 if (firmware_types->len == 0) {
1709 /* TRANSLATORS: nothing found */
1710 g_print ("%s\n", _("No firmware IDs found"));
1711 return TRUE;
1712 }
1713
1714 return TRUE;
1715}
1716
1717static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001718fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001719{
1720 g_autoptr(GPtrArray) firmware_types = NULL;
1721 guint idx;
1722 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1723
1724 /* TRANSLATORS: get interactive prompt */
1725 g_print ("%s\n", _("Choose a firmware type:"));
1726 /* TRANSLATORS: this is to abort the interactive prompt */
1727 g_print ("0.\t%s\n", _("Cancel"));
1728 for (guint i = 0; i < firmware_types->len; i++) {
1729 const gchar *id = g_ptr_array_index (firmware_types, i);
1730 g_print ("%u.\t%s\n", i + 1, id);
1731 }
1732 idx = fu_util_prompt_for_number (firmware_types->len);
1733 if (idx == 0) {
1734 g_set_error_literal (error,
1735 FWUPD_ERROR,
1736 FWUPD_ERROR_NOTHING_TO_DO,
1737 "Request canceled");
1738 return NULL;
1739 }
1740
1741 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1742}
1743
1744static gboolean
1745fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1746{
1747 GType gtype;
1748 g_autoptr(GBytes) blob = NULL;
1749 g_autoptr(FuFirmware) firmware = NULL;
1750 g_autofree gchar *firmware_type = NULL;
1751 g_autofree gchar *str = NULL;
1752
1753 /* check args */
1754 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1755 g_set_error_literal (error,
1756 FWUPD_ERROR,
1757 FWUPD_ERROR_INVALID_ARGS,
1758 "Invalid arguments: filename required");
1759 return FALSE;
1760 }
1761
1762 if (g_strv_length (values) == 2)
1763 firmware_type = g_strdup (values[1]);
1764
1765 /* load file */
1766 blob = fu_common_get_contents_bytes (values[0], error);
1767 if (blob == NULL)
1768 return FALSE;
1769
1770 /* load engine */
1771 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1772 return FALSE;
1773
1774 /* find the GType to use */
1775 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001776 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001777 if (firmware_type == NULL)
1778 return FALSE;
1779 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1780 if (gtype == G_TYPE_INVALID) {
1781 g_set_error (error,
1782 G_IO_ERROR,
1783 G_IO_ERROR_NOT_FOUND,
1784 "GType %s not supported", firmware_type);
1785 return FALSE;
1786 }
1787 firmware = g_object_new (gtype, NULL);
1788 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1789 return FALSE;
1790 str = fu_firmware_to_string (firmware);
1791 g_print ("%s", str);
1792 return TRUE;
1793}
1794
Richard Hughesdd653442020-09-22 10:23:52 +01001795static gboolean
1796fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1797{
1798 GType gtype;
1799 g_autofree gchar *firmware_type = NULL;
1800 g_autofree gchar *str = NULL;
1801 g_autoptr(FuFirmware) firmware = NULL;
1802 g_autoptr(GBytes) blob = NULL;
1803 g_autoptr(GPtrArray) images = NULL;
1804
1805 /* check args */
1806 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1807 g_set_error_literal (error,
1808 FWUPD_ERROR,
1809 FWUPD_ERROR_INVALID_ARGS,
1810 "Invalid arguments: filename required");
1811 return FALSE;
1812 }
1813 if (g_strv_length (values) == 2)
1814 firmware_type = g_strdup (values[1]);
1815
1816 /* load file */
1817 blob = fu_common_get_contents_bytes (values[0], error);
1818 if (blob == NULL)
1819 return FALSE;
1820
1821 /* load engine */
1822 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1823 return FALSE;
1824
1825 /* find the GType to use */
1826 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001827 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001828 if (firmware_type == NULL)
1829 return FALSE;
1830 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1831 if (gtype == G_TYPE_INVALID) {
1832 g_set_error (error,
1833 G_IO_ERROR,
1834 G_IO_ERROR_NOT_FOUND,
1835 "GType %s not supported", firmware_type);
1836 return FALSE;
1837 }
1838 firmware = g_object_new (gtype, NULL);
1839 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1840 return FALSE;
1841 str = fu_firmware_to_string (firmware);
1842 g_print ("%s", str);
1843 images = fu_firmware_get_images (firmware);
1844 for (guint i = 0; i < images->len; i++) {
1845 FuFirmwareImage *img = g_ptr_array_index (images, i);
1846 g_autofree gchar *fn = NULL;
1847 g_autoptr(GBytes) blob_img = NULL;
1848
Richard Hughes88dd7c42020-09-22 16:54:40 +01001849 /* get raw image without generated header, footer or crc */
1850 blob_img = fu_firmware_image_get_bytes (img);
1851 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1852 continue;
1853
Richard Hughesdd653442020-09-22 10:23:52 +01001854 /* use suitable filename */
1855 if (fu_firmware_image_get_filename (img) != NULL) {
1856 fn = g_strdup (fu_firmware_image_get_filename (img));
1857 } else if (fu_firmware_image_get_id (img) != NULL) {
1858 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1859 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1860 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1861 } else {
1862 fn = g_strdup_printf ("img-0x%x.fw", i);
1863 }
1864 /* TRANSLATORS: decompressing images from a container firmware */
1865 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01001866 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1867 return FALSE;
1868 }
1869
1870 /* success */
1871 return TRUE;
1872}
1873
Richard Hughes0924c932020-09-22 19:07:05 +01001874static gboolean
1875fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01001876{
Richard Hughes0924c932020-09-22 19:07:05 +01001877 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01001878 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01001879 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001880 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01001881 g_autoptr(FuFirmware) firmware_dst = NULL;
1882 g_autoptr(GBytes) blob_dst = NULL;
1883 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001884 g_autoptr(XbBuilder) builder = xb_builder_new ();
1885 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1886 g_autoptr(XbNode) n = NULL;
1887 g_autoptr(XbSilo) silo = NULL;
1888
Richard Hughes0924c932020-09-22 19:07:05 +01001889 /* check args */
1890 if (g_strv_length (values) != 2) {
1891 g_set_error_literal (error,
1892 FWUPD_ERROR,
1893 FWUPD_ERROR_INVALID_ARGS,
1894 "Invalid arguments: filename required");
1895 return FALSE;
1896 }
1897
1898 /* load file */
1899 blob_src = fu_common_get_contents_bytes (values[0], error);
1900 if (blob_src == NULL)
1901 return FALSE;
1902
1903 /* load engine */
1904 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1905 return FALSE;
1906
Richard Hughes41400a82020-09-21 13:43:15 +01001907 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01001908 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01001909 XB_BUILDER_SOURCE_FLAG_NONE,
1910 error)) {
1911 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01001912 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001913 }
1914 xb_builder_import_source (builder, source);
1915 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
1916 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001917 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001918
1919 /* create FuFirmware of specific GType */
1920 n = xb_silo_query_first (silo, "firmware", error);
1921 if (n == NULL)
1922 return FALSE;
1923 tmp = xb_node_get_attr (n, "gtype");
1924 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01001925 gtype = g_type_from_name (tmp);
1926 if (gtype == G_TYPE_INVALID) {
1927 g_set_error (error,
1928 G_IO_ERROR,
1929 G_IO_ERROR_NOT_FOUND,
1930 "GType %s not registered", tmp);
1931 return FALSE;
1932 }
1933 }
1934 tmp = xb_node_get_attr (n, "id");
1935 if (tmp != NULL) {
1936 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01001937 if (gtype == G_TYPE_INVALID) {
1938 g_set_error (error,
1939 G_IO_ERROR,
1940 G_IO_ERROR_NOT_FOUND,
1941 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01001942 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001943 }
Richard Hughes41400a82020-09-21 13:43:15 +01001944 }
Richard Hughes0924c932020-09-22 19:07:05 +01001945 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01001946 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01001947 return FALSE;
1948
1949 /* write new file */
1950 blob_dst = fu_firmware_write (firmware, error);
1951 if (blob_dst == NULL)
1952 return FALSE;
1953 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
1954 return FALSE;
1955
1956 /* show what we wrote */
1957 firmware_dst = g_object_new (gtype, NULL);
1958 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
1959 return FALSE;
1960 str = fu_firmware_to_string (firmware_dst);
1961 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01001962
1963 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01001964 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01001965}
1966
Richard Hughes95c98a92019-10-22 16:03:15 +01001967static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00001968fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
1969{
1970 GType gtype_dst;
1971 GType gtype_src;
1972 g_autofree gchar *firmware_type_dst = NULL;
1973 g_autofree gchar *firmware_type_src = NULL;
1974 g_autofree gchar *str_dst = NULL;
1975 g_autofree gchar *str_src = NULL;
1976 g_autoptr(FuFirmware) firmware_dst = NULL;
1977 g_autoptr(FuFirmware) firmware_src = NULL;
1978 g_autoptr(GBytes) blob_dst = NULL;
1979 g_autoptr(GBytes) blob_src = NULL;
1980 g_autoptr(GPtrArray) images = NULL;
1981
1982 /* check args */
1983 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
1984 g_set_error_literal (error,
1985 FWUPD_ERROR,
1986 FWUPD_ERROR_INVALID_ARGS,
1987 "Invalid arguments: filename required");
1988 return FALSE;
1989 }
1990
1991 if (g_strv_length (values) > 2)
1992 firmware_type_src = g_strdup (values[2]);
1993 if (g_strv_length (values) > 3)
1994 firmware_type_dst = g_strdup (values[3]);
1995
1996 /* load file */
1997 blob_src = fu_common_get_contents_bytes (values[0], error);
1998 if (blob_src == NULL)
1999 return FALSE;
2000
2001 /* load engine */
2002 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
2003 return FALSE;
2004
2005 /* find the GType to use */
2006 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002007 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002008 if (firmware_type_src == NULL)
2009 return FALSE;
2010 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002011 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002012 if (firmware_type_dst == NULL)
2013 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002014 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2015 if (gtype_src == G_TYPE_INVALID) {
2016 g_set_error (error,
2017 G_IO_ERROR,
2018 G_IO_ERROR_NOT_FOUND,
2019 "GType %s not supported", firmware_type_src);
2020 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002021 }
Richard Hughes0924c932020-09-22 19:07:05 +01002022 firmware_src = g_object_new (gtype_src, NULL);
2023 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2024 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002025 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2026 if (gtype_dst == G_TYPE_INVALID) {
2027 g_set_error (error,
2028 G_IO_ERROR,
2029 G_IO_ERROR_NOT_FOUND,
2030 "GType %s not supported", firmware_type_dst);
2031 return FALSE;
2032 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002033 str_src = fu_firmware_to_string (firmware_src);
2034 g_print ("%s", str_src);
2035
2036 /* copy images */
2037 firmware_dst = g_object_new (gtype_dst, NULL);
2038 images = fu_firmware_get_images (firmware_src);
2039 for (guint i = 0; i < images->len; i++) {
2040 FuFirmwareImage *img = g_ptr_array_index (images, i);
2041 fu_firmware_add_image (firmware_dst, img);
2042 }
2043
2044 /* write new file */
2045 blob_dst = fu_firmware_write (firmware_dst, error);
2046 if (blob_dst == NULL)
2047 return FALSE;
2048 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2049 return FALSE;
2050 str_dst = fu_firmware_to_string (firmware_dst);
2051 g_print ("%s", str_dst);
2052
2053 /* success */
2054 return TRUE;
2055}
2056
2057static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002058fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2059{
2060 g_autofree gchar *str = NULL;
2061 g_autoptr(FuDevice) dev = NULL;
2062
2063 /* load engine */
2064 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2065 return FALSE;
2066
2067 /* get device */
2068 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002069 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002070 if (dev == NULL)
2071 return FALSE;
2072 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002073 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002074 if (dev == NULL)
2075 return FALSE;
2076 }
2077
2078 /* add checksums */
2079 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2080 return FALSE;
2081
2082 /* show checksums */
2083 str = fu_device_to_string (dev);
2084 g_print ("%s\n", str);
2085 return TRUE;
2086}
2087
Mario Limonciellofe593942019-04-03 13:48:52 -05002088static gboolean
2089fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2090{
2091 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002092 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002093 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002094
2095 /* load engine */
2096 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2097 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002098 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002099
2100 /* get all devices from the history database */
2101 devices = fu_engine_get_history (priv->engine, error);
2102 if (devices == NULL)
2103 return FALSE;
2104
2105 /* show each device */
2106 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002107 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002108 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002109 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002110 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002111 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002112
Richard Hughes747f5702019-08-06 14:27:26 +01002113 if (!fu_util_filter_device (priv, dev))
2114 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002115 child = g_node_append_data (root, dev);
2116
2117 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002118 if (rel == NULL)
2119 continue;
2120 remote = fwupd_release_get_remote_id (rel);
2121
2122 /* doesn't actually map to remote */
2123 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002124 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002125 continue;
2126 }
2127
2128 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002129 rels = fu_engine_get_releases (priv->engine,
2130 priv->request,
2131 fwupd_device_get_id (dev),
2132 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002133 if (rels == NULL)
2134 return FALSE;
2135
2136 /* map to a release in client */
2137 for (guint j = 0; j < rels->len; j++) {
2138 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2139 if (g_strcmp0 (remote,
2140 fwupd_release_get_remote_id (rel2)) != 0)
2141 continue;
2142 if (g_strcmp0 (fwupd_release_get_version (rel),
2143 fwupd_release_get_version (rel2)) != 0)
2144 continue;
2145 g_node_append_data (child, g_object_ref (rel2));
2146 rel = NULL;
2147 break;
2148 }
2149
2150 /* didn't match anything */
2151 if (rels->len == 0 || rel != NULL) {
2152 g_node_append_data (child, rel);
2153 continue;
2154 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002155
Mario Limonciellofe593942019-04-03 13:48:52 -05002156 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002157 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002158
2159 return TRUE;
2160}
2161
Richard Hughesfd7e9942020-01-17 14:09:13 +00002162static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002163fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2164{
Richard Hughesc5710d92020-06-26 11:08:25 +01002165 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002166 g_autofree gchar *fn_raw = NULL;
2167 g_autofree gchar *fn_sig = NULL;
2168 g_autoptr(GBytes) bytes_raw = NULL;
2169 g_autoptr(GBytes) bytes_sig = NULL;
2170
2171 /* payload */
Richard Hughesc5710d92020-06-26 11:08:25 +01002172 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2173 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002174 g_set_error (error,
2175 FWUPD_ERROR,
2176 FWUPD_ERROR_NOTHING_TO_DO,
2177 "no metadata URI available for %s",
2178 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002179 return FALSE;
2180 }
2181 fn_raw = fu_util_get_user_cache_path (metadata_uri);
Richard Hughes4959baa2020-01-17 14:33:00 +00002182 if (!fu_common_mkdir_parent (fn_raw, error))
2183 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002184 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002185 return FALSE;
2186 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2187 if (bytes_raw == NULL)
2188 return FALSE;
2189
2190 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002191 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2192 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002193 g_set_error (error,
2194 FWUPD_ERROR,
2195 FWUPD_ERROR_NOTHING_TO_DO,
2196 "no metadata signature URI available for %s",
2197 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002198 return FALSE;
2199 }
2200 fn_sig = fu_util_get_user_cache_path (metadata_uri);
2201 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002202 return FALSE;
2203 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2204 if (bytes_sig == NULL)
2205 return FALSE;
2206
2207 /* send to daemon */
2208 g_debug ("updating %s", fwupd_remote_get_id (remote));
2209 return fu_engine_update_metadata_bytes (priv->engine,
2210 fwupd_remote_get_id (remote),
2211 bytes_raw,
2212 bytes_sig,
2213 error);
2214
2215}
2216
2217static gboolean
2218fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2219{
2220 g_autoptr(GPtrArray) remotes = NULL;
2221
2222 /* load engine */
2223 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2224 return FALSE;
2225
2226 /* download new metadata */
2227 remotes = fu_engine_get_remotes (priv->engine, error);
2228 if (remotes == NULL)
2229 return FALSE;
2230 for (guint i = 0; i < remotes->len; i++) {
2231 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2232 if (!fwupd_remote_get_enabled (remote))
2233 continue;
2234 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2235 continue;
2236 if (!fu_util_refresh_remote (priv, remote, error))
2237 return FALSE;
2238 }
2239 return TRUE;
2240}
2241
2242static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002243fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2244{
2245 g_autoptr(GNode) root = g_node_new (NULL);
2246 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002247 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002248
2249 /* load engine */
2250 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2251 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002252 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002253
2254 /* list remotes */
2255 remotes = fu_engine_get_remotes (priv->engine, error);
2256 if (remotes == NULL)
2257 return FALSE;
2258 if (remotes->len == 0) {
2259 g_set_error_literal (error,
2260 FWUPD_ERROR,
2261 FWUPD_ERROR_NOTHING_TO_DO,
2262 "no remotes available");
2263 return FALSE;
2264 }
2265 for (guint i = 0; i < remotes->len; i++) {
2266 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2267 g_node_append_data (root, remote_tmp);
2268 }
2269 fu_util_print_tree (root, title);
2270
2271 return TRUE;
2272}
2273
Richard Hughes196c6c62020-05-11 19:42:47 +01002274static gboolean
2275fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2276{
Richard Hughes5c82b942020-09-14 12:24:06 +01002277 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002278 g_autoptr(FuSecurityAttrs) attrs = NULL;
2279 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002280 g_autofree gchar *str = NULL;
2281
2282 /* not ready yet */
2283 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2284 g_set_error_literal (error,
2285 FWUPD_ERROR,
2286 FWUPD_ERROR_NOT_SUPPORTED,
2287 "The HSI specification is not yet complete. "
2288 "To ignore this warning, use --force");
2289 return FALSE;
2290 }
2291
2292 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2293 return FALSE;
2294
2295 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2296 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2297 fu_engine_get_host_security_id (priv->engine));
2298
Richard Hughes5c82b942020-09-14 12:24:06 +01002299 /* show or hide different elements */
2300 if (priv->show_all) {
2301 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2302 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2303 }
2304
Richard Hughes196c6c62020-05-11 19:42:47 +01002305 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002306 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002307 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002308 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002309 g_print ("%s\n", str);
2310 return TRUE;
2311}
2312
Richard Hughesa83deb42020-08-12 12:45:36 +01002313static FuVolume *
2314fu_util_prompt_for_volume (GError **error)
2315{
2316 FuVolume *volume;
2317 guint idx;
2318 g_autoptr(GPtrArray) volumes = NULL;
2319
2320 /* exactly one */
2321 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
Jochen Sprickerhof25b78a62020-09-10 23:57:02 +02002322 if (volumes == NULL)
2323 return NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002324 if (volumes->len == 1) {
2325 volume = g_ptr_array_index (volumes, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002326 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002327 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2328 return g_object_ref (volume);
2329 }
2330
2331 /* TRANSLATORS: get interactive prompt */
2332 g_print ("%s\n", _("Choose a volume:"));
2333 /* TRANSLATORS: this is to abort the interactive prompt */
2334 g_print ("0.\t%s\n", _("Cancel"));
2335 for (guint i = 0; i < volumes->len; i++) {
2336 volume = g_ptr_array_index (volumes, i);
2337 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2338 }
2339 idx = fu_util_prompt_for_number (volumes->len);
2340 if (idx == 0) {
2341 g_set_error_literal (error,
2342 FWUPD_ERROR,
2343 FWUPD_ERROR_NOTHING_TO_DO,
2344 "Request canceled");
2345 return NULL;
2346 }
2347 volume = g_ptr_array_index (volumes, idx - 1);
2348 return g_object_ref (volume);
2349
2350}
2351
2352static gboolean
2353fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2354{
2355 g_autoptr(FuVolume) volume = NULL;
2356 volume = fu_util_prompt_for_volume (error);
2357 if (volume == NULL)
2358 return FALSE;
2359 return fu_volume_mount (volume, error);
2360}
2361
2362static gboolean
2363fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2364{
2365 g_autoptr(FuVolume) volume = NULL;
2366 volume = fu_util_prompt_for_volume (error);
2367 if (volume == NULL)
2368 return FALSE;
2369 return fu_volume_unmount (volume, error);
2370}
2371
2372static gboolean
2373fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2374{
2375 g_autofree gchar *mount_point = NULL;
2376 g_autoptr(FuDeviceLocker) locker = NULL;
2377 g_autoptr(FuVolume) volume = NULL;
2378 g_autoptr(GPtrArray) files = NULL;
2379
2380 volume = fu_util_prompt_for_volume (error);
2381 if (volume == NULL)
2382 return FALSE;
2383 locker = fu_volume_locker (volume, error);
2384 if (locker == NULL)
2385 return FALSE;
2386 mount_point = fu_volume_get_mount_point (volume);
2387 files = fu_common_get_files_recursive (mount_point, error);
2388 if (files == NULL)
2389 return FALSE;
2390 for (guint i = 0; i < files->len; i++) {
2391 const gchar *fn = g_ptr_array_index (files, i);
2392 g_print ("%s\n", fn);
2393 }
2394 return TRUE;
2395}
2396
Richard Hughesb5976832018-05-18 10:02:09 +01002397int
2398main (int argc, char *argv[])
2399{
Richard Hughes460226a2018-05-21 20:56:21 +01002400 gboolean allow_older = FALSE;
2401 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002402 gboolean force = FALSE;
2403 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002404 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002405 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002406 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002407 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2408 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002409 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002410 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002411 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002412 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002413 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2414 /* TRANSLATORS: command line option */
2415 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002416 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2417 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002418 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002419 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2420 /* TRANSLATORS: command line option */
2421 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002422 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2423 /* TRANSLATORS: command line option */
2424 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002425 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2426 /* TRANSLATORS: command line option */
2427 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002428 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2429 /* TRANSLATORS: command line option */
2430 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002431 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2432 /* TRANSLATORS: command line option */
2433 _("Show all results"), NULL },
2434 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002435 /* TRANSLATORS: command line option */
2436 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002437 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002438 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002439 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002440 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2441 /* TRANSLATORS: command line option */
2442 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002443 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002444 /* TRANSLATORS: command line option */
2445 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002446 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002447 /* TRANSLATORS: command line option */
2448 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002449 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2450 /* TRANSLATORS: command line option */
2451 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002452 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2453 /* TRANSLATORS: command line option */
2454 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002455 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2456 /* TRANSLATORS: command line option */
2457 _("Filter with a set of device flags using a ~ prefix to "
2458 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002459 { NULL}
2460 };
2461
Richard Hughes429f72b2020-01-16 12:18:19 +00002462#ifdef _WIN32
2463 /* workaround Windows setting the codepage to 1252 */
2464 g_setenv ("LANG", "C.UTF-8", FALSE);
2465#endif
2466
Richard Hughesb5976832018-05-18 10:02:09 +01002467 setlocale (LC_ALL, "");
2468
Richard Hughes668ee212019-11-22 09:17:46 +00002469 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002470 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2471 textdomain (GETTEXT_PACKAGE);
2472
Richard Hughes01c0bad2019-11-22 09:08:51 +00002473#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002474 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002475 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002476 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002477 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002478#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002479
2480 /* create helper object */
2481 priv->loop = g_main_loop_new (NULL, FALSE);
2482 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002483 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002484
2485 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002486 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002487 "build-firmware",
2488 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
2489 /* TRANSLATORS: command description */
2490 _("Build firmware using a sandbox"),
2491 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002492 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002493 "smbios-dump",
2494 "FILE",
2495 /* TRANSLATORS: command description */
2496 _("Dump SMBIOS data from a file"),
2497 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002498 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002499 "get-plugins",
2500 NULL,
2501 /* TRANSLATORS: command description */
2502 _("Get all enabled plugins registered with the system"),
2503 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002504 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002505 "get-details",
2506 NULL,
2507 /* TRANSLATORS: command description */
2508 _("Gets details about a firmware file"),
2509 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002510 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002511 "get-history",
2512 NULL,
2513 /* TRANSLATORS: command description */
2514 _("Show history of firmware updates"),
2515 fu_util_get_history);
2516 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002517 "get-updates,get-upgrades",
Daniel Campello9fbd7fe2020-09-28 14:53:39 -06002518 "[DEVICE-ID|GUID]",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002519 /* TRANSLATORS: command description */
2520 _("Gets the list of updates for connected hardware"),
2521 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002522 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002523 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002524 NULL,
2525 /* TRANSLATORS: command description */
2526 _("Get all devices that support firmware updates"),
2527 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002528 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002529 "get-device-flags",
2530 NULL,
2531 /* TRANSLATORS: command description */
2532 _("Get all device flags supported by fwupd"),
2533 fu_util_get_device_flags);
2534 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002535 "watch",
2536 NULL,
2537 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002538 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002539 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002540 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002541 "install-blob",
2542 "FILENAME DEVICE-ID",
2543 /* TRANSLATORS: command description */
2544 _("Install a firmware blob on a device"),
2545 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002546 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002547 "install",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002548 "FILE [DEVICE-ID|GUID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002549 /* TRANSLATORS: command description */
2550 _("Install a firmware file on this hardware"),
2551 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002552 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002553 "reinstall",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002554 "DEVICE-ID|GUID",
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002555 /* TRANSLATORS: command description */
2556 _("Reinstall firmware on a device"),
2557 fu_util_reinstall);
2558 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002559 "attach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002560 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002561 /* TRANSLATORS: command description */
2562 _("Attach to firmware mode"),
2563 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002564 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002565 "detach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002566 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002567 /* TRANSLATORS: command description */
2568 _("Detach to bootloader mode"),
2569 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002570 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002571 "unbind-driver",
2572 "[DEVICE-ID|GUID]",
2573 /* TRANSLATORS: command description */
2574 _("Unbind current driver"),
2575 fu_util_unbind_driver);
2576 fu_util_cmd_array_add (cmd_array,
2577 "bind-driver",
2578 "subsystem driver [DEVICE-ID|GUID]",
2579 /* TRANSLATORS: command description */
2580 _("Bind new kernel driver"),
2581 fu_util_bind_driver);
2582 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002583 "activate",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002584 "[DEVICE-ID|GUID]",
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002585 /* TRANSLATORS: command description */
2586 _("Activate pending devices"),
2587 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002588 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002589 "hwids",
2590 "[FILE]",
2591 /* TRANSLATORS: command description */
2592 _("Return all the hardware IDs for the machine"),
2593 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002594 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002595 "monitor",
2596 NULL,
2597 /* TRANSLATORS: command description */
2598 _("Monitor the daemon for events"),
2599 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002600 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002601 "update,upgrade",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002602 "[DEVICE-ID|GUID]",
Mario Limonciello46aaee82019-01-10 12:58:00 -06002603 /* TRANSLATORS: command description */
2604 _("Update all devices that match local metadata"),
2605 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002606 fu_util_cmd_array_add (cmd_array,
2607 "self-sign",
2608 "TEXT",
2609 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002610 C_("command-description",
2611 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002612 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002613 fu_util_cmd_array_add (cmd_array,
2614 "verify-update",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002615 "[DEVICE-ID|GUID]",
Richard Hughes15684492019-03-15 16:27:50 +00002616 /* TRANSLATORS: command description */
2617 _("Update the stored metadata with current contents"),
2618 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002619 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002620 "firmware-dump",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002621 "FILENAME [DEVICE-ID|GUID]",
Richard Hughesa58510b2019-10-30 10:03:12 +00002622 /* TRANSLATORS: command description */
2623 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002624 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002625 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002626 "firmware-convert",
2627 "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]",
2628 /* TRANSLATORS: command description */
2629 _("Convert a firmware file"),
2630 fu_util_firmware_convert);
2631 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002632 "firmware-build",
2633 "BUILDER-XML FILENAME-DST",
2634 /* TRANSLATORS: command description */
2635 _("Build a firmware file"),
2636 fu_util_firmware_build);
2637 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002638 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06002639 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01002640 /* TRANSLATORS: command description */
2641 _("Parse and show details about a firmware file"),
2642 fu_util_firmware_parse);
2643 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002644 "firmware-extract",
2645 "FILENAME [FIRMWARE-TYPE]",
2646 /* TRANSLATORS: command description */
2647 _("Extract a firmware blob to images"),
2648 fu_util_firmware_extract);
2649 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002650 "get-firmware-types",
2651 NULL,
2652 /* TRANSLATORS: command description */
2653 _("List the available firmware types"),
2654 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002655 fu_util_cmd_array_add (cmd_array,
2656 "get-remotes",
2657 NULL,
2658 /* TRANSLATORS: command description */
2659 _("Gets the configured remotes"),
2660 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002661 fu_util_cmd_array_add (cmd_array,
2662 "refresh",
2663 NULL,
2664 /* TRANSLATORS: command description */
2665 _("Refresh metadata from remote server"),
2666 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002667 fu_util_cmd_array_add (cmd_array,
2668 "security",
2669 NULL,
2670 /* TRANSLATORS: command description */
2671 _("Gets the host security attributes."),
2672 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002673 fu_util_cmd_array_add (cmd_array,
2674 "esp-mount",
2675 NULL,
2676 /* TRANSLATORS: command description */
2677 _("Mounts the ESP."),
2678 fu_util_esp_mount);
2679 fu_util_cmd_array_add (cmd_array,
2680 "esp-unmount",
2681 NULL,
2682 /* TRANSLATORS: command description */
2683 _("Unmounts the ESP."),
2684 fu_util_esp_unmount);
2685 fu_util_cmd_array_add (cmd_array,
2686 "esp-list",
2687 NULL,
2688 /* TRANSLATORS: command description */
2689 _("Lists files on the ESP."),
2690 fu_util_esp_list);
Richard Hughesb5976832018-05-18 10:02:09 +01002691
2692 /* do stuff on ctrl+c */
2693 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002694#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002695 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2696 SIGINT, fu_util_sigint_cb,
2697 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002698#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002699 g_signal_connect (priv->cancellable, "cancelled",
2700 G_CALLBACK (fu_util_cancelled_cb), priv);
2701
2702 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002703 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002704
Mario Limonciello3f243a92019-01-21 22:05:23 -06002705 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002706 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002707 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002708 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002709 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01002710 } else {
2711 /* set our implemented feature set */
2712 fu_engine_request_set_feature_flags (priv->request,
2713 FWUPD_FEATURE_FLAG_DETACH_ACTION |
2714 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002715 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002716
Richard Hughesb5976832018-05-18 10:02:09 +01002717 /* get a list of the commands */
2718 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002719 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002720 g_option_context_set_summary (priv->context, cmd_descriptions);
2721 g_option_context_set_description (priv->context,
2722 "This tool allows an administrator to use the fwupd plugins "
2723 "without being installed on the host system.");
2724
2725 /* TRANSLATORS: program name */
2726 g_set_application_name (_("Firmware Utility"));
2727 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002728 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002729 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2730 if (!ret) {
2731 /* TRANSLATORS: the user didn't read the man page */
2732 g_print ("%s: %s\n", _("Failed to parse arguments"),
2733 error->message);
2734 return EXIT_FAILURE;
2735 }
2736
Richard Hughes0e46b222019-09-05 12:13:35 +01002737 /* allow disabling SSL strict mode for broken corporate proxies */
2738 if (priv->disable_ssl_strict) {
2739 /* TRANSLATORS: try to help */
2740 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2741 "to do this automatically in the future "
2742 "export DISABLE_SSL_STRICT in your environment"));
2743 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2744 }
2745
Richard Hughes747f5702019-08-06 14:27:26 +01002746 /* parse filter flags */
2747 if (filter != NULL) {
2748 if (!fu_util_parse_filter_flags (filter,
2749 &priv->filter_include,
2750 &priv->filter_exclude,
2751 &error)) {
2752 /* TRANSLATORS: the user didn't read the man page */
2753 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2754 error->message);
2755 return EXIT_FAILURE;
2756 }
2757 }
2758
2759
Richard Hughes460226a2018-05-21 20:56:21 +01002760 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002761 if (allow_reinstall)
2762 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2763 if (allow_older)
2764 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2765 if (force)
2766 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2767
Richard Hughes98ca9932018-05-18 10:24:07 +01002768 /* load engine */
2769 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2770 g_signal_connect (priv->engine, "device-added",
2771 G_CALLBACK (fu_main_engine_device_added_cb),
2772 priv);
2773 g_signal_connect (priv->engine, "device-removed",
2774 G_CALLBACK (fu_main_engine_device_removed_cb),
2775 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002776 g_signal_connect (priv->engine, "status-changed",
2777 G_CALLBACK (fu_main_engine_status_changed_cb),
2778 priv);
2779 g_signal_connect (priv->engine, "percentage-changed",
2780 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2781 priv);
2782
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002783 /* just show versions and exit */
2784 if (version) {
2785 g_autofree gchar *version_str = fu_util_get_versions ();
2786 g_print ("%s\n", version_str);
2787 return EXIT_SUCCESS;
2788 }
2789
Richard Hughes85226fd2020-06-30 14:43:48 +01002790 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002791 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2792 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2793
Richard Hughesb5976832018-05-18 10:02:09 +01002794 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002795 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002796 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05002797 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002798 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05002799 /* TRANSLATORS: error message explaining command to run to how to get help */
2800 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
2801 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2802 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002803 return EXIT_NOTHING_TO_DO;
2804 }
Richard Hughesb5976832018-05-18 10:02:09 +01002805 return EXIT_FAILURE;
2806 }
2807
2808 /* success */
2809 return EXIT_SUCCESS;
2810}