blob: 7bcf4759afd014fae96792de9cc137988017043a [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
Richard Hughes747f5702019-08-06 14:27:26 +0100317static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600318fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
319{
320 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500321 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600322 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500323 gboolean no_updates_header = FALSE;
324 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600325
326 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000327 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600328 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600329 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600330
331 /* get devices from daemon */
332 devices = fu_engine_get_devices (priv->engine, error);
333 if (devices == NULL)
334 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000335 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500336 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600337 for (guint i = 0; i < devices->len; i++) {
338 FwupdDevice *dev = g_ptr_array_index (devices, i);
339 g_autoptr(GPtrArray) rels = NULL;
340 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500341 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600342
Richard Hughes747f5702019-08-06 14:27:26 +0100343 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600344 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600345 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600346 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500347 if (!no_updates_header) {
348 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
349 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
350 no_updates_header = TRUE;
351 }
352 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600353 continue;
354 }
Richard Hughes747f5702019-08-06 14:27:26 +0100355 if (!fu_util_filter_device (priv, dev))
356 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600357
358 /* get the releases for this device and filter for validity */
359 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100360 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600361 fwupd_device_get_id (dev),
362 &error_local);
363 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500364 if (!latest_header) {
365 /* TRANSLATORS: message letting the user know no device upgrade available */
366 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
367 latest_header = TRUE;
368 }
369 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600370 /* discard the actual reason from user, but leave for debugging */
371 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600372 continue;
373 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500374 child = g_node_append_data (root, dev);
375
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600376 for (guint j = 0; j < rels->len; j++) {
377 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500378 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600379 }
380 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500381 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500382 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600383 /* save the device state for other applications to see */
384 if (!fu_util_save_current_state (priv, error))
385 return FALSE;
386
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600387 /* success */
388 return TRUE;
389}
390
391static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500392fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
393{
394 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500395 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600396 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500397 gint fd;
398
399 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000400 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500401 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600402 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500403
404 /* check args */
405 if (g_strv_length (values) != 1) {
406 g_set_error_literal (error,
407 FWUPD_ERROR,
408 FWUPD_ERROR_INVALID_ARGS,
409 "Invalid arguments");
410 return FALSE;
411 }
412
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600413 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100414 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600415
Mario Limonciello716ab272018-05-29 12:34:37 -0500416 /* open file */
417 fd = open (values[0], O_RDONLY);
418 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500419 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500420 g_set_error (error,
421 FWUPD_ERROR,
422 FWUPD_ERROR_INVALID_FILE,
423 "failed to open %s",
424 values[0]);
425 return FALSE;
426 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100427 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500428 close (fd);
429
430 if (array == NULL)
431 return FALSE;
432 for (guint i = 0; i < array->len; i++) {
433 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600434 FwupdRelease *rel;
435 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100436 if (!fu_util_filter_device (priv, dev))
437 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600438 child = g_node_append_data (root, dev);
439 rel = fwupd_device_get_release_default (dev);
440 if (rel != NULL)
441 g_node_append_data (child, rel);
442
Mario Limonciello716ab272018-05-29 12:34:37 -0500443 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500444 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500445
Mario Limonciello716ab272018-05-29 12:34:37 -0500446 return TRUE;
447}
448
449static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100450fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
451{
452 g_autoptr(GString) str = g_string_new (NULL);
453
454 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
455 const gchar *tmp = fwupd_device_flag_to_string (i);
456 if (tmp == NULL)
457 break;
458 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
459 g_string_append (str, " ");
460 g_string_append (str, tmp);
461 g_string_append (str, " ~");
462 g_string_append (str, tmp);
463 }
464 g_print ("%s\n", str->str);
465
466 return TRUE;
467}
468
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500469static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100470fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500471{
472 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100473 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100474 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
475 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100476 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500477 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500478 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100479 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500480 GNode *child = g_node_append_data (root, dev_tmp);
481 fu_util_build_device_tree (priv, child, devs, dev_tmp);
482 }
483 }
484}
485
486static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100487fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500488{
489 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600490 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500491 g_autoptr(GPtrArray) devs = NULL;
492
493 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000494 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500495 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600496 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500497
498 /* print */
499 devs = fu_engine_get_devices (priv->engine, error);
500 if (devs == NULL)
501 return FALSE;
502
503 /* print */
504 if (devs->len == 0) {
505 /* TRANSLATORS: nothing attached that can be upgraded */
506 g_print ("%s\n", _("No hardware detected with firmware update capability"));
507 return TRUE;
508 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000509 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500510 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500511 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500512
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100513 /* save the device state for other applications to see */
514 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500515}
516
Richard Hughes98ca9932018-05-18 10:24:07 +0100517static FuDevice *
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100518fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
Richard Hughes98ca9932018-05-18 10:24:07 +0100519{
520 FuDevice *dev;
521 guint idx;
522 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100523 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100524
525 /* get devices from daemon */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100526 if (devices_opt != NULL) {
527 devices = g_ptr_array_ref (devices_opt);
528 } else {
529 devices = fu_engine_get_devices (priv->engine, error);
530 if (devices == NULL)
531 return NULL;
532 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000533 fwupd_device_array_ensure_parents (devices);
Richard Hughes98ca9932018-05-18 10:24:07 +0100534
Richard Hughes747f5702019-08-06 14:27:26 +0100535 /* filter results */
536 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
537 for (guint i = 0; i < devices->len; i++) {
538 dev = g_ptr_array_index (devices, i);
539 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
540 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100541 g_ptr_array_add (devices_filtered, g_object_ref (dev));
542 }
543
544 /* nothing */
545 if (devices_filtered->len == 0) {
546 g_set_error_literal (error,
547 FWUPD_ERROR,
548 FWUPD_ERROR_NOTHING_TO_DO,
549 "No supported devices");
550 return NULL;
551 }
552
Richard Hughes98ca9932018-05-18 10:24:07 +0100553 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100554 if (devices_filtered->len == 1) {
555 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500556 /* TRANSLATORS: Device has been chosen by the daemon for the user */
557 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100558 return g_object_ref (dev);
559 }
560
561 /* TRANSLATORS: get interactive prompt */
562 g_print ("%s\n", _("Choose a device:"));
563 /* TRANSLATORS: this is to abort the interactive prompt */
564 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100565 for (guint i = 0; i < devices_filtered->len; i++) {
566 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100567 g_print ("%u.\t%s (%s)\n",
568 i + 1,
569 fu_device_get_id (dev),
570 fu_device_get_name (dev));
571 }
Richard Hughes747f5702019-08-06 14:27:26 +0100572 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100573 if (idx == 0) {
574 g_set_error_literal (error,
575 FWUPD_ERROR,
576 FWUPD_ERROR_NOTHING_TO_DO,
577 "Request canceled");
578 return NULL;
579 }
Richard Hughes747f5702019-08-06 14:27:26 +0100580 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100581 return g_object_ref (dev);
582}
583
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500584static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600585fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500586 FwupdDevice *device,
587 FuUtilPrivate *priv)
588{
589 g_autofree gchar *str = NULL;
590
Richard Hughes809abea2019-03-23 11:06:18 +0000591 /* allowed to set whenever the device has changed */
592 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
593 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
594 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
595 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
596
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500597 /* same as last time, so ignore */
598 if (priv->current_device != NULL &&
599 fwupd_device_compare (priv->current_device, device) == 0)
600 return;
601
Richard Hughesee562b52020-04-07 14:32:52 +0100602 /* ignore indirect devices that might have changed */
603 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
604 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
605 g_debug ("ignoring %s with status %s",
606 fwupd_device_get_name (device),
607 fwupd_status_to_string (fwupd_device_get_status (device)));
608 return;
609 }
610
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500611 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600612 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
613 /* TRANSLATORS: %1 is a device name */
614 str = g_strdup_printf (_("Updating %s…"),
615 fwupd_device_get_name (device));
616 fu_progressbar_set_title (priv->progressbar, str);
617 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
618 /* TRANSLATORS: %1 is a device name */
619 str = g_strdup_printf (_("Installing on %s…"),
620 fwupd_device_get_name (device));
621 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000622 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
623 /* TRANSLATORS: %1 is a device name */
624 str = g_strdup_printf (_("Reading from %s…"),
625 fwupd_device_get_name (device));
626 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600627 } else {
628 g_warning ("no FuUtilOperation set");
629 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500630 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600631
Mario Limonciello32241f42019-01-24 10:12:41 -0600632 if (priv->current_message == NULL) {
633 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
634 if (tmp != NULL)
635 priv->current_message = g_strdup (tmp);
636 }
637}
638
639static void
640fu_util_display_current_message (FuUtilPrivate *priv)
641{
642 if (priv->current_message == NULL)
643 return;
644 g_print ("%s\n", priv->current_message);
645 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500646}
647
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100648static FuDevice *
649fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
650{
651 if (fwupd_guid_is_valid (id)) {
652 g_autoptr(GPtrArray) devices = NULL;
653 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
654 if (devices == NULL)
655 return NULL;
656 return fu_util_prompt_for_device (priv, devices, error);
657 }
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500658
659 /* did this look like a GUID? */
660 for (guint i = 0; id[i] != '\0'; i++) {
661 if (id[i] == '-') {
662 g_set_error_literal (error,
663 FWUPD_ERROR,
664 FWUPD_ERROR_INVALID_ARGS,
665 "Invalid arguments");
Richard Hughes9c09e2c2020-04-22 10:52:42 +0100666 return NULL;
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500667 }
668 }
Mario Limoncielloaac9c742020-04-20 13:38:44 -0500669 return fu_engine_get_device (priv->engine, id, error);
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100670}
671
Richard Hughes98ca9932018-05-18 10:24:07 +0100672static gboolean
673fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
674{
675 g_autoptr(FuDevice) device = NULL;
676 g_autoptr(GBytes) blob_fw = NULL;
677
678 /* invalid args */
679 if (g_strv_length (values) == 0) {
680 g_set_error_literal (error,
681 FWUPD_ERROR,
682 FWUPD_ERROR_INVALID_ARGS,
683 "Invalid arguments");
684 return FALSE;
685 }
686
687 /* parse blob */
688 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500689 if (blob_fw == NULL) {
690 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100691 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500692 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100693
694 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000695 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100696 return FALSE;
697
698 /* get device */
699 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100700 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100701 if (device == NULL)
702 return FALSE;
703 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100704 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100705 if (device == NULL)
706 return FALSE;
707 }
708
Mario Limonciello3f243a92019-01-21 22:05:23 -0600709 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500710 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600711 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500712
Richard Hughes98ca9932018-05-18 10:24:07 +0100713 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000714 if (priv->prepare_blob) {
715 g_autoptr(GPtrArray) devices = NULL;
716 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
717 g_ptr_array_add (devices, g_object_ref (device));
718 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
719 g_prefix_error (error, "failed to prepare composite action: ");
720 return FALSE;
721 }
722 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100723 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000724 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600725 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000726 if (priv->cleanup_blob) {
727 g_autoptr(FuDevice) device_new = NULL;
728 g_autoptr(GError) error_local = NULL;
729
730 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100731 device_new = fu_util_get_device (priv,
732 fu_device_get_id (device),
733 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000734 if (device_new == NULL) {
735 g_debug ("failed to find new device: %s",
736 error_local->message);
737 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600738 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000739 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
740 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
741 g_prefix_error (error, "failed to cleanup composite action: ");
742 return FALSE;
743 }
744 }
745 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600746
Mario Limonciello32241f42019-01-24 10:12:41 -0600747 fu_util_display_current_message (priv);
748
Mario Limonciello3f243a92019-01-21 22:05:23 -0600749 /* success */
750 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100751}
752
Richard Hughesa58510b2019-10-30 10:03:12 +0000753static gboolean
754fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
755{
756 g_autoptr(FuDevice) device = NULL;
757 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
758 g_autoptr(GBytes) blob_fw = NULL;
759
760 /* invalid args */
761 if (g_strv_length (values) == 0) {
762 g_set_error_literal (error,
763 FWUPD_ERROR,
764 FWUPD_ERROR_INVALID_ARGS,
765 "Invalid arguments");
766 return FALSE;
767 }
768
769 /* file already exists */
770 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
771 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
772 g_set_error_literal (error,
773 FWUPD_ERROR,
774 FWUPD_ERROR_INVALID_ARGS,
775 "Filename already exists");
776 return FALSE;
777 }
778
Richard Hughes02792c02019-11-01 14:21:20 +0000779 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000780 * avoid failing at the end of a potentially lengthy operation */
781 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
782 return FALSE;
783
784 /* load engine */
785 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
786 return FALSE;
787
788 /* get device */
789 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100790 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000791 if (device == NULL)
792 return FALSE;
793 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100794 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000795 if (device == NULL)
796 return FALSE;
797 }
798 priv->current_operation = FU_UTIL_OPERATION_READ;
799 g_signal_connect (priv->engine, "device-changed",
800 G_CALLBACK (fu_util_update_device_changed_cb), priv);
801
802 /* dump firmware */
803 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
804 if (blob_fw == NULL)
805 return FALSE;
806 return fu_common_set_contents_bytes (values[0], blob_fw, error);
807}
808
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100809static gint
810fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
811{
812 FuInstallTask *task1 = *((FuInstallTask **) a);
813 FuInstallTask *task2 = *((FuInstallTask **) b);
814 return fu_install_task_compare (task1, task2);
815}
816
817static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100818fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
819{
Filipe Laínse0914272019-09-20 10:04:43 +0100820 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100821 { "curl", uri, "--output", fn, NULL },
822 { NULL } };
823 for (guint i = 0; argv[i][0] != NULL; i++) {
824 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100825 g_autofree gchar *fn_tmp = NULL;
826 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
827 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100828 g_debug ("%s", error_local->message);
829 continue;
830 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000831 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100832 }
833 g_set_error_literal (error,
834 FWUPD_ERROR,
835 FWUPD_ERROR_NOT_FOUND,
836 "no supported out-of-process downloaders found");
837 return FALSE;
838}
839
840static gchar *
841fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
842{
843 g_autofree gchar *filename = NULL;
844 g_autoptr(SoupURI) uri = NULL;
845
846 /* a local file */
847 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000848 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
849 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100850 if (uri == NULL)
851 return g_strdup (perhapsfn);
852
853 /* download the firmware to a cachedir */
854 filename = fu_util_get_user_cache_path (perhapsfn);
855 if (!fu_common_mkdir_parent (filename, error))
856 return NULL;
857 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
858 return NULL;
859 return g_steal_pointer (&filename);
860}
861
862static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100863fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
864{
Richard Hughes3d178be2018-08-30 11:14:24 +0100865 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100866 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100867 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100868 g_autoptr(GPtrArray) devices_possible = NULL;
869 g_autoptr(GPtrArray) errors = NULL;
870 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100871 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100872
Mario Limonciello8949e892018-05-25 08:03:06 -0500873 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000874 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500875 return FALSE;
876
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100877 /* handle both forms */
878 if (g_strv_length (values) == 1) {
879 devices_possible = fu_engine_get_devices (priv->engine, error);
880 if (devices_possible == NULL)
881 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000882 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100883 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100884 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100885 if (device == NULL)
886 return FALSE;
887 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
888 g_ptr_array_add (devices_possible, device);
889 } else {
890 g_set_error_literal (error,
891 FWUPD_ERROR,
892 FWUPD_ERROR_INVALID_ARGS,
893 "Invalid arguments");
894 return FALSE;
895 }
896
Richard Hughes3d178be2018-08-30 11:14:24 +0100897 /* download if required */
898 filename = fu_util_download_if_required (priv, values[0], error);
899 if (filename == NULL)
900 return FALSE;
901
Richard Hughes481aa2a2018-09-18 20:51:46 +0100902 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100903 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500904 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100905 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100906 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500907 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100908 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
909 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100910 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600911 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100912 if (components == NULL)
913 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100914
Richard Hughes481aa2a2018-09-18 20:51:46 +0100915 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100916 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
917 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100918 for (guint i = 0; i < components->len; i++) {
919 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100920
921 /* do any devices pass the requirements */
922 for (guint j = 0; j < devices_possible->len; j++) {
923 FuDevice *device = g_ptr_array_index (devices_possible, j);
924 g_autoptr(FuInstallTask) task = NULL;
925 g_autoptr(GError) error_local = NULL;
926
927 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100928 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100929 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100930 priv->request,
931 task,
932 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500933 &error_local)) {
934 g_debug ("first pass requirement on %s:%s failed: %s",
935 fu_device_get_id (device),
936 xb_node_query_text (component, "id", NULL),
937 error_local->message);
938 g_ptr_array_add (errors, g_steal_pointer (&error_local));
939 continue;
940 }
941
942 /* make a second pass using possibly updated version format now */
943 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
944 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100945 priv->request,
946 task,
947 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100948 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500949 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100950 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100951 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100952 error_local->message);
953 g_ptr_array_add (errors, g_steal_pointer (&error_local));
954 continue;
955 }
956
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600957 /* if component should have an update message from CAB */
958 fu_device_incorporate_from_component (device, component);
959
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100960 /* success */
961 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
962 }
963 }
964
965 /* order the install tasks by the device priority */
966 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
967
968 /* nothing suitable */
969 if (install_tasks->len == 0) {
970 GError *error_tmp = fu_common_error_array_get_best (errors);
971 g_propagate_error (error, error_tmp);
972 return FALSE;
973 }
974
Mario Limonciello3f243a92019-01-21 22:05:23 -0600975 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500976 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600977 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500978
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100979 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +0100980 if (!fu_engine_install_tasks (priv->engine,
981 priv->request,
982 install_tasks,
983 blob_cab,
984 priv->flags,
985 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +0100986 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100987
Mario Limonciello32241f42019-01-24 10:12:41 -0600988 fu_util_display_current_message (priv);
989
Mario Limonciello3f243a92019-01-21 22:05:23 -0600990 /* we don't want to ask anything */
991 if (priv->no_reboot_check) {
992 g_debug ("skipping reboot check");
993 return TRUE;
994 }
995
Mario Limonciello3143bad2019-02-27 07:31:00 -0600996 /* save the device state for other applications to see */
997 if (!fu_util_save_current_state (priv, error))
998 return FALSE;
999
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001000 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001001 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001002}
1003
Richard Hughes98ca9932018-05-18 10:24:07 +01001004static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001005fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001006{
Mario Limonciellofd734852019-08-01 16:41:42 -05001007 FwupdRemote *remote;
1008 const gchar *remote_id;
1009 const gchar *uri_tmp;
1010 g_auto(GStrv) argv = NULL;
Daniel Campello722f5322020-08-12 11:27:38 -06001011 g_autoptr(SoupURI) uri = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001012
Mario Limonciellofd734852019-08-01 16:41:42 -05001013 uri_tmp = fwupd_release_get_uri (rel);
1014 if (uri_tmp == NULL) {
1015 g_set_error_literal (error,
1016 FWUPD_ERROR,
1017 FWUPD_ERROR_INVALID_FILE,
1018 "release missing URI");
1019 return FALSE;
1020 }
1021 remote_id = fwupd_release_get_remote_id (rel);
1022 if (remote_id == NULL) {
1023 g_set_error (error,
1024 FWUPD_ERROR,
1025 FWUPD_ERROR_INVALID_FILE,
1026 "failed to find remote for %s",
1027 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001028 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001029 }
1030
1031 remote = fu_engine_get_remote_by_id (priv->engine,
1032 remote_id,
1033 error);
1034 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001035 return FALSE;
1036
Mario Limonciellofd734852019-08-01 16:41:42 -05001037 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001038 /* local remotes may have the firmware already */
1039 uri = soup_uri_new (uri_tmp);
1040 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL && uri == NULL) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001041 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1042 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1043 argv[0] = g_build_filename (path, uri_tmp, NULL);
1044 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1045 argv[0] = g_strdup (uri_tmp + 7);
1046 /* web remote, fu_util_install will download file */
1047 } else {
1048 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1049 }
1050 return fu_util_install (priv, argv, error);
1051}
1052
1053static gboolean
1054fu_util_update_all (FuUtilPrivate *priv, GError **error)
1055{
1056 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001057 gboolean no_updates_header = FALSE;
1058 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001059
Mario Limonciello46aaee82019-01-10 12:58:00 -06001060 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001061 if (devices == NULL)
1062 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001063 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001064 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001065 for (guint i = 0; i < devices->len; i++) {
1066 FwupdDevice *dev = g_ptr_array_index (devices, i);
1067 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001068 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001069 g_autoptr(GPtrArray) rels = NULL;
1070 g_autoptr(GError) error_local = NULL;
1071
1072 if (!fu_util_is_interesting_device (dev))
1073 continue;
1074 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001075 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001076 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001077 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001078 if (!no_updates_header) {
1079 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1080 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1081 no_updates_header = TRUE;
1082 }
1083 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001084 continue;
1085 }
Richard Hughes747f5702019-08-06 14:27:26 +01001086 if (!fu_util_filter_device (priv, dev))
1087 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001088
1089 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001090 rels = fu_engine_get_upgrades (priv->engine,
1091 priv->request,
1092 device_id,
1093 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001094 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001095 if (!latest_header) {
1096 /* TRANSLATORS: message letting the user know no device upgrade available */
1097 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1098 latest_header = TRUE;
1099 }
1100 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001101 /* discard the actual reason from user, but leave for debugging */
1102 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001103 continue;
1104 }
1105
Mario Limonciello98b95162019-10-30 09:20:43 -05001106 if (!priv->no_safety_check) {
1107 if (!fu_util_prompt_warning (dev,
1108 fu_util_get_tree_title (priv),
1109 error))
1110 return FALSE;
1111 }
1112
Mario Limonciello46aaee82019-01-10 12:58:00 -06001113 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001114 if (!fu_util_install_release (priv, rel, &error_local)) {
1115 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001116 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001117 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001118 fu_util_display_current_message (priv);
1119 }
1120 return TRUE;
1121}
1122
1123static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001124fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001125{
1126 FwupdRelease *rel;
1127 g_autoptr(FuDevice) dev = NULL;
1128 g_autoptr(GPtrArray) rels = NULL;
1129
Mario Limonciello9917bb42020-04-20 13:41:27 -05001130 /* do not allow a partial device-id, lookup GUIDs */
1131 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001132 if (dev == NULL)
1133 return FALSE;
1134
1135 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001136 rels = fu_engine_get_upgrades (priv->engine,
1137 priv->request,
1138 fu_device_get_id (dev),
1139 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001140 if (rels == NULL)
1141 return FALSE;
1142 rel = g_ptr_array_index (rels, 0);
1143 if (!fu_util_install_release (priv, rel, error))
1144 return FALSE;
1145 fu_util_display_current_message (priv);
1146
1147 return TRUE;
1148}
1149
1150static gboolean
1151fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1152{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001153 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1154 g_set_error_literal (error,
1155 FWUPD_ERROR,
1156 FWUPD_ERROR_INVALID_ARGS,
1157 "--allow-older is not supported for this command");
1158 return FALSE;
1159 }
1160
1161 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1162 g_set_error_literal (error,
1163 FWUPD_ERROR,
1164 FWUPD_ERROR_INVALID_ARGS,
1165 "--allow-reinstall is not supported for this command");
1166 return FALSE;
1167 }
1168
Mario Limonciellofd734852019-08-01 16:41:42 -05001169 if (g_strv_length (values) > 1) {
1170 g_set_error_literal (error,
1171 FWUPD_ERROR,
1172 FWUPD_ERROR_INVALID_ARGS,
1173 "Invalid arguments");
1174 return FALSE;
1175 }
1176
1177 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1178 return FALSE;
1179
1180 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1181 g_signal_connect (priv->engine, "device-changed",
1182 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1183
1184 if (g_strv_length (values) == 1) {
1185 if (!fu_util_update_by_id (priv, values[0], error))
1186 return FALSE;
1187 } else {
1188 if (!fu_util_update_all (priv, error))
1189 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001190 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001191
1192 /* we don't want to ask anything */
1193 if (priv->no_reboot_check) {
1194 g_debug ("skipping reboot check");
1195 return TRUE;
1196 }
1197
Mario Limonciello3143bad2019-02-27 07:31:00 -06001198 /* save the device state for other applications to see */
1199 if (!fu_util_save_current_state (priv, error))
1200 return FALSE;
1201
Mario Limonciello3f243a92019-01-21 22:05:23 -06001202 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001203}
1204
1205static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001206fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1207{
1208 g_autoptr(FwupdRelease) rel = NULL;
1209 g_autoptr(GPtrArray) rels = NULL;
1210 g_autoptr(FuDevice) dev = NULL;
1211
1212 if (g_strv_length (values) != 1) {
1213 g_set_error_literal (error,
1214 FWUPD_ERROR,
1215 FWUPD_ERROR_INVALID_ARGS,
1216 "Invalid arguments");
1217 return FALSE;
1218 }
1219
1220 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1221 return FALSE;
1222
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001223 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001224 if (dev == NULL)
1225 return FALSE;
1226
1227 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001228 rels = fu_engine_get_releases_for_device (priv->engine,
1229 priv->request,
1230 dev,
1231 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001232 if (rels == NULL)
1233 return FALSE;
1234
1235 for (guint j = 0; j < rels->len; j++) {
1236 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1237 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1238 fu_device_get_version (dev),
1239 fu_device_get_version_format (dev)) == 0) {
1240 rel = g_object_ref (rel_tmp);
1241 break;
1242 }
1243 }
1244 if (rel == NULL) {
1245 g_set_error (error,
1246 FWUPD_ERROR,
1247 FWUPD_ERROR_NOT_SUPPORTED,
1248 "Unable to locate release for %s version %s",
1249 fu_device_get_name (dev),
1250 fu_device_get_version (dev));
1251 return FALSE;
1252 }
1253
1254 /* update the console if composite devices are also updated */
1255 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1256 g_signal_connect (priv->engine, "device-changed",
1257 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1258 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1259 if (!fu_util_install_release (priv, rel, error))
1260 return FALSE;
1261 fu_util_display_current_message (priv);
1262
1263 /* we don't want to ask anything */
1264 if (priv->no_reboot_check) {
1265 g_debug ("skipping reboot check");
1266 return TRUE;
1267 }
1268
1269 /* save the device state for other applications to see */
1270 if (!fu_util_save_current_state (priv, error))
1271 return FALSE;
1272
1273 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1274}
1275
1276static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001277fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1278{
1279 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001280 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001281
1282 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001283 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001284 return FALSE;
1285
Richard Hughes98ca9932018-05-18 10:24:07 +01001286 /* get device */
1287 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001288 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001289 if (device == NULL)
1290 return FALSE;
1291 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001292 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001293 if (device == NULL)
1294 return FALSE;
1295 }
1296
1297 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001298 locker = fu_device_locker_new (device, error);
1299 if (locker == NULL)
1300 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001301 return fu_device_detach (device, error);
1302}
1303
1304static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001305fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1306{
1307 g_autoptr(FuDevice) device = NULL;
1308 g_autoptr(FuDeviceLocker) locker = NULL;
1309
1310 /* load engine */
1311 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1312 return FALSE;
1313
1314 /* get device */
1315 if (g_strv_length (values) == 1) {
1316 device = fu_util_get_device (priv, values[0], error);
1317 } else {
1318 device = fu_util_prompt_for_device (priv, NULL, error);
1319 }
1320 if (device == NULL)
1321 return FALSE;
1322
1323 /* run vfunc */
1324 locker = fu_device_locker_new (device, error);
1325 if (locker == NULL)
1326 return FALSE;
1327 return fu_device_unbind_driver (device, error);
1328}
1329
1330static gboolean
1331fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1332{
1333 g_autoptr(FuDevice) device = NULL;
1334 g_autoptr(FuDeviceLocker) locker = NULL;
1335
1336 /* load engine */
1337 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1338 return FALSE;
1339
1340 /* get device */
1341 if (g_strv_length (values) == 3) {
1342 device = fu_util_get_device (priv, values[2], error);
1343 if (device == NULL)
1344 return FALSE;
1345 } else if (g_strv_length (values) == 2) {
1346 device = fu_util_prompt_for_device (priv, NULL, error);
1347 if (device == NULL)
1348 return FALSE;
1349 } else {
1350 g_set_error_literal (error,
1351 FWUPD_ERROR,
1352 FWUPD_ERROR_INVALID_ARGS,
1353 "Invalid arguments");
1354 return FALSE;
1355 }
1356
1357 /* run vfunc */
1358 locker = fu_device_locker_new (device, error);
1359 if (locker == NULL)
1360 return FALSE;
1361 return fu_device_bind_driver (device, values[0], values[1], error);
1362}
1363
1364static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001365fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1366{
1367 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001368 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001369
1370 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001371 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001372 return FALSE;
1373
Richard Hughes98ca9932018-05-18 10:24:07 +01001374 /* get device */
1375 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001376 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001377 if (device == NULL)
1378 return FALSE;
1379 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001380 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001381 if (device == NULL)
1382 return FALSE;
1383 }
1384
1385 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001386 locker = fu_device_locker_new (device, error);
1387 if (locker == NULL)
1388 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001389 return fu_device_attach (device, error);
1390}
1391
Richard Hughes1d894f12018-08-31 13:05:51 +01001392static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001393fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001394{
1395 gboolean has_pending = FALSE;
1396 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001397 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1398 if (devices == NULL)
1399 return FALSE;
1400
1401 /* only start up the plugins needed */
1402 for (guint i = 0; i < devices->len; i++) {
1403 FuDevice *dev = g_ptr_array_index (devices, i);
1404 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1405 fu_engine_add_plugin_filter (priv->engine,
1406 fu_device_get_plugin (dev));
1407 has_pending = TRUE;
1408 }
1409 }
1410
1411 if (!has_pending) {
1412 g_set_error_literal (error,
1413 FWUPD_ERROR,
1414 FWUPD_ERROR_NOTHING_TO_DO,
1415 "No devices to activate");
1416 return FALSE;
1417 }
1418
1419 return TRUE;
1420}
1421
1422static gboolean
1423fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1424{
1425 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001426 g_autoptr(GPtrArray) devices = NULL;
1427
1428 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001429 if (!fu_util_check_activation_needed (priv, error))
1430 return FALSE;
1431
1432 /* load engine */
1433 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
1434 return FALSE;
1435
1436 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001437 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001438 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001439 if (devices == NULL)
1440 return FALSE;
1441 } else if (g_strv_length (values) == 1) {
1442 FuDevice *device;
Mario Limonciello02085a02020-09-11 14:59:35 -05001443 device = fu_engine_get_device_by_id (priv->engine, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001444 if (device == NULL)
1445 return FALSE;
1446 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1447 g_ptr_array_add (devices, device);
1448 } else {
1449 g_set_error_literal (error,
1450 FWUPD_ERROR,
1451 FWUPD_ERROR_INVALID_ARGS,
1452 "Invalid arguments");
1453 return FALSE;
1454 }
1455
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001456 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001457 /* order by device priority */
1458 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001459 for (guint i = 0; i < devices->len; i++) {
1460 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001461 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1462 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001463 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1464 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001465 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001466 /* TRANSLATORS: shown when shutting down to switch to the new version */
1467 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1468 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1469 return FALSE;
1470 }
1471
Mario Limonciello02085a02020-09-11 14:59:35 -05001472 if (!has_pending) {
1473 g_set_error_literal (error,
1474 FWUPD_ERROR,
1475 FWUPD_ERROR_NOTHING_TO_DO,
1476 "No devices to activate");
1477 return FALSE;
1478 }
1479
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001480 return TRUE;
1481}
1482
1483static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001484fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1485{
1486 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1487 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1488 const gchar *hwid_keys[] = {
1489 FU_HWIDS_KEY_BIOS_VENDOR,
1490 FU_HWIDS_KEY_BIOS_VERSION,
1491 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1492 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1493 FU_HWIDS_KEY_MANUFACTURER,
1494 FU_HWIDS_KEY_FAMILY,
1495 FU_HWIDS_KEY_PRODUCT_NAME,
1496 FU_HWIDS_KEY_PRODUCT_SKU,
1497 FU_HWIDS_KEY_ENCLOSURE_KIND,
1498 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1499 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1500 NULL };
1501
1502 /* read DMI data */
1503 if (g_strv_length (values) == 0) {
1504 if (!fu_smbios_setup (smbios, error))
1505 return FALSE;
1506 } else if (g_strv_length (values) == 1) {
1507 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1508 return FALSE;
1509 } else {
1510 g_set_error_literal (error,
1511 FWUPD_ERROR,
1512 FWUPD_ERROR_INVALID_ARGS,
1513 "Invalid arguments");
1514 return FALSE;
1515 }
1516 if (!fu_hwids_setup (hwids, smbios, error))
1517 return FALSE;
1518
1519 /* show debug output */
1520 g_print ("Computer Information\n");
1521 g_print ("--------------------\n");
1522 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1523 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1524 if (tmp == NULL)
1525 continue;
1526 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1527 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1528 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1529 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1530 } else {
1531 g_print ("%s: %s\n", hwid_keys[i], tmp);
1532 }
1533 }
1534
1535 /* show GUIDs */
1536 g_print ("\nHardware IDs\n");
1537 g_print ("------------\n");
1538 for (guint i = 0; i < 15; i++) {
1539 const gchar *keys = NULL;
1540 g_autofree gchar *guid = NULL;
1541 g_autofree gchar *key = NULL;
1542 g_autofree gchar *keys_str = NULL;
1543 g_auto(GStrv) keysv = NULL;
1544 g_autoptr(GError) error_local = NULL;
1545
1546 /* get the GUID */
1547 key = g_strdup_printf ("HardwareID-%u", i);
1548 keys = fu_hwids_get_replace_keys (hwids, key);
1549 guid = fu_hwids_get_guid (hwids, key, &error_local);
1550 if (guid == NULL) {
1551 g_print ("%s\n", error_local->message);
1552 continue;
1553 }
1554
1555 /* show what makes up the GUID */
1556 keysv = g_strsplit (keys, "&", -1);
1557 keys_str = g_strjoinv (" + ", keysv);
1558 g_print ("{%s} <- %s\n", guid, keys_str);
1559 }
1560
1561 return TRUE;
1562}
1563
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001564static gboolean
1565fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1566{
1567 const gchar *script_fn = "startup.sh";
1568 const gchar *output_fn = "firmware.bin";
1569 g_autoptr(GBytes) archive_blob = NULL;
1570 g_autoptr(GBytes) firmware_blob = NULL;
1571 if (g_strv_length (values) < 2) {
1572 g_set_error_literal (error,
1573 FWUPD_ERROR,
1574 FWUPD_ERROR_INVALID_ARGS,
1575 "Invalid arguments");
1576 return FALSE;
1577 }
1578 archive_blob = fu_common_get_contents_bytes (values[0], error);
1579 if (archive_blob == NULL)
1580 return FALSE;
1581 if (g_strv_length (values) > 2)
1582 script_fn = values[2];
1583 if (g_strv_length (values) > 3)
1584 output_fn = values[3];
1585 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1586 if (firmware_blob == NULL)
1587 return FALSE;
1588 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1589}
1590
Richard Hughes3d607622019-03-07 16:59:27 +00001591static gboolean
1592fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1593{
1594 g_autofree gchar *sig = NULL;
1595
1596 /* check args */
1597 if (g_strv_length (values) != 1) {
1598 g_set_error_literal (error,
1599 FWUPD_ERROR,
1600 FWUPD_ERROR_INVALID_ARGS,
1601 "Invalid arguments: value expected");
1602 return FALSE;
1603 }
1604
1605 /* start engine */
1606 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1607 return FALSE;
1608 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001609 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1610 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001611 if (sig == NULL)
1612 return FALSE;
1613 g_print ("%s\n", sig);
1614 return TRUE;
1615}
1616
Mario Limonciello62f84862018-10-18 13:15:23 -05001617static void
1618fu_util_device_added_cb (FwupdClient *client,
1619 FwupdDevice *device,
1620 gpointer user_data)
1621{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001622 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001623 /* TRANSLATORS: this is when a device is hotplugged */
1624 g_print ("%s\n%s", _("Device added:"), tmp);
1625}
1626
1627static void
1628fu_util_device_removed_cb (FwupdClient *client,
1629 FwupdDevice *device,
1630 gpointer user_data)
1631{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001632 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001633 /* TRANSLATORS: this is when a device is hotplugged */
1634 g_print ("%s\n%s", _("Device removed:"), tmp);
1635}
1636
1637static void
1638fu_util_device_changed_cb (FwupdClient *client,
1639 FwupdDevice *device,
1640 gpointer user_data)
1641{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001642 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001643 /* TRANSLATORS: this is when a device has been updated */
1644 g_print ("%s\n%s", _("Device changed:"), tmp);
1645}
1646
1647static void
1648fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1649{
1650 /* TRANSLATORS: this is when the daemon state changes */
1651 g_print ("%s\n", _("Changed"));
1652}
1653
1654static gboolean
1655fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1656{
1657 g_autoptr(FwupdClient) client = fwupd_client_new ();
1658
1659 /* get all the devices */
1660 if (!fwupd_client_connect (client, priv->cancellable, error))
1661 return FALSE;
1662
1663 /* watch for any hotplugged device */
1664 g_signal_connect (client, "changed",
1665 G_CALLBACK (fu_util_changed_cb), priv);
1666 g_signal_connect (client, "device-added",
1667 G_CALLBACK (fu_util_device_added_cb), priv);
1668 g_signal_connect (client, "device-removed",
1669 G_CALLBACK (fu_util_device_removed_cb), priv);
1670 g_signal_connect (client, "device-changed",
1671 G_CALLBACK (fu_util_device_changed_cb), priv);
1672 g_signal_connect (priv->cancellable, "cancelled",
1673 G_CALLBACK (fu_util_cancelled_cb), priv);
1674 g_main_loop_run (priv->loop);
1675 return TRUE;
1676}
1677
Richard Hughes15684492019-03-15 16:27:50 +00001678static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001679fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1680{
1681 g_autoptr(GPtrArray) firmware_types = NULL;
1682
1683 /* load engine */
1684 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1685 return FALSE;
1686
1687 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1688 for (guint i = 0; i < firmware_types->len; i++) {
1689 const gchar *id = g_ptr_array_index (firmware_types, i);
1690 g_print ("%s\n", id);
1691 }
1692 if (firmware_types->len == 0) {
1693 /* TRANSLATORS: nothing found */
1694 g_print ("%s\n", _("No firmware IDs found"));
1695 return TRUE;
1696 }
1697
1698 return TRUE;
1699}
1700
1701static gchar *
Richard Hughes41400a82020-09-21 13:43:15 +01001702fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, gboolean add_builder, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001703{
1704 g_autoptr(GPtrArray) firmware_types = NULL;
1705 guint idx;
1706 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1707
Richard Hughes41400a82020-09-21 13:43:15 +01001708 /* add fake entry */
1709 if (add_builder)
1710 g_ptr_array_add (firmware_types, g_strdup ("builder"));
1711
Richard Hughes95c98a92019-10-22 16:03:15 +01001712 /* TRANSLATORS: get interactive prompt */
1713 g_print ("%s\n", _("Choose a firmware type:"));
1714 /* TRANSLATORS: this is to abort the interactive prompt */
1715 g_print ("0.\t%s\n", _("Cancel"));
1716 for (guint i = 0; i < firmware_types->len; i++) {
1717 const gchar *id = g_ptr_array_index (firmware_types, i);
1718 g_print ("%u.\t%s\n", i + 1, id);
1719 }
1720 idx = fu_util_prompt_for_number (firmware_types->len);
1721 if (idx == 0) {
1722 g_set_error_literal (error,
1723 FWUPD_ERROR,
1724 FWUPD_ERROR_NOTHING_TO_DO,
1725 "Request canceled");
1726 return NULL;
1727 }
1728
1729 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1730}
1731
1732static gboolean
1733fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1734{
1735 GType gtype;
1736 g_autoptr(GBytes) blob = NULL;
1737 g_autoptr(FuFirmware) firmware = NULL;
1738 g_autofree gchar *firmware_type = NULL;
1739 g_autofree gchar *str = NULL;
1740
1741 /* check args */
1742 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1743 g_set_error_literal (error,
1744 FWUPD_ERROR,
1745 FWUPD_ERROR_INVALID_ARGS,
1746 "Invalid arguments: filename required");
1747 return FALSE;
1748 }
1749
1750 if (g_strv_length (values) == 2)
1751 firmware_type = g_strdup (values[1]);
1752
1753 /* load file */
1754 blob = fu_common_get_contents_bytes (values[0], error);
1755 if (blob == NULL)
1756 return FALSE;
1757
1758 /* load engine */
1759 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1760 return FALSE;
1761
1762 /* find the GType to use */
1763 if (firmware_type == NULL)
Richard Hughes41400a82020-09-21 13:43:15 +01001764 firmware_type = fu_util_prompt_for_firmware_type (priv, TRUE, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001765 if (firmware_type == NULL)
1766 return FALSE;
1767 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1768 if (gtype == G_TYPE_INVALID) {
1769 g_set_error (error,
1770 G_IO_ERROR,
1771 G_IO_ERROR_NOT_FOUND,
1772 "GType %s not supported", firmware_type);
1773 return FALSE;
1774 }
1775 firmware = g_object_new (gtype, NULL);
1776 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1777 return FALSE;
1778 str = fu_firmware_to_string (firmware);
1779 g_print ("%s", str);
1780 return TRUE;
1781}
1782
Richard Hughesdd653442020-09-22 10:23:52 +01001783static gboolean
1784fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1785{
1786 GType gtype;
1787 g_autofree gchar *firmware_type = NULL;
1788 g_autofree gchar *str = NULL;
1789 g_autoptr(FuFirmware) firmware = NULL;
1790 g_autoptr(GBytes) blob = NULL;
1791 g_autoptr(GPtrArray) images = NULL;
1792
1793 /* check args */
1794 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1795 g_set_error_literal (error,
1796 FWUPD_ERROR,
1797 FWUPD_ERROR_INVALID_ARGS,
1798 "Invalid arguments: filename required");
1799 return FALSE;
1800 }
1801 if (g_strv_length (values) == 2)
1802 firmware_type = g_strdup (values[1]);
1803
1804 /* load file */
1805 blob = fu_common_get_contents_bytes (values[0], error);
1806 if (blob == NULL)
1807 return FALSE;
1808
1809 /* load engine */
1810 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1811 return FALSE;
1812
1813 /* find the GType to use */
1814 if (firmware_type == NULL)
1815 firmware_type = fu_util_prompt_for_firmware_type (priv, TRUE, error);
1816 if (firmware_type == NULL)
1817 return FALSE;
1818 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1819 if (gtype == G_TYPE_INVALID) {
1820 g_set_error (error,
1821 G_IO_ERROR,
1822 G_IO_ERROR_NOT_FOUND,
1823 "GType %s not supported", firmware_type);
1824 return FALSE;
1825 }
1826 firmware = g_object_new (gtype, NULL);
1827 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1828 return FALSE;
1829 str = fu_firmware_to_string (firmware);
1830 g_print ("%s", str);
1831 images = fu_firmware_get_images (firmware);
1832 for (guint i = 0; i < images->len; i++) {
1833 FuFirmwareImage *img = g_ptr_array_index (images, i);
1834 g_autofree gchar *fn = NULL;
1835 g_autoptr(GBytes) blob_img = NULL;
1836
1837 /* use suitable filename */
1838 if (fu_firmware_image_get_filename (img) != NULL) {
1839 fn = g_strdup (fu_firmware_image_get_filename (img));
1840 } else if (fu_firmware_image_get_id (img) != NULL) {
1841 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1842 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1843 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1844 } else {
1845 fn = g_strdup_printf ("img-0x%x.fw", i);
1846 }
1847 /* TRANSLATORS: decompressing images from a container firmware */
1848 g_print ("%s : %s\n", _("Writing file:"), fn);
1849 blob_img = fu_firmware_image_write (img, error);
1850 if (blob_img == NULL)
1851 return FALSE;
1852 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1853 return FALSE;
1854 }
1855
1856 /* success */
1857 return TRUE;
1858}
1859
Richard Hughes41400a82020-09-21 13:43:15 +01001860static FuFirmware *
1861fu_util_firmware_builder_new (FuUtilPrivate *priv, GBytes *fw, GError **error)
1862{
1863 const gchar *tmp;
1864 g_autoptr(FuFirmware) firmware = NULL;
1865 g_autoptr(XbBuilder) builder = xb_builder_new ();
1866 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1867 g_autoptr(XbNode) n = NULL;
1868 g_autoptr(XbSilo) silo = NULL;
1869
1870 /* parse XML */
1871 if (!xb_builder_source_load_bytes (source, fw,
1872 XB_BUILDER_SOURCE_FLAG_NONE,
1873 error)) {
1874 g_prefix_error (error, "could not parse XML: ");
1875 return NULL;
1876 }
1877 xb_builder_import_source (builder, source);
1878 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
1879 if (silo == NULL)
1880 return NULL;
1881
1882 /* create FuFirmware of specific GType */
1883 n = xb_silo_query_first (silo, "firmware", error);
1884 if (n == NULL)
1885 return FALSE;
1886 tmp = xb_node_get_attr (n, "gtype");
1887 if (tmp != NULL) {
1888 GType gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
1889 if (gtype == G_TYPE_INVALID) {
1890 g_set_error (error,
1891 G_IO_ERROR,
1892 G_IO_ERROR_NOT_FOUND,
1893 "GType %s not supported", tmp);
1894 return NULL;
1895 }
1896 firmware = g_object_new (gtype, NULL);
1897 } else {
1898 firmware = fu_firmware_new ();
1899 }
1900 if (!fu_firmware_build (firmware, n, error))
1901 return NULL;
1902
1903 /* success */
1904 return g_steal_pointer (&firmware);
1905}
1906
Richard Hughes95c98a92019-10-22 16:03:15 +01001907static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00001908fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
1909{
1910 GType gtype_dst;
1911 GType gtype_src;
1912 g_autofree gchar *firmware_type_dst = NULL;
1913 g_autofree gchar *firmware_type_src = NULL;
1914 g_autofree gchar *str_dst = NULL;
1915 g_autofree gchar *str_src = NULL;
1916 g_autoptr(FuFirmware) firmware_dst = NULL;
1917 g_autoptr(FuFirmware) firmware_src = NULL;
1918 g_autoptr(GBytes) blob_dst = NULL;
1919 g_autoptr(GBytes) blob_src = NULL;
1920 g_autoptr(GPtrArray) images = NULL;
1921
1922 /* check args */
1923 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
1924 g_set_error_literal (error,
1925 FWUPD_ERROR,
1926 FWUPD_ERROR_INVALID_ARGS,
1927 "Invalid arguments: filename required");
1928 return FALSE;
1929 }
1930
1931 if (g_strv_length (values) > 2)
1932 firmware_type_src = g_strdup (values[2]);
1933 if (g_strv_length (values) > 3)
1934 firmware_type_dst = g_strdup (values[3]);
1935
1936 /* load file */
1937 blob_src = fu_common_get_contents_bytes (values[0], error);
1938 if (blob_src == NULL)
1939 return FALSE;
1940
1941 /* load engine */
1942 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1943 return FALSE;
1944
1945 /* find the GType to use */
1946 if (firmware_type_src == NULL)
Richard Hughes41400a82020-09-21 13:43:15 +01001947 firmware_type_src = fu_util_prompt_for_firmware_type (priv, TRUE, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00001948 if (firmware_type_src == NULL)
1949 return FALSE;
1950 if (firmware_type_dst == NULL)
Richard Hughes41400a82020-09-21 13:43:15 +01001951 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, FALSE, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00001952 if (firmware_type_dst == NULL)
1953 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001954 if (g_strcmp0 (firmware_type_src, "builder") == 0) {
1955 firmware_src = fu_util_firmware_builder_new (priv, blob_src, error);
1956 if (firmware_src == NULL)
1957 return FALSE;
1958 } else {
1959 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
1960 if (gtype_src == G_TYPE_INVALID) {
1961 g_set_error (error,
1962 G_IO_ERROR,
1963 G_IO_ERROR_NOT_FOUND,
1964 "GType %s not supported", firmware_type_src);
1965 return FALSE;
1966 }
1967 firmware_src = g_object_new (gtype_src, NULL);
1968 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
1969 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00001970 }
1971 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
1972 if (gtype_dst == G_TYPE_INVALID) {
1973 g_set_error (error,
1974 G_IO_ERROR,
1975 G_IO_ERROR_NOT_FOUND,
1976 "GType %s not supported", firmware_type_dst);
1977 return FALSE;
1978 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00001979 str_src = fu_firmware_to_string (firmware_src);
1980 g_print ("%s", str_src);
1981
1982 /* copy images */
1983 firmware_dst = g_object_new (gtype_dst, NULL);
1984 images = fu_firmware_get_images (firmware_src);
1985 for (guint i = 0; i < images->len; i++) {
1986 FuFirmwareImage *img = g_ptr_array_index (images, i);
1987 fu_firmware_add_image (firmware_dst, img);
1988 }
1989
1990 /* write new file */
1991 blob_dst = fu_firmware_write (firmware_dst, error);
1992 if (blob_dst == NULL)
1993 return FALSE;
1994 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
1995 return FALSE;
1996 str_dst = fu_firmware_to_string (firmware_dst);
1997 g_print ("%s", str_dst);
1998
1999 /* success */
2000 return TRUE;
2001}
2002
2003static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002004fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2005{
2006 g_autofree gchar *str = NULL;
2007 g_autoptr(FuDevice) dev = NULL;
2008
2009 /* load engine */
2010 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2011 return FALSE;
2012
2013 /* get device */
2014 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002015 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002016 if (dev == NULL)
2017 return FALSE;
2018 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002019 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002020 if (dev == NULL)
2021 return FALSE;
2022 }
2023
2024 /* add checksums */
2025 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2026 return FALSE;
2027
2028 /* show checksums */
2029 str = fu_device_to_string (dev);
2030 g_print ("%s\n", str);
2031 return TRUE;
2032}
2033
Mario Limonciellofe593942019-04-03 13:48:52 -05002034static gboolean
2035fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2036{
2037 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002038 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002039 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002040
2041 /* load engine */
2042 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2043 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002044 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002045
2046 /* get all devices from the history database */
2047 devices = fu_engine_get_history (priv->engine, error);
2048 if (devices == NULL)
2049 return FALSE;
2050
2051 /* show each device */
2052 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002053 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002054 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002055 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002056 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002057 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002058
Richard Hughes747f5702019-08-06 14:27:26 +01002059 if (!fu_util_filter_device (priv, dev))
2060 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002061 child = g_node_append_data (root, dev);
2062
2063 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002064 if (rel == NULL)
2065 continue;
2066 remote = fwupd_release_get_remote_id (rel);
2067
2068 /* doesn't actually map to remote */
2069 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002070 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002071 continue;
2072 }
2073
2074 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002075 rels = fu_engine_get_releases (priv->engine,
2076 priv->request,
2077 fwupd_device_get_id (dev),
2078 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002079 if (rels == NULL)
2080 return FALSE;
2081
2082 /* map to a release in client */
2083 for (guint j = 0; j < rels->len; j++) {
2084 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2085 if (g_strcmp0 (remote,
2086 fwupd_release_get_remote_id (rel2)) != 0)
2087 continue;
2088 if (g_strcmp0 (fwupd_release_get_version (rel),
2089 fwupd_release_get_version (rel2)) != 0)
2090 continue;
2091 g_node_append_data (child, g_object_ref (rel2));
2092 rel = NULL;
2093 break;
2094 }
2095
2096 /* didn't match anything */
2097 if (rels->len == 0 || rel != NULL) {
2098 g_node_append_data (child, rel);
2099 continue;
2100 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002101
Mario Limonciellofe593942019-04-03 13:48:52 -05002102 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002103 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002104
2105 return TRUE;
2106}
2107
Richard Hughesfd7e9942020-01-17 14:09:13 +00002108static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002109fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2110{
Richard Hughesc5710d92020-06-26 11:08:25 +01002111 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002112 g_autofree gchar *fn_raw = NULL;
2113 g_autofree gchar *fn_sig = NULL;
2114 g_autoptr(GBytes) bytes_raw = NULL;
2115 g_autoptr(GBytes) bytes_sig = NULL;
2116
2117 /* payload */
Richard Hughesc5710d92020-06-26 11:08:25 +01002118 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2119 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002120 g_set_error (error,
2121 FWUPD_ERROR,
2122 FWUPD_ERROR_NOTHING_TO_DO,
2123 "no metadata URI available for %s",
2124 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002125 return FALSE;
2126 }
2127 fn_raw = fu_util_get_user_cache_path (metadata_uri);
Richard Hughes4959baa2020-01-17 14:33:00 +00002128 if (!fu_common_mkdir_parent (fn_raw, error))
2129 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002130 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002131 return FALSE;
2132 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2133 if (bytes_raw == NULL)
2134 return FALSE;
2135
2136 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002137 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2138 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002139 g_set_error (error,
2140 FWUPD_ERROR,
2141 FWUPD_ERROR_NOTHING_TO_DO,
2142 "no metadata signature URI available for %s",
2143 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002144 return FALSE;
2145 }
2146 fn_sig = fu_util_get_user_cache_path (metadata_uri);
2147 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002148 return FALSE;
2149 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2150 if (bytes_sig == NULL)
2151 return FALSE;
2152
2153 /* send to daemon */
2154 g_debug ("updating %s", fwupd_remote_get_id (remote));
2155 return fu_engine_update_metadata_bytes (priv->engine,
2156 fwupd_remote_get_id (remote),
2157 bytes_raw,
2158 bytes_sig,
2159 error);
2160
2161}
2162
2163static gboolean
2164fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2165{
2166 g_autoptr(GPtrArray) remotes = NULL;
2167
2168 /* load engine */
2169 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2170 return FALSE;
2171
2172 /* download new metadata */
2173 remotes = fu_engine_get_remotes (priv->engine, error);
2174 if (remotes == NULL)
2175 return FALSE;
2176 for (guint i = 0; i < remotes->len; i++) {
2177 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2178 if (!fwupd_remote_get_enabled (remote))
2179 continue;
2180 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2181 continue;
2182 if (!fu_util_refresh_remote (priv, remote, error))
2183 return FALSE;
2184 }
2185 return TRUE;
2186}
2187
2188static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002189fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2190{
2191 g_autoptr(GNode) root = g_node_new (NULL);
2192 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002193 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002194
2195 /* load engine */
2196 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2197 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002198 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002199
2200 /* list remotes */
2201 remotes = fu_engine_get_remotes (priv->engine, error);
2202 if (remotes == NULL)
2203 return FALSE;
2204 if (remotes->len == 0) {
2205 g_set_error_literal (error,
2206 FWUPD_ERROR,
2207 FWUPD_ERROR_NOTHING_TO_DO,
2208 "no remotes available");
2209 return FALSE;
2210 }
2211 for (guint i = 0; i < remotes->len; i++) {
2212 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2213 g_node_append_data (root, remote_tmp);
2214 }
2215 fu_util_print_tree (root, title);
2216
2217 return TRUE;
2218}
2219
Richard Hughes196c6c62020-05-11 19:42:47 +01002220static gboolean
2221fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2222{
Richard Hughes5c82b942020-09-14 12:24:06 +01002223 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002224 g_autoptr(FuSecurityAttrs) attrs = NULL;
2225 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002226 g_autofree gchar *str = NULL;
2227
2228 /* not ready yet */
2229 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2230 g_set_error_literal (error,
2231 FWUPD_ERROR,
2232 FWUPD_ERROR_NOT_SUPPORTED,
2233 "The HSI specification is not yet complete. "
2234 "To ignore this warning, use --force");
2235 return FALSE;
2236 }
2237
2238 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2239 return FALSE;
2240
2241 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2242 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2243 fu_engine_get_host_security_id (priv->engine));
2244
Richard Hughes5c82b942020-09-14 12:24:06 +01002245 /* show or hide different elements */
2246 if (priv->show_all) {
2247 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2248 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2249 }
2250
Richard Hughes196c6c62020-05-11 19:42:47 +01002251 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002252 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002253 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002254 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002255 g_print ("%s\n", str);
2256 return TRUE;
2257}
2258
Richard Hughesa83deb42020-08-12 12:45:36 +01002259static FuVolume *
2260fu_util_prompt_for_volume (GError **error)
2261{
2262 FuVolume *volume;
2263 guint idx;
2264 g_autoptr(GPtrArray) volumes = NULL;
2265
2266 /* exactly one */
2267 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, error);
Jochen Sprickerhof25b78a62020-09-10 23:57:02 +02002268 if (volumes == NULL)
2269 return NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002270 if (volumes->len == 1) {
2271 volume = g_ptr_array_index (volumes, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002272 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002273 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2274 return g_object_ref (volume);
2275 }
2276
2277 /* TRANSLATORS: get interactive prompt */
2278 g_print ("%s\n", _("Choose a volume:"));
2279 /* TRANSLATORS: this is to abort the interactive prompt */
2280 g_print ("0.\t%s\n", _("Cancel"));
2281 for (guint i = 0; i < volumes->len; i++) {
2282 volume = g_ptr_array_index (volumes, i);
2283 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2284 }
2285 idx = fu_util_prompt_for_number (volumes->len);
2286 if (idx == 0) {
2287 g_set_error_literal (error,
2288 FWUPD_ERROR,
2289 FWUPD_ERROR_NOTHING_TO_DO,
2290 "Request canceled");
2291 return NULL;
2292 }
2293 volume = g_ptr_array_index (volumes, idx - 1);
2294 return g_object_ref (volume);
2295
2296}
2297
2298static gboolean
2299fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2300{
2301 g_autoptr(FuVolume) volume = NULL;
2302 volume = fu_util_prompt_for_volume (error);
2303 if (volume == NULL)
2304 return FALSE;
2305 return fu_volume_mount (volume, error);
2306}
2307
2308static gboolean
2309fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2310{
2311 g_autoptr(FuVolume) volume = NULL;
2312 volume = fu_util_prompt_for_volume (error);
2313 if (volume == NULL)
2314 return FALSE;
2315 return fu_volume_unmount (volume, error);
2316}
2317
2318static gboolean
2319fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2320{
2321 g_autofree gchar *mount_point = NULL;
2322 g_autoptr(FuDeviceLocker) locker = NULL;
2323 g_autoptr(FuVolume) volume = NULL;
2324 g_autoptr(GPtrArray) files = NULL;
2325
2326 volume = fu_util_prompt_for_volume (error);
2327 if (volume == NULL)
2328 return FALSE;
2329 locker = fu_volume_locker (volume, error);
2330 if (locker == NULL)
2331 return FALSE;
2332 mount_point = fu_volume_get_mount_point (volume);
2333 files = fu_common_get_files_recursive (mount_point, error);
2334 if (files == NULL)
2335 return FALSE;
2336 for (guint i = 0; i < files->len; i++) {
2337 const gchar *fn = g_ptr_array_index (files, i);
2338 g_print ("%s\n", fn);
2339 }
2340 return TRUE;
2341}
2342
Richard Hughesb5976832018-05-18 10:02:09 +01002343int
2344main (int argc, char *argv[])
2345{
Richard Hughes460226a2018-05-21 20:56:21 +01002346 gboolean allow_older = FALSE;
2347 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002348 gboolean force = FALSE;
2349 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002350 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002351 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002352 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002353 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2354 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002355 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002356 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002357 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002358 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002359 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2360 /* TRANSLATORS: command line option */
2361 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002362 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2363 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002364 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002365 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2366 /* TRANSLATORS: command line option */
2367 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002368 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2369 /* TRANSLATORS: command line option */
2370 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002371 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2372 /* TRANSLATORS: command line option */
2373 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002374 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2375 /* TRANSLATORS: command line option */
2376 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002377 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2378 /* TRANSLATORS: command line option */
2379 _("Show all results"), NULL },
2380 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002381 /* TRANSLATORS: command line option */
2382 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002383 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002384 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002385 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002386 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2387 /* TRANSLATORS: command line option */
2388 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002389 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002390 /* TRANSLATORS: command line option */
2391 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002392 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002393 /* TRANSLATORS: command line option */
2394 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002395 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2396 /* TRANSLATORS: command line option */
2397 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002398 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2399 /* TRANSLATORS: command line option */
2400 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002401 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2402 /* TRANSLATORS: command line option */
2403 _("Filter with a set of device flags using a ~ prefix to "
2404 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002405 { NULL}
2406 };
2407
Richard Hughes429f72b2020-01-16 12:18:19 +00002408#ifdef _WIN32
2409 /* workaround Windows setting the codepage to 1252 */
2410 g_setenv ("LANG", "C.UTF-8", FALSE);
2411#endif
2412
Richard Hughesb5976832018-05-18 10:02:09 +01002413 setlocale (LC_ALL, "");
2414
Richard Hughes668ee212019-11-22 09:17:46 +00002415 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002416 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2417 textdomain (GETTEXT_PACKAGE);
2418
Richard Hughes01c0bad2019-11-22 09:08:51 +00002419#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002420 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002421 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002422 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002423 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002424#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002425
2426 /* create helper object */
2427 priv->loop = g_main_loop_new (NULL, FALSE);
2428 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002429 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002430
2431 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002432 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002433 "build-firmware",
2434 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
2435 /* TRANSLATORS: command description */
2436 _("Build firmware using a sandbox"),
2437 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002438 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002439 "smbios-dump",
2440 "FILE",
2441 /* TRANSLATORS: command description */
2442 _("Dump SMBIOS data from a file"),
2443 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002444 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002445 "get-plugins",
2446 NULL,
2447 /* TRANSLATORS: command description */
2448 _("Get all enabled plugins registered with the system"),
2449 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002450 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002451 "get-details",
2452 NULL,
2453 /* TRANSLATORS: command description */
2454 _("Gets details about a firmware file"),
2455 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002456 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002457 "get-history",
2458 NULL,
2459 /* TRANSLATORS: command description */
2460 _("Show history of firmware updates"),
2461 fu_util_get_history);
2462 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002463 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002464 NULL,
2465 /* TRANSLATORS: command description */
2466 _("Gets the list of updates for connected hardware"),
2467 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002468 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002469 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002470 NULL,
2471 /* TRANSLATORS: command description */
2472 _("Get all devices that support firmware updates"),
2473 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002474 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002475 "get-device-flags",
2476 NULL,
2477 /* TRANSLATORS: command description */
2478 _("Get all device flags supported by fwupd"),
2479 fu_util_get_device_flags);
2480 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002481 "watch",
2482 NULL,
2483 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002484 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002485 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002486 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002487 "install-blob",
2488 "FILENAME DEVICE-ID",
2489 /* TRANSLATORS: command description */
2490 _("Install a firmware blob on a device"),
2491 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002492 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002493 "install",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002494 "FILE [DEVICE-ID|GUID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002495 /* TRANSLATORS: command description */
2496 _("Install a firmware file on this hardware"),
2497 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002498 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002499 "reinstall",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002500 "DEVICE-ID|GUID",
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002501 /* TRANSLATORS: command description */
2502 _("Reinstall firmware on a device"),
2503 fu_util_reinstall);
2504 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002505 "attach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002506 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002507 /* TRANSLATORS: command description */
2508 _("Attach to firmware mode"),
2509 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002510 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002511 "detach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002512 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002513 /* TRANSLATORS: command description */
2514 _("Detach to bootloader mode"),
2515 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002516 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002517 "unbind-driver",
2518 "[DEVICE-ID|GUID]",
2519 /* TRANSLATORS: command description */
2520 _("Unbind current driver"),
2521 fu_util_unbind_driver);
2522 fu_util_cmd_array_add (cmd_array,
2523 "bind-driver",
2524 "subsystem driver [DEVICE-ID|GUID]",
2525 /* TRANSLATORS: command description */
2526 _("Bind new kernel driver"),
2527 fu_util_bind_driver);
2528 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002529 "activate",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002530 "[DEVICE-ID|GUID]",
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002531 /* TRANSLATORS: command description */
2532 _("Activate pending devices"),
2533 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002534 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002535 "hwids",
2536 "[FILE]",
2537 /* TRANSLATORS: command description */
2538 _("Return all the hardware IDs for the machine"),
2539 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002540 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002541 "monitor",
2542 NULL,
2543 /* TRANSLATORS: command description */
2544 _("Monitor the daemon for events"),
2545 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002546 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002547 "update,upgrade",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002548 "[DEVICE-ID|GUID]",
Mario Limonciello46aaee82019-01-10 12:58:00 -06002549 /* TRANSLATORS: command description */
2550 _("Update all devices that match local metadata"),
2551 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002552 fu_util_cmd_array_add (cmd_array,
2553 "self-sign",
2554 "TEXT",
2555 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002556 C_("command-description",
2557 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002558 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002559 fu_util_cmd_array_add (cmd_array,
2560 "verify-update",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002561 "[DEVICE-ID|GUID]",
Richard Hughes15684492019-03-15 16:27:50 +00002562 /* TRANSLATORS: command description */
2563 _("Update the stored metadata with current contents"),
2564 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002565 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00002566 "firmware-read",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002567 "FILENAME [DEVICE-ID|GUID]",
Richard Hughesa58510b2019-10-30 10:03:12 +00002568 /* TRANSLATORS: command description */
2569 _("Read a firmware blob from a device"),
2570 fu_util_firmware_read);
2571 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002572 "firmware-convert",
2573 "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]",
2574 /* TRANSLATORS: command description */
2575 _("Convert a firmware file"),
2576 fu_util_firmware_convert);
2577 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002578 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06002579 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01002580 /* TRANSLATORS: command description */
2581 _("Parse and show details about a firmware file"),
2582 fu_util_firmware_parse);
2583 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002584 "firmware-extract",
2585 "FILENAME [FIRMWARE-TYPE]",
2586 /* TRANSLATORS: command description */
2587 _("Extract a firmware blob to images"),
2588 fu_util_firmware_extract);
2589 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002590 "get-firmware-types",
2591 NULL,
2592 /* TRANSLATORS: command description */
2593 _("List the available firmware types"),
2594 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002595 fu_util_cmd_array_add (cmd_array,
2596 "get-remotes",
2597 NULL,
2598 /* TRANSLATORS: command description */
2599 _("Gets the configured remotes"),
2600 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002601 fu_util_cmd_array_add (cmd_array,
2602 "refresh",
2603 NULL,
2604 /* TRANSLATORS: command description */
2605 _("Refresh metadata from remote server"),
2606 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002607 fu_util_cmd_array_add (cmd_array,
2608 "security",
2609 NULL,
2610 /* TRANSLATORS: command description */
2611 _("Gets the host security attributes."),
2612 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002613 fu_util_cmd_array_add (cmd_array,
2614 "esp-mount",
2615 NULL,
2616 /* TRANSLATORS: command description */
2617 _("Mounts the ESP."),
2618 fu_util_esp_mount);
2619 fu_util_cmd_array_add (cmd_array,
2620 "esp-unmount",
2621 NULL,
2622 /* TRANSLATORS: command description */
2623 _("Unmounts the ESP."),
2624 fu_util_esp_unmount);
2625 fu_util_cmd_array_add (cmd_array,
2626 "esp-list",
2627 NULL,
2628 /* TRANSLATORS: command description */
2629 _("Lists files on the ESP."),
2630 fu_util_esp_list);
Richard Hughesb5976832018-05-18 10:02:09 +01002631
2632 /* do stuff on ctrl+c */
2633 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002634#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002635 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2636 SIGINT, fu_util_sigint_cb,
2637 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002638#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002639 g_signal_connect (priv->cancellable, "cancelled",
2640 G_CALLBACK (fu_util_cancelled_cb), priv);
2641
2642 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002643 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002644
Mario Limonciello3f243a92019-01-21 22:05:23 -06002645 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002646 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002647 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002648 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002649 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01002650 } else {
2651 /* set our implemented feature set */
2652 fu_engine_request_set_feature_flags (priv->request,
2653 FWUPD_FEATURE_FLAG_DETACH_ACTION |
2654 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002655 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002656
Richard Hughesb5976832018-05-18 10:02:09 +01002657 /* get a list of the commands */
2658 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002659 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002660 g_option_context_set_summary (priv->context, cmd_descriptions);
2661 g_option_context_set_description (priv->context,
2662 "This tool allows an administrator to use the fwupd plugins "
2663 "without being installed on the host system.");
2664
2665 /* TRANSLATORS: program name */
2666 g_set_application_name (_("Firmware Utility"));
2667 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002668 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002669 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2670 if (!ret) {
2671 /* TRANSLATORS: the user didn't read the man page */
2672 g_print ("%s: %s\n", _("Failed to parse arguments"),
2673 error->message);
2674 return EXIT_FAILURE;
2675 }
2676
Richard Hughes0e46b222019-09-05 12:13:35 +01002677 /* allow disabling SSL strict mode for broken corporate proxies */
2678 if (priv->disable_ssl_strict) {
2679 /* TRANSLATORS: try to help */
2680 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2681 "to do this automatically in the future "
2682 "export DISABLE_SSL_STRICT in your environment"));
2683 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2684 }
2685
Richard Hughes747f5702019-08-06 14:27:26 +01002686 /* parse filter flags */
2687 if (filter != NULL) {
2688 if (!fu_util_parse_filter_flags (filter,
2689 &priv->filter_include,
2690 &priv->filter_exclude,
2691 &error)) {
2692 /* TRANSLATORS: the user didn't read the man page */
2693 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2694 error->message);
2695 return EXIT_FAILURE;
2696 }
2697 }
2698
2699
Richard Hughes460226a2018-05-21 20:56:21 +01002700 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002701 if (allow_reinstall)
2702 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2703 if (allow_older)
2704 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2705 if (force)
2706 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2707
Richard Hughes98ca9932018-05-18 10:24:07 +01002708 /* load engine */
2709 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2710 g_signal_connect (priv->engine, "device-added",
2711 G_CALLBACK (fu_main_engine_device_added_cb),
2712 priv);
2713 g_signal_connect (priv->engine, "device-removed",
2714 G_CALLBACK (fu_main_engine_device_removed_cb),
2715 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002716 g_signal_connect (priv->engine, "status-changed",
2717 G_CALLBACK (fu_main_engine_status_changed_cb),
2718 priv);
2719 g_signal_connect (priv->engine, "percentage-changed",
2720 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2721 priv);
2722
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002723 /* just show versions and exit */
2724 if (version) {
2725 g_autofree gchar *version_str = fu_util_get_versions ();
2726 g_print ("%s\n", version_str);
2727 return EXIT_SUCCESS;
2728 }
2729
Richard Hughes85226fd2020-06-30 14:43:48 +01002730 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002731 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2732 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2733
Richard Hughesb5976832018-05-18 10:02:09 +01002734 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002735 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002736 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05002737 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002738 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05002739 /* TRANSLATORS: error message explaining command to run to how to get help */
2740 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
2741 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2742 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002743 return EXIT_NOTHING_TO_DO;
2744 }
Richard Hughesb5976832018-05-18 10:02:09 +01002745 return EXIT_FAILURE;
2746 }
2747
2748 /* success */
2749 return EXIT_SUCCESS;
2750}