blob: 12863db494b98248200dd46f42365b93526bd68a [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 Hughesd5aab652020-02-25 12:47:50 +000021#include <jcat.h>
Richard Hughesb5976832018-05-18 10:02:09 +010022
Mario Limonciello7a3df4b2019-01-31 10:27:22 -060023#include "fu-device-private.h"
Richard Hughes98ca9932018-05-18 10:24:07 +010024#include "fu-engine.h"
Mario Limonciello96a0dd52019-02-25 13:50:03 -060025#include "fu-history.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010026#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010027#include "fu-progressbar.h"
Richard Hughesf58ac732020-05-12 15:23:44 +010028#include "fu-security-attrs-private.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 Hughesdf89cd52020-06-26 20:25:18 +010055 FuEngineRequest *request;
Richard Hughesb5976832018-05-18 10:02:09 +010056 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060057 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050058 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000059 gboolean prepare_blob;
60 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060061 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010062 FwupdInstallFlags flags;
Richard Hughes5c82b942020-09-14 12:24:06 +010063 gboolean show_all;
Richard Hughes0e46b222019-09-05 12:13:35 +010064 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050065 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060066 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050067 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060068 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060069 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010070 FwupdDeviceFlags filter_include;
71 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000072};
Richard Hughesb5976832018-05-18 10:02:09 +010073
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050074static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060075fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
76{
77 g_autoptr(JsonBuilder) builder = NULL;
78 g_autoptr(JsonGenerator) json_generator = NULL;
79 g_autoptr(JsonNode) json_root = NULL;
80 g_autoptr(GPtrArray) devices = NULL;
81 g_autofree gchar *state = NULL;
82 g_autofree gchar *dirname = NULL;
83 g_autofree gchar *filename = NULL;
84
85 if (!priv->enable_json_state)
86 return TRUE;
87
88 devices = fu_engine_get_devices (priv->engine, error);
89 if (devices == NULL)
90 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000091 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060092
93 /* create header */
94 builder = json_builder_new ();
95 json_builder_begin_object (builder);
96
97 /* add each device */
98 json_builder_set_member_name (builder, "Devices");
99 json_builder_begin_array (builder);
100 for (guint i = 0; i < devices->len; i++) {
101 FwupdDevice *dev = g_ptr_array_index (devices, i);
102 json_builder_begin_object (builder);
103 fwupd_device_to_json (dev, builder);
104 json_builder_end_object (builder);
105 }
106 json_builder_end_array (builder);
107 json_builder_end_object (builder);
108
109 /* export as a string */
110 json_root = json_builder_get_root (builder);
111 json_generator = json_generator_new ();
112 json_generator_set_pretty (json_generator, TRUE);
113 json_generator_set_root (json_generator, json_root);
114 state = json_generator_to_data (json_generator, NULL);
115 if (state == NULL)
116 return FALSE;
117 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
118 filename = g_build_filename (dirname, "state.json", NULL);
119 return g_file_set_contents (filename, state, -1, error);
120}
121
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100122static void
123fu_util_show_plugin_warnings (FuUtilPrivate *priv)
124{
125 FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE;
126 GPtrArray *plugins;
127
128 /* get a superset so we do not show the same message more than once */
129 plugins = fu_engine_get_plugins (priv->engine);
130 for (guint i = 0; i < plugins->len; i++) {
131 FwupdPlugin *plugin = g_ptr_array_index (plugins, i);
132 if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING))
133 continue;
134 flags |= fwupd_plugin_get_flags (plugin);
135 }
136
137 /* never show these, they're way too generic */
138 flags &= ~FWUPD_PLUGIN_FLAG_DISABLED;
139 flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE;
140
141 /* print */
142 for (guint i = 0; i < 64; i++) {
Richard Hughes1df9b822020-10-30 15:18:45 +0000143 FwupdPluginFlags flag = (guint64) 1 << i;
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100144 const gchar *tmp;
145 g_autofree gchar *fmt = NULL;
Richard Hughes1df9b822020-10-30 15:18:45 +0000146 g_autofree gchar *url= NULL;
147 g_autoptr(GString) str = g_string_new (NULL);
148 if ((flags & flag) == 0)
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100149 continue;
150 tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i);
151 if (tmp == NULL)
152 continue;
153 /* TRANSLATORS: this is a prefix on the console */
154 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes1df9b822020-10-30 15:18:45 +0000155 g_string_append_printf (str, "%s %s\n", fmt, tmp);
156
157 url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
158 fwupd_plugin_flag_to_string (flag));
159 g_string_append (str, " ");
160 /* TRANSLATORS: %s is a link to a website */
161 g_string_append_printf (str, _("See %s for more information."), url);
162 g_string_append (str, "\n");
163 g_printerr ("%s", str->str);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100164 }
165}
166
Mario Limonciello3143bad2019-02-27 07:31:00 -0600167static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000168fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600169{
Richard Hughesd92ccca2019-05-20 11:28:31 +0100170#ifdef HAVE_SYSTEMD
Richard Hughes3a0ee302020-12-14 14:22:18 +0000171 g_autoptr(GError) error_local = NULL;
Richard Hughes3d005222019-05-17 14:02:41 +0100172 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500173 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100174#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000175 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000176 return FALSE;
177 if (fu_engine_get_tainted (priv->engine)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100178 g_autofree gchar *fmt = NULL;
179
180 /* TRANSLATORS: this is a prefix on the console */
181 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
182 g_printerr ("%s This tool has loaded 3rd party code and "
183 "is no longer supported by the upstream developers!\n",
184 fmt);
Richard Hughesf425d292019-01-18 17:57:39 +0000185 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100186 fu_util_show_plugin_warnings (priv);
Mario Limonciellobd60de12020-11-11 13:09:43 -0600187 fu_util_show_unsupported_warn ();
Richard Hughesf425d292019-01-18 17:57:39 +0000188 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500189}
190
Richard Hughesb5976832018-05-18 10:02:09 +0100191static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500192fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
193{
194 g_autofree gchar *path = g_path_get_dirname (value);
195 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
196 g_prefix_error (error,
197 "Unable to access %s. You may need to copy %s to %s: ",
198 path, value, g_getenv ("HOME"));
199 }
200}
201
202static void
Richard Hughesb5976832018-05-18 10:02:09 +0100203fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
204{
205 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
206 /* TRANSLATORS: this is when a device ctrl+c's a watch */
207 g_print ("%s\n", _("Cancelled"));
208 g_main_loop_quit (priv->loop);
209}
210
211static gboolean
212fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
213{
214 g_autofree gchar *tmp = NULL;
215 g_autoptr(FuSmbios) smbios = NULL;
216 if (g_strv_length (values) < 1) {
217 g_set_error_literal (error,
218 FWUPD_ERROR,
219 FWUPD_ERROR_INVALID_ARGS,
220 "Invalid arguments");
221 return FALSE;
222 }
223 smbios = fu_smbios_new ();
224 if (!fu_smbios_setup_from_file (smbios, values[0], error))
225 return FALSE;
226 tmp = fu_smbios_to_string (smbios);
227 g_print ("%s\n", tmp);
228 return TRUE;
229}
230
Richard Hughes9e5675e2019-11-22 09:35:03 +0000231#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100232static gboolean
233fu_util_sigint_cb (gpointer user_data)
234{
235 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
236 g_debug ("Handling SIGINT");
237 g_cancellable_cancel (priv->cancellable);
238 return FALSE;
239}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000240#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100241
242static void
243fu_util_private_free (FuUtilPrivate *priv)
244{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500245 if (priv->current_device != NULL)
246 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100247 if (priv->engine != NULL)
248 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100249 if (priv->request != NULL)
250 g_object_unref (priv->request);
Richard Hughesb5976832018-05-18 10:02:09 +0100251 if (priv->loop != NULL)
252 g_main_loop_unref (priv->loop);
253 if (priv->cancellable != NULL)
254 g_object_unref (priv->cancellable);
255 if (priv->progressbar != NULL)
256 g_object_unref (priv->progressbar);
257 if (priv->context != NULL)
258 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600259 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100260 g_free (priv);
261}
262
263#pragma clang diagnostic push
264#pragma clang diagnostic ignored "-Wunused-function"
265G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
266#pragma clang diagnostic pop
267
Richard Hughes98ca9932018-05-18 10:24:07 +0100268
269static void
270fu_main_engine_device_added_cb (FuEngine *engine,
271 FuDevice *device,
272 FuUtilPrivate *priv)
273{
274 g_autofree gchar *tmp = fu_device_to_string (device);
275 g_debug ("ADDED:\n%s", tmp);
276}
277
278static void
279fu_main_engine_device_removed_cb (FuEngine *engine,
280 FuDevice *device,
281 FuUtilPrivate *priv)
282{
283 g_autofree gchar *tmp = fu_device_to_string (device);
284 g_debug ("REMOVED:\n%s", tmp);
285}
286
287static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100288fu_main_engine_status_changed_cb (FuEngine *engine,
289 FwupdStatus status,
290 FuUtilPrivate *priv)
291{
292 fu_progressbar_update (priv->progressbar, status, 0);
293}
294
295static void
296fu_main_engine_percentage_changed_cb (FuEngine *engine,
297 guint percentage,
298 FuUtilPrivate *priv)
299{
300 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
301}
302
303static gboolean
304fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
305{
Richard Hughesc7d870a2020-12-10 10:05:35 +0000306 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100307 return FALSE;
308 g_main_loop_run (priv->loop);
309 return TRUE;
310}
311
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100312static gint
313fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
314{
315 return fu_plugin_name_compare (*item1, *item2);
316}
317
318static gboolean
319fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
320{
321 GPtrArray *plugins;
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100322
323 /* load engine */
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100324 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100325 return FALSE;
326
327 /* print */
328 plugins = fu_engine_get_plugins (priv->engine);
329 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
330 for (guint i = 0; i < plugins->len; i++) {
331 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100332 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
333 g_print ("%s\n", str);
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100334 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100335 if (plugins->len == 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100336 /* TRANSLATORS: nothing found */
337 g_print ("%s\n", _("No plugins found"));
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100338 }
339
340 return TRUE;
341}
342
Richard Hughes98ca9932018-05-18 10:24:07 +0100343static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100344fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
345{
346 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
347 if (!fwupd_device_has_flag (dev, priv->filter_include))
348 return FALSE;
349 }
350 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
351 if (fwupd_device_has_flag (dev, priv->filter_exclude))
352 return FALSE;
353 }
354 return TRUE;
355}
356
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500357static gchar *
358fu_util_get_tree_title (FuUtilPrivate *priv)
359{
360 return g_strdup (fu_engine_get_host_product (priv->engine));
361}
362
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600363static FuDevice *
364fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
365{
366 FuDevice *dev;
367 guint idx;
368 g_autoptr(GPtrArray) devices = NULL;
369 g_autoptr(GPtrArray) devices_filtered = NULL;
370
371 /* get devices from daemon */
372 if (devices_opt != NULL) {
373 devices = g_ptr_array_ref (devices_opt);
374 } else {
375 devices = fu_engine_get_devices (priv->engine, error);
376 if (devices == NULL)
377 return NULL;
378 }
379 fwupd_device_array_ensure_parents (devices);
380
381 /* filter results */
382 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
383 for (guint i = 0; i < devices->len; i++) {
384 dev = g_ptr_array_index (devices, i);
385 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
386 continue;
387 g_ptr_array_add (devices_filtered, g_object_ref (dev));
388 }
389
390 /* nothing */
391 if (devices_filtered->len == 0) {
392 g_set_error_literal (error,
393 FWUPD_ERROR,
394 FWUPD_ERROR_NOTHING_TO_DO,
395 "No supported devices");
396 return NULL;
397 }
398
399 /* exactly one */
400 if (devices_filtered->len == 1) {
401 dev = g_ptr_array_index (devices_filtered, 0);
402 /* TRANSLATORS: Device has been chosen by the daemon for the user */
403 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
404 return g_object_ref (dev);
405 }
406
407 /* TRANSLATORS: get interactive prompt */
408 g_print ("%s\n", _("Choose a device:"));
409 /* TRANSLATORS: this is to abort the interactive prompt */
410 g_print ("0.\t%s\n", _("Cancel"));
411 for (guint i = 0; i < devices_filtered->len; i++) {
412 dev = g_ptr_array_index (devices_filtered, i);
413 g_print ("%u.\t%s (%s)\n",
414 i + 1,
415 fu_device_get_id (dev),
416 fu_device_get_name (dev));
417 }
418 idx = fu_util_prompt_for_number (devices_filtered->len);
419 if (idx == 0) {
420 g_set_error_literal (error,
421 FWUPD_ERROR,
422 FWUPD_ERROR_NOTHING_TO_DO,
423 "Request canceled");
424 return NULL;
425 }
426 dev = g_ptr_array_index (devices_filtered, idx - 1);
427 return g_object_ref (dev);
428}
429
430static FuDevice *
431fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
432{
433 if (fwupd_guid_is_valid (id)) {
434 g_autoptr(GPtrArray) devices = NULL;
435 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
436 if (devices == NULL)
437 return NULL;
438 return fu_util_prompt_for_device (priv, devices, error);
439 }
440
441 /* did this look like a GUID? */
442 for (guint i = 0; id[i] != '\0'; i++) {
443 if (id[i] == '-') {
444 g_set_error_literal (error,
445 FWUPD_ERROR,
446 FWUPD_ERROR_INVALID_ARGS,
447 "Invalid arguments");
448 return NULL;
449 }
450 }
451 return fu_engine_get_device (priv->engine, id, error);
452}
453
Richard Hughes747f5702019-08-06 14:27:26 +0100454static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600455fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
456{
457 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500458 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600459 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500460 gboolean no_updates_header = FALSE;
461 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600462
463 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000464 if (!fu_util_start_engine (priv,
465 FU_ENGINE_LOAD_FLAG_COLDPLUG |
466 FU_ENGINE_LOAD_FLAG_HWINFO |
467 FU_ENGINE_LOAD_FLAG_REMOTES,
468 error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600469 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600470 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600471
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600472 /* parse arguments */
473 if (g_strv_length (values) == 0) {
474 devices = fu_engine_get_devices (priv->engine, error);
475 if (devices == NULL)
476 return FALSE;
477 } else if (g_strv_length (values) == 1) {
478 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600479 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600480 if (device == NULL)
481 return FALSE;
482 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
483 g_ptr_array_add (devices, device);
484 } else {
485 g_set_error_literal (error,
486 FWUPD_ERROR,
487 FWUPD_ERROR_INVALID_ARGS,
488 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600489 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600490 }
491
Richard Hughes0ef47202020-01-06 13:59:09 +0000492 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500493 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600494 for (guint i = 0; i < devices->len; i++) {
495 FwupdDevice *dev = g_ptr_array_index (devices, i);
496 g_autoptr(GPtrArray) rels = NULL;
497 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500498 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600499
Richard Hughes747f5702019-08-06 14:27:26 +0100500 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600501 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600502 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600503 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500504 if (!no_updates_header) {
505 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
506 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
507 no_updates_header = TRUE;
508 }
509 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600510 continue;
511 }
Richard Hughes747f5702019-08-06 14:27:26 +0100512 if (!fu_util_filter_device (priv, dev))
513 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600514
515 /* get the releases for this device and filter for validity */
516 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100517 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600518 fwupd_device_get_id (dev),
519 &error_local);
520 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500521 if (!latest_header) {
522 /* TRANSLATORS: message letting the user know no device upgrade available */
523 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
524 latest_header = TRUE;
525 }
526 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600527 /* discard the actual reason from user, but leave for debugging */
528 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600529 continue;
530 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500531 child = g_node_append_data (root, dev);
532
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600533 for (guint j = 0; j < rels->len; j++) {
534 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500535 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600536 }
537 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600538 /* save the device state for other applications to see */
539 if (!fu_util_save_current_state (priv, error))
540 return FALSE;
541
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500542 /* updates */
543 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
544 g_set_error_literal (error,
545 FWUPD_ERROR,
546 FWUPD_ERROR_NOTHING_TO_DO,
547 "No updates available for remaining devices");
548 return FALSE;
549 }
550
551 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600552 return TRUE;
553}
554
555static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500556fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
557{
558 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500559 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600560 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500561 gint fd;
562
563 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000564 if (!fu_util_start_engine (priv,
565 FU_ENGINE_LOAD_FLAG_COLDPLUG |
566 FU_ENGINE_LOAD_FLAG_HWINFO |
567 FU_ENGINE_LOAD_FLAG_REMOTES,
568 error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500569 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600570 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500571
572 /* check args */
573 if (g_strv_length (values) != 1) {
574 g_set_error_literal (error,
575 FWUPD_ERROR,
576 FWUPD_ERROR_INVALID_ARGS,
577 "Invalid arguments");
578 return FALSE;
579 }
580
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600581 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100582 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600583
Mario Limonciello716ab272018-05-29 12:34:37 -0500584 /* open file */
585 fd = open (values[0], O_RDONLY);
586 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500587 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500588 g_set_error (error,
589 FWUPD_ERROR,
590 FWUPD_ERROR_INVALID_FILE,
591 "failed to open %s",
592 values[0]);
593 return FALSE;
594 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100595 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500596 close (fd);
597
598 if (array == NULL)
599 return FALSE;
600 for (guint i = 0; i < array->len; i++) {
601 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600602 FwupdRelease *rel;
603 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100604 if (!fu_util_filter_device (priv, dev))
605 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600606 child = g_node_append_data (root, dev);
607 rel = fwupd_device_get_release_default (dev);
608 if (rel != NULL)
609 g_node_append_data (child, rel);
610
Mario Limonciello716ab272018-05-29 12:34:37 -0500611 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500612 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500613
Mario Limonciello716ab272018-05-29 12:34:37 -0500614 return TRUE;
615}
616
617static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100618fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
619{
620 g_autoptr(GString) str = g_string_new (NULL);
621
622 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
623 const gchar *tmp = fwupd_device_flag_to_string (i);
624 if (tmp == NULL)
625 break;
626 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
627 g_string_append (str, " ");
628 g_string_append (str, tmp);
629 g_string_append (str, " ~");
630 g_string_append (str, tmp);
631 }
632 g_print ("%s\n", str->str);
633
634 return TRUE;
635}
636
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500637static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100638fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500639{
640 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100641 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100642 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
643 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100644 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500645 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500646 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100647 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500648 GNode *child = g_node_append_data (root, dev_tmp);
649 fu_util_build_device_tree (priv, child, devs, dev_tmp);
650 }
651 }
652}
653
654static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100655fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500656{
657 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600658 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500659 g_autoptr(GPtrArray) devs = NULL;
660
661 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000662 if (!fu_util_start_engine (priv,
663 FU_ENGINE_LOAD_FLAG_COLDPLUG |
664 FU_ENGINE_LOAD_FLAG_HWINFO |
665 FU_ENGINE_LOAD_FLAG_REMOTES,
666 error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500667 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600668 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500669
670 /* print */
671 devs = fu_engine_get_devices (priv->engine, error);
672 if (devs == NULL)
673 return FALSE;
674
675 /* print */
676 if (devs->len == 0) {
677 /* TRANSLATORS: nothing attached that can be upgraded */
678 g_print ("%s\n", _("No hardware detected with firmware update capability"));
679 return TRUE;
680 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000681 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500682 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500683 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500684
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100685 /* save the device state for other applications to see */
686 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500687}
688
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500689static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600690fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500691 FwupdDevice *device,
692 FuUtilPrivate *priv)
693{
694 g_autofree gchar *str = NULL;
695
Richard Hughes809abea2019-03-23 11:06:18 +0000696 /* allowed to set whenever the device has changed */
697 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
698 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
699 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
700 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
701
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500702 /* same as last time, so ignore */
703 if (priv->current_device != NULL &&
704 fwupd_device_compare (priv->current_device, device) == 0)
705 return;
706
Richard Hughesee562b52020-04-07 14:32:52 +0100707 /* ignore indirect devices that might have changed */
708 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
709 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
710 g_debug ("ignoring %s with status %s",
711 fwupd_device_get_name (device),
712 fwupd_status_to_string (fwupd_device_get_status (device)));
713 return;
714 }
715
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500716 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600717 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
718 /* TRANSLATORS: %1 is a device name */
719 str = g_strdup_printf (_("Updating %s…"),
720 fwupd_device_get_name (device));
721 fu_progressbar_set_title (priv->progressbar, str);
722 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
723 /* TRANSLATORS: %1 is a device name */
724 str = g_strdup_printf (_("Installing on %s…"),
725 fwupd_device_get_name (device));
726 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000727 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
728 /* TRANSLATORS: %1 is a device name */
729 str = g_strdup_printf (_("Reading from %s…"),
730 fwupd_device_get_name (device));
731 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600732 } else {
733 g_warning ("no FuUtilOperation set");
734 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500735 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600736
Mario Limonciello32241f42019-01-24 10:12:41 -0600737 if (priv->current_message == NULL) {
738 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
739 if (tmp != NULL)
740 priv->current_message = g_strdup (tmp);
741 }
742}
743
744static void
745fu_util_display_current_message (FuUtilPrivate *priv)
746{
747 if (priv->current_message == NULL)
748 return;
749 g_print ("%s\n", priv->current_message);
750 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500751}
752
Richard Hughes98ca9932018-05-18 10:24:07 +0100753static gboolean
754fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
755{
756 g_autoptr(FuDevice) device = NULL;
757 g_autoptr(GBytes) blob_fw = NULL;
758
759 /* invalid args */
760 if (g_strv_length (values) == 0) {
761 g_set_error_literal (error,
762 FWUPD_ERROR,
763 FWUPD_ERROR_INVALID_ARGS,
764 "Invalid arguments");
765 return FALSE;
766 }
767
768 /* parse blob */
769 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500770 if (blob_fw == NULL) {
771 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100772 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500773 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100774
775 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000776 if (!fu_util_start_engine (priv,
777 FU_ENGINE_LOAD_FLAG_COLDPLUG |
778 FU_ENGINE_LOAD_FLAG_HWINFO |
779 FU_ENGINE_LOAD_FLAG_REMOTES,
780 error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100781 return FALSE;
782
783 /* get device */
784 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100785 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100786 if (device == NULL)
787 return FALSE;
788 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100789 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100790 if (device == NULL)
791 return FALSE;
792 }
793
Mario Limonciello3f243a92019-01-21 22:05:23 -0600794 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500795 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600796 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500797
Richard Hughes98ca9932018-05-18 10:24:07 +0100798 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000799 if (priv->prepare_blob) {
800 g_autoptr(GPtrArray) devices = NULL;
801 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
802 g_ptr_array_add (devices, g_object_ref (device));
803 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
804 g_prefix_error (error, "failed to prepare composite action: ");
805 return FALSE;
806 }
807 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100808 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000809 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600810 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000811 if (priv->cleanup_blob) {
812 g_autoptr(FuDevice) device_new = NULL;
813 g_autoptr(GError) error_local = NULL;
814
815 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100816 device_new = fu_util_get_device (priv,
817 fu_device_get_id (device),
818 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000819 if (device_new == NULL) {
820 g_debug ("failed to find new device: %s",
821 error_local->message);
822 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600823 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000824 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
825 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
826 g_prefix_error (error, "failed to cleanup composite action: ");
827 return FALSE;
828 }
829 }
830 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600831
Mario Limonciello32241f42019-01-24 10:12:41 -0600832 fu_util_display_current_message (priv);
833
Mario Limonciello3f243a92019-01-21 22:05:23 -0600834 /* success */
835 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100836}
837
Richard Hughesa58510b2019-10-30 10:03:12 +0000838static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100839fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000840{
841 g_autoptr(FuDevice) device = NULL;
842 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
843 g_autoptr(GBytes) blob_fw = NULL;
844
845 /* invalid args */
846 if (g_strv_length (values) == 0) {
847 g_set_error_literal (error,
848 FWUPD_ERROR,
849 FWUPD_ERROR_INVALID_ARGS,
850 "Invalid arguments");
851 return FALSE;
852 }
853
854 /* file already exists */
855 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
856 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
857 g_set_error_literal (error,
858 FWUPD_ERROR,
859 FWUPD_ERROR_INVALID_ARGS,
860 "Filename already exists");
861 return FALSE;
862 }
863
Richard Hughes02792c02019-11-01 14:21:20 +0000864 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000865 * avoid failing at the end of a potentially lengthy operation */
866 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
867 return FALSE;
868
869 /* load engine */
870 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
871 return FALSE;
872
873 /* get device */
874 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100875 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000876 if (device == NULL)
877 return FALSE;
878 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100879 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000880 if (device == NULL)
881 return FALSE;
882 }
883 priv->current_operation = FU_UTIL_OPERATION_READ;
884 g_signal_connect (priv->engine, "device-changed",
885 G_CALLBACK (fu_util_update_device_changed_cb), priv);
886
887 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100888 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000889 if (blob_fw == NULL)
890 return FALSE;
891 return fu_common_set_contents_bytes (values[0], blob_fw, error);
892}
893
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100894static gint
895fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
896{
897 FuInstallTask *task1 = *((FuInstallTask **) a);
898 FuInstallTask *task2 = *((FuInstallTask **) b);
899 return fu_install_task_compare (task1, task2);
900}
901
902static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100903fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
904{
Filipe Laínse0914272019-09-20 10:04:43 +0100905 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100906 { "curl", uri, "--output", fn, NULL },
907 { NULL } };
908 for (guint i = 0; argv[i][0] != NULL; i++) {
909 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100910 g_autofree gchar *fn_tmp = NULL;
911 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
912 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100913 g_debug ("%s", error_local->message);
914 continue;
915 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000916 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100917 }
918 g_set_error_literal (error,
919 FWUPD_ERROR,
920 FWUPD_ERROR_NOT_FOUND,
921 "no supported out-of-process downloaders found");
922 return FALSE;
923}
924
925static gchar *
926fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
927{
928 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100929
930 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000931 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
932 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000933 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100934 return g_strdup (perhapsfn);
935
936 /* download the firmware to a cachedir */
937 filename = fu_util_get_user_cache_path (perhapsfn);
938 if (!fu_common_mkdir_parent (filename, error))
939 return NULL;
940 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
941 return NULL;
942 return g_steal_pointer (&filename);
943}
944
945static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100946fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
947{
Richard Hughes3d178be2018-08-30 11:14:24 +0100948 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100949 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100950 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100951 g_autoptr(GPtrArray) devices_possible = NULL;
952 g_autoptr(GPtrArray) errors = NULL;
953 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100954 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100955
Mario Limonciello8949e892018-05-25 08:03:06 -0500956 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000957 if (!fu_util_start_engine (priv,
958 FU_ENGINE_LOAD_FLAG_COLDPLUG |
959 FU_ENGINE_LOAD_FLAG_HWINFO |
960 FU_ENGINE_LOAD_FLAG_REMOTES,
961 error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500962 return FALSE;
963
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100964 /* handle both forms */
965 if (g_strv_length (values) == 1) {
966 devices_possible = fu_engine_get_devices (priv->engine, error);
967 if (devices_possible == NULL)
968 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000969 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100970 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100971 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100972 if (device == NULL)
973 return FALSE;
974 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
975 g_ptr_array_add (devices_possible, device);
976 } else {
977 g_set_error_literal (error,
978 FWUPD_ERROR,
979 FWUPD_ERROR_INVALID_ARGS,
980 "Invalid arguments");
981 return FALSE;
982 }
983
Richard Hughes3d178be2018-08-30 11:14:24 +0100984 /* download if required */
985 filename = fu_util_download_if_required (priv, values[0], error);
986 if (filename == NULL)
987 return FALSE;
988
Richard Hughes481aa2a2018-09-18 20:51:46 +0100989 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100990 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500991 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100992 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100993 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500994 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100995 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
996 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100997 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600998 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100999 if (components == NULL)
1000 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001001
Richard Hughes481aa2a2018-09-18 20:51:46 +01001002 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001003 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
1004 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001005 for (guint i = 0; i < components->len; i++) {
1006 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001007
1008 /* do any devices pass the requirements */
1009 for (guint j = 0; j < devices_possible->len; j++) {
1010 FuDevice *device = g_ptr_array_index (devices_possible, j);
1011 g_autoptr(FuInstallTask) task = NULL;
1012 g_autoptr(GError) error_local = NULL;
1013
1014 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001015 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001016 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001017 priv->request,
1018 task,
1019 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001020 &error_local)) {
1021 g_debug ("first pass requirement on %s:%s failed: %s",
1022 fu_device_get_id (device),
1023 xb_node_query_text (component, "id", NULL),
1024 error_local->message);
1025 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1026 continue;
1027 }
1028
1029 /* make a second pass using possibly updated version format now */
1030 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1031 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001032 priv->request,
1033 task,
1034 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001035 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001036 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001037 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001038 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001039 error_local->message);
1040 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1041 continue;
1042 }
1043
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001044 /* if component should have an update message from CAB */
1045 fu_device_incorporate_from_component (device, component);
1046
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001047 /* success */
1048 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1049 }
1050 }
1051
1052 /* order the install tasks by the device priority */
1053 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1054
1055 /* nothing suitable */
1056 if (install_tasks->len == 0) {
1057 GError *error_tmp = fu_common_error_array_get_best (errors);
1058 g_propagate_error (error, error_tmp);
1059 return FALSE;
1060 }
1061
Mario Limonciello3f243a92019-01-21 22:05:23 -06001062 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001063 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001064 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001065
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001066 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001067 if (!fu_engine_install_tasks (priv->engine,
1068 priv->request,
1069 install_tasks,
1070 blob_cab,
1071 priv->flags,
1072 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001073 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001074
Mario Limonciello32241f42019-01-24 10:12:41 -06001075 fu_util_display_current_message (priv);
1076
Mario Limonciello3f243a92019-01-21 22:05:23 -06001077 /* we don't want to ask anything */
1078 if (priv->no_reboot_check) {
1079 g_debug ("skipping reboot check");
1080 return TRUE;
1081 }
1082
Mario Limonciello3143bad2019-02-27 07:31:00 -06001083 /* save the device state for other applications to see */
1084 if (!fu_util_save_current_state (priv, error))
1085 return FALSE;
1086
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001087 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001088 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001089}
1090
Richard Hughes98ca9932018-05-18 10:24:07 +01001091static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001092fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001093{
Mario Limonciellofd734852019-08-01 16:41:42 -05001094 FwupdRemote *remote;
1095 const gchar *remote_id;
1096 const gchar *uri_tmp;
1097 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001098
Mario Limonciellofd734852019-08-01 16:41:42 -05001099 uri_tmp = fwupd_release_get_uri (rel);
1100 if (uri_tmp == NULL) {
1101 g_set_error_literal (error,
1102 FWUPD_ERROR,
1103 FWUPD_ERROR_INVALID_FILE,
1104 "release missing URI");
1105 return FALSE;
1106 }
1107 remote_id = fwupd_release_get_remote_id (rel);
1108 if (remote_id == NULL) {
1109 g_set_error (error,
1110 FWUPD_ERROR,
1111 FWUPD_ERROR_INVALID_FILE,
1112 "failed to find remote for %s",
1113 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001114 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001115 }
1116
1117 remote = fu_engine_get_remote_by_id (priv->engine,
1118 remote_id,
1119 error);
1120 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001121 return FALSE;
1122
Mario Limonciellofd734852019-08-01 16:41:42 -05001123 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001124 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001125 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1126 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001127 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1128 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1129 argv[0] = g_build_filename (path, uri_tmp, NULL);
1130 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1131 argv[0] = g_strdup (uri_tmp + 7);
1132 /* web remote, fu_util_install will download file */
1133 } else {
1134 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1135 }
1136 return fu_util_install (priv, argv, error);
1137}
1138
1139static gboolean
1140fu_util_update_all (FuUtilPrivate *priv, GError **error)
1141{
1142 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001143 gboolean no_updates_header = FALSE;
1144 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001145
Mario Limonciello46aaee82019-01-10 12:58:00 -06001146 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001147 if (devices == NULL)
1148 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001149 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001150 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001151 for (guint i = 0; i < devices->len; i++) {
1152 FwupdDevice *dev = g_ptr_array_index (devices, i);
1153 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001154 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001155 g_autoptr(GPtrArray) rels = NULL;
1156 g_autoptr(GError) error_local = NULL;
1157
1158 if (!fu_util_is_interesting_device (dev))
1159 continue;
1160 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001161 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001162 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001163 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001164 if (!no_updates_header) {
1165 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1166 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1167 no_updates_header = TRUE;
1168 }
1169 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001170 continue;
1171 }
Richard Hughes747f5702019-08-06 14:27:26 +01001172 if (!fu_util_filter_device (priv, dev))
1173 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001174
1175 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001176 rels = fu_engine_get_upgrades (priv->engine,
1177 priv->request,
1178 device_id,
1179 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001180 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001181 if (!latest_header) {
1182 /* TRANSLATORS: message letting the user know no device upgrade available */
1183 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1184 latest_header = TRUE;
1185 }
1186 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001187 /* discard the actual reason from user, but leave for debugging */
1188 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001189 continue;
1190 }
1191
Mario Limonciello98b95162019-10-30 09:20:43 -05001192 if (!priv->no_safety_check) {
1193 if (!fu_util_prompt_warning (dev,
1194 fu_util_get_tree_title (priv),
1195 error))
1196 return FALSE;
1197 }
1198
Mario Limonciello46aaee82019-01-10 12:58:00 -06001199 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001200 if (!fu_util_install_release (priv, rel, &error_local)) {
1201 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001202 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001203 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001204 fu_util_display_current_message (priv);
1205 }
1206 return TRUE;
1207}
1208
1209static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001210fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001211{
1212 FwupdRelease *rel;
1213 g_autoptr(FuDevice) dev = NULL;
1214 g_autoptr(GPtrArray) rels = NULL;
1215
Mario Limonciello9917bb42020-04-20 13:41:27 -05001216 /* do not allow a partial device-id, lookup GUIDs */
1217 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001218 if (dev == NULL)
1219 return FALSE;
1220
1221 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001222 rels = fu_engine_get_upgrades (priv->engine,
1223 priv->request,
1224 fu_device_get_id (dev),
1225 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001226 if (rels == NULL)
1227 return FALSE;
1228 rel = g_ptr_array_index (rels, 0);
1229 if (!fu_util_install_release (priv, rel, error))
1230 return FALSE;
1231 fu_util_display_current_message (priv);
1232
1233 return TRUE;
1234}
1235
1236static gboolean
1237fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1238{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001239 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1240 g_set_error_literal (error,
1241 FWUPD_ERROR,
1242 FWUPD_ERROR_INVALID_ARGS,
1243 "--allow-older is not supported for this command");
1244 return FALSE;
1245 }
1246
1247 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1248 g_set_error_literal (error,
1249 FWUPD_ERROR,
1250 FWUPD_ERROR_INVALID_ARGS,
1251 "--allow-reinstall is not supported for this command");
1252 return FALSE;
1253 }
1254
Mario Limonciellofd734852019-08-01 16:41:42 -05001255 if (g_strv_length (values) > 1) {
1256 g_set_error_literal (error,
1257 FWUPD_ERROR,
1258 FWUPD_ERROR_INVALID_ARGS,
1259 "Invalid arguments");
1260 return FALSE;
1261 }
1262
Richard Hughesc7d870a2020-12-10 10:05:35 +00001263 if (!fu_util_start_engine (priv,
1264 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1265 FU_ENGINE_LOAD_FLAG_HWINFO |
1266 FU_ENGINE_LOAD_FLAG_REMOTES,
1267 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001268 return FALSE;
1269
1270 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1271 g_signal_connect (priv->engine, "device-changed",
1272 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1273
1274 if (g_strv_length (values) == 1) {
1275 if (!fu_util_update_by_id (priv, values[0], error))
1276 return FALSE;
1277 } else {
1278 if (!fu_util_update_all (priv, error))
1279 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001280 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001281
1282 /* we don't want to ask anything */
1283 if (priv->no_reboot_check) {
1284 g_debug ("skipping reboot check");
1285 return TRUE;
1286 }
1287
Mario Limonciello3143bad2019-02-27 07:31:00 -06001288 /* save the device state for other applications to see */
1289 if (!fu_util_save_current_state (priv, error))
1290 return FALSE;
1291
Mario Limonciello3f243a92019-01-21 22:05:23 -06001292 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001293}
1294
1295static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001296fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1297{
1298 g_autoptr(FwupdRelease) rel = NULL;
1299 g_autoptr(GPtrArray) rels = NULL;
1300 g_autoptr(FuDevice) dev = NULL;
1301
1302 if (g_strv_length (values) != 1) {
1303 g_set_error_literal (error,
1304 FWUPD_ERROR,
1305 FWUPD_ERROR_INVALID_ARGS,
1306 "Invalid arguments");
1307 return FALSE;
1308 }
1309
Richard Hughesc7d870a2020-12-10 10:05:35 +00001310 if (!fu_util_start_engine (priv,
1311 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1312 FU_ENGINE_LOAD_FLAG_HWINFO |
1313 FU_ENGINE_LOAD_FLAG_REMOTES,
1314 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001315 return FALSE;
1316
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001317 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001318 if (dev == NULL)
1319 return FALSE;
1320
1321 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001322 rels = fu_engine_get_releases_for_device (priv->engine,
1323 priv->request,
1324 dev,
1325 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001326 if (rels == NULL)
1327 return FALSE;
1328
1329 for (guint j = 0; j < rels->len; j++) {
1330 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1331 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1332 fu_device_get_version (dev),
1333 fu_device_get_version_format (dev)) == 0) {
1334 rel = g_object_ref (rel_tmp);
1335 break;
1336 }
1337 }
1338 if (rel == NULL) {
1339 g_set_error (error,
1340 FWUPD_ERROR,
1341 FWUPD_ERROR_NOT_SUPPORTED,
1342 "Unable to locate release for %s version %s",
1343 fu_device_get_name (dev),
1344 fu_device_get_version (dev));
1345 return FALSE;
1346 }
1347
1348 /* update the console if composite devices are also updated */
1349 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1350 g_signal_connect (priv->engine, "device-changed",
1351 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1352 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1353 if (!fu_util_install_release (priv, rel, error))
1354 return FALSE;
1355 fu_util_display_current_message (priv);
1356
1357 /* we don't want to ask anything */
1358 if (priv->no_reboot_check) {
1359 g_debug ("skipping reboot check");
1360 return TRUE;
1361 }
1362
1363 /* save the device state for other applications to see */
1364 if (!fu_util_save_current_state (priv, error))
1365 return FALSE;
1366
1367 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1368}
1369
1370static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001371fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1372{
1373 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001374 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001375
1376 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001377 if (!fu_util_start_engine (priv,
1378 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1379 FU_ENGINE_LOAD_FLAG_HWINFO |
1380 FU_ENGINE_LOAD_FLAG_REMOTES,
1381 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001382 return FALSE;
1383
Richard Hughes98ca9932018-05-18 10:24:07 +01001384 /* get device */
1385 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001386 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001387 if (device == NULL)
1388 return FALSE;
1389 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001390 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001391 if (device == NULL)
1392 return FALSE;
1393 }
1394
1395 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001396 locker = fu_device_locker_new (device, error);
1397 if (locker == NULL)
1398 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001399 return fu_device_detach (device, error);
1400}
1401
1402static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001403fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1404{
1405 g_autoptr(FuDevice) device = NULL;
1406 g_autoptr(FuDeviceLocker) locker = NULL;
1407
1408 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001409 if (!fu_util_start_engine (priv,
1410 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1411 FU_ENGINE_LOAD_FLAG_HWINFO |
1412 FU_ENGINE_LOAD_FLAG_REMOTES,
1413 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001414 return FALSE;
1415
1416 /* get device */
1417 if (g_strv_length (values) == 1) {
1418 device = fu_util_get_device (priv, values[0], error);
1419 } else {
1420 device = fu_util_prompt_for_device (priv, NULL, error);
1421 }
1422 if (device == NULL)
1423 return FALSE;
1424
1425 /* run vfunc */
1426 locker = fu_device_locker_new (device, error);
1427 if (locker == NULL)
1428 return FALSE;
1429 return fu_device_unbind_driver (device, error);
1430}
1431
1432static gboolean
1433fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1434{
1435 g_autoptr(FuDevice) device = NULL;
1436 g_autoptr(FuDeviceLocker) locker = NULL;
1437
1438 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001439 if (!fu_util_start_engine (priv,
1440 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1441 FU_ENGINE_LOAD_FLAG_HWINFO |
1442 FU_ENGINE_LOAD_FLAG_REMOTES,
1443 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001444 return FALSE;
1445
1446 /* get device */
1447 if (g_strv_length (values) == 3) {
1448 device = fu_util_get_device (priv, values[2], error);
1449 if (device == NULL)
1450 return FALSE;
1451 } else if (g_strv_length (values) == 2) {
1452 device = fu_util_prompt_for_device (priv, NULL, error);
1453 if (device == NULL)
1454 return FALSE;
1455 } else {
1456 g_set_error_literal (error,
1457 FWUPD_ERROR,
1458 FWUPD_ERROR_INVALID_ARGS,
1459 "Invalid arguments");
1460 return FALSE;
1461 }
1462
1463 /* run vfunc */
1464 locker = fu_device_locker_new (device, error);
1465 if (locker == NULL)
1466 return FALSE;
1467 return fu_device_bind_driver (device, values[0], values[1], error);
1468}
1469
1470static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001471fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1472{
1473 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001474 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001475
1476 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001477 if (!fu_util_start_engine (priv,
1478 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1479 FU_ENGINE_LOAD_FLAG_HWINFO |
1480 FU_ENGINE_LOAD_FLAG_REMOTES,
1481 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001482 return FALSE;
1483
Richard Hughes98ca9932018-05-18 10:24:07 +01001484 /* get device */
1485 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001486 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001487 if (device == NULL)
1488 return FALSE;
1489 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001490 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001491 if (device == NULL)
1492 return FALSE;
1493 }
1494
1495 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001496 locker = fu_device_locker_new (device, error);
1497 if (locker == NULL)
1498 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001499 return fu_device_attach (device, error);
1500}
1501
Richard Hughes1d894f12018-08-31 13:05:51 +01001502static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001503fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001504{
1505 gboolean has_pending = FALSE;
1506 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001507 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1508 if (devices == NULL)
1509 return FALSE;
1510
1511 /* only start up the plugins needed */
1512 for (guint i = 0; i < devices->len; i++) {
1513 FuDevice *dev = g_ptr_array_index (devices, i);
1514 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1515 fu_engine_add_plugin_filter (priv->engine,
1516 fu_device_get_plugin (dev));
1517 has_pending = TRUE;
1518 }
1519 }
1520
1521 if (!has_pending) {
1522 g_set_error_literal (error,
1523 FWUPD_ERROR,
1524 FWUPD_ERROR_NOTHING_TO_DO,
1525 "No devices to activate");
1526 return FALSE;
1527 }
1528
1529 return TRUE;
1530}
1531
1532static gboolean
1533fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1534{
1535 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001536 g_autoptr(GPtrArray) devices = NULL;
1537
1538 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001539 if (!fu_util_check_activation_needed (priv, error))
1540 return FALSE;
1541
1542 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001543 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY, error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001544 return FALSE;
1545
1546 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001547 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001548 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001549 if (devices == NULL)
1550 return FALSE;
1551 } else if (g_strv_length (values) == 1) {
1552 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001553 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001554 if (device == NULL)
1555 return FALSE;
1556 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1557 g_ptr_array_add (devices, device);
1558 } else {
1559 g_set_error_literal (error,
1560 FWUPD_ERROR,
1561 FWUPD_ERROR_INVALID_ARGS,
1562 "Invalid arguments");
1563 return FALSE;
1564 }
1565
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001566 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001567 /* order by device priority */
1568 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001569 for (guint i = 0; i < devices->len; i++) {
1570 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001571 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1572 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001573 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1574 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001575 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001576 /* TRANSLATORS: shown when shutting down to switch to the new version */
1577 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1578 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1579 return FALSE;
1580 }
1581
Mario Limonciello02085a02020-09-11 14:59:35 -05001582 if (!has_pending) {
1583 g_set_error_literal (error,
1584 FWUPD_ERROR,
1585 FWUPD_ERROR_NOTHING_TO_DO,
1586 "No devices to activate");
1587 return FALSE;
1588 }
1589
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001590 return TRUE;
1591}
1592
1593static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001594fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1595{
1596 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1597 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1598 const gchar *hwid_keys[] = {
1599 FU_HWIDS_KEY_BIOS_VENDOR,
1600 FU_HWIDS_KEY_BIOS_VERSION,
1601 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1602 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1603 FU_HWIDS_KEY_MANUFACTURER,
1604 FU_HWIDS_KEY_FAMILY,
1605 FU_HWIDS_KEY_PRODUCT_NAME,
1606 FU_HWIDS_KEY_PRODUCT_SKU,
1607 FU_HWIDS_KEY_ENCLOSURE_KIND,
1608 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1609 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1610 NULL };
1611
1612 /* read DMI data */
1613 if (g_strv_length (values) == 0) {
1614 if (!fu_smbios_setup (smbios, error))
1615 return FALSE;
1616 } else if (g_strv_length (values) == 1) {
1617 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1618 return FALSE;
1619 } else {
1620 g_set_error_literal (error,
1621 FWUPD_ERROR,
1622 FWUPD_ERROR_INVALID_ARGS,
1623 "Invalid arguments");
1624 return FALSE;
1625 }
1626 if (!fu_hwids_setup (hwids, smbios, error))
1627 return FALSE;
1628
1629 /* show debug output */
1630 g_print ("Computer Information\n");
1631 g_print ("--------------------\n");
1632 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1633 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1634 if (tmp == NULL)
1635 continue;
1636 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1637 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1638 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1639 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1640 } else {
1641 g_print ("%s: %s\n", hwid_keys[i], tmp);
1642 }
1643 }
1644
1645 /* show GUIDs */
1646 g_print ("\nHardware IDs\n");
1647 g_print ("------------\n");
1648 for (guint i = 0; i < 15; i++) {
1649 const gchar *keys = NULL;
1650 g_autofree gchar *guid = NULL;
1651 g_autofree gchar *key = NULL;
1652 g_autofree gchar *keys_str = NULL;
1653 g_auto(GStrv) keysv = NULL;
1654 g_autoptr(GError) error_local = NULL;
1655
1656 /* get the GUID */
1657 key = g_strdup_printf ("HardwareID-%u", i);
1658 keys = fu_hwids_get_replace_keys (hwids, key);
1659 guid = fu_hwids_get_guid (hwids, key, &error_local);
1660 if (guid == NULL) {
1661 g_print ("%s\n", error_local->message);
1662 continue;
1663 }
1664
1665 /* show what makes up the GUID */
1666 keysv = g_strsplit (keys, "&", -1);
1667 keys_str = g_strjoinv (" + ", keysv);
1668 g_print ("{%s} <- %s\n", guid, keys_str);
1669 }
1670
1671 return TRUE;
1672}
1673
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001674static gboolean
1675fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1676{
1677 const gchar *script_fn = "startup.sh";
1678 const gchar *output_fn = "firmware.bin";
1679 g_autoptr(GBytes) archive_blob = NULL;
1680 g_autoptr(GBytes) firmware_blob = NULL;
1681 if (g_strv_length (values) < 2) {
1682 g_set_error_literal (error,
1683 FWUPD_ERROR,
1684 FWUPD_ERROR_INVALID_ARGS,
1685 "Invalid arguments");
1686 return FALSE;
1687 }
1688 archive_blob = fu_common_get_contents_bytes (values[0], error);
1689 if (archive_blob == NULL)
1690 return FALSE;
1691 if (g_strv_length (values) > 2)
1692 script_fn = values[2];
1693 if (g_strv_length (values) > 3)
1694 output_fn = values[3];
1695 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1696 if (firmware_blob == NULL)
1697 return FALSE;
1698 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1699}
1700
Richard Hughes3d607622019-03-07 16:59:27 +00001701static gboolean
1702fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1703{
1704 g_autofree gchar *sig = NULL;
1705
1706 /* check args */
1707 if (g_strv_length (values) != 1) {
1708 g_set_error_literal (error,
1709 FWUPD_ERROR,
1710 FWUPD_ERROR_INVALID_ARGS,
1711 "Invalid arguments: value expected");
1712 return FALSE;
1713 }
1714
1715 /* start engine */
1716 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1717 return FALSE;
1718 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001719 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1720 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001721 if (sig == NULL)
1722 return FALSE;
1723 g_print ("%s\n", sig);
1724 return TRUE;
1725}
1726
Mario Limonciello62f84862018-10-18 13:15:23 -05001727static void
1728fu_util_device_added_cb (FwupdClient *client,
1729 FwupdDevice *device,
1730 gpointer user_data)
1731{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001732 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001733 /* TRANSLATORS: this is when a device is hotplugged */
1734 g_print ("%s\n%s", _("Device added:"), tmp);
1735}
1736
1737static void
1738fu_util_device_removed_cb (FwupdClient *client,
1739 FwupdDevice *device,
1740 gpointer user_data)
1741{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001742 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001743 /* TRANSLATORS: this is when a device is hotplugged */
1744 g_print ("%s\n%s", _("Device removed:"), tmp);
1745}
1746
1747static void
1748fu_util_device_changed_cb (FwupdClient *client,
1749 FwupdDevice *device,
1750 gpointer user_data)
1751{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001752 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001753 /* TRANSLATORS: this is when a device has been updated */
1754 g_print ("%s\n%s", _("Device changed:"), tmp);
1755}
1756
1757static void
1758fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1759{
1760 /* TRANSLATORS: this is when the daemon state changes */
1761 g_print ("%s\n", _("Changed"));
1762}
1763
1764static gboolean
1765fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1766{
1767 g_autoptr(FwupdClient) client = fwupd_client_new ();
1768
1769 /* get all the devices */
1770 if (!fwupd_client_connect (client, priv->cancellable, error))
1771 return FALSE;
1772
1773 /* watch for any hotplugged device */
1774 g_signal_connect (client, "changed",
1775 G_CALLBACK (fu_util_changed_cb), priv);
1776 g_signal_connect (client, "device-added",
1777 G_CALLBACK (fu_util_device_added_cb), priv);
1778 g_signal_connect (client, "device-removed",
1779 G_CALLBACK (fu_util_device_removed_cb), priv);
1780 g_signal_connect (client, "device-changed",
1781 G_CALLBACK (fu_util_device_changed_cb), priv);
1782 g_signal_connect (priv->cancellable, "cancelled",
1783 G_CALLBACK (fu_util_cancelled_cb), priv);
1784 g_main_loop_run (priv->loop);
1785 return TRUE;
1786}
1787
Richard Hughes15684492019-03-15 16:27:50 +00001788static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001789fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1790{
1791 g_autoptr(GPtrArray) firmware_types = NULL;
1792
1793 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001794 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001795 return FALSE;
1796
1797 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1798 for (guint i = 0; i < firmware_types->len; i++) {
1799 const gchar *id = g_ptr_array_index (firmware_types, i);
1800 g_print ("%s\n", id);
1801 }
1802 if (firmware_types->len == 0) {
1803 /* TRANSLATORS: nothing found */
1804 g_print ("%s\n", _("No firmware IDs found"));
1805 return TRUE;
1806 }
1807
1808 return TRUE;
1809}
1810
1811static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001812fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001813{
1814 g_autoptr(GPtrArray) firmware_types = NULL;
1815 guint idx;
1816 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1817
1818 /* TRANSLATORS: get interactive prompt */
1819 g_print ("%s\n", _("Choose a firmware type:"));
1820 /* TRANSLATORS: this is to abort the interactive prompt */
1821 g_print ("0.\t%s\n", _("Cancel"));
1822 for (guint i = 0; i < firmware_types->len; i++) {
1823 const gchar *id = g_ptr_array_index (firmware_types, i);
1824 g_print ("%u.\t%s\n", i + 1, id);
1825 }
1826 idx = fu_util_prompt_for_number (firmware_types->len);
1827 if (idx == 0) {
1828 g_set_error_literal (error,
1829 FWUPD_ERROR,
1830 FWUPD_ERROR_NOTHING_TO_DO,
1831 "Request canceled");
1832 return NULL;
1833 }
1834
1835 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1836}
1837
1838static gboolean
1839fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1840{
1841 GType gtype;
1842 g_autoptr(GBytes) blob = NULL;
1843 g_autoptr(FuFirmware) firmware = NULL;
1844 g_autofree gchar *firmware_type = NULL;
1845 g_autofree gchar *str = NULL;
1846
1847 /* check args */
1848 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1849 g_set_error_literal (error,
1850 FWUPD_ERROR,
1851 FWUPD_ERROR_INVALID_ARGS,
1852 "Invalid arguments: filename required");
1853 return FALSE;
1854 }
1855
1856 if (g_strv_length (values) == 2)
1857 firmware_type = g_strdup (values[1]);
1858
1859 /* load file */
1860 blob = fu_common_get_contents_bytes (values[0], error);
1861 if (blob == NULL)
1862 return FALSE;
1863
1864 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001865 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001866 return FALSE;
1867
1868 /* find the GType to use */
1869 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001870 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001871 if (firmware_type == NULL)
1872 return FALSE;
1873 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1874 if (gtype == G_TYPE_INVALID) {
1875 g_set_error (error,
1876 G_IO_ERROR,
1877 G_IO_ERROR_NOT_FOUND,
1878 "GType %s not supported", firmware_type);
1879 return FALSE;
1880 }
1881 firmware = g_object_new (gtype, NULL);
1882 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1883 return FALSE;
1884 str = fu_firmware_to_string (firmware);
1885 g_print ("%s", str);
1886 return TRUE;
1887}
1888
Richard Hughesdd653442020-09-22 10:23:52 +01001889static gboolean
1890fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1891{
1892 GType gtype;
1893 g_autofree gchar *firmware_type = NULL;
1894 g_autofree gchar *str = NULL;
1895 g_autoptr(FuFirmware) firmware = NULL;
1896 g_autoptr(GBytes) blob = NULL;
1897 g_autoptr(GPtrArray) images = NULL;
1898
1899 /* check args */
1900 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1901 g_set_error_literal (error,
1902 FWUPD_ERROR,
1903 FWUPD_ERROR_INVALID_ARGS,
1904 "Invalid arguments: filename required");
1905 return FALSE;
1906 }
1907 if (g_strv_length (values) == 2)
1908 firmware_type = g_strdup (values[1]);
1909
1910 /* load file */
1911 blob = fu_common_get_contents_bytes (values[0], error);
1912 if (blob == NULL)
1913 return FALSE;
1914
1915 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001916 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01001917 return FALSE;
1918
1919 /* find the GType to use */
1920 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001921 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001922 if (firmware_type == NULL)
1923 return FALSE;
1924 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1925 if (gtype == G_TYPE_INVALID) {
1926 g_set_error (error,
1927 G_IO_ERROR,
1928 G_IO_ERROR_NOT_FOUND,
1929 "GType %s not supported", firmware_type);
1930 return FALSE;
1931 }
1932 firmware = g_object_new (gtype, NULL);
1933 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1934 return FALSE;
1935 str = fu_firmware_to_string (firmware);
1936 g_print ("%s", str);
1937 images = fu_firmware_get_images (firmware);
1938 for (guint i = 0; i < images->len; i++) {
1939 FuFirmwareImage *img = g_ptr_array_index (images, i);
1940 g_autofree gchar *fn = NULL;
1941 g_autoptr(GBytes) blob_img = NULL;
1942
Richard Hughes88dd7c42020-09-22 16:54:40 +01001943 /* get raw image without generated header, footer or crc */
1944 blob_img = fu_firmware_image_get_bytes (img);
1945 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1946 continue;
1947
Richard Hughesdd653442020-09-22 10:23:52 +01001948 /* use suitable filename */
1949 if (fu_firmware_image_get_filename (img) != NULL) {
1950 fn = g_strdup (fu_firmware_image_get_filename (img));
1951 } else if (fu_firmware_image_get_id (img) != NULL) {
1952 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1953 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1954 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1955 } else {
1956 fn = g_strdup_printf ("img-0x%x.fw", i);
1957 }
1958 /* TRANSLATORS: decompressing images from a container firmware */
1959 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01001960 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1961 return FALSE;
1962 }
1963
1964 /* success */
1965 return TRUE;
1966}
1967
Richard Hughes0924c932020-09-22 19:07:05 +01001968static gboolean
1969fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01001970{
Richard Hughes0924c932020-09-22 19:07:05 +01001971 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01001972 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01001973 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001974 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01001975 g_autoptr(FuFirmware) firmware_dst = NULL;
1976 g_autoptr(GBytes) blob_dst = NULL;
1977 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001978 g_autoptr(XbBuilder) builder = xb_builder_new ();
1979 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1980 g_autoptr(XbNode) n = NULL;
1981 g_autoptr(XbSilo) silo = NULL;
1982
Richard Hughes0924c932020-09-22 19:07:05 +01001983 /* check args */
1984 if (g_strv_length (values) != 2) {
1985 g_set_error_literal (error,
1986 FWUPD_ERROR,
1987 FWUPD_ERROR_INVALID_ARGS,
1988 "Invalid arguments: filename required");
1989 return FALSE;
1990 }
1991
1992 /* load file */
1993 blob_src = fu_common_get_contents_bytes (values[0], error);
1994 if (blob_src == NULL)
1995 return FALSE;
1996
1997 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001998 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01001999 return FALSE;
2000
Richard Hughes41400a82020-09-21 13:43:15 +01002001 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002002 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002003 XB_BUILDER_SOURCE_FLAG_NONE,
2004 error)) {
2005 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002006 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002007 }
2008 xb_builder_import_source (builder, source);
2009 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2010 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002011 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002012
2013 /* create FuFirmware of specific GType */
2014 n = xb_silo_query_first (silo, "firmware", error);
2015 if (n == NULL)
2016 return FALSE;
2017 tmp = xb_node_get_attr (n, "gtype");
2018 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002019 gtype = g_type_from_name (tmp);
2020 if (gtype == G_TYPE_INVALID) {
2021 g_set_error (error,
2022 G_IO_ERROR,
2023 G_IO_ERROR_NOT_FOUND,
2024 "GType %s not registered", tmp);
2025 return FALSE;
2026 }
2027 }
2028 tmp = xb_node_get_attr (n, "id");
2029 if (tmp != NULL) {
2030 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002031 if (gtype == G_TYPE_INVALID) {
2032 g_set_error (error,
2033 G_IO_ERROR,
2034 G_IO_ERROR_NOT_FOUND,
2035 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002036 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002037 }
Richard Hughes41400a82020-09-21 13:43:15 +01002038 }
Richard Hughes0924c932020-09-22 19:07:05 +01002039 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002040 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002041 return FALSE;
2042
2043 /* write new file */
2044 blob_dst = fu_firmware_write (firmware, error);
2045 if (blob_dst == NULL)
2046 return FALSE;
2047 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2048 return FALSE;
2049
2050 /* show what we wrote */
2051 firmware_dst = g_object_new (gtype, NULL);
2052 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2053 return FALSE;
2054 str = fu_firmware_to_string (firmware_dst);
2055 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002056
2057 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002058 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002059}
2060
Richard Hughes95c98a92019-10-22 16:03:15 +01002061static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002062fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2063{
2064 GType gtype_dst;
2065 GType gtype_src;
2066 g_autofree gchar *firmware_type_dst = NULL;
2067 g_autofree gchar *firmware_type_src = NULL;
2068 g_autofree gchar *str_dst = NULL;
2069 g_autofree gchar *str_src = NULL;
2070 g_autoptr(FuFirmware) firmware_dst = NULL;
2071 g_autoptr(FuFirmware) firmware_src = NULL;
2072 g_autoptr(GBytes) blob_dst = NULL;
2073 g_autoptr(GBytes) blob_src = NULL;
2074 g_autoptr(GPtrArray) images = NULL;
2075
2076 /* check args */
2077 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2078 g_set_error_literal (error,
2079 FWUPD_ERROR,
2080 FWUPD_ERROR_INVALID_ARGS,
2081 "Invalid arguments: filename required");
2082 return FALSE;
2083 }
2084
2085 if (g_strv_length (values) > 2)
2086 firmware_type_src = g_strdup (values[2]);
2087 if (g_strv_length (values) > 3)
2088 firmware_type_dst = g_strdup (values[3]);
2089
2090 /* load file */
2091 blob_src = fu_common_get_contents_bytes (values[0], error);
2092 if (blob_src == NULL)
2093 return FALSE;
2094
2095 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002096 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002097 return FALSE;
2098
2099 /* find the GType to use */
2100 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002101 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002102 if (firmware_type_src == NULL)
2103 return FALSE;
2104 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002105 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002106 if (firmware_type_dst == NULL)
2107 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002108 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2109 if (gtype_src == G_TYPE_INVALID) {
2110 g_set_error (error,
2111 G_IO_ERROR,
2112 G_IO_ERROR_NOT_FOUND,
2113 "GType %s not supported", firmware_type_src);
2114 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002115 }
Richard Hughes0924c932020-09-22 19:07:05 +01002116 firmware_src = g_object_new (gtype_src, NULL);
2117 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2118 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002119 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2120 if (gtype_dst == G_TYPE_INVALID) {
2121 g_set_error (error,
2122 G_IO_ERROR,
2123 G_IO_ERROR_NOT_FOUND,
2124 "GType %s not supported", firmware_type_dst);
2125 return FALSE;
2126 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002127 str_src = fu_firmware_to_string (firmware_src);
2128 g_print ("%s", str_src);
2129
2130 /* copy images */
2131 firmware_dst = g_object_new (gtype_dst, NULL);
2132 images = fu_firmware_get_images (firmware_src);
2133 for (guint i = 0; i < images->len; i++) {
2134 FuFirmwareImage *img = g_ptr_array_index (images, i);
2135 fu_firmware_add_image (firmware_dst, img);
2136 }
2137
2138 /* write new file */
2139 blob_dst = fu_firmware_write (firmware_dst, error);
2140 if (blob_dst == NULL)
2141 return FALSE;
2142 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2143 return FALSE;
2144 str_dst = fu_firmware_to_string (firmware_dst);
2145 g_print ("%s", str_dst);
2146
2147 /* success */
2148 return TRUE;
2149}
2150
2151static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002152fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2153{
2154 g_autofree gchar *str = NULL;
2155 g_autoptr(FuDevice) dev = NULL;
2156
2157 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002158 if (!fu_util_start_engine (priv,
2159 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2160 FU_ENGINE_LOAD_FLAG_HWINFO |
2161 FU_ENGINE_LOAD_FLAG_REMOTES,
2162 error))
Richard Hughes15684492019-03-15 16:27:50 +00002163 return FALSE;
2164
2165 /* get device */
2166 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002167 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002168 if (dev == NULL)
2169 return FALSE;
2170 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002171 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002172 if (dev == NULL)
2173 return FALSE;
2174 }
2175
2176 /* add checksums */
2177 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2178 return FALSE;
2179
2180 /* show checksums */
2181 str = fu_device_to_string (dev);
2182 g_print ("%s\n", str);
2183 return TRUE;
2184}
2185
Mario Limonciellofe593942019-04-03 13:48:52 -05002186static gboolean
2187fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2188{
2189 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002190 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002191 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002192
2193 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002194 if (!fu_util_start_engine (priv,
2195 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2196 FU_ENGINE_LOAD_FLAG_HWINFO |
2197 FU_ENGINE_LOAD_FLAG_REMOTES,
2198 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002199 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002200 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002201
2202 /* get all devices from the history database */
2203 devices = fu_engine_get_history (priv->engine, error);
2204 if (devices == NULL)
2205 return FALSE;
2206
2207 /* show each device */
2208 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002209 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002210 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002211 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002212 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002213 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002214
Richard Hughes747f5702019-08-06 14:27:26 +01002215 if (!fu_util_filter_device (priv, dev))
2216 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002217 child = g_node_append_data (root, dev);
2218
2219 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002220 if (rel == NULL)
2221 continue;
2222 remote = fwupd_release_get_remote_id (rel);
2223
2224 /* doesn't actually map to remote */
2225 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002226 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002227 continue;
2228 }
2229
2230 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002231 rels = fu_engine_get_releases (priv->engine,
2232 priv->request,
2233 fwupd_device_get_id (dev),
2234 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002235 if (rels == NULL)
2236 return FALSE;
2237
2238 /* map to a release in client */
2239 for (guint j = 0; j < rels->len; j++) {
2240 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2241 if (g_strcmp0 (remote,
2242 fwupd_release_get_remote_id (rel2)) != 0)
2243 continue;
2244 if (g_strcmp0 (fwupd_release_get_version (rel),
2245 fwupd_release_get_version (rel2)) != 0)
2246 continue;
2247 g_node_append_data (child, g_object_ref (rel2));
2248 rel = NULL;
2249 break;
2250 }
2251
2252 /* didn't match anything */
2253 if (rels->len == 0 || rel != NULL) {
2254 g_node_append_data (child, rel);
2255 continue;
2256 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002257
Mario Limonciellofe593942019-04-03 13:48:52 -05002258 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002259 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002260
2261 return TRUE;
2262}
2263
Richard Hughesfd7e9942020-01-17 14:09:13 +00002264static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002265fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2266{
Richard Hughesc5710d92020-06-26 11:08:25 +01002267 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002268 g_autofree gchar *fn_raw = NULL;
2269 g_autofree gchar *fn_sig = NULL;
2270 g_autoptr(GBytes) bytes_raw = NULL;
2271 g_autoptr(GBytes) bytes_sig = NULL;
2272
Richard Hughes4959baa2020-01-17 14:33:00 +00002273 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002274 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2275 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002276 g_set_error (error,
2277 FWUPD_ERROR,
2278 FWUPD_ERROR_NOTHING_TO_DO,
2279 "no metadata signature URI available for %s",
2280 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002281 return FALSE;
2282 }
2283 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002284 if (!fu_common_mkdir_parent (fn_sig, error))
2285 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002286 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002287 return FALSE;
2288 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2289 if (bytes_sig == NULL)
2290 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002291 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2292 return FALSE;
2293
2294 /* payload */
2295 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2296 if (metadata_uri == NULL) {
2297 g_set_error (error,
2298 FWUPD_ERROR,
2299 FWUPD_ERROR_NOTHING_TO_DO,
2300 "no metadata URI available for %s",
2301 fwupd_remote_get_id (remote));
2302 return FALSE;
2303 }
2304 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2305 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2306 return FALSE;
2307 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2308 if (bytes_raw == NULL)
2309 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002310
2311 /* send to daemon */
2312 g_debug ("updating %s", fwupd_remote_get_id (remote));
2313 return fu_engine_update_metadata_bytes (priv->engine,
2314 fwupd_remote_get_id (remote),
2315 bytes_raw,
2316 bytes_sig,
2317 error);
2318
2319}
2320
2321static gboolean
2322fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2323{
2324 g_autoptr(GPtrArray) remotes = NULL;
2325
2326 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002327 if (!fu_util_start_engine (priv,
2328 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2329 FU_ENGINE_LOAD_FLAG_HWINFO |
2330 FU_ENGINE_LOAD_FLAG_REMOTES,
2331 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002332 return FALSE;
2333
2334 /* download new metadata */
2335 remotes = fu_engine_get_remotes (priv->engine, error);
2336 if (remotes == NULL)
2337 return FALSE;
2338 for (guint i = 0; i < remotes->len; i++) {
2339 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2340 if (!fwupd_remote_get_enabled (remote))
2341 continue;
2342 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2343 continue;
2344 if (!fu_util_refresh_remote (priv, remote, error))
2345 return FALSE;
2346 }
2347 return TRUE;
2348}
2349
2350static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002351fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2352{
2353 g_autoptr(GNode) root = g_node_new (NULL);
2354 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002355 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002356
2357 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002358 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002359 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002360 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002361
2362 /* list remotes */
2363 remotes = fu_engine_get_remotes (priv->engine, error);
2364 if (remotes == NULL)
2365 return FALSE;
2366 if (remotes->len == 0) {
2367 g_set_error_literal (error,
2368 FWUPD_ERROR,
2369 FWUPD_ERROR_NOTHING_TO_DO,
2370 "no remotes available");
2371 return FALSE;
2372 }
2373 for (guint i = 0; i < remotes->len; i++) {
2374 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2375 g_node_append_data (root, remote_tmp);
2376 }
2377 fu_util_print_tree (root, title);
2378
2379 return TRUE;
2380}
2381
Richard Hughes196c6c62020-05-11 19:42:47 +01002382static gboolean
2383fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2384{
Richard Hughes5c82b942020-09-14 12:24:06 +01002385 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002386 g_autoptr(FuSecurityAttrs) attrs = NULL;
2387 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002388 g_autofree gchar *str = NULL;
2389
2390 /* not ready yet */
2391 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2392 g_set_error_literal (error,
2393 FWUPD_ERROR,
2394 FWUPD_ERROR_NOT_SUPPORTED,
2395 "The HSI specification is not yet complete. "
2396 "To ignore this warning, use --force");
2397 return FALSE;
2398 }
2399
Richard Hughesc7d870a2020-12-10 10:05:35 +00002400 if (!fu_util_start_engine (priv,
2401 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2402 FU_ENGINE_LOAD_FLAG_HWINFO |
2403 FU_ENGINE_LOAD_FLAG_REMOTES,
2404 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002405 return FALSE;
2406
2407 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2408 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2409 fu_engine_get_host_security_id (priv->engine));
2410
Richard Hughes5c82b942020-09-14 12:24:06 +01002411 /* show or hide different elements */
2412 if (priv->show_all) {
2413 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2414 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2415 }
2416
Richard Hughes196c6c62020-05-11 19:42:47 +01002417 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002418 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002419 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002420 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002421 g_print ("%s\n", str);
2422 return TRUE;
2423}
2424
Richard Hughesa83deb42020-08-12 12:45:36 +01002425static FuVolume *
2426fu_util_prompt_for_volume (GError **error)
2427{
2428 FuVolume *volume;
2429 guint idx;
2430 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002431 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2432 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002433
2434 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002435 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2436 if (volumes == NULL) {
2437 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2438 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2439 if (volumes == NULL) {
2440 g_prefix_error (error, "%s: ", error_local->message);
2441 return NULL;
2442 }
2443 }
2444 /* only add internal vfat partitions */
2445 for (guint i = 0; i < volumes->len; i++) {
2446 FuVolume *vol = g_ptr_array_index (volumes, i);
2447 g_autofree gchar *type = fu_volume_get_id_type (vol);
2448 if (type == NULL)
2449 continue;
2450 if (!fu_volume_is_internal (vol))
2451 continue;
2452 if (g_strcmp0 (type, "vfat") == 0)
2453 g_ptr_array_add (volumes_vfat, vol);
2454 }
2455 if (volumes_vfat->len == 1) {
2456 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002457 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002458 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2459 return g_object_ref (volume);
2460 }
2461
2462 /* TRANSLATORS: get interactive prompt */
2463 g_print ("%s\n", _("Choose a volume:"));
2464 /* TRANSLATORS: this is to abort the interactive prompt */
2465 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002466 for (guint i = 0; i < volumes_vfat->len; i++) {
2467 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002468 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2469 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002470 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002471 if (idx == 0) {
2472 g_set_error_literal (error,
2473 FWUPD_ERROR,
2474 FWUPD_ERROR_NOTHING_TO_DO,
2475 "Request canceled");
2476 return NULL;
2477 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002478 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002479 return g_object_ref (volume);
2480
2481}
2482
2483static gboolean
2484fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2485{
2486 g_autoptr(FuVolume) volume = NULL;
2487 volume = fu_util_prompt_for_volume (error);
2488 if (volume == NULL)
2489 return FALSE;
2490 return fu_volume_mount (volume, error);
2491}
2492
2493static gboolean
2494fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2495{
2496 g_autoptr(FuVolume) volume = NULL;
2497 volume = fu_util_prompt_for_volume (error);
2498 if (volume == NULL)
2499 return FALSE;
2500 return fu_volume_unmount (volume, error);
2501}
2502
2503static gboolean
2504fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2505{
2506 g_autofree gchar *mount_point = NULL;
2507 g_autoptr(FuDeviceLocker) locker = NULL;
2508 g_autoptr(FuVolume) volume = NULL;
2509 g_autoptr(GPtrArray) files = NULL;
2510
2511 volume = fu_util_prompt_for_volume (error);
2512 if (volume == NULL)
2513 return FALSE;
2514 locker = fu_volume_locker (volume, error);
2515 if (locker == NULL)
2516 return FALSE;
2517 mount_point = fu_volume_get_mount_point (volume);
2518 files = fu_common_get_files_recursive (mount_point, error);
2519 if (files == NULL)
2520 return FALSE;
2521 for (guint i = 0; i < files->len; i++) {
2522 const gchar *fn = g_ptr_array_index (files, i);
2523 g_print ("%s\n", fn);
2524 }
2525 return TRUE;
2526}
2527
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002528
2529static gboolean
2530fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2531{
2532 const gchar *branch;
2533 g_autoptr(FwupdRelease) rel = NULL;
2534 g_autoptr(GPtrArray) rels = NULL;
2535 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2536 g_autoptr(FuDevice) dev = NULL;
2537
2538 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002539 if (!fu_util_start_engine (priv,
2540 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2541 FU_ENGINE_LOAD_FLAG_HWINFO |
2542 FU_ENGINE_LOAD_FLAG_REMOTES,
2543 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002544 return FALSE;
2545
2546 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002547 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002548 if (g_strv_length (values) == 1)
2549 dev = fu_util_get_device (priv, values[1], error);
2550 else
2551 dev = fu_util_prompt_for_device (priv, NULL, error);
2552 if (dev == NULL)
2553 return FALSE;
2554 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2555 g_set_error_literal (error,
2556 FWUPD_ERROR,
2557 FWUPD_ERROR_NOT_SUPPORTED,
2558 "Multiple branches not available");
2559 return FALSE;
2560 }
2561
2562 /* get all releases, including the alternate branch versions */
2563 rels = fu_engine_get_releases (priv->engine,
2564 priv->request,
2565 fu_device_get_id (dev),
2566 error);
2567 if (rels == NULL)
2568 return FALSE;
2569
2570 /* get all the unique branches */
2571 for (guint i = 0; i < rels->len; i++) {
2572 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
2573 const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00002574#if GLIB_CHECK_VERSION(2,54,3)
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002575 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
2576 g_str_equal, NULL))
2577 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00002578#endif
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002579 g_ptr_array_add (branches, g_strdup (branch_tmp));
2580 }
2581
2582 /* branch name is optional */
2583 if (g_strv_length (values) > 1) {
2584 branch = values[1];
2585 } else if (branches->len == 1) {
2586 branch = g_ptr_array_index (branches, 0);
2587 } else {
2588 guint idx;
2589
2590 /* TRANSLATORS: get interactive prompt, where branch is the
2591 * supplier of the firmware, e.g. "non-free" or "free" */
2592 g_print ("%s\n", _("Choose a branch:"));
2593 /* TRANSLATORS: this is to abort the interactive prompt */
2594 g_print ("0.\t%s\n", _("Cancel"));
2595 for (guint i = 0; i < branches->len; i++) {
2596 const gchar *branch_tmp = g_ptr_array_index (branches, i);
2597 g_print ("%u.\t%s\n", i + 1, branch_tmp);
2598 }
2599 idx = fu_util_prompt_for_number (branches->len);
2600 if (idx == 0) {
2601 g_set_error_literal (error,
2602 FWUPD_ERROR,
2603 FWUPD_ERROR_NOTHING_TO_DO,
2604 "Request canceled");
2605 return FALSE;
2606 }
2607 branch = g_ptr_array_index (branches, idx - 1);
2608 }
2609
2610 /* sanity check */
2611 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2612 g_set_error (error,
2613 FWUPD_ERROR,
2614 FWUPD_ERROR_NOT_SUPPORTED,
2615 "Device %s is already on branch %s",
2616 fu_device_get_name (dev),
2617 branch);
2618 return FALSE;
2619 }
2620
2621 /* the releases are ordered by version */
2622 for (guint j = 0; j < rels->len; j++) {
2623 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2624 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2625 rel = g_object_ref (rel_tmp);
2626 break;
2627 }
2628 }
2629 if (rel == NULL) {
2630 g_set_error (error,
2631 FWUPD_ERROR,
2632 FWUPD_ERROR_NOT_SUPPORTED,
2633 "No releases for branch %s",
2634 branch);
2635 return FALSE;
2636 }
2637
2638 /* we're switching branch */
2639 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2640 return FALSE;
2641
2642 /* update the console if composite devices are also updated */
2643 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2644 g_signal_connect (priv->engine, "device-changed",
2645 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2646 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2647 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2648 if (!fu_util_install_release (priv, rel, error))
2649 return FALSE;
2650 fu_util_display_current_message (priv);
2651
2652 /* we don't want to ask anything */
2653 if (priv->no_reboot_check) {
2654 g_debug ("skipping reboot check");
2655 return TRUE;
2656 }
2657
2658 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2659}
2660
Richard Hughesb5976832018-05-18 10:02:09 +01002661int
2662main (int argc, char *argv[])
2663{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002664 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002665 gboolean allow_older = FALSE;
2666 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002667 gboolean force = FALSE;
2668 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002669 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002670 gboolean ignore_checksum = FALSE;
2671 gboolean ignore_power = FALSE;
2672 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002673 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002674 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002675 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2676 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002677 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002678 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002679 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002680 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002681 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2682 /* TRANSLATORS: command line option */
2683 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002684 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2685 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002686 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002687 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2688 /* TRANSLATORS: command line option */
2689 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002690 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2691 /* TRANSLATORS: command line option */
2692 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002693 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2694 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002695 _("Force the action by relaxing some runtime checks"), NULL },
2696 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2697 /* TRANSLATORS: command line option */
2698 _("Ignore firmware checksum failures"), NULL },
2699 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2700 /* TRANSLATORS: command line option */
2701 _("Ignore firmware hardware mismatch failures"), NULL },
2702 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2703 /* TRANSLATORS: command line option */
2704 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002705 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2706 /* TRANSLATORS: command line option */
2707 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002708 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2709 /* TRANSLATORS: command line option */
2710 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002711 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2712 /* TRANSLATORS: command line option */
2713 _("Show all results"), NULL },
2714 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002715 /* TRANSLATORS: command line option */
2716 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002717 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002718 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002719 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002720 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2721 /* TRANSLATORS: command line option */
2722 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002723 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002724 /* TRANSLATORS: command line option */
2725 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002726 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002727 /* TRANSLATORS: command line option */
2728 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002729 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2730 /* TRANSLATORS: command line option */
2731 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002732 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2733 /* TRANSLATORS: command line option */
2734 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002735 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2736 /* TRANSLATORS: command line option */
2737 _("Filter with a set of device flags using a ~ prefix to "
2738 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002739 { NULL}
2740 };
2741
Richard Hughes429f72b2020-01-16 12:18:19 +00002742#ifdef _WIN32
2743 /* workaround Windows setting the codepage to 1252 */
2744 g_setenv ("LANG", "C.UTF-8", FALSE);
2745#endif
2746
Richard Hughesb5976832018-05-18 10:02:09 +01002747 setlocale (LC_ALL, "");
2748
Richard Hughes668ee212019-11-22 09:17:46 +00002749 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002750 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2751 textdomain (GETTEXT_PACKAGE);
2752
Richard Hughes01c0bad2019-11-22 09:08:51 +00002753#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002754 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002755 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002756 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002757 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002758#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002759
2760 /* create helper object */
2761 priv->loop = g_main_loop_new (NULL, FALSE);
2762 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002763 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002764
2765 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002766 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002767 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002768 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2769 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002770 /* TRANSLATORS: command description */
2771 _("Build firmware using a sandbox"),
2772 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002773 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002774 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002775 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2776 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002777 /* TRANSLATORS: command description */
2778 _("Dump SMBIOS data from a file"),
2779 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002780 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002781 "get-plugins",
2782 NULL,
2783 /* TRANSLATORS: command description */
2784 _("Get all enabled plugins registered with the system"),
2785 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002786 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002787 "get-details",
2788 NULL,
2789 /* TRANSLATORS: command description */
2790 _("Gets details about a firmware file"),
2791 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002792 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002793 "get-history",
2794 NULL,
2795 /* TRANSLATORS: command description */
2796 _("Show history of firmware updates"),
2797 fu_util_get_history);
2798 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002799 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002800 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2801 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002802 /* TRANSLATORS: command description */
2803 _("Gets the list of updates for connected hardware"),
2804 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002805 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002806 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002807 NULL,
2808 /* TRANSLATORS: command description */
2809 _("Get all devices that support firmware updates"),
2810 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002811 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002812 "get-device-flags",
2813 NULL,
2814 /* TRANSLATORS: command description */
2815 _("Get all device flags supported by fwupd"),
2816 fu_util_get_device_flags);
2817 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002818 "watch",
2819 NULL,
2820 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002821 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002822 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002823 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002824 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002825 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2826 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002827 /* TRANSLATORS: command description */
2828 _("Install a firmware blob on a device"),
2829 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002830 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002831 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002832 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2833 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002834 /* TRANSLATORS: command description */
2835 _("Install a firmware file on this hardware"),
2836 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002837 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002838 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002839 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2840 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002841 /* TRANSLATORS: command description */
2842 _("Reinstall firmware on a device"),
2843 fu_util_reinstall);
2844 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002845 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002846 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2847 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002848 /* TRANSLATORS: command description */
2849 _("Attach to firmware mode"),
2850 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002851 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002852 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002853 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2854 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002855 /* TRANSLATORS: command description */
2856 _("Detach to bootloader mode"),
2857 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002858 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002859 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002860 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2861 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002862 /* TRANSLATORS: command description */
2863 _("Unbind current driver"),
2864 fu_util_unbind_driver);
2865 fu_util_cmd_array_add (cmd_array,
2866 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002867 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2868 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002869 /* TRANSLATORS: command description */
2870 _("Bind new kernel driver"),
2871 fu_util_bind_driver);
2872 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002873 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002874 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2875 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002876 /* TRANSLATORS: command description */
2877 _("Activate pending devices"),
2878 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002879 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002880 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002881 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2882 _("[FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002883 /* TRANSLATORS: command description */
2884 _("Return all the hardware IDs for the machine"),
2885 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002886 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002887 "monitor",
2888 NULL,
2889 /* TRANSLATORS: command description */
2890 _("Monitor the daemon for events"),
2891 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002892 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002893 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002894 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2895 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002896 /* TRANSLATORS: command description */
2897 _("Update all devices that match local metadata"),
2898 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002899 fu_util_cmd_array_add (cmd_array,
2900 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002901 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2902 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002903 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002904 C_("command-description",
2905 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002906 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002907 fu_util_cmd_array_add (cmd_array,
2908 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002909 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2910 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002911 /* TRANSLATORS: command description */
2912 _("Update the stored metadata with current contents"),
2913 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002914 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002915 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002916 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2917 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002918 /* TRANSLATORS: command description */
2919 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002920 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002921 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002922 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002923 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2924 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002925 /* TRANSLATORS: command description */
2926 _("Convert a firmware file"),
2927 fu_util_firmware_convert);
2928 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002929 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002930 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2931 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002932 /* TRANSLATORS: command description */
2933 _("Build a firmware file"),
2934 fu_util_firmware_build);
2935 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002936 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002937 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2938 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01002939 /* TRANSLATORS: command description */
2940 _("Parse and show details about a firmware file"),
2941 fu_util_firmware_parse);
2942 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002943 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002944 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2945 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01002946 /* TRANSLATORS: command description */
2947 _("Extract a firmware blob to images"),
2948 fu_util_firmware_extract);
2949 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002950 "get-firmware-types",
2951 NULL,
2952 /* TRANSLATORS: command description */
2953 _("List the available firmware types"),
2954 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002955 fu_util_cmd_array_add (cmd_array,
2956 "get-remotes",
2957 NULL,
2958 /* TRANSLATORS: command description */
2959 _("Gets the configured remotes"),
2960 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002961 fu_util_cmd_array_add (cmd_array,
2962 "refresh",
2963 NULL,
2964 /* TRANSLATORS: command description */
2965 _("Refresh metadata from remote server"),
2966 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002967 fu_util_cmd_array_add (cmd_array,
2968 "security",
2969 NULL,
2970 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002971 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01002972 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002973 fu_util_cmd_array_add (cmd_array,
2974 "esp-mount",
2975 NULL,
2976 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002977 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002978 fu_util_esp_mount);
2979 fu_util_cmd_array_add (cmd_array,
2980 "esp-unmount",
2981 NULL,
2982 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002983 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002984 fu_util_esp_unmount);
2985 fu_util_cmd_array_add (cmd_array,
2986 "esp-list",
2987 NULL,
2988 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002989 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002990 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002991 fu_util_cmd_array_add (cmd_array,
2992 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002993 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2994 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002995 /* TRANSLATORS: command description */
2996 _("Switch the firmware branch on the device"),
2997 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01002998
2999 /* do stuff on ctrl+c */
3000 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003001#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003002 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3003 SIGINT, fu_util_sigint_cb,
3004 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003005#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003006 g_signal_connect (priv->cancellable, "cancelled",
3007 G_CALLBACK (fu_util_cancelled_cb), priv);
3008
3009 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003010 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003011
Mario Limonciello3f243a92019-01-21 22:05:23 -06003012 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003013 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003014 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003015 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003016 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003017 } else {
3018 /* set our implemented feature set */
3019 fu_engine_request_set_feature_flags (priv->request,
3020 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003021 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003022 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003023 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003024
Richard Hughesb5976832018-05-18 10:02:09 +01003025 /* get a list of the commands */
3026 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003027 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003028 g_option_context_set_summary (priv->context, cmd_descriptions);
3029 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003030 /* TRANSLATORS: CLI description */
3031 _("This tool allows an administrator to use the fwupd plugins "
3032 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003033
3034 /* TRANSLATORS: program name */
3035 g_set_application_name (_("Firmware Utility"));
3036 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003037 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003038 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3039 if (!ret) {
3040 /* TRANSLATORS: the user didn't read the man page */
3041 g_print ("%s: %s\n", _("Failed to parse arguments"),
3042 error->message);
3043 return EXIT_FAILURE;
3044 }
3045
Richard Hughes0e46b222019-09-05 12:13:35 +01003046 /* allow disabling SSL strict mode for broken corporate proxies */
3047 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003048 g_autofree gchar *fmt = NULL;
3049 /* TRANSLATORS: this is a prefix on the console */
3050 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003051 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003052 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3053 "to do this automatically in the future "
3054 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003055 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3056 }
3057
Richard Hughes747f5702019-08-06 14:27:26 +01003058 /* parse filter flags */
3059 if (filter != NULL) {
3060 if (!fu_util_parse_filter_flags (filter,
3061 &priv->filter_include,
3062 &priv->filter_exclude,
3063 &error)) {
3064 /* TRANSLATORS: the user didn't read the man page */
3065 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3066 error->message);
3067 return EXIT_FAILURE;
3068 }
3069 }
3070
3071
Richard Hughes460226a2018-05-21 20:56:21 +01003072 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003073 if (allow_reinstall)
3074 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3075 if (allow_older)
3076 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003077 if (allow_branch_switch)
3078 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3079 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003080 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003081 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3082 }
3083 if (ignore_checksum)
3084 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3085 if (ignore_vid_pid)
3086 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3087 if (ignore_power)
3088 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003089
Richard Hughes98ca9932018-05-18 10:24:07 +01003090 /* load engine */
3091 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3092 g_signal_connect (priv->engine, "device-added",
3093 G_CALLBACK (fu_main_engine_device_added_cb),
3094 priv);
3095 g_signal_connect (priv->engine, "device-removed",
3096 G_CALLBACK (fu_main_engine_device_removed_cb),
3097 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003098 g_signal_connect (priv->engine, "status-changed",
3099 G_CALLBACK (fu_main_engine_status_changed_cb),
3100 priv);
3101 g_signal_connect (priv->engine, "percentage-changed",
3102 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3103 priv);
3104
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003105 /* just show versions and exit */
3106 if (version) {
3107 g_autofree gchar *version_str = fu_util_get_versions ();
3108 g_print ("%s\n", version_str);
3109 return EXIT_SUCCESS;
3110 }
3111
Richard Hughes85226fd2020-06-30 14:43:48 +01003112 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003113 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3114 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3115
Richard Hughesb5976832018-05-18 10:02:09 +01003116 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003117 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003118 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003119 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003120 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003121 /* TRANSLATORS: error message explaining command to run to how to get help */
3122 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3123 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3124 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003125 return EXIT_NOTHING_TO_DO;
3126 }
Richard Hughesb5976832018-05-18 10:02:09 +01003127 return EXIT_FAILURE;
3128 }
3129
3130 /* success */
3131 return EXIT_SUCCESS;
3132}