blob: 971b4fbf11be5ede58e22554e6b1081c3f322cea [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 Hughesb5976832018-05-18 10:02:09 +010056 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060057 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050058 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000059 gboolean prepare_blob;
60 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060061 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010062 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050063 gboolean show_all_devices;
Richard Hughes0e46b222019-09-05 12:13:35 +010064 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050065 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060066 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050067 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060068 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060069 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010070 FwupdDeviceFlags filter_include;
71 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000072};
Richard Hughesb5976832018-05-18 10:02:09 +010073
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050074static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060075fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
76{
77 g_autoptr(JsonBuilder) builder = NULL;
78 g_autoptr(JsonGenerator) json_generator = NULL;
79 g_autoptr(JsonNode) json_root = NULL;
80 g_autoptr(GPtrArray) devices = NULL;
81 g_autofree gchar *state = NULL;
82 g_autofree gchar *dirname = NULL;
83 g_autofree gchar *filename = NULL;
84
85 if (!priv->enable_json_state)
86 return TRUE;
87
88 devices = fu_engine_get_devices (priv->engine, error);
89 if (devices == NULL)
90 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000091 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060092
93 /* create header */
94 builder = json_builder_new ();
95 json_builder_begin_object (builder);
96
97 /* add each device */
98 json_builder_set_member_name (builder, "Devices");
99 json_builder_begin_array (builder);
100 for (guint i = 0; i < devices->len; i++) {
101 FwupdDevice *dev = g_ptr_array_index (devices, i);
102 json_builder_begin_object (builder);
103 fwupd_device_to_json (dev, builder);
104 json_builder_end_object (builder);
105 }
106 json_builder_end_array (builder);
107 json_builder_end_object (builder);
108
109 /* export as a string */
110 json_root = json_builder_get_root (builder);
111 json_generator = json_generator_new ();
112 json_generator_set_pretty (json_generator, TRUE);
113 json_generator_set_root (json_generator, json_root);
114 state = json_generator_to_data (json_generator, NULL);
115 if (state == NULL)
116 return FALSE;
117 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
118 filename = g_build_filename (dirname, "state.json", NULL);
119 return g_file_set_contents (filename, state, -1, error);
120}
121
122static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000123fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600124{
125 g_autoptr(GError) error_local = NULL;
126
Richard Hughesd92ccca2019-05-20 11:28:31 +0100127#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100128 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500129 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100130#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000131 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000132 return FALSE;
133 if (fu_engine_get_tainted (priv->engine)) {
134 g_printerr ("WARNING: This tool has loaded 3rd party code and "
135 "is no longer supported by the upstream developers!\n");
136 }
137 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500138}
139
Richard Hughesb5976832018-05-18 10:02:09 +0100140static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500141fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
142{
143 g_autofree gchar *path = g_path_get_dirname (value);
144 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
145 g_prefix_error (error,
146 "Unable to access %s. You may need to copy %s to %s: ",
147 path, value, g_getenv ("HOME"));
148 }
149}
150
151static void
Richard Hughesb5976832018-05-18 10:02:09 +0100152fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
153{
154 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
155 /* TRANSLATORS: this is when a device ctrl+c's a watch */
156 g_print ("%s\n", _("Cancelled"));
157 g_main_loop_quit (priv->loop);
158}
159
160static gboolean
161fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
162{
163 g_autofree gchar *tmp = NULL;
164 g_autoptr(FuSmbios) smbios = NULL;
165 if (g_strv_length (values) < 1) {
166 g_set_error_literal (error,
167 FWUPD_ERROR,
168 FWUPD_ERROR_INVALID_ARGS,
169 "Invalid arguments");
170 return FALSE;
171 }
172 smbios = fu_smbios_new ();
173 if (!fu_smbios_setup_from_file (smbios, values[0], error))
174 return FALSE;
175 tmp = fu_smbios_to_string (smbios);
176 g_print ("%s\n", tmp);
177 return TRUE;
178}
179
Richard Hughes9e5675e2019-11-22 09:35:03 +0000180#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100181static gboolean
182fu_util_sigint_cb (gpointer user_data)
183{
184 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
185 g_debug ("Handling SIGINT");
186 g_cancellable_cancel (priv->cancellable);
187 return FALSE;
188}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000189#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100190
191static void
192fu_util_private_free (FuUtilPrivate *priv)
193{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500194 if (priv->current_device != NULL)
195 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100196 if (priv->engine != NULL)
197 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100198 if (priv->loop != NULL)
199 g_main_loop_unref (priv->loop);
200 if (priv->cancellable != NULL)
201 g_object_unref (priv->cancellable);
202 if (priv->progressbar != NULL)
203 g_object_unref (priv->progressbar);
204 if (priv->context != NULL)
205 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600206 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100207 g_free (priv);
208}
209
210#pragma clang diagnostic push
211#pragma clang diagnostic ignored "-Wunused-function"
212G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
213#pragma clang diagnostic pop
214
Richard Hughes98ca9932018-05-18 10:24:07 +0100215
216static void
217fu_main_engine_device_added_cb (FuEngine *engine,
218 FuDevice *device,
219 FuUtilPrivate *priv)
220{
221 g_autofree gchar *tmp = fu_device_to_string (device);
222 g_debug ("ADDED:\n%s", tmp);
223}
224
225static void
226fu_main_engine_device_removed_cb (FuEngine *engine,
227 FuDevice *device,
228 FuUtilPrivate *priv)
229{
230 g_autofree gchar *tmp = fu_device_to_string (device);
231 g_debug ("REMOVED:\n%s", tmp);
232}
233
234static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100235fu_main_engine_status_changed_cb (FuEngine *engine,
236 FwupdStatus status,
237 FuUtilPrivate *priv)
238{
239 fu_progressbar_update (priv->progressbar, status, 0);
240}
241
242static void
243fu_main_engine_percentage_changed_cb (FuEngine *engine,
244 guint percentage,
245 FuUtilPrivate *priv)
246{
247 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
248}
249
250static gboolean
251fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
252{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000253 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100254 return FALSE;
255 g_main_loop_run (priv->loop);
256 return TRUE;
257}
258
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100259static gint
260fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
261{
262 return fu_plugin_name_compare (*item1, *item2);
263}
264
265static gboolean
266fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
267{
268 GPtrArray *plugins;
269 guint cnt = 0;
270
271 /* load engine */
272 if (!fu_engine_load_plugins (priv->engine, error))
273 return FALSE;
274
275 /* print */
276 plugins = fu_engine_get_plugins (priv->engine);
277 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
278 for (guint i = 0; i < plugins->len; i++) {
279 FuPlugin *plugin = g_ptr_array_index (plugins, i);
280 if (!fu_plugin_get_enabled (plugin))
281 continue;
282 g_print ("%s\n", fu_plugin_get_name (plugin));
283 cnt++;
284 }
285 if (cnt == 0) {
286 /* TRANSLATORS: nothing found */
287 g_print ("%s\n", _("No plugins found"));
288 return TRUE;
289 }
290
291 return TRUE;
292}
293
Richard Hughes98ca9932018-05-18 10:24:07 +0100294static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100295fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
296{
297 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
298 if (!fwupd_device_has_flag (dev, priv->filter_include))
299 return FALSE;
300 }
301 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
302 if (fwupd_device_has_flag (dev, priv->filter_exclude))
303 return FALSE;
304 }
305 return TRUE;
306}
307
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500308static gchar *
309fu_util_get_tree_title (FuUtilPrivate *priv)
310{
311 return g_strdup (fu_engine_get_host_product (priv->engine));
312}
313
Richard Hughes747f5702019-08-06 14:27:26 +0100314static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600315fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
316{
317 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500318 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600319 g_autofree gchar *title = NULL;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600320
321 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000322 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600323 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600324 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600325
326 /* get devices from daemon */
327 devices = fu_engine_get_devices (priv->engine, error);
328 if (devices == NULL)
329 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000330 fwupd_device_array_ensure_parents (devices);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600331 for (guint i = 0; i < devices->len; i++) {
332 FwupdDevice *dev = g_ptr_array_index (devices, i);
333 g_autoptr(GPtrArray) rels = NULL;
334 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500335 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600336
Richard Hughes747f5702019-08-06 14:27:26 +0100337 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600338 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600339 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600340 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
341 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
342 * %1 is the device name */
343 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
344 fwupd_device_get_name (dev));
345 g_printerr ("%s\n", tmp);
346 continue;
347 }
Richard Hughes747f5702019-08-06 14:27:26 +0100348 if (!fu_util_filter_device (priv, dev))
349 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600350
351 /* get the releases for this device and filter for validity */
352 rels = fu_engine_get_upgrades (priv->engine,
353 fwupd_device_get_id (dev),
354 &error_local);
355 if (rels == NULL) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -0600356 /* TRANSLATORS: message letting the user know no device upgrade available
357 * %1 is the device name */
Mario Limonciello27164b72020-02-17 23:19:36 -0600358 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
359 fwupd_device_get_name (dev));
360 g_printerr ("%s\n", tmp);
361 /* discard the actual reason from user, but leave for debugging */
362 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600363 continue;
364 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500365 child = g_node_append_data (root, dev);
366
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600367 for (guint j = 0; j < rels->len; j++) {
368 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500369 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600370 }
371 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500372 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500373 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600374 /* save the device state for other applications to see */
375 if (!fu_util_save_current_state (priv, error))
376 return FALSE;
377
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600378 /* success */
379 return TRUE;
380}
381
382static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500383fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
384{
385 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500386 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600387 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500388 gint fd;
389
390 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000391 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500392 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600393 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500394
395 /* check args */
396 if (g_strv_length (values) != 1) {
397 g_set_error_literal (error,
398 FWUPD_ERROR,
399 FWUPD_ERROR_INVALID_ARGS,
400 "Invalid arguments");
401 return FALSE;
402 }
403
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600404 /* implied, important for get-details on a device not in your system */
405 priv->show_all_devices = TRUE;
406
Mario Limonciello716ab272018-05-29 12:34:37 -0500407 /* open file */
408 fd = open (values[0], O_RDONLY);
409 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500410 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500411 g_set_error (error,
412 FWUPD_ERROR,
413 FWUPD_ERROR_INVALID_FILE,
414 "failed to open %s",
415 values[0]);
416 return FALSE;
417 }
418 array = fu_engine_get_details (priv->engine, fd, error);
419 close (fd);
420
421 if (array == NULL)
422 return FALSE;
423 for (guint i = 0; i < array->len; i++) {
424 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600425 FwupdRelease *rel;
426 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100427 if (!fu_util_filter_device (priv, dev))
428 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600429 child = g_node_append_data (root, dev);
430 rel = fwupd_device_get_release_default (dev);
431 if (rel != NULL)
432 g_node_append_data (child, rel);
433
Mario Limonciello716ab272018-05-29 12:34:37 -0500434 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500435 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500436
Mario Limonciello716ab272018-05-29 12:34:37 -0500437 return TRUE;
438}
439
440static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100441fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
442{
443 g_autoptr(GString) str = g_string_new (NULL);
444
445 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
446 const gchar *tmp = fwupd_device_flag_to_string (i);
447 if (tmp == NULL)
448 break;
449 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
450 g_string_append (str, " ");
451 g_string_append (str, tmp);
452 g_string_append (str, " ~");
453 g_string_append (str, tmp);
454 }
455 g_print ("%s\n", str->str);
456
457 return TRUE;
458}
459
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500460static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100461fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500462{
463 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100464 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100465 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
466 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500467 if (!priv->show_all_devices &&
468 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500469 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100470 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500471 GNode *child = g_node_append_data (root, dev_tmp);
472 fu_util_build_device_tree (priv, child, devs, dev_tmp);
473 }
474 }
475}
476
477static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100478fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500479{
480 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600481 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500482 g_autoptr(GPtrArray) devs = NULL;
483
484 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000485 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500486 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600487 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500488
489 /* print */
490 devs = fu_engine_get_devices (priv->engine, error);
491 if (devs == NULL)
492 return FALSE;
493
494 /* print */
495 if (devs->len == 0) {
496 /* TRANSLATORS: nothing attached that can be upgraded */
497 g_print ("%s\n", _("No hardware detected with firmware update capability"));
498 return TRUE;
499 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000500 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500501 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500502 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500503
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100504 /* save the device state for other applications to see */
505 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500506}
507
Richard Hughes98ca9932018-05-18 10:24:07 +0100508static FuDevice *
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100509fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
Richard Hughes98ca9932018-05-18 10:24:07 +0100510{
511 FuDevice *dev;
512 guint idx;
513 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100514 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100515
516 /* get devices from daemon */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100517 if (devices_opt != NULL) {
518 devices = g_ptr_array_ref (devices_opt);
519 } else {
520 devices = fu_engine_get_devices (priv->engine, error);
521 if (devices == NULL)
522 return NULL;
523 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000524 fwupd_device_array_ensure_parents (devices);
Richard Hughes98ca9932018-05-18 10:24:07 +0100525
Richard Hughes747f5702019-08-06 14:27:26 +0100526 /* filter results */
527 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
528 for (guint i = 0; i < devices->len; i++) {
529 dev = g_ptr_array_index (devices, i);
530 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
531 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100532 g_ptr_array_add (devices_filtered, g_object_ref (dev));
533 }
534
535 /* nothing */
536 if (devices_filtered->len == 0) {
537 g_set_error_literal (error,
538 FWUPD_ERROR,
539 FWUPD_ERROR_NOTHING_TO_DO,
540 "No supported devices");
541 return NULL;
542 }
543
Richard Hughes98ca9932018-05-18 10:24:07 +0100544 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100545 if (devices_filtered->len == 1) {
546 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500547 /* TRANSLATORS: Device has been chosen by the daemon for the user */
548 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100549 return g_object_ref (dev);
550 }
551
552 /* TRANSLATORS: get interactive prompt */
553 g_print ("%s\n", _("Choose a device:"));
554 /* TRANSLATORS: this is to abort the interactive prompt */
555 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100556 for (guint i = 0; i < devices_filtered->len; i++) {
557 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100558 g_print ("%u.\t%s (%s)\n",
559 i + 1,
560 fu_device_get_id (dev),
561 fu_device_get_name (dev));
562 }
Richard Hughes747f5702019-08-06 14:27:26 +0100563 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100564 if (idx == 0) {
565 g_set_error_literal (error,
566 FWUPD_ERROR,
567 FWUPD_ERROR_NOTHING_TO_DO,
568 "Request canceled");
569 return NULL;
570 }
Richard Hughes747f5702019-08-06 14:27:26 +0100571 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100572 return g_object_ref (dev);
573}
574
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500575static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600576fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500577 FwupdDevice *device,
578 FuUtilPrivate *priv)
579{
580 g_autofree gchar *str = NULL;
581
Richard Hughes809abea2019-03-23 11:06:18 +0000582 /* allowed to set whenever the device has changed */
583 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
584 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
585 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
586 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
587
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500588 /* same as last time, so ignore */
589 if (priv->current_device != NULL &&
590 fwupd_device_compare (priv->current_device, device) == 0)
591 return;
592
Richard Hughesee562b52020-04-07 14:32:52 +0100593 /* ignore indirect devices that might have changed */
594 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
595 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
596 g_debug ("ignoring %s with status %s",
597 fwupd_device_get_name (device),
598 fwupd_status_to_string (fwupd_device_get_status (device)));
599 return;
600 }
601
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500602 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600603 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
604 /* TRANSLATORS: %1 is a device name */
605 str = g_strdup_printf (_("Updating %s…"),
606 fwupd_device_get_name (device));
607 fu_progressbar_set_title (priv->progressbar, str);
608 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
609 /* TRANSLATORS: %1 is a device name */
610 str = g_strdup_printf (_("Installing on %s…"),
611 fwupd_device_get_name (device));
612 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000613 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
614 /* TRANSLATORS: %1 is a device name */
615 str = g_strdup_printf (_("Reading from %s…"),
616 fwupd_device_get_name (device));
617 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600618 } else {
619 g_warning ("no FuUtilOperation set");
620 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500621 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600622
Mario Limonciello32241f42019-01-24 10:12:41 -0600623 if (priv->current_message == NULL) {
624 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
625 if (tmp != NULL)
626 priv->current_message = g_strdup (tmp);
627 }
628}
629
630static void
631fu_util_display_current_message (FuUtilPrivate *priv)
632{
633 if (priv->current_message == NULL)
634 return;
635 g_print ("%s\n", priv->current_message);
636 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500637}
638
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100639static FuDevice *
640fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
641{
642 if (fwupd_guid_is_valid (id)) {
643 g_autoptr(GPtrArray) devices = NULL;
644 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
645 if (devices == NULL)
646 return NULL;
647 return fu_util_prompt_for_device (priv, devices, error);
648 }
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500649
650 /* did this look like a GUID? */
651 for (guint i = 0; id[i] != '\0'; i++) {
652 if (id[i] == '-') {
653 g_set_error_literal (error,
654 FWUPD_ERROR,
655 FWUPD_ERROR_INVALID_ARGS,
656 "Invalid arguments");
Richard Hughes9c09e2c2020-04-22 10:52:42 +0100657 return NULL;
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500658 }
659 }
Mario Limoncielloaac9c742020-04-20 13:38:44 -0500660 return fu_engine_get_device (priv->engine, id, error);
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100661}
662
Richard Hughes98ca9932018-05-18 10:24:07 +0100663static gboolean
664fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
665{
666 g_autoptr(FuDevice) device = NULL;
667 g_autoptr(GBytes) blob_fw = NULL;
668
669 /* invalid args */
670 if (g_strv_length (values) == 0) {
671 g_set_error_literal (error,
672 FWUPD_ERROR,
673 FWUPD_ERROR_INVALID_ARGS,
674 "Invalid arguments");
675 return FALSE;
676 }
677
678 /* parse blob */
679 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500680 if (blob_fw == NULL) {
681 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100682 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500683 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100684
685 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000686 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100687 return FALSE;
688
689 /* get device */
690 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100691 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100692 if (device == NULL)
693 return FALSE;
694 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100695 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100696 if (device == NULL)
697 return FALSE;
698 }
699
Mario Limonciello3f243a92019-01-21 22:05:23 -0600700 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500701 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600702 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500703
Richard Hughes98ca9932018-05-18 10:24:07 +0100704 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000705 if (priv->prepare_blob) {
706 g_autoptr(GPtrArray) devices = NULL;
707 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
708 g_ptr_array_add (devices, g_object_ref (device));
709 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
710 g_prefix_error (error, "failed to prepare composite action: ");
711 return FALSE;
712 }
713 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100714 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000715 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600716 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000717 if (priv->cleanup_blob) {
718 g_autoptr(FuDevice) device_new = NULL;
719 g_autoptr(GError) error_local = NULL;
720
721 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100722 device_new = fu_util_get_device (priv,
723 fu_device_get_id (device),
724 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000725 if (device_new == NULL) {
726 g_debug ("failed to find new device: %s",
727 error_local->message);
728 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600729 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000730 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
731 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
732 g_prefix_error (error, "failed to cleanup composite action: ");
733 return FALSE;
734 }
735 }
736 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600737
Mario Limonciello32241f42019-01-24 10:12:41 -0600738 fu_util_display_current_message (priv);
739
Mario Limonciello3f243a92019-01-21 22:05:23 -0600740 /* success */
741 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100742}
743
Richard Hughesa58510b2019-10-30 10:03:12 +0000744static gboolean
745fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
746{
747 g_autoptr(FuDevice) device = NULL;
748 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
749 g_autoptr(GBytes) blob_fw = NULL;
750
751 /* invalid args */
752 if (g_strv_length (values) == 0) {
753 g_set_error_literal (error,
754 FWUPD_ERROR,
755 FWUPD_ERROR_INVALID_ARGS,
756 "Invalid arguments");
757 return FALSE;
758 }
759
760 /* file already exists */
761 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
762 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
763 g_set_error_literal (error,
764 FWUPD_ERROR,
765 FWUPD_ERROR_INVALID_ARGS,
766 "Filename already exists");
767 return FALSE;
768 }
769
Richard Hughes02792c02019-11-01 14:21:20 +0000770 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000771 * avoid failing at the end of a potentially lengthy operation */
772 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
773 return FALSE;
774
775 /* load engine */
776 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
777 return FALSE;
778
779 /* get device */
780 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100781 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000782 if (device == NULL)
783 return FALSE;
784 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100785 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000786 if (device == NULL)
787 return FALSE;
788 }
789 priv->current_operation = FU_UTIL_OPERATION_READ;
790 g_signal_connect (priv->engine, "device-changed",
791 G_CALLBACK (fu_util_update_device_changed_cb), priv);
792
793 /* dump firmware */
794 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
795 if (blob_fw == NULL)
796 return FALSE;
797 return fu_common_set_contents_bytes (values[0], blob_fw, error);
798}
799
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100800static gint
801fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
802{
803 FuInstallTask *task1 = *((FuInstallTask **) a);
804 FuInstallTask *task2 = *((FuInstallTask **) b);
805 return fu_install_task_compare (task1, task2);
806}
807
808static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100809fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
810{
Filipe Laínse0914272019-09-20 10:04:43 +0100811 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100812 { "curl", uri, "--output", fn, NULL },
813 { NULL } };
814 for (guint i = 0; argv[i][0] != NULL; i++) {
815 g_autoptr(GError) error_local = NULL;
816 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
817 g_debug ("%s", error_local->message);
818 continue;
819 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000820 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100821 }
822 g_set_error_literal (error,
823 FWUPD_ERROR,
824 FWUPD_ERROR_NOT_FOUND,
825 "no supported out-of-process downloaders found");
826 return FALSE;
827}
828
829static gchar *
830fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
831{
832 g_autofree gchar *filename = NULL;
833 g_autoptr(SoupURI) uri = NULL;
834
835 /* a local file */
836 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000837 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
838 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100839 if (uri == NULL)
840 return g_strdup (perhapsfn);
841
842 /* download the firmware to a cachedir */
843 filename = fu_util_get_user_cache_path (perhapsfn);
844 if (!fu_common_mkdir_parent (filename, error))
845 return NULL;
846 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
847 return NULL;
848 return g_steal_pointer (&filename);
849}
850
851static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100852fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
853{
Richard Hughes3d178be2018-08-30 11:14:24 +0100854 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100855 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100856 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100857 g_autoptr(GPtrArray) devices_possible = NULL;
858 g_autoptr(GPtrArray) errors = NULL;
859 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100860 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100861
Mario Limonciello8949e892018-05-25 08:03:06 -0500862 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000863 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500864 return FALSE;
865
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100866 /* handle both forms */
867 if (g_strv_length (values) == 1) {
868 devices_possible = fu_engine_get_devices (priv->engine, error);
869 if (devices_possible == NULL)
870 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000871 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100872 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100873 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100874 if (device == NULL)
875 return FALSE;
876 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
877 g_ptr_array_add (devices_possible, device);
878 } else {
879 g_set_error_literal (error,
880 FWUPD_ERROR,
881 FWUPD_ERROR_INVALID_ARGS,
882 "Invalid arguments");
883 return FALSE;
884 }
885
Richard Hughes3d178be2018-08-30 11:14:24 +0100886 /* download if required */
887 filename = fu_util_download_if_required (priv, values[0], error);
888 if (filename == NULL)
889 return FALSE;
890
Richard Hughes481aa2a2018-09-18 20:51:46 +0100891 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100892 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500893 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100894 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100895 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500896 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100897 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
898 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100899 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600900 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100901 if (components == NULL)
902 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100903
Richard Hughes481aa2a2018-09-18 20:51:46 +0100904 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100905 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
906 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100907 for (guint i = 0; i < components->len; i++) {
908 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100909
910 /* do any devices pass the requirements */
911 for (guint j = 0; j < devices_possible->len; j++) {
912 FuDevice *device = g_ptr_array_index (devices_possible, j);
913 g_autoptr(FuInstallTask) task = NULL;
914 g_autoptr(GError) error_local = NULL;
915
916 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100917 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100918 if (!fu_engine_check_requirements (priv->engine,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500919 task, priv->flags | FWUPD_INSTALL_FLAG_FORCE,
920 &error_local)) {
921 g_debug ("first pass requirement on %s:%s failed: %s",
922 fu_device_get_id (device),
923 xb_node_query_text (component, "id", NULL),
924 error_local->message);
925 g_ptr_array_add (errors, g_steal_pointer (&error_local));
926 continue;
927 }
928
929 /* make a second pass using possibly updated version format now */
930 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
931 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100932 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100933 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500934 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100935 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100936 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100937 error_local->message);
938 g_ptr_array_add (errors, g_steal_pointer (&error_local));
939 continue;
940 }
941
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600942 /* if component should have an update message from CAB */
943 fu_device_incorporate_from_component (device, component);
944
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100945 /* success */
946 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
947 }
948 }
949
950 /* order the install tasks by the device priority */
951 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
952
953 /* nothing suitable */
954 if (install_tasks->len == 0) {
955 GError *error_tmp = fu_common_error_array_get_best (errors);
956 g_propagate_error (error, error_tmp);
957 return FALSE;
958 }
959
Mario Limonciello3f243a92019-01-21 22:05:23 -0600960 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500961 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600962 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500963
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100964 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100965 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
966 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100967
Mario Limonciello32241f42019-01-24 10:12:41 -0600968 fu_util_display_current_message (priv);
969
Mario Limonciello3f243a92019-01-21 22:05:23 -0600970 /* we don't want to ask anything */
971 if (priv->no_reboot_check) {
972 g_debug ("skipping reboot check");
973 return TRUE;
974 }
975
Mario Limonciello3143bad2019-02-27 07:31:00 -0600976 /* save the device state for other applications to see */
977 if (!fu_util_save_current_state (priv, error))
978 return FALSE;
979
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100980 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600981 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100982}
983
Richard Hughes98ca9932018-05-18 10:24:07 +0100984static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500985fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600986{
Mario Limonciellofd734852019-08-01 16:41:42 -0500987 FwupdRemote *remote;
988 const gchar *remote_id;
989 const gchar *uri_tmp;
990 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600991
Mario Limonciellofd734852019-08-01 16:41:42 -0500992 uri_tmp = fwupd_release_get_uri (rel);
993 if (uri_tmp == NULL) {
994 g_set_error_literal (error,
995 FWUPD_ERROR,
996 FWUPD_ERROR_INVALID_FILE,
997 "release missing URI");
998 return FALSE;
999 }
1000 remote_id = fwupd_release_get_remote_id (rel);
1001 if (remote_id == NULL) {
1002 g_set_error (error,
1003 FWUPD_ERROR,
1004 FWUPD_ERROR_INVALID_FILE,
1005 "failed to find remote for %s",
1006 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001007 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001008 }
1009
1010 remote = fu_engine_get_remote_by_id (priv->engine,
1011 remote_id,
1012 error);
1013 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001014 return FALSE;
1015
Mario Limonciellofd734852019-08-01 16:41:42 -05001016 argv = g_new0 (gchar *, 2);
1017 /* local remotes have the firmware already */
1018 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
1019 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1020 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1021 argv[0] = g_build_filename (path, uri_tmp, NULL);
1022 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1023 argv[0] = g_strdup (uri_tmp + 7);
1024 /* web remote, fu_util_install will download file */
1025 } else {
1026 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1027 }
1028 return fu_util_install (priv, argv, error);
1029}
1030
1031static gboolean
1032fu_util_update_all (FuUtilPrivate *priv, GError **error)
1033{
1034 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001035
Mario Limonciello46aaee82019-01-10 12:58:00 -06001036 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001037 if (devices == NULL)
1038 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001039 fwupd_device_array_ensure_parents (devices);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001040 for (guint i = 0; i < devices->len; i++) {
1041 FwupdDevice *dev = g_ptr_array_index (devices, i);
1042 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001043 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001044 g_autoptr(GPtrArray) rels = NULL;
1045 g_autoptr(GError) error_local = NULL;
1046
1047 if (!fu_util_is_interesting_device (dev))
1048 continue;
1049 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001050 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001051 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001052 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
1053 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
1054 * %1 is the device name */
1055 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
1056 fwupd_device_get_name (dev));
1057 g_printerr ("%s\n", tmp);
1058 continue;
1059 }
Richard Hughes747f5702019-08-06 14:27:26 +01001060 if (!fu_util_filter_device (priv, dev))
1061 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001062
1063 device_id = fu_device_get_id (dev);
1064 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
1065 if (rels == NULL) {
Mario Limonciello27164b72020-02-17 23:19:36 -06001066 /* TRANSLATORS: message letting the user know no device upgrade available
1067 * %1 is the device name */
1068 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
1069 fwupd_device_get_name (dev));
1070 g_printerr ("%s\n", tmp);
1071 /* discard the actual reason from user, but leave for debugging */
1072 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001073 continue;
1074 }
1075
Mario Limonciello98b95162019-10-30 09:20:43 -05001076 if (!priv->no_safety_check) {
1077 if (!fu_util_prompt_warning (dev,
1078 fu_util_get_tree_title (priv),
1079 error))
1080 return FALSE;
1081 }
1082
Mario Limonciello46aaee82019-01-10 12:58:00 -06001083 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001084 if (!fu_util_install_release (priv, rel, &error_local)) {
1085 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001086 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001087 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001088 fu_util_display_current_message (priv);
1089 }
1090 return TRUE;
1091}
1092
1093static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001094fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001095{
1096 FwupdRelease *rel;
1097 g_autoptr(FuDevice) dev = NULL;
1098 g_autoptr(GPtrArray) rels = NULL;
1099
Mario Limonciello9917bb42020-04-20 13:41:27 -05001100 /* do not allow a partial device-id, lookup GUIDs */
1101 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001102 if (dev == NULL)
1103 return FALSE;
1104
1105 /* get the releases for this device and filter for validity */
Mario Limonciello9917bb42020-04-20 13:41:27 -05001106 rels = fu_engine_get_upgrades (priv->engine, fu_device_get_id (dev), error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001107 if (rels == NULL)
1108 return FALSE;
1109 rel = g_ptr_array_index (rels, 0);
1110 if (!fu_util_install_release (priv, rel, error))
1111 return FALSE;
1112 fu_util_display_current_message (priv);
1113
1114 return TRUE;
1115}
1116
1117static gboolean
1118fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1119{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001120 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1121 g_set_error_literal (error,
1122 FWUPD_ERROR,
1123 FWUPD_ERROR_INVALID_ARGS,
1124 "--allow-older is not supported for this command");
1125 return FALSE;
1126 }
1127
1128 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1129 g_set_error_literal (error,
1130 FWUPD_ERROR,
1131 FWUPD_ERROR_INVALID_ARGS,
1132 "--allow-reinstall is not supported for this command");
1133 return FALSE;
1134 }
1135
Mario Limonciellofd734852019-08-01 16:41:42 -05001136 if (g_strv_length (values) > 1) {
1137 g_set_error_literal (error,
1138 FWUPD_ERROR,
1139 FWUPD_ERROR_INVALID_ARGS,
1140 "Invalid arguments");
1141 return FALSE;
1142 }
1143
1144 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1145 return FALSE;
1146
1147 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1148 g_signal_connect (priv->engine, "device-changed",
1149 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1150
1151 if (g_strv_length (values) == 1) {
1152 if (!fu_util_update_by_id (priv, values[0], error))
1153 return FALSE;
1154 } else {
1155 if (!fu_util_update_all (priv, error))
1156 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001157 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001158
1159 /* we don't want to ask anything */
1160 if (priv->no_reboot_check) {
1161 g_debug ("skipping reboot check");
1162 return TRUE;
1163 }
1164
Mario Limonciello3143bad2019-02-27 07:31:00 -06001165 /* save the device state for other applications to see */
1166 if (!fu_util_save_current_state (priv, error))
1167 return FALSE;
1168
Mario Limonciello3f243a92019-01-21 22:05:23 -06001169 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001170}
1171
1172static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001173fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1174{
1175 g_autoptr(FwupdRelease) rel = NULL;
1176 g_autoptr(GPtrArray) rels = NULL;
1177 g_autoptr(FuDevice) dev = NULL;
1178
1179 if (g_strv_length (values) != 1) {
1180 g_set_error_literal (error,
1181 FWUPD_ERROR,
1182 FWUPD_ERROR_INVALID_ARGS,
1183 "Invalid arguments");
1184 return FALSE;
1185 }
1186
1187 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1188 return FALSE;
1189
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001190 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001191 if (dev == NULL)
1192 return FALSE;
1193
1194 /* try to lookup/match release from client */
1195 rels = fu_engine_get_releases_for_device (priv->engine, dev, error);
1196 if (rels == NULL)
1197 return FALSE;
1198
1199 for (guint j = 0; j < rels->len; j++) {
1200 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1201 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1202 fu_device_get_version (dev),
1203 fu_device_get_version_format (dev)) == 0) {
1204 rel = g_object_ref (rel_tmp);
1205 break;
1206 }
1207 }
1208 if (rel == NULL) {
1209 g_set_error (error,
1210 FWUPD_ERROR,
1211 FWUPD_ERROR_NOT_SUPPORTED,
1212 "Unable to locate release for %s version %s",
1213 fu_device_get_name (dev),
1214 fu_device_get_version (dev));
1215 return FALSE;
1216 }
1217
1218 /* update the console if composite devices are also updated */
1219 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1220 g_signal_connect (priv->engine, "device-changed",
1221 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1222 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1223 if (!fu_util_install_release (priv, rel, error))
1224 return FALSE;
1225 fu_util_display_current_message (priv);
1226
1227 /* we don't want to ask anything */
1228 if (priv->no_reboot_check) {
1229 g_debug ("skipping reboot check");
1230 return TRUE;
1231 }
1232
1233 /* save the device state for other applications to see */
1234 if (!fu_util_save_current_state (priv, error))
1235 return FALSE;
1236
1237 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1238}
1239
1240static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001241fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1242{
1243 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001244 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001245
1246 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001247 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001248 return FALSE;
1249
Richard Hughes98ca9932018-05-18 10:24:07 +01001250 /* get device */
1251 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001252 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001253 if (device == NULL)
1254 return FALSE;
1255 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001256 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001257 if (device == NULL)
1258 return FALSE;
1259 }
1260
1261 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001262 locker = fu_device_locker_new (device, error);
1263 if (locker == NULL)
1264 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001265 return fu_device_detach (device, error);
1266}
1267
1268static gboolean
1269fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1270{
1271 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001272 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001273
1274 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001275 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001276 return FALSE;
1277
Richard Hughes98ca9932018-05-18 10:24:07 +01001278 /* get device */
1279 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001280 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001281 if (device == NULL)
1282 return FALSE;
1283 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001284 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001285 if (device == NULL)
1286 return FALSE;
1287 }
1288
1289 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001290 locker = fu_device_locker_new (device, error);
1291 if (locker == NULL)
1292 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001293 return fu_device_attach (device, error);
1294}
1295
Richard Hughes1d894f12018-08-31 13:05:51 +01001296static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001297fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1298{
1299 gboolean has_pending = FALSE;
1300 g_autoptr(FuHistory) history = fu_history_new ();
1301 g_autoptr(GPtrArray) devices = NULL;
1302
1303 /* check the history database before starting the daemon */
1304 if (g_strv_length (values) == 0) {
1305 devices = fu_history_get_devices (history, error);
1306 if (devices == NULL)
1307 return FALSE;
1308 } else if (g_strv_length (values) == 1) {
1309 FuDevice *device;
1310 device = fu_history_get_device_by_id (history, values[0], error);
1311 if (device == NULL)
1312 return FALSE;
1313 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1314 g_ptr_array_add (devices, device);
1315 } else {
1316 g_set_error_literal (error,
1317 FWUPD_ERROR,
1318 FWUPD_ERROR_INVALID_ARGS,
1319 "Invalid arguments");
1320 return FALSE;
1321 }
1322
1323 /* nothing to do */
1324 for (guint i = 0; i < devices->len; i++) {
1325 FuDevice *dev = g_ptr_array_index (devices, i);
1326 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1327 fu_engine_add_plugin_filter (priv->engine,
1328 fu_device_get_plugin (dev));
1329 has_pending = TRUE;
1330 }
1331 }
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001332
dkadioglu1db7e982019-12-04 21:13:21 +01001333 if (!has_pending) {
1334 g_printerr ("No firmware to activate\n");
1335 return TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001336 }
1337
1338 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001339 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001340 return FALSE;
1341
1342 /* activate anything with _NEEDS_ACTIVATION */
1343 for (guint i = 0; i < devices->len; i++) {
1344 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001345 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1346 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001347 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1348 continue;
1349 /* TRANSLATORS: shown when shutting down to switch to the new version */
1350 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1351 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1352 return FALSE;
1353 }
1354
1355 return TRUE;
1356}
1357
1358static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001359fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1360{
1361 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1362 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1363 const gchar *hwid_keys[] = {
1364 FU_HWIDS_KEY_BIOS_VENDOR,
1365 FU_HWIDS_KEY_BIOS_VERSION,
1366 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1367 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1368 FU_HWIDS_KEY_MANUFACTURER,
1369 FU_HWIDS_KEY_FAMILY,
1370 FU_HWIDS_KEY_PRODUCT_NAME,
1371 FU_HWIDS_KEY_PRODUCT_SKU,
1372 FU_HWIDS_KEY_ENCLOSURE_KIND,
1373 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1374 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1375 NULL };
1376
1377 /* read DMI data */
1378 if (g_strv_length (values) == 0) {
1379 if (!fu_smbios_setup (smbios, error))
1380 return FALSE;
1381 } else if (g_strv_length (values) == 1) {
1382 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1383 return FALSE;
1384 } else {
1385 g_set_error_literal (error,
1386 FWUPD_ERROR,
1387 FWUPD_ERROR_INVALID_ARGS,
1388 "Invalid arguments");
1389 return FALSE;
1390 }
1391 if (!fu_hwids_setup (hwids, smbios, error))
1392 return FALSE;
1393
1394 /* show debug output */
1395 g_print ("Computer Information\n");
1396 g_print ("--------------------\n");
1397 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1398 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1399 if (tmp == NULL)
1400 continue;
1401 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1402 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1403 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1404 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1405 } else {
1406 g_print ("%s: %s\n", hwid_keys[i], tmp);
1407 }
1408 }
1409
1410 /* show GUIDs */
1411 g_print ("\nHardware IDs\n");
1412 g_print ("------------\n");
1413 for (guint i = 0; i < 15; i++) {
1414 const gchar *keys = NULL;
1415 g_autofree gchar *guid = NULL;
1416 g_autofree gchar *key = NULL;
1417 g_autofree gchar *keys_str = NULL;
1418 g_auto(GStrv) keysv = NULL;
1419 g_autoptr(GError) error_local = NULL;
1420
1421 /* get the GUID */
1422 key = g_strdup_printf ("HardwareID-%u", i);
1423 keys = fu_hwids_get_replace_keys (hwids, key);
1424 guid = fu_hwids_get_guid (hwids, key, &error_local);
1425 if (guid == NULL) {
1426 g_print ("%s\n", error_local->message);
1427 continue;
1428 }
1429
1430 /* show what makes up the GUID */
1431 keysv = g_strsplit (keys, "&", -1);
1432 keys_str = g_strjoinv (" + ", keysv);
1433 g_print ("{%s} <- %s\n", guid, keys_str);
1434 }
1435
1436 return TRUE;
1437}
1438
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001439static gboolean
1440fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1441{
1442 const gchar *script_fn = "startup.sh";
1443 const gchar *output_fn = "firmware.bin";
1444 g_autoptr(GBytes) archive_blob = NULL;
1445 g_autoptr(GBytes) firmware_blob = NULL;
1446 if (g_strv_length (values) < 2) {
1447 g_set_error_literal (error,
1448 FWUPD_ERROR,
1449 FWUPD_ERROR_INVALID_ARGS,
1450 "Invalid arguments");
1451 return FALSE;
1452 }
1453 archive_blob = fu_common_get_contents_bytes (values[0], error);
1454 if (archive_blob == NULL)
1455 return FALSE;
1456 if (g_strv_length (values) > 2)
1457 script_fn = values[2];
1458 if (g_strv_length (values) > 3)
1459 output_fn = values[3];
1460 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1461 if (firmware_blob == NULL)
1462 return FALSE;
1463 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1464}
1465
Richard Hughes3d607622019-03-07 16:59:27 +00001466static gboolean
1467fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1468{
1469 g_autofree gchar *sig = NULL;
1470
1471 /* check args */
1472 if (g_strv_length (values) != 1) {
1473 g_set_error_literal (error,
1474 FWUPD_ERROR,
1475 FWUPD_ERROR_INVALID_ARGS,
1476 "Invalid arguments: value expected");
1477 return FALSE;
1478 }
1479
1480 /* start engine */
1481 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1482 return FALSE;
1483 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001484 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1485 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001486 if (sig == NULL)
1487 return FALSE;
1488 g_print ("%s\n", sig);
1489 return TRUE;
1490}
1491
Mario Limonciello62f84862018-10-18 13:15:23 -05001492static void
1493fu_util_device_added_cb (FwupdClient *client,
1494 FwupdDevice *device,
1495 gpointer user_data)
1496{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001497 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001498 /* TRANSLATORS: this is when a device is hotplugged */
1499 g_print ("%s\n%s", _("Device added:"), tmp);
1500}
1501
1502static void
1503fu_util_device_removed_cb (FwupdClient *client,
1504 FwupdDevice *device,
1505 gpointer user_data)
1506{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001507 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001508 /* TRANSLATORS: this is when a device is hotplugged */
1509 g_print ("%s\n%s", _("Device removed:"), tmp);
1510}
1511
1512static void
1513fu_util_device_changed_cb (FwupdClient *client,
1514 FwupdDevice *device,
1515 gpointer user_data)
1516{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001517 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001518 /* TRANSLATORS: this is when a device has been updated */
1519 g_print ("%s\n%s", _("Device changed:"), tmp);
1520}
1521
1522static void
1523fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1524{
1525 /* TRANSLATORS: this is when the daemon state changes */
1526 g_print ("%s\n", _("Changed"));
1527}
1528
1529static gboolean
1530fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1531{
1532 g_autoptr(FwupdClient) client = fwupd_client_new ();
1533
1534 /* get all the devices */
1535 if (!fwupd_client_connect (client, priv->cancellable, error))
1536 return FALSE;
1537
1538 /* watch for any hotplugged device */
1539 g_signal_connect (client, "changed",
1540 G_CALLBACK (fu_util_changed_cb), priv);
1541 g_signal_connect (client, "device-added",
1542 G_CALLBACK (fu_util_device_added_cb), priv);
1543 g_signal_connect (client, "device-removed",
1544 G_CALLBACK (fu_util_device_removed_cb), priv);
1545 g_signal_connect (client, "device-changed",
1546 G_CALLBACK (fu_util_device_changed_cb), priv);
1547 g_signal_connect (priv->cancellable, "cancelled",
1548 G_CALLBACK (fu_util_cancelled_cb), priv);
1549 g_main_loop_run (priv->loop);
1550 return TRUE;
1551}
1552
Richard Hughes15684492019-03-15 16:27:50 +00001553static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001554fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1555{
1556 g_autoptr(GPtrArray) firmware_types = NULL;
1557
1558 /* load engine */
1559 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1560 return FALSE;
1561
1562 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1563 for (guint i = 0; i < firmware_types->len; i++) {
1564 const gchar *id = g_ptr_array_index (firmware_types, i);
1565 g_print ("%s\n", id);
1566 }
1567 if (firmware_types->len == 0) {
1568 /* TRANSLATORS: nothing found */
1569 g_print ("%s\n", _("No firmware IDs found"));
1570 return TRUE;
1571 }
1572
1573 return TRUE;
1574}
1575
1576static gchar *
1577fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1578{
1579 g_autoptr(GPtrArray) firmware_types = NULL;
1580 guint idx;
1581 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1582
1583 /* TRANSLATORS: get interactive prompt */
1584 g_print ("%s\n", _("Choose a firmware type:"));
1585 /* TRANSLATORS: this is to abort the interactive prompt */
1586 g_print ("0.\t%s\n", _("Cancel"));
1587 for (guint i = 0; i < firmware_types->len; i++) {
1588 const gchar *id = g_ptr_array_index (firmware_types, i);
1589 g_print ("%u.\t%s\n", i + 1, id);
1590 }
1591 idx = fu_util_prompt_for_number (firmware_types->len);
1592 if (idx == 0) {
1593 g_set_error_literal (error,
1594 FWUPD_ERROR,
1595 FWUPD_ERROR_NOTHING_TO_DO,
1596 "Request canceled");
1597 return NULL;
1598 }
1599
1600 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1601}
1602
1603static gboolean
1604fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1605{
1606 GType gtype;
1607 g_autoptr(GBytes) blob = NULL;
1608 g_autoptr(FuFirmware) firmware = NULL;
1609 g_autofree gchar *firmware_type = NULL;
1610 g_autofree gchar *str = NULL;
1611
1612 /* check args */
1613 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1614 g_set_error_literal (error,
1615 FWUPD_ERROR,
1616 FWUPD_ERROR_INVALID_ARGS,
1617 "Invalid arguments: filename required");
1618 return FALSE;
1619 }
1620
1621 if (g_strv_length (values) == 2)
1622 firmware_type = g_strdup (values[1]);
1623
1624 /* load file */
1625 blob = fu_common_get_contents_bytes (values[0], error);
1626 if (blob == NULL)
1627 return FALSE;
1628
1629 /* load engine */
1630 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1631 return FALSE;
1632
1633 /* find the GType to use */
1634 if (firmware_type == NULL)
1635 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1636 if (firmware_type == NULL)
1637 return FALSE;
1638 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1639 if (gtype == G_TYPE_INVALID) {
1640 g_set_error (error,
1641 G_IO_ERROR,
1642 G_IO_ERROR_NOT_FOUND,
1643 "GType %s not supported", firmware_type);
1644 return FALSE;
1645 }
1646 firmware = g_object_new (gtype, NULL);
1647 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1648 return FALSE;
1649 str = fu_firmware_to_string (firmware);
1650 g_print ("%s", str);
1651 return TRUE;
1652}
1653
1654static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00001655fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
1656{
1657 GType gtype_dst;
1658 GType gtype_src;
1659 g_autofree gchar *firmware_type_dst = NULL;
1660 g_autofree gchar *firmware_type_src = NULL;
1661 g_autofree gchar *str_dst = NULL;
1662 g_autofree gchar *str_src = NULL;
1663 g_autoptr(FuFirmware) firmware_dst = NULL;
1664 g_autoptr(FuFirmware) firmware_src = NULL;
1665 g_autoptr(GBytes) blob_dst = NULL;
1666 g_autoptr(GBytes) blob_src = NULL;
1667 g_autoptr(GPtrArray) images = NULL;
1668
1669 /* check args */
1670 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
1671 g_set_error_literal (error,
1672 FWUPD_ERROR,
1673 FWUPD_ERROR_INVALID_ARGS,
1674 "Invalid arguments: filename required");
1675 return FALSE;
1676 }
1677
1678 if (g_strv_length (values) > 2)
1679 firmware_type_src = g_strdup (values[2]);
1680 if (g_strv_length (values) > 3)
1681 firmware_type_dst = g_strdup (values[3]);
1682
1683 /* load file */
1684 blob_src = fu_common_get_contents_bytes (values[0], error);
1685 if (blob_src == NULL)
1686 return FALSE;
1687
1688 /* load engine */
1689 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1690 return FALSE;
1691
1692 /* find the GType to use */
1693 if (firmware_type_src == NULL)
1694 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
1695 if (firmware_type_src == NULL)
1696 return FALSE;
1697 if (firmware_type_dst == NULL)
1698 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
1699 if (firmware_type_dst == NULL)
1700 return FALSE;
1701 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
1702 if (gtype_src == G_TYPE_INVALID) {
1703 g_set_error (error,
1704 G_IO_ERROR,
1705 G_IO_ERROR_NOT_FOUND,
1706 "GType %s not supported", firmware_type_src);
1707 return FALSE;
1708 }
1709 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
1710 if (gtype_dst == G_TYPE_INVALID) {
1711 g_set_error (error,
1712 G_IO_ERROR,
1713 G_IO_ERROR_NOT_FOUND,
1714 "GType %s not supported", firmware_type_dst);
1715 return FALSE;
1716 }
1717 firmware_src = g_object_new (gtype_src, NULL);
1718 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
1719 return FALSE;
1720 str_src = fu_firmware_to_string (firmware_src);
1721 g_print ("%s", str_src);
1722
1723 /* copy images */
1724 firmware_dst = g_object_new (gtype_dst, NULL);
1725 images = fu_firmware_get_images (firmware_src);
1726 for (guint i = 0; i < images->len; i++) {
1727 FuFirmwareImage *img = g_ptr_array_index (images, i);
1728 fu_firmware_add_image (firmware_dst, img);
1729 }
1730
1731 /* write new file */
1732 blob_dst = fu_firmware_write (firmware_dst, error);
1733 if (blob_dst == NULL)
1734 return FALSE;
1735 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
1736 return FALSE;
1737 str_dst = fu_firmware_to_string (firmware_dst);
1738 g_print ("%s", str_dst);
1739
1740 /* success */
1741 return TRUE;
1742}
1743
1744static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001745fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1746{
1747 g_autofree gchar *str = NULL;
1748 g_autoptr(FuDevice) dev = NULL;
1749
1750 /* load engine */
1751 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1752 return FALSE;
1753
1754 /* get device */
1755 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001756 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00001757 if (dev == NULL)
1758 return FALSE;
1759 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001760 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00001761 if (dev == NULL)
1762 return FALSE;
1763 }
1764
1765 /* add checksums */
1766 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1767 return FALSE;
1768
1769 /* show checksums */
1770 str = fu_device_to_string (dev);
1771 g_print ("%s\n", str);
1772 return TRUE;
1773}
1774
Mario Limonciellofe593942019-04-03 13:48:52 -05001775static gboolean
1776fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1777{
1778 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001779 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001780 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001781
1782 /* load engine */
1783 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1784 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001785 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001786
1787 /* get all devices from the history database */
1788 devices = fu_engine_get_history (priv->engine, error);
1789 if (devices == NULL)
1790 return FALSE;
1791
1792 /* show each device */
1793 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001794 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001795 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001796 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001797 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001798 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001799
Richard Hughes747f5702019-08-06 14:27:26 +01001800 if (!fu_util_filter_device (priv, dev))
1801 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001802 child = g_node_append_data (root, dev);
1803
1804 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001805 if (rel == NULL)
1806 continue;
1807 remote = fwupd_release_get_remote_id (rel);
1808
1809 /* doesn't actually map to remote */
1810 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001811 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001812 continue;
1813 }
1814
1815 /* try to lookup releases from client */
1816 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1817 if (rels == NULL)
1818 return FALSE;
1819
1820 /* map to a release in client */
1821 for (guint j = 0; j < rels->len; j++) {
1822 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1823 if (g_strcmp0 (remote,
1824 fwupd_release_get_remote_id (rel2)) != 0)
1825 continue;
1826 if (g_strcmp0 (fwupd_release_get_version (rel),
1827 fwupd_release_get_version (rel2)) != 0)
1828 continue;
1829 g_node_append_data (child, g_object_ref (rel2));
1830 rel = NULL;
1831 break;
1832 }
1833
1834 /* didn't match anything */
1835 if (rels->len == 0 || rel != NULL) {
1836 g_node_append_data (child, rel);
1837 continue;
1838 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001839
Mario Limonciellofe593942019-04-03 13:48:52 -05001840 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001841 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001842
1843 return TRUE;
1844}
1845
Richard Hughesfd7e9942020-01-17 14:09:13 +00001846static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00001847fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
1848{
Richard Hughesc5710d92020-06-26 11:08:25 +01001849 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00001850 g_autofree gchar *fn_raw = NULL;
1851 g_autofree gchar *fn_sig = NULL;
1852 g_autoptr(GBytes) bytes_raw = NULL;
1853 g_autoptr(GBytes) bytes_sig = NULL;
1854
1855 /* payload */
Richard Hughesc5710d92020-06-26 11:08:25 +01001856 metadata_uri = fwupd_remote_get_metadata_uri (remote);
1857 if (metadata_uri == NULL) {
1858 g_set_error_literal (error,
1859 FWUPD_ERROR,
1860 FWUPD_ERROR_NOTHING_TO_DO,
1861 "no metadata URI available");
1862 return FALSE;
1863 }
1864 fn_raw = fu_util_get_user_cache_path (metadata_uri);
Richard Hughes4959baa2020-01-17 14:33:00 +00001865 if (!fu_common_mkdir_parent (fn_raw, error))
1866 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01001867 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00001868 return FALSE;
1869 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
1870 if (bytes_raw == NULL)
1871 return FALSE;
1872
1873 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01001874 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
1875 if (metadata_uri == NULL) {
1876 g_set_error_literal (error,
1877 FWUPD_ERROR,
1878 FWUPD_ERROR_NOTHING_TO_DO,
1879 "no metadata signature URI available");
1880 return FALSE;
1881 }
1882 fn_sig = fu_util_get_user_cache_path (metadata_uri);
1883 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00001884 return FALSE;
1885 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
1886 if (bytes_sig == NULL)
1887 return FALSE;
1888
1889 /* send to daemon */
1890 g_debug ("updating %s", fwupd_remote_get_id (remote));
1891 return fu_engine_update_metadata_bytes (priv->engine,
1892 fwupd_remote_get_id (remote),
1893 bytes_raw,
1894 bytes_sig,
1895 error);
1896
1897}
1898
1899static gboolean
1900fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
1901{
1902 g_autoptr(GPtrArray) remotes = NULL;
1903
1904 /* load engine */
1905 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1906 return FALSE;
1907
1908 /* download new metadata */
1909 remotes = fu_engine_get_remotes (priv->engine, error);
1910 if (remotes == NULL)
1911 return FALSE;
1912 for (guint i = 0; i < remotes->len; i++) {
1913 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1914 if (!fwupd_remote_get_enabled (remote))
1915 continue;
1916 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
1917 continue;
1918 if (!fu_util_refresh_remote (priv, remote, error))
1919 return FALSE;
1920 }
1921 return TRUE;
1922}
1923
1924static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00001925fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
1926{
1927 g_autoptr(GNode) root = g_node_new (NULL);
1928 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001929 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00001930
1931 /* load engine */
1932 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1933 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001934 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00001935
1936 /* list remotes */
1937 remotes = fu_engine_get_remotes (priv->engine, error);
1938 if (remotes == NULL)
1939 return FALSE;
1940 if (remotes->len == 0) {
1941 g_set_error_literal (error,
1942 FWUPD_ERROR,
1943 FWUPD_ERROR_NOTHING_TO_DO,
1944 "no remotes available");
1945 return FALSE;
1946 }
1947 for (guint i = 0; i < remotes->len; i++) {
1948 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
1949 g_node_append_data (root, remote_tmp);
1950 }
1951 fu_util_print_tree (root, title);
1952
1953 return TRUE;
1954}
1955
Richard Hughes196c6c62020-05-11 19:42:47 +01001956static gboolean
1957fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
1958{
Richard Hughesf58ac732020-05-12 15:23:44 +01001959 g_autoptr(FuSecurityAttrs) attrs = NULL;
1960 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01001961 g_autofree gchar *str = NULL;
1962
1963 /* not ready yet */
1964 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
1965 g_set_error_literal (error,
1966 FWUPD_ERROR,
1967 FWUPD_ERROR_NOT_SUPPORTED,
1968 "The HSI specification is not yet complete. "
1969 "To ignore this warning, use --force");
1970 return FALSE;
1971 }
1972
1973 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1974 return FALSE;
1975
1976 /* TRANSLATORS: this is a string like 'HSI:2-U' */
1977 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
1978 fu_engine_get_host_security_id (priv->engine));
1979
1980 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01001981 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01001982 items = fu_security_attrs_get_all (attrs);
1983 str = fu_util_security_attrs_to_string (items);
Richard Hughes196c6c62020-05-11 19:42:47 +01001984 g_print ("%s\n", str);
1985 return TRUE;
1986}
1987
Richard Hughesb5976832018-05-18 10:02:09 +01001988int
1989main (int argc, char *argv[])
1990{
Richard Hughes460226a2018-05-21 20:56:21 +01001991 gboolean allow_older = FALSE;
1992 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001993 gboolean force = FALSE;
1994 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001995 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001996 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001997 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001998 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1999 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002000 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002001 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002002 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002003 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002004 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2005 /* TRANSLATORS: command line option */
2006 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002007 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2008 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002009 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002010 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2011 /* TRANSLATORS: command line option */
2012 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002013 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2014 /* TRANSLATORS: command line option */
2015 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002016 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2017 /* TRANSLATORS: command line option */
2018 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002019 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2020 /* TRANSLATORS: command line option */
2021 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002022 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
2023 /* TRANSLATORS: command line option */
2024 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002025 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2026 /* TRANSLATORS: command line option */
2027 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002028 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002029 /* TRANSLATORS: command line option */
2030 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002031 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002032 /* TRANSLATORS: command line option */
2033 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002034 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2035 /* TRANSLATORS: command line option */
2036 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002037 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2038 /* TRANSLATORS: command line option */
2039 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002040 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2041 /* TRANSLATORS: command line option */
2042 _("Filter with a set of device flags using a ~ prefix to "
2043 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002044 { NULL}
2045 };
2046
Richard Hughes429f72b2020-01-16 12:18:19 +00002047#ifdef _WIN32
2048 /* workaround Windows setting the codepage to 1252 */
2049 g_setenv ("LANG", "C.UTF-8", FALSE);
2050#endif
2051
Richard Hughesb5976832018-05-18 10:02:09 +01002052 setlocale (LC_ALL, "");
2053
Richard Hughes668ee212019-11-22 09:17:46 +00002054 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002055 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2056 textdomain (GETTEXT_PACKAGE);
2057
Richard Hughes01c0bad2019-11-22 09:08:51 +00002058#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002059 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002060 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002061 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002062 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002063#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002064
2065 /* create helper object */
2066 priv->loop = g_main_loop_new (NULL, FALSE);
2067 priv->progressbar = fu_progressbar_new ();
2068
2069 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002070 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002071 "build-firmware",
2072 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
2073 /* TRANSLATORS: command description */
2074 _("Build firmware using a sandbox"),
2075 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002076 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002077 "smbios-dump",
2078 "FILE",
2079 /* TRANSLATORS: command description */
2080 _("Dump SMBIOS data from a file"),
2081 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002082 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002083 "get-plugins",
2084 NULL,
2085 /* TRANSLATORS: command description */
2086 _("Get all enabled plugins registered with the system"),
2087 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002088 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002089 "get-details",
2090 NULL,
2091 /* TRANSLATORS: command description */
2092 _("Gets details about a firmware file"),
2093 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002094 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002095 "get-history",
2096 NULL,
2097 /* TRANSLATORS: command description */
2098 _("Show history of firmware updates"),
2099 fu_util_get_history);
2100 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002101 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002102 NULL,
2103 /* TRANSLATORS: command description */
2104 _("Gets the list of updates for connected hardware"),
2105 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002106 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002107 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002108 NULL,
2109 /* TRANSLATORS: command description */
2110 _("Get all devices that support firmware updates"),
2111 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002112 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002113 "get-device-flags",
2114 NULL,
2115 /* TRANSLATORS: command description */
2116 _("Get all device flags supported by fwupd"),
2117 fu_util_get_device_flags);
2118 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002119 "watch",
2120 NULL,
2121 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002122 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002123 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002124 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002125 "install-blob",
2126 "FILENAME DEVICE-ID",
2127 /* TRANSLATORS: command description */
2128 _("Install a firmware blob on a device"),
2129 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002130 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002131 "install",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002132 "FILE [DEVICE-ID|GUID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002133 /* TRANSLATORS: command description */
2134 _("Install a firmware file on this hardware"),
2135 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002136 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002137 "reinstall",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002138 "DEVICE-ID|GUID",
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002139 /* TRANSLATORS: command description */
2140 _("Reinstall firmware on a device"),
2141 fu_util_reinstall);
2142 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002143 "attach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002144 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002145 /* TRANSLATORS: command description */
2146 _("Attach to firmware mode"),
2147 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002148 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002149 "detach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002150 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002151 /* TRANSLATORS: command description */
2152 _("Detach to bootloader mode"),
2153 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002154 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002155 "activate",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002156 "[DEVICE-ID|GUID]",
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002157 /* TRANSLATORS: command description */
2158 _("Activate pending devices"),
2159 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002160 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002161 "hwids",
2162 "[FILE]",
2163 /* TRANSLATORS: command description */
2164 _("Return all the hardware IDs for the machine"),
2165 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002166 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002167 "monitor",
2168 NULL,
2169 /* TRANSLATORS: command description */
2170 _("Monitor the daemon for events"),
2171 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002172 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002173 "update,upgrade",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002174 "[DEVICE-ID|GUID]",
Mario Limonciello46aaee82019-01-10 12:58:00 -06002175 /* TRANSLATORS: command description */
2176 _("Update all devices that match local metadata"),
2177 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002178 fu_util_cmd_array_add (cmd_array,
2179 "self-sign",
2180 "TEXT",
2181 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002182 C_("command-description",
2183 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002184 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002185 fu_util_cmd_array_add (cmd_array,
2186 "verify-update",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002187 "[DEVICE-ID|GUID]",
Richard Hughes15684492019-03-15 16:27:50 +00002188 /* TRANSLATORS: command description */
2189 _("Update the stored metadata with current contents"),
2190 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002191 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00002192 "firmware-read",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002193 "FILENAME [DEVICE-ID|GUID]",
Richard Hughesa58510b2019-10-30 10:03:12 +00002194 /* TRANSLATORS: command description */
2195 _("Read a firmware blob from a device"),
2196 fu_util_firmware_read);
2197 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002198 "firmware-convert",
2199 "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]",
2200 /* TRANSLATORS: command description */
2201 _("Convert a firmware file"),
2202 fu_util_firmware_convert);
2203 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002204 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06002205 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01002206 /* TRANSLATORS: command description */
2207 _("Parse and show details about a firmware file"),
2208 fu_util_firmware_parse);
2209 fu_util_cmd_array_add (cmd_array,
2210 "get-firmware-types",
2211 NULL,
2212 /* TRANSLATORS: command description */
2213 _("List the available firmware types"),
2214 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002215 fu_util_cmd_array_add (cmd_array,
2216 "get-remotes",
2217 NULL,
2218 /* TRANSLATORS: command description */
2219 _("Gets the configured remotes"),
2220 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002221 fu_util_cmd_array_add (cmd_array,
2222 "refresh",
2223 NULL,
2224 /* TRANSLATORS: command description */
2225 _("Refresh metadata from remote server"),
2226 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002227 fu_util_cmd_array_add (cmd_array,
2228 "security",
2229 NULL,
2230 /* TRANSLATORS: command description */
2231 _("Gets the host security attributes."),
2232 fu_util_security);
Richard Hughesb5976832018-05-18 10:02:09 +01002233
2234 /* do stuff on ctrl+c */
2235 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002236#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002237 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2238 SIGINT, fu_util_sigint_cb,
2239 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002240#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002241 g_signal_connect (priv->cancellable, "cancelled",
2242 G_CALLBACK (fu_util_cancelled_cb), priv);
2243
2244 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002245 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002246
Mario Limonciello3f243a92019-01-21 22:05:23 -06002247 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002248 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002249 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002250 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002251 fu_progressbar_set_interactive (priv->progressbar, FALSE);
2252 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002253
Richard Hughesb5976832018-05-18 10:02:09 +01002254 /* get a list of the commands */
2255 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002256 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002257 g_option_context_set_summary (priv->context, cmd_descriptions);
2258 g_option_context_set_description (priv->context,
2259 "This tool allows an administrator to use the fwupd plugins "
2260 "without being installed on the host system.");
2261
2262 /* TRANSLATORS: program name */
2263 g_set_application_name (_("Firmware Utility"));
2264 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002265 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002266 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2267 if (!ret) {
2268 /* TRANSLATORS: the user didn't read the man page */
2269 g_print ("%s: %s\n", _("Failed to parse arguments"),
2270 error->message);
2271 return EXIT_FAILURE;
2272 }
2273
Richard Hughes0e46b222019-09-05 12:13:35 +01002274 /* allow disabling SSL strict mode for broken corporate proxies */
2275 if (priv->disable_ssl_strict) {
2276 /* TRANSLATORS: try to help */
2277 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2278 "to do this automatically in the future "
2279 "export DISABLE_SSL_STRICT in your environment"));
2280 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2281 }
2282
Richard Hughes747f5702019-08-06 14:27:26 +01002283 /* parse filter flags */
2284 if (filter != NULL) {
2285 if (!fu_util_parse_filter_flags (filter,
2286 &priv->filter_include,
2287 &priv->filter_exclude,
2288 &error)) {
2289 /* TRANSLATORS: the user didn't read the man page */
2290 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2291 error->message);
2292 return EXIT_FAILURE;
2293 }
2294 }
2295
2296
Richard Hughes460226a2018-05-21 20:56:21 +01002297 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002298 if (allow_reinstall)
2299 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2300 if (allow_older)
2301 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2302 if (force)
2303 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2304
Richard Hughes98ca9932018-05-18 10:24:07 +01002305 /* load engine */
2306 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2307 g_signal_connect (priv->engine, "device-added",
2308 G_CALLBACK (fu_main_engine_device_added_cb),
2309 priv);
2310 g_signal_connect (priv->engine, "device-removed",
2311 G_CALLBACK (fu_main_engine_device_removed_cb),
2312 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002313 g_signal_connect (priv->engine, "status-changed",
2314 G_CALLBACK (fu_main_engine_status_changed_cb),
2315 priv);
2316 g_signal_connect (priv->engine, "percentage-changed",
2317 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2318 priv);
2319
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002320 /* just show versions and exit */
2321 if (version) {
2322 g_autofree gchar *version_str = fu_util_get_versions ();
2323 g_print ("%s\n", version_str);
2324 return EXIT_SUCCESS;
2325 }
2326
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002327 /* any plugin whitelist specified */
2328 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2329 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2330
Richard Hughesb5976832018-05-18 10:02:09 +01002331 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002332 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002333 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05002334 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002335 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05002336 /* TRANSLATORS: error message explaining command to run to how to get help */
2337 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
2338 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2339 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002340 return EXIT_NOTHING_TO_DO;
2341 }
Richard Hughesb5976832018-05-18 10:02:09 +01002342 return EXIT_FAILURE;
2343 }
2344
2345 /* success */
2346 return EXIT_SUCCESS;
2347}