blob: 52ed222f0c9ba6d87498fd73394c90b0890f616f [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"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060029#include "fu-smbios-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010030#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050031#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060032#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060033#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010034
Richard Hughes3d005222019-05-17 14:02:41 +010035#ifdef HAVE_SYSTEMD
36#include "fu-systemd.h"
37#endif
38
Richard Hughesb5976832018-05-18 10:02:09 +010039/* custom return code */
40#define EXIT_NOTHING_TO_DO 2
41
Mario Limonciello3f243a92019-01-21 22:05:23 -060042typedef enum {
43 FU_UTIL_OPERATION_UNKNOWN,
44 FU_UTIL_OPERATION_UPDATE,
45 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000046 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060047 FU_UTIL_OPERATION_LAST
48} FuUtilOperation;
49
Richard Hughesc77e1112019-03-01 10:16:26 +000050struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010051 GCancellable *cancellable;
52 GMainLoop *loop;
53 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010054 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010055 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060056 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050057 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000058 gboolean prepare_blob;
59 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060060 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010061 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050062 gboolean show_all_devices;
Richard Hughes0e46b222019-09-05 12:13:35 +010063 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050064 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060065 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050066 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060067 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060068 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010069 FwupdDeviceFlags filter_include;
70 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000071};
Richard Hughesb5976832018-05-18 10:02:09 +010072
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050073static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060074fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
75{
76 g_autoptr(JsonBuilder) builder = NULL;
77 g_autoptr(JsonGenerator) json_generator = NULL;
78 g_autoptr(JsonNode) json_root = NULL;
79 g_autoptr(GPtrArray) devices = NULL;
80 g_autofree gchar *state = NULL;
81 g_autofree gchar *dirname = NULL;
82 g_autofree gchar *filename = NULL;
83
84 if (!priv->enable_json_state)
85 return TRUE;
86
87 devices = fu_engine_get_devices (priv->engine, error);
88 if (devices == NULL)
89 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000090 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060091
92 /* create header */
93 builder = json_builder_new ();
94 json_builder_begin_object (builder);
95
96 /* add each device */
97 json_builder_set_member_name (builder, "Devices");
98 json_builder_begin_array (builder);
99 for (guint i = 0; i < devices->len; i++) {
100 FwupdDevice *dev = g_ptr_array_index (devices, i);
101 json_builder_begin_object (builder);
102 fwupd_device_to_json (dev, builder);
103 json_builder_end_object (builder);
104 }
105 json_builder_end_array (builder);
106 json_builder_end_object (builder);
107
108 /* export as a string */
109 json_root = json_builder_get_root (builder);
110 json_generator = json_generator_new ();
111 json_generator_set_pretty (json_generator, TRUE);
112 json_generator_set_root (json_generator, json_root);
113 state = json_generator_to_data (json_generator, NULL);
114 if (state == NULL)
115 return FALSE;
116 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
117 filename = g_build_filename (dirname, "state.json", NULL);
118 return g_file_set_contents (filename, state, -1, error);
119}
120
121static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000122fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600123{
124 g_autoptr(GError) error_local = NULL;
125
Richard Hughesd92ccca2019-05-20 11:28:31 +0100126#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100127 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500128 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100129#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000130 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000131 return FALSE;
132 if (fu_engine_get_tainted (priv->engine)) {
133 g_printerr ("WARNING: This tool has loaded 3rd party code and "
134 "is no longer supported by the upstream developers!\n");
135 }
136 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500137}
138
Richard Hughesb5976832018-05-18 10:02:09 +0100139static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500140fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
141{
142 g_autofree gchar *path = g_path_get_dirname (value);
143 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
144 g_prefix_error (error,
145 "Unable to access %s. You may need to copy %s to %s: ",
146 path, value, g_getenv ("HOME"));
147 }
148}
149
150static void
Richard Hughesb5976832018-05-18 10:02:09 +0100151fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
152{
153 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
154 /* TRANSLATORS: this is when a device ctrl+c's a watch */
155 g_print ("%s\n", _("Cancelled"));
156 g_main_loop_quit (priv->loop);
157}
158
159static gboolean
160fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
161{
162 g_autofree gchar *tmp = NULL;
163 g_autoptr(FuSmbios) smbios = NULL;
164 if (g_strv_length (values) < 1) {
165 g_set_error_literal (error,
166 FWUPD_ERROR,
167 FWUPD_ERROR_INVALID_ARGS,
168 "Invalid arguments");
169 return FALSE;
170 }
171 smbios = fu_smbios_new ();
172 if (!fu_smbios_setup_from_file (smbios, values[0], error))
173 return FALSE;
174 tmp = fu_smbios_to_string (smbios);
175 g_print ("%s\n", tmp);
176 return TRUE;
177}
178
Richard Hughes9e5675e2019-11-22 09:35:03 +0000179#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100180static gboolean
181fu_util_sigint_cb (gpointer user_data)
182{
183 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
184 g_debug ("Handling SIGINT");
185 g_cancellable_cancel (priv->cancellable);
186 return FALSE;
187}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000188#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100189
190static void
191fu_util_private_free (FuUtilPrivate *priv)
192{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500193 if (priv->current_device != NULL)
194 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100195 if (priv->engine != NULL)
196 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100197 if (priv->loop != NULL)
198 g_main_loop_unref (priv->loop);
199 if (priv->cancellable != NULL)
200 g_object_unref (priv->cancellable);
201 if (priv->progressbar != NULL)
202 g_object_unref (priv->progressbar);
203 if (priv->context != NULL)
204 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600205 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100206 g_free (priv);
207}
208
209#pragma clang diagnostic push
210#pragma clang diagnostic ignored "-Wunused-function"
211G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
212#pragma clang diagnostic pop
213
Richard Hughes98ca9932018-05-18 10:24:07 +0100214
215static void
216fu_main_engine_device_added_cb (FuEngine *engine,
217 FuDevice *device,
218 FuUtilPrivate *priv)
219{
220 g_autofree gchar *tmp = fu_device_to_string (device);
221 g_debug ("ADDED:\n%s", tmp);
222}
223
224static void
225fu_main_engine_device_removed_cb (FuEngine *engine,
226 FuDevice *device,
227 FuUtilPrivate *priv)
228{
229 g_autofree gchar *tmp = fu_device_to_string (device);
230 g_debug ("REMOVED:\n%s", tmp);
231}
232
233static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100234fu_main_engine_status_changed_cb (FuEngine *engine,
235 FwupdStatus status,
236 FuUtilPrivate *priv)
237{
238 fu_progressbar_update (priv->progressbar, status, 0);
239}
240
241static void
242fu_main_engine_percentage_changed_cb (FuEngine *engine,
243 guint percentage,
244 FuUtilPrivate *priv)
245{
246 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
247}
248
249static gboolean
250fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
251{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000252 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100253 return FALSE;
254 g_main_loop_run (priv->loop);
255 return TRUE;
256}
257
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100258static gint
259fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
260{
261 return fu_plugin_name_compare (*item1, *item2);
262}
263
264static gboolean
265fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
266{
267 GPtrArray *plugins;
268 guint cnt = 0;
269
270 /* load engine */
271 if (!fu_engine_load_plugins (priv->engine, error))
272 return FALSE;
273
274 /* print */
275 plugins = fu_engine_get_plugins (priv->engine);
276 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
277 for (guint i = 0; i < plugins->len; i++) {
278 FuPlugin *plugin = g_ptr_array_index (plugins, i);
279 if (!fu_plugin_get_enabled (plugin))
280 continue;
281 g_print ("%s\n", fu_plugin_get_name (plugin));
282 cnt++;
283 }
284 if (cnt == 0) {
285 /* TRANSLATORS: nothing found */
286 g_print ("%s\n", _("No plugins found"));
287 return TRUE;
288 }
289
290 return TRUE;
291}
292
Richard Hughes98ca9932018-05-18 10:24:07 +0100293static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100294fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
295{
296 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
297 if (!fwupd_device_has_flag (dev, priv->filter_include))
298 return FALSE;
299 }
300 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
301 if (fwupd_device_has_flag (dev, priv->filter_exclude))
302 return FALSE;
303 }
304 return TRUE;
305}
306
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500307static gchar *
308fu_util_get_tree_title (FuUtilPrivate *priv)
309{
310 return g_strdup (fu_engine_get_host_product (priv->engine));
311}
312
Richard Hughes747f5702019-08-06 14:27:26 +0100313static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600314fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
315{
316 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500317 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600318 g_autofree gchar *title = NULL;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600319
320 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000321 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600322 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600323 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600324
325 /* get devices from daemon */
326 devices = fu_engine_get_devices (priv->engine, error);
327 if (devices == NULL)
328 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000329 fwupd_device_array_ensure_parents (devices);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600330 for (guint i = 0; i < devices->len; i++) {
331 FwupdDevice *dev = g_ptr_array_index (devices, i);
332 g_autoptr(GPtrArray) rels = NULL;
333 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500334 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600335
Richard Hughes747f5702019-08-06 14:27:26 +0100336 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600337 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600338 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600339 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
340 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
341 * %1 is the device name */
342 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
343 fwupd_device_get_name (dev));
344 g_printerr ("%s\n", tmp);
345 continue;
346 }
Richard Hughes747f5702019-08-06 14:27:26 +0100347 if (!fu_util_filter_device (priv, dev))
348 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600349
350 /* get the releases for this device and filter for validity */
351 rels = fu_engine_get_upgrades (priv->engine,
352 fwupd_device_get_id (dev),
353 &error_local);
354 if (rels == NULL) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -0600355 /* TRANSLATORS: message letting the user know no device upgrade available
356 * %1 is the device name */
Mario Limonciello27164b72020-02-17 23:19:36 -0600357 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
358 fwupd_device_get_name (dev));
359 g_printerr ("%s\n", tmp);
360 /* discard the actual reason from user, but leave for debugging */
361 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600362 continue;
363 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500364 child = g_node_append_data (root, dev);
365
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600366 for (guint j = 0; j < rels->len; j++) {
367 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500368 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600369 }
370 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500371 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500372 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600373 /* save the device state for other applications to see */
374 if (!fu_util_save_current_state (priv, error))
375 return FALSE;
376
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600377 /* success */
378 return TRUE;
379}
380
381static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500382fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
383{
384 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500385 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600386 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500387 gint fd;
388
389 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000390 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500391 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600392 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500393
394 /* check args */
395 if (g_strv_length (values) != 1) {
396 g_set_error_literal (error,
397 FWUPD_ERROR,
398 FWUPD_ERROR_INVALID_ARGS,
399 "Invalid arguments");
400 return FALSE;
401 }
402
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600403 /* implied, important for get-details on a device not in your system */
404 priv->show_all_devices = TRUE;
405
Mario Limonciello716ab272018-05-29 12:34:37 -0500406 /* open file */
407 fd = open (values[0], O_RDONLY);
408 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500409 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500410 g_set_error (error,
411 FWUPD_ERROR,
412 FWUPD_ERROR_INVALID_FILE,
413 "failed to open %s",
414 values[0]);
415 return FALSE;
416 }
417 array = fu_engine_get_details (priv->engine, fd, error);
418 close (fd);
419
420 if (array == NULL)
421 return FALSE;
422 for (guint i = 0; i < array->len; i++) {
423 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600424 FwupdRelease *rel;
425 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100426 if (!fu_util_filter_device (priv, dev))
427 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600428 child = g_node_append_data (root, dev);
429 rel = fwupd_device_get_release_default (dev);
430 if (rel != NULL)
431 g_node_append_data (child, rel);
432
Mario Limonciello716ab272018-05-29 12:34:37 -0500433 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500434 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500435
Mario Limonciello716ab272018-05-29 12:34:37 -0500436 return TRUE;
437}
438
439static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100440fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
441{
442 g_autoptr(GString) str = g_string_new (NULL);
443
444 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
445 const gchar *tmp = fwupd_device_flag_to_string (i);
446 if (tmp == NULL)
447 break;
448 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
449 g_string_append (str, " ");
450 g_string_append (str, tmp);
451 g_string_append (str, " ~");
452 g_string_append (str, tmp);
453 }
454 g_print ("%s\n", str->str);
455
456 return TRUE;
457}
458
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500459static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100460fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500461{
462 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100463 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100464 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
465 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500466 if (!priv->show_all_devices &&
467 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500468 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100469 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500470 GNode *child = g_node_append_data (root, dev_tmp);
471 fu_util_build_device_tree (priv, child, devs, dev_tmp);
472 }
473 }
474}
475
476static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100477fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500478{
479 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600480 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500481 g_autoptr(GPtrArray) devs = NULL;
482
483 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000484 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500485 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600486 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500487
488 /* print */
489 devs = fu_engine_get_devices (priv->engine, error);
490 if (devs == NULL)
491 return FALSE;
492
493 /* print */
494 if (devs->len == 0) {
495 /* TRANSLATORS: nothing attached that can be upgraded */
496 g_print ("%s\n", _("No hardware detected with firmware update capability"));
497 return TRUE;
498 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000499 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500500 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500501 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500502
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100503 /* save the device state for other applications to see */
504 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500505}
506
Richard Hughes98ca9932018-05-18 10:24:07 +0100507static FuDevice *
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100508fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
Richard Hughes98ca9932018-05-18 10:24:07 +0100509{
510 FuDevice *dev;
511 guint idx;
512 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100513 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100514
515 /* get devices from daemon */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100516 if (devices_opt != NULL) {
517 devices = g_ptr_array_ref (devices_opt);
518 } else {
519 devices = fu_engine_get_devices (priv->engine, error);
520 if (devices == NULL)
521 return NULL;
522 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000523 fwupd_device_array_ensure_parents (devices);
Richard Hughes98ca9932018-05-18 10:24:07 +0100524
Richard Hughes747f5702019-08-06 14:27:26 +0100525 /* filter results */
526 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
527 for (guint i = 0; i < devices->len; i++) {
528 dev = g_ptr_array_index (devices, i);
529 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
530 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100531 g_ptr_array_add (devices_filtered, g_object_ref (dev));
532 }
533
534 /* nothing */
535 if (devices_filtered->len == 0) {
536 g_set_error_literal (error,
537 FWUPD_ERROR,
538 FWUPD_ERROR_NOTHING_TO_DO,
539 "No supported devices");
540 return NULL;
541 }
542
Richard Hughes98ca9932018-05-18 10:24:07 +0100543 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100544 if (devices_filtered->len == 1) {
545 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500546 /* TRANSLATORS: Device has been chosen by the daemon for the user */
547 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100548 return g_object_ref (dev);
549 }
550
551 /* TRANSLATORS: get interactive prompt */
552 g_print ("%s\n", _("Choose a device:"));
553 /* TRANSLATORS: this is to abort the interactive prompt */
554 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100555 for (guint i = 0; i < devices_filtered->len; i++) {
556 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100557 g_print ("%u.\t%s (%s)\n",
558 i + 1,
559 fu_device_get_id (dev),
560 fu_device_get_name (dev));
561 }
Richard Hughes747f5702019-08-06 14:27:26 +0100562 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100563 if (idx == 0) {
564 g_set_error_literal (error,
565 FWUPD_ERROR,
566 FWUPD_ERROR_NOTHING_TO_DO,
567 "Request canceled");
568 return NULL;
569 }
Richard Hughes747f5702019-08-06 14:27:26 +0100570 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100571 return g_object_ref (dev);
572}
573
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500574static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600575fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500576 FwupdDevice *device,
577 FuUtilPrivate *priv)
578{
579 g_autofree gchar *str = NULL;
580
Richard Hughes809abea2019-03-23 11:06:18 +0000581 /* allowed to set whenever the device has changed */
582 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
583 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
584 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
585 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
586
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500587 /* same as last time, so ignore */
588 if (priv->current_device != NULL &&
589 fwupd_device_compare (priv->current_device, device) == 0)
590 return;
591
Richard Hughesee562b52020-04-07 14:32:52 +0100592 /* ignore indirect devices that might have changed */
593 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
594 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
595 g_debug ("ignoring %s with status %s",
596 fwupd_device_get_name (device),
597 fwupd_status_to_string (fwupd_device_get_status (device)));
598 return;
599 }
600
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500601 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600602 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
603 /* TRANSLATORS: %1 is a device name */
604 str = g_strdup_printf (_("Updating %s…"),
605 fwupd_device_get_name (device));
606 fu_progressbar_set_title (priv->progressbar, str);
607 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
608 /* TRANSLATORS: %1 is a device name */
609 str = g_strdup_printf (_("Installing on %s…"),
610 fwupd_device_get_name (device));
611 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000612 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
613 /* TRANSLATORS: %1 is a device name */
614 str = g_strdup_printf (_("Reading from %s…"),
615 fwupd_device_get_name (device));
616 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600617 } else {
618 g_warning ("no FuUtilOperation set");
619 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500620 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600621
Mario Limonciello32241f42019-01-24 10:12:41 -0600622 if (priv->current_message == NULL) {
623 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
624 if (tmp != NULL)
625 priv->current_message = g_strdup (tmp);
626 }
627}
628
629static void
630fu_util_display_current_message (FuUtilPrivate *priv)
631{
632 if (priv->current_message == NULL)
633 return;
634 g_print ("%s\n", priv->current_message);
635 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500636}
637
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100638static FuDevice *
639fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
640{
641 if (fwupd_guid_is_valid (id)) {
642 g_autoptr(GPtrArray) devices = NULL;
643 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
644 if (devices == NULL)
645 return NULL;
646 return fu_util_prompt_for_device (priv, devices, error);
647 }
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500648
649 /* did this look like a GUID? */
650 for (guint i = 0; id[i] != '\0'; i++) {
651 if (id[i] == '-') {
652 g_set_error_literal (error,
653 FWUPD_ERROR,
654 FWUPD_ERROR_INVALID_ARGS,
655 "Invalid arguments");
Richard Hughes9c09e2c2020-04-22 10:52:42 +0100656 return NULL;
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500657 }
658 }
Mario Limoncielloaac9c742020-04-20 13:38:44 -0500659 return fu_engine_get_device (priv->engine, id, error);
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100660}
661
Richard Hughes98ca9932018-05-18 10:24:07 +0100662static gboolean
663fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
664{
665 g_autoptr(FuDevice) device = NULL;
666 g_autoptr(GBytes) blob_fw = NULL;
667
668 /* invalid args */
669 if (g_strv_length (values) == 0) {
670 g_set_error_literal (error,
671 FWUPD_ERROR,
672 FWUPD_ERROR_INVALID_ARGS,
673 "Invalid arguments");
674 return FALSE;
675 }
676
677 /* parse blob */
678 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500679 if (blob_fw == NULL) {
680 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100681 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500682 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100683
684 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000685 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100686 return FALSE;
687
688 /* get device */
689 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100690 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100691 if (device == NULL)
692 return FALSE;
693 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100694 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100695 if (device == NULL)
696 return FALSE;
697 }
698
Mario Limonciello3f243a92019-01-21 22:05:23 -0600699 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500700 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600701 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500702
Richard Hughes98ca9932018-05-18 10:24:07 +0100703 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000704 if (priv->prepare_blob) {
705 g_autoptr(GPtrArray) devices = NULL;
706 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
707 g_ptr_array_add (devices, g_object_ref (device));
708 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
709 g_prefix_error (error, "failed to prepare composite action: ");
710 return FALSE;
711 }
712 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100713 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000714 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600715 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000716 if (priv->cleanup_blob) {
717 g_autoptr(FuDevice) device_new = NULL;
718 g_autoptr(GError) error_local = NULL;
719
720 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100721 device_new = fu_util_get_device (priv,
722 fu_device_get_id (device),
723 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000724 if (device_new == NULL) {
725 g_debug ("failed to find new device: %s",
726 error_local->message);
727 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600728 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000729 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
730 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
731 g_prefix_error (error, "failed to cleanup composite action: ");
732 return FALSE;
733 }
734 }
735 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600736
Mario Limonciello32241f42019-01-24 10:12:41 -0600737 fu_util_display_current_message (priv);
738
Mario Limonciello3f243a92019-01-21 22:05:23 -0600739 /* success */
740 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100741}
742
Richard Hughesa58510b2019-10-30 10:03:12 +0000743static gboolean
744fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
745{
746 g_autoptr(FuDevice) device = NULL;
747 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
748 g_autoptr(GBytes) blob_fw = NULL;
749
750 /* invalid args */
751 if (g_strv_length (values) == 0) {
752 g_set_error_literal (error,
753 FWUPD_ERROR,
754 FWUPD_ERROR_INVALID_ARGS,
755 "Invalid arguments");
756 return FALSE;
757 }
758
759 /* file already exists */
760 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
761 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
762 g_set_error_literal (error,
763 FWUPD_ERROR,
764 FWUPD_ERROR_INVALID_ARGS,
765 "Filename already exists");
766 return FALSE;
767 }
768
Richard Hughes02792c02019-11-01 14:21:20 +0000769 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000770 * avoid failing at the end of a potentially lengthy operation */
771 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
772 return FALSE;
773
774 /* load engine */
775 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
776 return FALSE;
777
778 /* get device */
779 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100780 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000781 if (device == NULL)
782 return FALSE;
783 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100784 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000785 if (device == NULL)
786 return FALSE;
787 }
788 priv->current_operation = FU_UTIL_OPERATION_READ;
789 g_signal_connect (priv->engine, "device-changed",
790 G_CALLBACK (fu_util_update_device_changed_cb), priv);
791
792 /* dump firmware */
793 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
794 if (blob_fw == NULL)
795 return FALSE;
796 return fu_common_set_contents_bytes (values[0], blob_fw, error);
797}
798
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100799static gint
800fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
801{
802 FuInstallTask *task1 = *((FuInstallTask **) a);
803 FuInstallTask *task2 = *((FuInstallTask **) b);
804 return fu_install_task_compare (task1, task2);
805}
806
807static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100808fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
809{
Filipe Laínse0914272019-09-20 10:04:43 +0100810 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100811 { "curl", uri, "--output", fn, NULL },
812 { NULL } };
813 for (guint i = 0; argv[i][0] != NULL; i++) {
814 g_autoptr(GError) error_local = NULL;
815 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
816 g_debug ("%s", error_local->message);
817 continue;
818 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000819 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100820 }
821 g_set_error_literal (error,
822 FWUPD_ERROR,
823 FWUPD_ERROR_NOT_FOUND,
824 "no supported out-of-process downloaders found");
825 return FALSE;
826}
827
828static gchar *
829fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
830{
831 g_autofree gchar *filename = NULL;
832 g_autoptr(SoupURI) uri = NULL;
833
834 /* a local file */
835 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000836 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
837 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100838 if (uri == NULL)
839 return g_strdup (perhapsfn);
840
841 /* download the firmware to a cachedir */
842 filename = fu_util_get_user_cache_path (perhapsfn);
843 if (!fu_common_mkdir_parent (filename, error))
844 return NULL;
845 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
846 return NULL;
847 return g_steal_pointer (&filename);
848}
849
850static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100851fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
852{
Richard Hughes3d178be2018-08-30 11:14:24 +0100853 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100854 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100855 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100856 g_autoptr(GPtrArray) devices_possible = NULL;
857 g_autoptr(GPtrArray) errors = NULL;
858 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100859 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100860
Mario Limonciello8949e892018-05-25 08:03:06 -0500861 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000862 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500863 return FALSE;
864
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100865 /* handle both forms */
866 if (g_strv_length (values) == 1) {
867 devices_possible = fu_engine_get_devices (priv->engine, error);
868 if (devices_possible == NULL)
869 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000870 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100871 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100872 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100873 if (device == NULL)
874 return FALSE;
875 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
876 g_ptr_array_add (devices_possible, device);
877 } else {
878 g_set_error_literal (error,
879 FWUPD_ERROR,
880 FWUPD_ERROR_INVALID_ARGS,
881 "Invalid arguments");
882 return FALSE;
883 }
884
Richard Hughes3d178be2018-08-30 11:14:24 +0100885 /* download if required */
886 filename = fu_util_download_if_required (priv, values[0], error);
887 if (filename == NULL)
888 return FALSE;
889
Richard Hughes481aa2a2018-09-18 20:51:46 +0100890 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100891 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500892 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100893 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100894 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500895 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100896 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
897 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100898 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600899 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100900 if (components == NULL)
901 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100902
Richard Hughes481aa2a2018-09-18 20:51:46 +0100903 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100904 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
905 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100906 for (guint i = 0; i < components->len; i++) {
907 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100908
909 /* do any devices pass the requirements */
910 for (guint j = 0; j < devices_possible->len; j++) {
911 FuDevice *device = g_ptr_array_index (devices_possible, j);
912 g_autoptr(FuInstallTask) task = NULL;
913 g_autoptr(GError) error_local = NULL;
914
915 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100916 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100917 if (!fu_engine_check_requirements (priv->engine,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500918 task, priv->flags | FWUPD_INSTALL_FLAG_FORCE,
919 &error_local)) {
920 g_debug ("first pass requirement on %s:%s failed: %s",
921 fu_device_get_id (device),
922 xb_node_query_text (component, "id", NULL),
923 error_local->message);
924 g_ptr_array_add (errors, g_steal_pointer (&error_local));
925 continue;
926 }
927
928 /* make a second pass using possibly updated version format now */
929 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
930 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100931 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100932 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500933 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100934 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100935 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100936 error_local->message);
937 g_ptr_array_add (errors, g_steal_pointer (&error_local));
938 continue;
939 }
940
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600941 /* if component should have an update message from CAB */
942 fu_device_incorporate_from_component (device, component);
943
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100944 /* success */
945 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
946 }
947 }
948
949 /* order the install tasks by the device priority */
950 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
951
952 /* nothing suitable */
953 if (install_tasks->len == 0) {
954 GError *error_tmp = fu_common_error_array_get_best (errors);
955 g_propagate_error (error, error_tmp);
956 return FALSE;
957 }
958
Mario Limonciello3f243a92019-01-21 22:05:23 -0600959 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500960 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600961 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500962
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100963 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100964 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
965 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100966
Mario Limonciello32241f42019-01-24 10:12:41 -0600967 fu_util_display_current_message (priv);
968
Mario Limonciello3f243a92019-01-21 22:05:23 -0600969 /* we don't want to ask anything */
970 if (priv->no_reboot_check) {
971 g_debug ("skipping reboot check");
972 return TRUE;
973 }
974
Mario Limonciello3143bad2019-02-27 07:31:00 -0600975 /* save the device state for other applications to see */
976 if (!fu_util_save_current_state (priv, error))
977 return FALSE;
978
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100979 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600980 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100981}
982
Richard Hughes98ca9932018-05-18 10:24:07 +0100983static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500984fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600985{
Mario Limonciellofd734852019-08-01 16:41:42 -0500986 FwupdRemote *remote;
987 const gchar *remote_id;
988 const gchar *uri_tmp;
989 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600990
Mario Limonciellofd734852019-08-01 16:41:42 -0500991 uri_tmp = fwupd_release_get_uri (rel);
992 if (uri_tmp == NULL) {
993 g_set_error_literal (error,
994 FWUPD_ERROR,
995 FWUPD_ERROR_INVALID_FILE,
996 "release missing URI");
997 return FALSE;
998 }
999 remote_id = fwupd_release_get_remote_id (rel);
1000 if (remote_id == NULL) {
1001 g_set_error (error,
1002 FWUPD_ERROR,
1003 FWUPD_ERROR_INVALID_FILE,
1004 "failed to find remote for %s",
1005 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001006 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001007 }
1008
1009 remote = fu_engine_get_remote_by_id (priv->engine,
1010 remote_id,
1011 error);
1012 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001013 return FALSE;
1014
Mario Limonciellofd734852019-08-01 16:41:42 -05001015 argv = g_new0 (gchar *, 2);
1016 /* local remotes have the firmware already */
1017 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
1018 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1019 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1020 argv[0] = g_build_filename (path, uri_tmp, NULL);
1021 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1022 argv[0] = g_strdup (uri_tmp + 7);
1023 /* web remote, fu_util_install will download file */
1024 } else {
1025 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1026 }
1027 return fu_util_install (priv, argv, error);
1028}
1029
1030static gboolean
1031fu_util_update_all (FuUtilPrivate *priv, GError **error)
1032{
1033 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001034
Mario Limonciello46aaee82019-01-10 12:58:00 -06001035 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001036 if (devices == NULL)
1037 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001038 fwupd_device_array_ensure_parents (devices);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001039 for (guint i = 0; i < devices->len; i++) {
1040 FwupdDevice *dev = g_ptr_array_index (devices, i);
1041 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001042 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001043 g_autoptr(GPtrArray) rels = NULL;
1044 g_autoptr(GError) error_local = NULL;
1045
1046 if (!fu_util_is_interesting_device (dev))
1047 continue;
1048 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001049 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001050 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001051 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
1052 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
1053 * %1 is the device name */
1054 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
1055 fwupd_device_get_name (dev));
1056 g_printerr ("%s\n", tmp);
1057 continue;
1058 }
Richard Hughes747f5702019-08-06 14:27:26 +01001059 if (!fu_util_filter_device (priv, dev))
1060 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001061
1062 device_id = fu_device_get_id (dev);
1063 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
1064 if (rels == NULL) {
Mario Limonciello27164b72020-02-17 23:19:36 -06001065 /* TRANSLATORS: message letting the user know no device upgrade available
1066 * %1 is the device name */
1067 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
1068 fwupd_device_get_name (dev));
1069 g_printerr ("%s\n", tmp);
1070 /* discard the actual reason from user, but leave for debugging */
1071 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001072 continue;
1073 }
1074
Mario Limonciello98b95162019-10-30 09:20:43 -05001075 if (!priv->no_safety_check) {
1076 if (!fu_util_prompt_warning (dev,
1077 fu_util_get_tree_title (priv),
1078 error))
1079 return FALSE;
1080 }
1081
Mario Limonciello46aaee82019-01-10 12:58:00 -06001082 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001083 if (!fu_util_install_release (priv, rel, &error_local)) {
1084 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001085 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001086 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001087 fu_util_display_current_message (priv);
1088 }
1089 return TRUE;
1090}
1091
1092static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001093fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001094{
1095 FwupdRelease *rel;
1096 g_autoptr(FuDevice) dev = NULL;
1097 g_autoptr(GPtrArray) rels = NULL;
1098
Mario Limonciello9917bb42020-04-20 13:41:27 -05001099 /* do not allow a partial device-id, lookup GUIDs */
1100 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001101 if (dev == NULL)
1102 return FALSE;
1103
1104 /* get the releases for this device and filter for validity */
Mario Limonciello9917bb42020-04-20 13:41:27 -05001105 rels = fu_engine_get_upgrades (priv->engine, fu_device_get_id (dev), error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001106 if (rels == NULL)
1107 return FALSE;
1108 rel = g_ptr_array_index (rels, 0);
1109 if (!fu_util_install_release (priv, rel, error))
1110 return FALSE;
1111 fu_util_display_current_message (priv);
1112
1113 return TRUE;
1114}
1115
1116static gboolean
1117fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1118{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001119 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1120 g_set_error_literal (error,
1121 FWUPD_ERROR,
1122 FWUPD_ERROR_INVALID_ARGS,
1123 "--allow-older is not supported for this command");
1124 return FALSE;
1125 }
1126
1127 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1128 g_set_error_literal (error,
1129 FWUPD_ERROR,
1130 FWUPD_ERROR_INVALID_ARGS,
1131 "--allow-reinstall is not supported for this command");
1132 return FALSE;
1133 }
1134
Mario Limonciellofd734852019-08-01 16:41:42 -05001135 if (g_strv_length (values) > 1) {
1136 g_set_error_literal (error,
1137 FWUPD_ERROR,
1138 FWUPD_ERROR_INVALID_ARGS,
1139 "Invalid arguments");
1140 return FALSE;
1141 }
1142
1143 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1144 return FALSE;
1145
1146 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1147 g_signal_connect (priv->engine, "device-changed",
1148 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1149
1150 if (g_strv_length (values) == 1) {
1151 if (!fu_util_update_by_id (priv, values[0], error))
1152 return FALSE;
1153 } else {
1154 if (!fu_util_update_all (priv, error))
1155 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001156 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001157
1158 /* we don't want to ask anything */
1159 if (priv->no_reboot_check) {
1160 g_debug ("skipping reboot check");
1161 return TRUE;
1162 }
1163
Mario Limonciello3143bad2019-02-27 07:31:00 -06001164 /* save the device state for other applications to see */
1165 if (!fu_util_save_current_state (priv, error))
1166 return FALSE;
1167
Mario Limonciello3f243a92019-01-21 22:05:23 -06001168 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001169}
1170
1171static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001172fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1173{
1174 g_autoptr(FwupdRelease) rel = NULL;
1175 g_autoptr(GPtrArray) rels = NULL;
1176 g_autoptr(FuDevice) dev = NULL;
1177
1178 if (g_strv_length (values) != 1) {
1179 g_set_error_literal (error,
1180 FWUPD_ERROR,
1181 FWUPD_ERROR_INVALID_ARGS,
1182 "Invalid arguments");
1183 return FALSE;
1184 }
1185
1186 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1187 return FALSE;
1188
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001189 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001190 if (dev == NULL)
1191 return FALSE;
1192
1193 /* try to lookup/match release from client */
1194 rels = fu_engine_get_releases_for_device (priv->engine, dev, error);
1195 if (rels == NULL)
1196 return FALSE;
1197
1198 for (guint j = 0; j < rels->len; j++) {
1199 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1200 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1201 fu_device_get_version (dev),
1202 fu_device_get_version_format (dev)) == 0) {
1203 rel = g_object_ref (rel_tmp);
1204 break;
1205 }
1206 }
1207 if (rel == NULL) {
1208 g_set_error (error,
1209 FWUPD_ERROR,
1210 FWUPD_ERROR_NOT_SUPPORTED,
1211 "Unable to locate release for %s version %s",
1212 fu_device_get_name (dev),
1213 fu_device_get_version (dev));
1214 return FALSE;
1215 }
1216
1217 /* update the console if composite devices are also updated */
1218 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1219 g_signal_connect (priv->engine, "device-changed",
1220 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1221 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1222 if (!fu_util_install_release (priv, rel, error))
1223 return FALSE;
1224 fu_util_display_current_message (priv);
1225
1226 /* we don't want to ask anything */
1227 if (priv->no_reboot_check) {
1228 g_debug ("skipping reboot check");
1229 return TRUE;
1230 }
1231
1232 /* save the device state for other applications to see */
1233 if (!fu_util_save_current_state (priv, error))
1234 return FALSE;
1235
1236 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1237}
1238
1239static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001240fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1241{
1242 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001243 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001244
1245 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001246 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001247 return FALSE;
1248
Richard Hughes98ca9932018-05-18 10:24:07 +01001249 /* get device */
1250 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001251 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001252 if (device == NULL)
1253 return FALSE;
1254 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001255 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001256 if (device == NULL)
1257 return FALSE;
1258 }
1259
1260 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001261 locker = fu_device_locker_new (device, error);
1262 if (locker == NULL)
1263 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001264 return fu_device_detach (device, error);
1265}
1266
1267static gboolean
1268fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1269{
1270 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001271 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001272
1273 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001274 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001275 return FALSE;
1276
Richard Hughes98ca9932018-05-18 10:24:07 +01001277 /* get device */
1278 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001279 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001280 if (device == NULL)
1281 return FALSE;
1282 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001283 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001284 if (device == NULL)
1285 return FALSE;
1286 }
1287
1288 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001289 locker = fu_device_locker_new (device, error);
1290 if (locker == NULL)
1291 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001292 return fu_device_attach (device, error);
1293}
1294
Richard Hughes1d894f12018-08-31 13:05:51 +01001295static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001296fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1297{
1298 gboolean has_pending = FALSE;
1299 g_autoptr(FuHistory) history = fu_history_new ();
1300 g_autoptr(GPtrArray) devices = NULL;
1301
1302 /* check the history database before starting the daemon */
1303 if (g_strv_length (values) == 0) {
1304 devices = fu_history_get_devices (history, error);
1305 if (devices == NULL)
1306 return FALSE;
1307 } else if (g_strv_length (values) == 1) {
1308 FuDevice *device;
1309 device = fu_history_get_device_by_id (history, values[0], error);
1310 if (device == NULL)
1311 return FALSE;
1312 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1313 g_ptr_array_add (devices, device);
1314 } else {
1315 g_set_error_literal (error,
1316 FWUPD_ERROR,
1317 FWUPD_ERROR_INVALID_ARGS,
1318 "Invalid arguments");
1319 return FALSE;
1320 }
1321
1322 /* nothing to do */
1323 for (guint i = 0; i < devices->len; i++) {
1324 FuDevice *dev = g_ptr_array_index (devices, i);
1325 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1326 fu_engine_add_plugin_filter (priv->engine,
1327 fu_device_get_plugin (dev));
1328 has_pending = TRUE;
1329 }
1330 }
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001331
dkadioglu1db7e982019-12-04 21:13:21 +01001332 if (!has_pending) {
1333 g_printerr ("No firmware to activate\n");
1334 return TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001335 }
1336
1337 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001338 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001339 return FALSE;
1340
1341 /* activate anything with _NEEDS_ACTIVATION */
1342 for (guint i = 0; i < devices->len; i++) {
1343 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001344 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1345 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001346 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1347 continue;
1348 /* TRANSLATORS: shown when shutting down to switch to the new version */
1349 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1350 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1351 return FALSE;
1352 }
1353
1354 return TRUE;
1355}
1356
1357static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001358fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1359{
1360 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1361 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1362 const gchar *hwid_keys[] = {
1363 FU_HWIDS_KEY_BIOS_VENDOR,
1364 FU_HWIDS_KEY_BIOS_VERSION,
1365 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1366 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1367 FU_HWIDS_KEY_MANUFACTURER,
1368 FU_HWIDS_KEY_FAMILY,
1369 FU_HWIDS_KEY_PRODUCT_NAME,
1370 FU_HWIDS_KEY_PRODUCT_SKU,
1371 FU_HWIDS_KEY_ENCLOSURE_KIND,
1372 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1373 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1374 NULL };
1375
1376 /* read DMI data */
1377 if (g_strv_length (values) == 0) {
1378 if (!fu_smbios_setup (smbios, error))
1379 return FALSE;
1380 } else if (g_strv_length (values) == 1) {
1381 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1382 return FALSE;
1383 } else {
1384 g_set_error_literal (error,
1385 FWUPD_ERROR,
1386 FWUPD_ERROR_INVALID_ARGS,
1387 "Invalid arguments");
1388 return FALSE;
1389 }
1390 if (!fu_hwids_setup (hwids, smbios, error))
1391 return FALSE;
1392
1393 /* show debug output */
1394 g_print ("Computer Information\n");
1395 g_print ("--------------------\n");
1396 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1397 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1398 if (tmp == NULL)
1399 continue;
1400 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1401 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1402 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1403 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1404 } else {
1405 g_print ("%s: %s\n", hwid_keys[i], tmp);
1406 }
1407 }
1408
1409 /* show GUIDs */
1410 g_print ("\nHardware IDs\n");
1411 g_print ("------------\n");
1412 for (guint i = 0; i < 15; i++) {
1413 const gchar *keys = NULL;
1414 g_autofree gchar *guid = NULL;
1415 g_autofree gchar *key = NULL;
1416 g_autofree gchar *keys_str = NULL;
1417 g_auto(GStrv) keysv = NULL;
1418 g_autoptr(GError) error_local = NULL;
1419
1420 /* get the GUID */
1421 key = g_strdup_printf ("HardwareID-%u", i);
1422 keys = fu_hwids_get_replace_keys (hwids, key);
1423 guid = fu_hwids_get_guid (hwids, key, &error_local);
1424 if (guid == NULL) {
1425 g_print ("%s\n", error_local->message);
1426 continue;
1427 }
1428
1429 /* show what makes up the GUID */
1430 keysv = g_strsplit (keys, "&", -1);
1431 keys_str = g_strjoinv (" + ", keysv);
1432 g_print ("{%s} <- %s\n", guid, keys_str);
1433 }
1434
1435 return TRUE;
1436}
1437
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001438static gboolean
1439fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1440{
1441 const gchar *script_fn = "startup.sh";
1442 const gchar *output_fn = "firmware.bin";
1443 g_autoptr(GBytes) archive_blob = NULL;
1444 g_autoptr(GBytes) firmware_blob = NULL;
1445 if (g_strv_length (values) < 2) {
1446 g_set_error_literal (error,
1447 FWUPD_ERROR,
1448 FWUPD_ERROR_INVALID_ARGS,
1449 "Invalid arguments");
1450 return FALSE;
1451 }
1452 archive_blob = fu_common_get_contents_bytes (values[0], error);
1453 if (archive_blob == NULL)
1454 return FALSE;
1455 if (g_strv_length (values) > 2)
1456 script_fn = values[2];
1457 if (g_strv_length (values) > 3)
1458 output_fn = values[3];
1459 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1460 if (firmware_blob == NULL)
1461 return FALSE;
1462 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1463}
1464
Richard Hughes3d607622019-03-07 16:59:27 +00001465static gboolean
1466fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1467{
1468 g_autofree gchar *sig = NULL;
1469
1470 /* check args */
1471 if (g_strv_length (values) != 1) {
1472 g_set_error_literal (error,
1473 FWUPD_ERROR,
1474 FWUPD_ERROR_INVALID_ARGS,
1475 "Invalid arguments: value expected");
1476 return FALSE;
1477 }
1478
1479 /* start engine */
1480 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1481 return FALSE;
1482 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001483 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1484 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001485 if (sig == NULL)
1486 return FALSE;
1487 g_print ("%s\n", sig);
1488 return TRUE;
1489}
1490
Mario Limonciello62f84862018-10-18 13:15:23 -05001491static void
1492fu_util_device_added_cb (FwupdClient *client,
1493 FwupdDevice *device,
1494 gpointer user_data)
1495{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001496 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001497 /* TRANSLATORS: this is when a device is hotplugged */
1498 g_print ("%s\n%s", _("Device added:"), tmp);
1499}
1500
1501static void
1502fu_util_device_removed_cb (FwupdClient *client,
1503 FwupdDevice *device,
1504 gpointer user_data)
1505{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001506 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001507 /* TRANSLATORS: this is when a device is hotplugged */
1508 g_print ("%s\n%s", _("Device removed:"), tmp);
1509}
1510
1511static void
1512fu_util_device_changed_cb (FwupdClient *client,
1513 FwupdDevice *device,
1514 gpointer user_data)
1515{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001516 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001517 /* TRANSLATORS: this is when a device has been updated */
1518 g_print ("%s\n%s", _("Device changed:"), tmp);
1519}
1520
1521static void
1522fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1523{
1524 /* TRANSLATORS: this is when the daemon state changes */
1525 g_print ("%s\n", _("Changed"));
1526}
1527
1528static gboolean
1529fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1530{
1531 g_autoptr(FwupdClient) client = fwupd_client_new ();
1532
1533 /* get all the devices */
1534 if (!fwupd_client_connect (client, priv->cancellable, error))
1535 return FALSE;
1536
1537 /* watch for any hotplugged device */
1538 g_signal_connect (client, "changed",
1539 G_CALLBACK (fu_util_changed_cb), priv);
1540 g_signal_connect (client, "device-added",
1541 G_CALLBACK (fu_util_device_added_cb), priv);
1542 g_signal_connect (client, "device-removed",
1543 G_CALLBACK (fu_util_device_removed_cb), priv);
1544 g_signal_connect (client, "device-changed",
1545 G_CALLBACK (fu_util_device_changed_cb), priv);
1546 g_signal_connect (priv->cancellable, "cancelled",
1547 G_CALLBACK (fu_util_cancelled_cb), priv);
1548 g_main_loop_run (priv->loop);
1549 return TRUE;
1550}
1551
Richard Hughes15684492019-03-15 16:27:50 +00001552static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001553fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1554{
1555 g_autoptr(GPtrArray) firmware_types = NULL;
1556
1557 /* load engine */
1558 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1559 return FALSE;
1560
1561 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1562 for (guint i = 0; i < firmware_types->len; i++) {
1563 const gchar *id = g_ptr_array_index (firmware_types, i);
1564 g_print ("%s\n", id);
1565 }
1566 if (firmware_types->len == 0) {
1567 /* TRANSLATORS: nothing found */
1568 g_print ("%s\n", _("No firmware IDs found"));
1569 return TRUE;
1570 }
1571
1572 return TRUE;
1573}
1574
1575static gchar *
1576fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1577{
1578 g_autoptr(GPtrArray) firmware_types = NULL;
1579 guint idx;
1580 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1581
1582 /* TRANSLATORS: get interactive prompt */
1583 g_print ("%s\n", _("Choose a firmware type:"));
1584 /* TRANSLATORS: this is to abort the interactive prompt */
1585 g_print ("0.\t%s\n", _("Cancel"));
1586 for (guint i = 0; i < firmware_types->len; i++) {
1587 const gchar *id = g_ptr_array_index (firmware_types, i);
1588 g_print ("%u.\t%s\n", i + 1, id);
1589 }
1590 idx = fu_util_prompt_for_number (firmware_types->len);
1591 if (idx == 0) {
1592 g_set_error_literal (error,
1593 FWUPD_ERROR,
1594 FWUPD_ERROR_NOTHING_TO_DO,
1595 "Request canceled");
1596 return NULL;
1597 }
1598
1599 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1600}
1601
1602static gboolean
1603fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1604{
1605 GType gtype;
1606 g_autoptr(GBytes) blob = NULL;
1607 g_autoptr(FuFirmware) firmware = NULL;
1608 g_autofree gchar *firmware_type = NULL;
1609 g_autofree gchar *str = NULL;
1610
1611 /* check args */
1612 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1613 g_set_error_literal (error,
1614 FWUPD_ERROR,
1615 FWUPD_ERROR_INVALID_ARGS,
1616 "Invalid arguments: filename required");
1617 return FALSE;
1618 }
1619
1620 if (g_strv_length (values) == 2)
1621 firmware_type = g_strdup (values[1]);
1622
1623 /* load file */
1624 blob = fu_common_get_contents_bytes (values[0], error);
1625 if (blob == NULL)
1626 return FALSE;
1627
1628 /* load engine */
1629 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1630 return FALSE;
1631
1632 /* find the GType to use */
1633 if (firmware_type == NULL)
1634 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1635 if (firmware_type == NULL)
1636 return FALSE;
1637 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1638 if (gtype == G_TYPE_INVALID) {
1639 g_set_error (error,
1640 G_IO_ERROR,
1641 G_IO_ERROR_NOT_FOUND,
1642 "GType %s not supported", firmware_type);
1643 return FALSE;
1644 }
1645 firmware = g_object_new (gtype, NULL);
1646 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1647 return FALSE;
1648 str = fu_firmware_to_string (firmware);
1649 g_print ("%s", str);
1650 return TRUE;
1651}
1652
1653static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00001654fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
1655{
1656 GType gtype_dst;
1657 GType gtype_src;
1658 g_autofree gchar *firmware_type_dst = NULL;
1659 g_autofree gchar *firmware_type_src = NULL;
1660 g_autofree gchar *str_dst = NULL;
1661 g_autofree gchar *str_src = NULL;
1662 g_autoptr(FuFirmware) firmware_dst = NULL;
1663 g_autoptr(FuFirmware) firmware_src = NULL;
1664 g_autoptr(GBytes) blob_dst = NULL;
1665 g_autoptr(GBytes) blob_src = NULL;
1666 g_autoptr(GPtrArray) images = NULL;
1667
1668 /* check args */
1669 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
1670 g_set_error_literal (error,
1671 FWUPD_ERROR,
1672 FWUPD_ERROR_INVALID_ARGS,
1673 "Invalid arguments: filename required");
1674 return FALSE;
1675 }
1676
1677 if (g_strv_length (values) > 2)
1678 firmware_type_src = g_strdup (values[2]);
1679 if (g_strv_length (values) > 3)
1680 firmware_type_dst = g_strdup (values[3]);
1681
1682 /* load file */
1683 blob_src = fu_common_get_contents_bytes (values[0], error);
1684 if (blob_src == NULL)
1685 return FALSE;
1686
1687 /* load engine */
1688 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1689 return FALSE;
1690
1691 /* find the GType to use */
1692 if (firmware_type_src == NULL)
1693 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
1694 if (firmware_type_src == NULL)
1695 return FALSE;
1696 if (firmware_type_dst == NULL)
1697 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
1698 if (firmware_type_dst == NULL)
1699 return FALSE;
1700 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
1701 if (gtype_src == G_TYPE_INVALID) {
1702 g_set_error (error,
1703 G_IO_ERROR,
1704 G_IO_ERROR_NOT_FOUND,
1705 "GType %s not supported", firmware_type_src);
1706 return FALSE;
1707 }
1708 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
1709 if (gtype_dst == G_TYPE_INVALID) {
1710 g_set_error (error,
1711 G_IO_ERROR,
1712 G_IO_ERROR_NOT_FOUND,
1713 "GType %s not supported", firmware_type_dst);
1714 return FALSE;
1715 }
1716 firmware_src = g_object_new (gtype_src, NULL);
1717 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
1718 return FALSE;
1719 str_src = fu_firmware_to_string (firmware_src);
1720 g_print ("%s", str_src);
1721
1722 /* copy images */
1723 firmware_dst = g_object_new (gtype_dst, NULL);
1724 images = fu_firmware_get_images (firmware_src);
1725 for (guint i = 0; i < images->len; i++) {
1726 FuFirmwareImage *img = g_ptr_array_index (images, i);
1727 fu_firmware_add_image (firmware_dst, img);
1728 }
1729
1730 /* write new file */
1731 blob_dst = fu_firmware_write (firmware_dst, error);
1732 if (blob_dst == NULL)
1733 return FALSE;
1734 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
1735 return FALSE;
1736 str_dst = fu_firmware_to_string (firmware_dst);
1737 g_print ("%s", str_dst);
1738
1739 /* success */
1740 return TRUE;
1741}
1742
1743static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001744fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1745{
1746 g_autofree gchar *str = NULL;
1747 g_autoptr(FuDevice) dev = NULL;
1748
1749 /* load engine */
1750 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1751 return FALSE;
1752
1753 /* get device */
1754 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001755 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00001756 if (dev == NULL)
1757 return FALSE;
1758 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001759 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00001760 if (dev == NULL)
1761 return FALSE;
1762 }
1763
1764 /* add checksums */
1765 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1766 return FALSE;
1767
1768 /* show checksums */
1769 str = fu_device_to_string (dev);
1770 g_print ("%s\n", str);
1771 return TRUE;
1772}
1773
Mario Limonciellofe593942019-04-03 13:48:52 -05001774static gboolean
1775fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1776{
1777 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001778 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001779 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001780
1781 /* load engine */
1782 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1783 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001784 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001785
1786 /* get all devices from the history database */
1787 devices = fu_engine_get_history (priv->engine, error);
1788 if (devices == NULL)
1789 return FALSE;
1790
1791 /* show each device */
1792 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001793 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001794 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001795 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001796 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001797 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001798
Richard Hughes747f5702019-08-06 14:27:26 +01001799 if (!fu_util_filter_device (priv, dev))
1800 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001801 child = g_node_append_data (root, dev);
1802
1803 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001804 if (rel == NULL)
1805 continue;
1806 remote = fwupd_release_get_remote_id (rel);
1807
1808 /* doesn't actually map to remote */
1809 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001810 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001811 continue;
1812 }
1813
1814 /* try to lookup releases from client */
1815 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1816 if (rels == NULL)
1817 return FALSE;
1818
1819 /* map to a release in client */
1820 for (guint j = 0; j < rels->len; j++) {
1821 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1822 if (g_strcmp0 (remote,
1823 fwupd_release_get_remote_id (rel2)) != 0)
1824 continue;
1825 if (g_strcmp0 (fwupd_release_get_version (rel),
1826 fwupd_release_get_version (rel2)) != 0)
1827 continue;
1828 g_node_append_data (child, g_object_ref (rel2));
1829 rel = NULL;
1830 break;
1831 }
1832
1833 /* didn't match anything */
1834 if (rels->len == 0 || rel != NULL) {
1835 g_node_append_data (child, rel);
1836 continue;
1837 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001838
Mario Limonciellofe593942019-04-03 13:48:52 -05001839 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001840 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001841
1842 return TRUE;
1843}
1844
Richard Hughesfd7e9942020-01-17 14:09:13 +00001845static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00001846fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
1847{
1848 g_autofree gchar *fn_raw = NULL;
1849 g_autofree gchar *fn_sig = NULL;
1850 g_autoptr(GBytes) bytes_raw = NULL;
1851 g_autoptr(GBytes) bytes_sig = NULL;
1852
1853 /* payload */
1854 fn_raw = fu_util_get_user_cache_path (fwupd_remote_get_metadata_uri (remote));
1855 if (!fu_common_mkdir_parent (fn_raw, error))
1856 return FALSE;
1857 if (!fu_util_download_out_of_process (fwupd_remote_get_metadata_uri (remote),
1858 fn_raw, error))
1859 return FALSE;
1860 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
1861 if (bytes_raw == NULL)
1862 return FALSE;
1863
1864 /* signature */
1865 fn_sig = fu_util_get_user_cache_path (fwupd_remote_get_metadata_uri_sig (remote));
1866 if (!fu_util_download_out_of_process (fwupd_remote_get_metadata_uri_sig (remote),
1867 fn_sig, error))
1868 return FALSE;
1869 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
1870 if (bytes_sig == NULL)
1871 return FALSE;
1872
1873 /* send to daemon */
1874 g_debug ("updating %s", fwupd_remote_get_id (remote));
1875 return fu_engine_update_metadata_bytes (priv->engine,
1876 fwupd_remote_get_id (remote),
1877 bytes_raw,
1878 bytes_sig,
1879 error);
1880
1881}
1882
1883static gboolean
1884fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
1885{
1886 g_autoptr(GPtrArray) remotes = NULL;
1887
1888 /* load engine */
1889 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1890 return FALSE;
1891
1892 /* download new metadata */
1893 remotes = fu_engine_get_remotes (priv->engine, error);
1894 if (remotes == NULL)
1895 return FALSE;
1896 for (guint i = 0; i < remotes->len; i++) {
1897 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1898 if (!fwupd_remote_get_enabled (remote))
1899 continue;
1900 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
1901 continue;
1902 if (!fu_util_refresh_remote (priv, remote, error))
1903 return FALSE;
1904 }
1905 return TRUE;
1906}
1907
1908static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00001909fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
1910{
1911 g_autoptr(GNode) root = g_node_new (NULL);
1912 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001913 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00001914
1915 /* load engine */
1916 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1917 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001918 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00001919
1920 /* list remotes */
1921 remotes = fu_engine_get_remotes (priv->engine, error);
1922 if (remotes == NULL)
1923 return FALSE;
1924 if (remotes->len == 0) {
1925 g_set_error_literal (error,
1926 FWUPD_ERROR,
1927 FWUPD_ERROR_NOTHING_TO_DO,
1928 "no remotes available");
1929 return FALSE;
1930 }
1931 for (guint i = 0; i < remotes->len; i++) {
1932 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
1933 g_node_append_data (root, remote_tmp);
1934 }
1935 fu_util_print_tree (root, title);
1936
1937 return TRUE;
1938}
1939
Richard Hughes196c6c62020-05-11 19:42:47 +01001940static gboolean
1941fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
1942{
1943 g_autoptr(GPtrArray) attrs = NULL;
1944 g_autofree gchar *str = NULL;
1945
1946 /* not ready yet */
1947 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
1948 g_set_error_literal (error,
1949 FWUPD_ERROR,
1950 FWUPD_ERROR_NOT_SUPPORTED,
1951 "The HSI specification is not yet complete. "
1952 "To ignore this warning, use --force");
1953 return FALSE;
1954 }
1955
1956 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1957 return FALSE;
1958
1959 /* TRANSLATORS: this is a string like 'HSI:2-U' */
1960 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
1961 fu_engine_get_host_security_id (priv->engine));
1962
1963 /* print the "why" */
1964 attrs = fu_engine_get_host_security_attrs (priv->engine, error);
1965 if (attrs == NULL)
1966 return FALSE;
1967 str = fu_util_security_attrs_to_string (attrs);
1968 g_print ("%s\n", str);
1969 return TRUE;
1970}
1971
Richard Hughesb5976832018-05-18 10:02:09 +01001972int
1973main (int argc, char *argv[])
1974{
Richard Hughes460226a2018-05-21 20:56:21 +01001975 gboolean allow_older = FALSE;
1976 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001977 gboolean force = FALSE;
1978 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001979 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001980 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001981 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001982 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1983 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001984 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001985 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001986 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001987 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001988 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1989 /* TRANSLATORS: command line option */
1990 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001991 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1992 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001993 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001994 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1995 /* TRANSLATORS: command line option */
1996 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001997 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1998 /* TRANSLATORS: command line option */
1999 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002000 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2001 /* TRANSLATORS: command line option */
2002 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002003 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2004 /* TRANSLATORS: command line option */
2005 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002006 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
2007 /* TRANSLATORS: command line option */
2008 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002009 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2010 /* TRANSLATORS: command line option */
2011 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002012 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002013 /* TRANSLATORS: command line option */
2014 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002015 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002016 /* TRANSLATORS: command line option */
2017 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002018 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2019 /* TRANSLATORS: command line option */
2020 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002021 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2022 /* TRANSLATORS: command line option */
2023 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002024 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2025 /* TRANSLATORS: command line option */
2026 _("Filter with a set of device flags using a ~ prefix to "
2027 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002028 { NULL}
2029 };
2030
Richard Hughes429f72b2020-01-16 12:18:19 +00002031#ifdef _WIN32
2032 /* workaround Windows setting the codepage to 1252 */
2033 g_setenv ("LANG", "C.UTF-8", FALSE);
2034#endif
2035
Richard Hughesb5976832018-05-18 10:02:09 +01002036 setlocale (LC_ALL, "");
2037
Richard Hughes668ee212019-11-22 09:17:46 +00002038 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002039 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2040 textdomain (GETTEXT_PACKAGE);
2041
Richard Hughes01c0bad2019-11-22 09:08:51 +00002042#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002043 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002044 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002045 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002046 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002047#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002048
2049 /* create helper object */
2050 priv->loop = g_main_loop_new (NULL, FALSE);
2051 priv->progressbar = fu_progressbar_new ();
2052
2053 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002054 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002055 "build-firmware",
2056 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
2057 /* TRANSLATORS: command description */
2058 _("Build firmware using a sandbox"),
2059 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002060 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002061 "smbios-dump",
2062 "FILE",
2063 /* TRANSLATORS: command description */
2064 _("Dump SMBIOS data from a file"),
2065 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002066 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002067 "get-plugins",
2068 NULL,
2069 /* TRANSLATORS: command description */
2070 _("Get all enabled plugins registered with the system"),
2071 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002072 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002073 "get-details",
2074 NULL,
2075 /* TRANSLATORS: command description */
2076 _("Gets details about a firmware file"),
2077 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002078 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002079 "get-history",
2080 NULL,
2081 /* TRANSLATORS: command description */
2082 _("Show history of firmware updates"),
2083 fu_util_get_history);
2084 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002085 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002086 NULL,
2087 /* TRANSLATORS: command description */
2088 _("Gets the list of updates for connected hardware"),
2089 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002090 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002091 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002092 NULL,
2093 /* TRANSLATORS: command description */
2094 _("Get all devices that support firmware updates"),
2095 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002096 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002097 "get-device-flags",
2098 NULL,
2099 /* TRANSLATORS: command description */
2100 _("Get all device flags supported by fwupd"),
2101 fu_util_get_device_flags);
2102 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002103 "watch",
2104 NULL,
2105 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002106 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002107 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002108 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002109 "install-blob",
2110 "FILENAME DEVICE-ID",
2111 /* TRANSLATORS: command description */
2112 _("Install a firmware blob on a device"),
2113 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002114 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002115 "install",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002116 "FILE [DEVICE-ID|GUID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002117 /* TRANSLATORS: command description */
2118 _("Install a firmware file on this hardware"),
2119 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002120 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002121 "reinstall",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002122 "DEVICE-ID|GUID",
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002123 /* TRANSLATORS: command description */
2124 _("Reinstall firmware on a device"),
2125 fu_util_reinstall);
2126 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002127 "attach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002128 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002129 /* TRANSLATORS: command description */
2130 _("Attach to firmware mode"),
2131 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002132 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002133 "detach",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002134 "DEVICE-ID|GUID",
Richard Hughes98ca9932018-05-18 10:24:07 +01002135 /* TRANSLATORS: command description */
2136 _("Detach to bootloader mode"),
2137 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002138 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002139 "activate",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002140 "[DEVICE-ID|GUID]",
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002141 /* TRANSLATORS: command description */
2142 _("Activate pending devices"),
2143 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002144 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002145 "hwids",
2146 "[FILE]",
2147 /* TRANSLATORS: command description */
2148 _("Return all the hardware IDs for the machine"),
2149 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002150 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002151 "monitor",
2152 NULL,
2153 /* TRANSLATORS: command description */
2154 _("Monitor the daemon for events"),
2155 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002156 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002157 "update,upgrade",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002158 "[DEVICE-ID|GUID]",
Mario Limonciello46aaee82019-01-10 12:58:00 -06002159 /* TRANSLATORS: command description */
2160 _("Update all devices that match local metadata"),
2161 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002162 fu_util_cmd_array_add (cmd_array,
2163 "self-sign",
2164 "TEXT",
2165 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002166 C_("command-description",
2167 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002168 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002169 fu_util_cmd_array_add (cmd_array,
2170 "verify-update",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002171 "[DEVICE-ID|GUID]",
Richard Hughes15684492019-03-15 16:27:50 +00002172 /* TRANSLATORS: command description */
2173 _("Update the stored metadata with current contents"),
2174 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002175 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00002176 "firmware-read",
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002177 "FILENAME [DEVICE-ID|GUID]",
Richard Hughesa58510b2019-10-30 10:03:12 +00002178 /* TRANSLATORS: command description */
2179 _("Read a firmware blob from a device"),
2180 fu_util_firmware_read);
2181 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002182 "firmware-convert",
2183 "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]",
2184 /* TRANSLATORS: command description */
2185 _("Convert a firmware file"),
2186 fu_util_firmware_convert);
2187 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002188 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06002189 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01002190 /* TRANSLATORS: command description */
2191 _("Parse and show details about a firmware file"),
2192 fu_util_firmware_parse);
2193 fu_util_cmd_array_add (cmd_array,
2194 "get-firmware-types",
2195 NULL,
2196 /* TRANSLATORS: command description */
2197 _("List the available firmware types"),
2198 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002199 fu_util_cmd_array_add (cmd_array,
2200 "get-remotes",
2201 NULL,
2202 /* TRANSLATORS: command description */
2203 _("Gets the configured remotes"),
2204 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002205 fu_util_cmd_array_add (cmd_array,
2206 "refresh",
2207 NULL,
2208 /* TRANSLATORS: command description */
2209 _("Refresh metadata from remote server"),
2210 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002211 fu_util_cmd_array_add (cmd_array,
2212 "security",
2213 NULL,
2214 /* TRANSLATORS: command description */
2215 _("Gets the host security attributes."),
2216 fu_util_security);
Richard Hughesb5976832018-05-18 10:02:09 +01002217
2218 /* do stuff on ctrl+c */
2219 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002220#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002221 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2222 SIGINT, fu_util_sigint_cb,
2223 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002224#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002225 g_signal_connect (priv->cancellable, "cancelled",
2226 G_CALLBACK (fu_util_cancelled_cb), priv);
2227
2228 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002229 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002230
Mario Limonciello3f243a92019-01-21 22:05:23 -06002231 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002232 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002233 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002234 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002235 fu_progressbar_set_interactive (priv->progressbar, FALSE);
2236 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002237
Richard Hughesb5976832018-05-18 10:02:09 +01002238 /* get a list of the commands */
2239 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002240 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002241 g_option_context_set_summary (priv->context, cmd_descriptions);
2242 g_option_context_set_description (priv->context,
2243 "This tool allows an administrator to use the fwupd plugins "
2244 "without being installed on the host system.");
2245
2246 /* TRANSLATORS: program name */
2247 g_set_application_name (_("Firmware Utility"));
2248 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002249 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002250 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2251 if (!ret) {
2252 /* TRANSLATORS: the user didn't read the man page */
2253 g_print ("%s: %s\n", _("Failed to parse arguments"),
2254 error->message);
2255 return EXIT_FAILURE;
2256 }
2257
Richard Hughes0e46b222019-09-05 12:13:35 +01002258 /* allow disabling SSL strict mode for broken corporate proxies */
2259 if (priv->disable_ssl_strict) {
2260 /* TRANSLATORS: try to help */
2261 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2262 "to do this automatically in the future "
2263 "export DISABLE_SSL_STRICT in your environment"));
2264 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2265 }
2266
Richard Hughes747f5702019-08-06 14:27:26 +01002267 /* parse filter flags */
2268 if (filter != NULL) {
2269 if (!fu_util_parse_filter_flags (filter,
2270 &priv->filter_include,
2271 &priv->filter_exclude,
2272 &error)) {
2273 /* TRANSLATORS: the user didn't read the man page */
2274 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2275 error->message);
2276 return EXIT_FAILURE;
2277 }
2278 }
2279
2280
Richard Hughes460226a2018-05-21 20:56:21 +01002281 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002282 if (allow_reinstall)
2283 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2284 if (allow_older)
2285 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2286 if (force)
2287 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2288
Richard Hughes98ca9932018-05-18 10:24:07 +01002289 /* load engine */
2290 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2291 g_signal_connect (priv->engine, "device-added",
2292 G_CALLBACK (fu_main_engine_device_added_cb),
2293 priv);
2294 g_signal_connect (priv->engine, "device-removed",
2295 G_CALLBACK (fu_main_engine_device_removed_cb),
2296 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002297 g_signal_connect (priv->engine, "status-changed",
2298 G_CALLBACK (fu_main_engine_status_changed_cb),
2299 priv);
2300 g_signal_connect (priv->engine, "percentage-changed",
2301 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2302 priv);
2303
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002304 /* just show versions and exit */
2305 if (version) {
2306 g_autofree gchar *version_str = fu_util_get_versions ();
2307 g_print ("%s\n", version_str);
2308 return EXIT_SUCCESS;
2309 }
2310
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002311 /* any plugin whitelist specified */
2312 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2313 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2314
Richard Hughesb5976832018-05-18 10:02:09 +01002315 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002316 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002317 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05002318 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002319 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05002320 /* TRANSLATORS: error message explaining command to run to how to get help */
2321 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
2322 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2323 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01002324 return EXIT_NOTHING_TO_DO;
2325 }
Richard Hughesb5976832018-05-18 10:02:09 +01002326 return EXIT_FAILURE;
2327 }
2328
2329 /* success */
2330 return EXIT_SUCCESS;
2331}