blob: 3825ab3bd4b44a69ee7171235ae618fd15dd4767 [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{
170 g_autoptr(GError) error_local = NULL;
171
Richard Hughesd92ccca2019-05-20 11:28:31 +0100172#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100173 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500174 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100175#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000176 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000177 return FALSE;
178 if (fu_engine_get_tainted (priv->engine)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100179 g_autofree gchar *fmt = NULL;
180
181 /* TRANSLATORS: this is a prefix on the console */
182 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
183 g_printerr ("%s This tool has loaded 3rd party code and "
184 "is no longer supported by the upstream developers!\n",
185 fmt);
Richard Hughesf425d292019-01-18 17:57:39 +0000186 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100187 fu_util_show_plugin_warnings (priv);
Mario Limonciellobd60de12020-11-11 13:09:43 -0600188 fu_util_show_unsupported_warn ();
Richard Hughesf425d292019-01-18 17:57:39 +0000189 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500190}
191
Richard Hughesb5976832018-05-18 10:02:09 +0100192static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500193fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
194{
195 g_autofree gchar *path = g_path_get_dirname (value);
196 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
197 g_prefix_error (error,
198 "Unable to access %s. You may need to copy %s to %s: ",
199 path, value, g_getenv ("HOME"));
200 }
201}
202
203static void
Richard Hughesb5976832018-05-18 10:02:09 +0100204fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
205{
206 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
207 /* TRANSLATORS: this is when a device ctrl+c's a watch */
208 g_print ("%s\n", _("Cancelled"));
209 g_main_loop_quit (priv->loop);
210}
211
212static gboolean
213fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
214{
215 g_autofree gchar *tmp = NULL;
216 g_autoptr(FuSmbios) smbios = NULL;
217 if (g_strv_length (values) < 1) {
218 g_set_error_literal (error,
219 FWUPD_ERROR,
220 FWUPD_ERROR_INVALID_ARGS,
221 "Invalid arguments");
222 return FALSE;
223 }
224 smbios = fu_smbios_new ();
225 if (!fu_smbios_setup_from_file (smbios, values[0], error))
226 return FALSE;
227 tmp = fu_smbios_to_string (smbios);
228 g_print ("%s\n", tmp);
229 return TRUE;
230}
231
Richard Hughes9e5675e2019-11-22 09:35:03 +0000232#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100233static gboolean
234fu_util_sigint_cb (gpointer user_data)
235{
236 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
237 g_debug ("Handling SIGINT");
238 g_cancellable_cancel (priv->cancellable);
239 return FALSE;
240}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000241#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100242
243static void
244fu_util_private_free (FuUtilPrivate *priv)
245{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500246 if (priv->current_device != NULL)
247 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100248 if (priv->engine != NULL)
249 g_object_unref (priv->engine);
Richard Hughesdf89cd52020-06-26 20:25:18 +0100250 if (priv->request != NULL)
251 g_object_unref (priv->request);
Richard Hughesb5976832018-05-18 10:02:09 +0100252 if (priv->loop != NULL)
253 g_main_loop_unref (priv->loop);
254 if (priv->cancellable != NULL)
255 g_object_unref (priv->cancellable);
256 if (priv->progressbar != NULL)
257 g_object_unref (priv->progressbar);
258 if (priv->context != NULL)
259 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600260 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100261 g_free (priv);
262}
263
264#pragma clang diagnostic push
265#pragma clang diagnostic ignored "-Wunused-function"
266G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
267#pragma clang diagnostic pop
268
Richard Hughes98ca9932018-05-18 10:24:07 +0100269
270static void
271fu_main_engine_device_added_cb (FuEngine *engine,
272 FuDevice *device,
273 FuUtilPrivate *priv)
274{
275 g_autofree gchar *tmp = fu_device_to_string (device);
276 g_debug ("ADDED:\n%s", tmp);
277}
278
279static void
280fu_main_engine_device_removed_cb (FuEngine *engine,
281 FuDevice *device,
282 FuUtilPrivate *priv)
283{
284 g_autofree gchar *tmp = fu_device_to_string (device);
285 g_debug ("REMOVED:\n%s", tmp);
286}
287
288static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100289fu_main_engine_status_changed_cb (FuEngine *engine,
290 FwupdStatus status,
291 FuUtilPrivate *priv)
292{
293 fu_progressbar_update (priv->progressbar, status, 0);
294}
295
296static void
297fu_main_engine_percentage_changed_cb (FuEngine *engine,
298 guint percentage,
299 FuUtilPrivate *priv)
300{
301 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
302}
303
304static gboolean
305fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
306{
Richard Hughesc7d870a2020-12-10 10:05:35 +0000307 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_COLDPLUG, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100308 return FALSE;
309 g_main_loop_run (priv->loop);
310 return TRUE;
311}
312
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100313static gint
314fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
315{
316 return fu_plugin_name_compare (*item1, *item2);
317}
318
319static gboolean
320fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
321{
322 GPtrArray *plugins;
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100323
324 /* load engine */
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100325 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100326 return FALSE;
327
328 /* print */
329 plugins = fu_engine_get_plugins (priv->engine);
330 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
331 for (guint i = 0; i < plugins->len; i++) {
332 FuPlugin *plugin = g_ptr_array_index (plugins, i);
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100333 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
334 g_print ("%s\n", str);
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100335 }
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100336 if (plugins->len == 0) {
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100337 /* TRANSLATORS: nothing found */
338 g_print ("%s\n", _("No plugins found"));
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100339 }
340
341 return TRUE;
342}
343
Richard Hughes98ca9932018-05-18 10:24:07 +0100344static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100345fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
346{
347 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
348 if (!fwupd_device_has_flag (dev, priv->filter_include))
349 return FALSE;
350 }
351 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
352 if (fwupd_device_has_flag (dev, priv->filter_exclude))
353 return FALSE;
354 }
355 return TRUE;
356}
357
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500358static gchar *
359fu_util_get_tree_title (FuUtilPrivate *priv)
360{
361 return g_strdup (fu_engine_get_host_product (priv->engine));
362}
363
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600364static FuDevice *
365fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error)
366{
367 FuDevice *dev;
368 guint idx;
369 g_autoptr(GPtrArray) devices = NULL;
370 g_autoptr(GPtrArray) devices_filtered = NULL;
371
372 /* get devices from daemon */
373 if (devices_opt != NULL) {
374 devices = g_ptr_array_ref (devices_opt);
375 } else {
376 devices = fu_engine_get_devices (priv->engine, error);
377 if (devices == NULL)
378 return NULL;
379 }
380 fwupd_device_array_ensure_parents (devices);
381
382 /* filter results */
383 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
384 for (guint i = 0; i < devices->len; i++) {
385 dev = g_ptr_array_index (devices, i);
386 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
387 continue;
388 g_ptr_array_add (devices_filtered, g_object_ref (dev));
389 }
390
391 /* nothing */
392 if (devices_filtered->len == 0) {
393 g_set_error_literal (error,
394 FWUPD_ERROR,
395 FWUPD_ERROR_NOTHING_TO_DO,
396 "No supported devices");
397 return NULL;
398 }
399
400 /* exactly one */
401 if (devices_filtered->len == 1) {
402 dev = g_ptr_array_index (devices_filtered, 0);
403 /* TRANSLATORS: Device has been chosen by the daemon for the user */
404 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
405 return g_object_ref (dev);
406 }
407
408 /* TRANSLATORS: get interactive prompt */
409 g_print ("%s\n", _("Choose a device:"));
410 /* TRANSLATORS: this is to abort the interactive prompt */
411 g_print ("0.\t%s\n", _("Cancel"));
412 for (guint i = 0; i < devices_filtered->len; i++) {
413 dev = g_ptr_array_index (devices_filtered, i);
414 g_print ("%u.\t%s (%s)\n",
415 i + 1,
416 fu_device_get_id (dev),
417 fu_device_get_name (dev));
418 }
419 idx = fu_util_prompt_for_number (devices_filtered->len);
420 if (idx == 0) {
421 g_set_error_literal (error,
422 FWUPD_ERROR,
423 FWUPD_ERROR_NOTHING_TO_DO,
424 "Request canceled");
425 return NULL;
426 }
427 dev = g_ptr_array_index (devices_filtered, idx - 1);
428 return g_object_ref (dev);
429}
430
431static FuDevice *
432fu_util_get_device (FuUtilPrivate *priv, const gchar *id, GError **error)
433{
434 if (fwupd_guid_is_valid (id)) {
435 g_autoptr(GPtrArray) devices = NULL;
436 devices = fu_engine_get_devices_by_guid (priv->engine, id, error);
437 if (devices == NULL)
438 return NULL;
439 return fu_util_prompt_for_device (priv, devices, error);
440 }
441
442 /* did this look like a GUID? */
443 for (guint i = 0; id[i] != '\0'; i++) {
444 if (id[i] == '-') {
445 g_set_error_literal (error,
446 FWUPD_ERROR,
447 FWUPD_ERROR_INVALID_ARGS,
448 "Invalid arguments");
449 return NULL;
450 }
451 }
452 return fu_engine_get_device (priv->engine, id, error);
453}
454
Richard Hughes747f5702019-08-06 14:27:26 +0100455static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600456fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
457{
458 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500459 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600460 g_autofree gchar *title = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500461 gboolean no_updates_header = FALSE;
462 gboolean latest_header = FALSE;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600463
464 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000465 if (!fu_util_start_engine (priv,
466 FU_ENGINE_LOAD_FLAG_COLDPLUG |
467 FU_ENGINE_LOAD_FLAG_HWINFO |
468 FU_ENGINE_LOAD_FLAG_REMOTES,
469 error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600470 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600471 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600472
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600473 /* parse arguments */
474 if (g_strv_length (values) == 0) {
475 devices = fu_engine_get_devices (priv->engine, error);
476 if (devices == NULL)
477 return FALSE;
478 } else if (g_strv_length (values) == 1) {
479 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600480 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600481 if (device == NULL)
482 return FALSE;
483 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
484 g_ptr_array_add (devices, device);
485 } else {
486 g_set_error_literal (error,
487 FWUPD_ERROR,
488 FWUPD_ERROR_INVALID_ARGS,
489 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600490 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600491 }
492
Richard Hughes0ef47202020-01-06 13:59:09 +0000493 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500494 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600495 for (guint i = 0; i < devices->len; i++) {
496 FwupdDevice *dev = g_ptr_array_index (devices, i);
497 g_autoptr(GPtrArray) rels = NULL;
498 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500499 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600500
Richard Hughes747f5702019-08-06 14:27:26 +0100501 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600502 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600503 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600504 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500505 if (!no_updates_header) {
506 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
507 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
508 no_updates_header = TRUE;
509 }
510 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600511 continue;
512 }
Richard Hughes747f5702019-08-06 14:27:26 +0100513 if (!fu_util_filter_device (priv, dev))
514 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600515
516 /* get the releases for this device and filter for validity */
517 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100518 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600519 fwupd_device_get_id (dev),
520 &error_local);
521 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500522 if (!latest_header) {
523 /* TRANSLATORS: message letting the user know no device upgrade available */
524 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
525 latest_header = TRUE;
526 }
527 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600528 /* discard the actual reason from user, but leave for debugging */
529 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600530 continue;
531 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500532 child = g_node_append_data (root, dev);
533
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600534 for (guint j = 0; j < rels->len; j++) {
535 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500536 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600537 }
538 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600539 /* save the device state for other applications to see */
540 if (!fu_util_save_current_state (priv, error))
541 return FALSE;
542
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500543 /* updates */
544 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
545 g_set_error_literal (error,
546 FWUPD_ERROR,
547 FWUPD_ERROR_NOTHING_TO_DO,
548 "No updates available for remaining devices");
549 return FALSE;
550 }
551
552 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600553 return TRUE;
554}
555
556static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500557fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
558{
559 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500560 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600561 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500562 gint fd;
563
564 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000565 if (!fu_util_start_engine (priv,
566 FU_ENGINE_LOAD_FLAG_COLDPLUG |
567 FU_ENGINE_LOAD_FLAG_HWINFO |
568 FU_ENGINE_LOAD_FLAG_REMOTES,
569 error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500570 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600571 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500572
573 /* check args */
574 if (g_strv_length (values) != 1) {
575 g_set_error_literal (error,
576 FWUPD_ERROR,
577 FWUPD_ERROR_INVALID_ARGS,
578 "Invalid arguments");
579 return FALSE;
580 }
581
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600582 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100583 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600584
Mario Limonciello716ab272018-05-29 12:34:37 -0500585 /* open file */
586 fd = open (values[0], O_RDONLY);
587 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500588 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500589 g_set_error (error,
590 FWUPD_ERROR,
591 FWUPD_ERROR_INVALID_FILE,
592 "failed to open %s",
593 values[0]);
594 return FALSE;
595 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100596 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500597 close (fd);
598
599 if (array == NULL)
600 return FALSE;
601 for (guint i = 0; i < array->len; i++) {
602 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600603 FwupdRelease *rel;
604 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100605 if (!fu_util_filter_device (priv, dev))
606 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600607 child = g_node_append_data (root, dev);
608 rel = fwupd_device_get_release_default (dev);
609 if (rel != NULL)
610 g_node_append_data (child, rel);
611
Mario Limonciello716ab272018-05-29 12:34:37 -0500612 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500613 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500614
Mario Limonciello716ab272018-05-29 12:34:37 -0500615 return TRUE;
616}
617
618static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100619fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
620{
621 g_autoptr(GString) str = g_string_new (NULL);
622
623 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
624 const gchar *tmp = fwupd_device_flag_to_string (i);
625 if (tmp == NULL)
626 break;
627 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
628 g_string_append (str, " ");
629 g_string_append (str, tmp);
630 g_string_append (str, " ~");
631 g_string_append (str, tmp);
632 }
633 g_print ("%s\n", str->str);
634
635 return TRUE;
636}
637
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500638static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100639fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500640{
641 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100642 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100643 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
644 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100645 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500646 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500647 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100648 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500649 GNode *child = g_node_append_data (root, dev_tmp);
650 fu_util_build_device_tree (priv, child, devs, dev_tmp);
651 }
652 }
653}
654
655static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100656fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500657{
658 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600659 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500660 g_autoptr(GPtrArray) devs = NULL;
661
662 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000663 if (!fu_util_start_engine (priv,
664 FU_ENGINE_LOAD_FLAG_COLDPLUG |
665 FU_ENGINE_LOAD_FLAG_HWINFO |
666 FU_ENGINE_LOAD_FLAG_REMOTES,
667 error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500668 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600669 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500670
671 /* print */
672 devs = fu_engine_get_devices (priv->engine, error);
673 if (devs == NULL)
674 return FALSE;
675
676 /* print */
677 if (devs->len == 0) {
678 /* TRANSLATORS: nothing attached that can be upgraded */
679 g_print ("%s\n", _("No hardware detected with firmware update capability"));
680 return TRUE;
681 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000682 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500683 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500684 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500685
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100686 /* save the device state for other applications to see */
687 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500688}
689
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500690static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600691fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500692 FwupdDevice *device,
693 FuUtilPrivate *priv)
694{
695 g_autofree gchar *str = NULL;
696
Richard Hughes809abea2019-03-23 11:06:18 +0000697 /* allowed to set whenever the device has changed */
698 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
699 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
700 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
701 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
702
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500703 /* same as last time, so ignore */
704 if (priv->current_device != NULL &&
705 fwupd_device_compare (priv->current_device, device) == 0)
706 return;
707
Richard Hughesee562b52020-04-07 14:32:52 +0100708 /* ignore indirect devices that might have changed */
709 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
710 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
711 g_debug ("ignoring %s with status %s",
712 fwupd_device_get_name (device),
713 fwupd_status_to_string (fwupd_device_get_status (device)));
714 return;
715 }
716
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500717 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600718 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
719 /* TRANSLATORS: %1 is a device name */
720 str = g_strdup_printf (_("Updating %s…"),
721 fwupd_device_get_name (device));
722 fu_progressbar_set_title (priv->progressbar, str);
723 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
724 /* TRANSLATORS: %1 is a device name */
725 str = g_strdup_printf (_("Installing on %s…"),
726 fwupd_device_get_name (device));
727 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000728 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
729 /* TRANSLATORS: %1 is a device name */
730 str = g_strdup_printf (_("Reading from %s…"),
731 fwupd_device_get_name (device));
732 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600733 } else {
734 g_warning ("no FuUtilOperation set");
735 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500736 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600737
Mario Limonciello32241f42019-01-24 10:12:41 -0600738 if (priv->current_message == NULL) {
739 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
740 if (tmp != NULL)
741 priv->current_message = g_strdup (tmp);
742 }
743}
744
745static void
746fu_util_display_current_message (FuUtilPrivate *priv)
747{
748 if (priv->current_message == NULL)
749 return;
750 g_print ("%s\n", priv->current_message);
751 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500752}
753
Richard Hughes98ca9932018-05-18 10:24:07 +0100754static gboolean
755fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
756{
757 g_autoptr(FuDevice) device = NULL;
758 g_autoptr(GBytes) blob_fw = NULL;
759
760 /* invalid args */
761 if (g_strv_length (values) == 0) {
762 g_set_error_literal (error,
763 FWUPD_ERROR,
764 FWUPD_ERROR_INVALID_ARGS,
765 "Invalid arguments");
766 return FALSE;
767 }
768
769 /* parse blob */
770 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500771 if (blob_fw == NULL) {
772 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100773 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500774 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100775
776 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000777 if (!fu_util_start_engine (priv,
778 FU_ENGINE_LOAD_FLAG_COLDPLUG |
779 FU_ENGINE_LOAD_FLAG_HWINFO |
780 FU_ENGINE_LOAD_FLAG_REMOTES,
781 error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100782 return FALSE;
783
784 /* get device */
785 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100786 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100787 if (device == NULL)
788 return FALSE;
789 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100790 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100791 if (device == NULL)
792 return FALSE;
793 }
794
Mario Limonciello3f243a92019-01-21 22:05:23 -0600795 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500796 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600797 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500798
Richard Hughes98ca9932018-05-18 10:24:07 +0100799 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000800 if (priv->prepare_blob) {
801 g_autoptr(GPtrArray) devices = NULL;
802 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
803 g_ptr_array_add (devices, g_object_ref (device));
804 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
805 g_prefix_error (error, "failed to prepare composite action: ");
806 return FALSE;
807 }
808 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100809 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000810 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600811 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000812 if (priv->cleanup_blob) {
813 g_autoptr(FuDevice) device_new = NULL;
814 g_autoptr(GError) error_local = NULL;
815
816 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100817 device_new = fu_util_get_device (priv,
818 fu_device_get_id (device),
819 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000820 if (device_new == NULL) {
821 g_debug ("failed to find new device: %s",
822 error_local->message);
823 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600824 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000825 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
826 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
827 g_prefix_error (error, "failed to cleanup composite action: ");
828 return FALSE;
829 }
830 }
831 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600832
Mario Limonciello32241f42019-01-24 10:12:41 -0600833 fu_util_display_current_message (priv);
834
Mario Limonciello3f243a92019-01-21 22:05:23 -0600835 /* success */
836 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100837}
838
Richard Hughesa58510b2019-10-30 10:03:12 +0000839static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100840fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000841{
842 g_autoptr(FuDevice) device = NULL;
843 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
844 g_autoptr(GBytes) blob_fw = NULL;
845
846 /* invalid args */
847 if (g_strv_length (values) == 0) {
848 g_set_error_literal (error,
849 FWUPD_ERROR,
850 FWUPD_ERROR_INVALID_ARGS,
851 "Invalid arguments");
852 return FALSE;
853 }
854
855 /* file already exists */
856 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
857 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
858 g_set_error_literal (error,
859 FWUPD_ERROR,
860 FWUPD_ERROR_INVALID_ARGS,
861 "Filename already exists");
862 return FALSE;
863 }
864
Richard Hughes02792c02019-11-01 14:21:20 +0000865 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000866 * avoid failing at the end of a potentially lengthy operation */
867 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
868 return FALSE;
869
870 /* load engine */
871 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
872 return FALSE;
873
874 /* get device */
875 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100876 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000877 if (device == NULL)
878 return FALSE;
879 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100880 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000881 if (device == NULL)
882 return FALSE;
883 }
884 priv->current_operation = FU_UTIL_OPERATION_READ;
885 g_signal_connect (priv->engine, "device-changed",
886 G_CALLBACK (fu_util_update_device_changed_cb), priv);
887
888 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100889 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000890 if (blob_fw == NULL)
891 return FALSE;
892 return fu_common_set_contents_bytes (values[0], blob_fw, error);
893}
894
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100895static gint
896fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
897{
898 FuInstallTask *task1 = *((FuInstallTask **) a);
899 FuInstallTask *task2 = *((FuInstallTask **) b);
900 return fu_install_task_compare (task1, task2);
901}
902
903static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100904fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
905{
Filipe Laínse0914272019-09-20 10:04:43 +0100906 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100907 { "curl", uri, "--output", fn, NULL },
908 { NULL } };
909 for (guint i = 0; argv[i][0] != NULL; i++) {
910 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100911 g_autofree gchar *fn_tmp = NULL;
912 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
913 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100914 g_debug ("%s", error_local->message);
915 continue;
916 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000917 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100918 }
919 g_set_error_literal (error,
920 FWUPD_ERROR,
921 FWUPD_ERROR_NOT_FOUND,
922 "no supported out-of-process downloaders found");
923 return FALSE;
924}
925
926static gchar *
927fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
928{
929 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100930
931 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000932 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
933 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000934 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100935 return g_strdup (perhapsfn);
936
937 /* download the firmware to a cachedir */
938 filename = fu_util_get_user_cache_path (perhapsfn);
939 if (!fu_common_mkdir_parent (filename, error))
940 return NULL;
941 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
942 return NULL;
943 return g_steal_pointer (&filename);
944}
945
946static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100947fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
948{
Richard Hughes3d178be2018-08-30 11:14:24 +0100949 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100950 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100951 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100952 g_autoptr(GPtrArray) devices_possible = NULL;
953 g_autoptr(GPtrArray) errors = NULL;
954 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100955 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100956
Mario Limonciello8949e892018-05-25 08:03:06 -0500957 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +0000958 if (!fu_util_start_engine (priv,
959 FU_ENGINE_LOAD_FLAG_COLDPLUG |
960 FU_ENGINE_LOAD_FLAG_HWINFO |
961 FU_ENGINE_LOAD_FLAG_REMOTES,
962 error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500963 return FALSE;
964
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100965 /* handle both forms */
966 if (g_strv_length (values) == 1) {
967 devices_possible = fu_engine_get_devices (priv->engine, error);
968 if (devices_possible == NULL)
969 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000970 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100971 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100972 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100973 if (device == NULL)
974 return FALSE;
975 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
976 g_ptr_array_add (devices_possible, device);
977 } else {
978 g_set_error_literal (error,
979 FWUPD_ERROR,
980 FWUPD_ERROR_INVALID_ARGS,
981 "Invalid arguments");
982 return FALSE;
983 }
984
Richard Hughes3d178be2018-08-30 11:14:24 +0100985 /* download if required */
986 filename = fu_util_download_if_required (priv, values[0], error);
987 if (filename == NULL)
988 return FALSE;
989
Richard Hughes481aa2a2018-09-18 20:51:46 +0100990 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100991 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500992 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100993 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100994 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500995 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100996 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
997 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100998 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600999 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001000 if (components == NULL)
1001 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001002
Richard Hughes481aa2a2018-09-18 20:51:46 +01001003 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001004 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
1005 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +01001006 for (guint i = 0; i < components->len; i++) {
1007 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001008
1009 /* do any devices pass the requirements */
1010 for (guint j = 0; j < devices_possible->len; j++) {
1011 FuDevice *device = g_ptr_array_index (devices_possible, j);
1012 g_autoptr(FuInstallTask) task = NULL;
1013 g_autoptr(GError) error_local = NULL;
1014
1015 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +01001016 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001017 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001018 priv->request,
1019 task,
1020 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001021 &error_local)) {
1022 g_debug ("first pass requirement on %s:%s failed: %s",
1023 fu_device_get_id (device),
1024 xb_node_query_text (component, "id", NULL),
1025 error_local->message);
1026 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1027 continue;
1028 }
1029
1030 /* make a second pass using possibly updated version format now */
1031 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1032 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001033 priv->request,
1034 task,
1035 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001036 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001037 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001038 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001039 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001040 error_local->message);
1041 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1042 continue;
1043 }
1044
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001045 /* if component should have an update message from CAB */
1046 fu_device_incorporate_from_component (device, component);
1047
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001048 /* success */
1049 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1050 }
1051 }
1052
1053 /* order the install tasks by the device priority */
1054 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1055
1056 /* nothing suitable */
1057 if (install_tasks->len == 0) {
1058 GError *error_tmp = fu_common_error_array_get_best (errors);
1059 g_propagate_error (error, error_tmp);
1060 return FALSE;
1061 }
1062
Mario Limonciello3f243a92019-01-21 22:05:23 -06001063 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001064 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001065 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001066
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001067 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001068 if (!fu_engine_install_tasks (priv->engine,
1069 priv->request,
1070 install_tasks,
1071 blob_cab,
1072 priv->flags,
1073 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001074 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001075
Mario Limonciello32241f42019-01-24 10:12:41 -06001076 fu_util_display_current_message (priv);
1077
Mario Limonciello3f243a92019-01-21 22:05:23 -06001078 /* we don't want to ask anything */
1079 if (priv->no_reboot_check) {
1080 g_debug ("skipping reboot check");
1081 return TRUE;
1082 }
1083
Mario Limonciello3143bad2019-02-27 07:31:00 -06001084 /* save the device state for other applications to see */
1085 if (!fu_util_save_current_state (priv, error))
1086 return FALSE;
1087
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001088 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001089 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001090}
1091
Richard Hughes98ca9932018-05-18 10:24:07 +01001092static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001093fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001094{
Mario Limonciellofd734852019-08-01 16:41:42 -05001095 FwupdRemote *remote;
1096 const gchar *remote_id;
1097 const gchar *uri_tmp;
1098 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001099
Mario Limonciellofd734852019-08-01 16:41:42 -05001100 uri_tmp = fwupd_release_get_uri (rel);
1101 if (uri_tmp == NULL) {
1102 g_set_error_literal (error,
1103 FWUPD_ERROR,
1104 FWUPD_ERROR_INVALID_FILE,
1105 "release missing URI");
1106 return FALSE;
1107 }
1108 remote_id = fwupd_release_get_remote_id (rel);
1109 if (remote_id == NULL) {
1110 g_set_error (error,
1111 FWUPD_ERROR,
1112 FWUPD_ERROR_INVALID_FILE,
1113 "failed to find remote for %s",
1114 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001115 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001116 }
1117
1118 remote = fu_engine_get_remote_by_id (priv->engine,
1119 remote_id,
1120 error);
1121 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001122 return FALSE;
1123
Mario Limonciellofd734852019-08-01 16:41:42 -05001124 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001125 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001126 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1127 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001128 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1129 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1130 argv[0] = g_build_filename (path, uri_tmp, NULL);
1131 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1132 argv[0] = g_strdup (uri_tmp + 7);
1133 /* web remote, fu_util_install will download file */
1134 } else {
1135 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1136 }
1137 return fu_util_install (priv, argv, error);
1138}
1139
1140static gboolean
1141fu_util_update_all (FuUtilPrivate *priv, GError **error)
1142{
1143 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001144 gboolean no_updates_header = FALSE;
1145 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001146
Mario Limonciello46aaee82019-01-10 12:58:00 -06001147 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001148 if (devices == NULL)
1149 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001150 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001151 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001152 for (guint i = 0; i < devices->len; i++) {
1153 FwupdDevice *dev = g_ptr_array_index (devices, i);
1154 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001155 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001156 g_autoptr(GPtrArray) rels = NULL;
1157 g_autoptr(GError) error_local = NULL;
1158
1159 if (!fu_util_is_interesting_device (dev))
1160 continue;
1161 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001162 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001163 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001164 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001165 if (!no_updates_header) {
1166 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1167 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1168 no_updates_header = TRUE;
1169 }
1170 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001171 continue;
1172 }
Richard Hughes747f5702019-08-06 14:27:26 +01001173 if (!fu_util_filter_device (priv, dev))
1174 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001175
1176 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001177 rels = fu_engine_get_upgrades (priv->engine,
1178 priv->request,
1179 device_id,
1180 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001181 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001182 if (!latest_header) {
1183 /* TRANSLATORS: message letting the user know no device upgrade available */
1184 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1185 latest_header = TRUE;
1186 }
1187 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001188 /* discard the actual reason from user, but leave for debugging */
1189 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001190 continue;
1191 }
1192
Mario Limonciello98b95162019-10-30 09:20:43 -05001193 if (!priv->no_safety_check) {
1194 if (!fu_util_prompt_warning (dev,
1195 fu_util_get_tree_title (priv),
1196 error))
1197 return FALSE;
1198 }
1199
Mario Limonciello46aaee82019-01-10 12:58:00 -06001200 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001201 if (!fu_util_install_release (priv, rel, &error_local)) {
1202 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001203 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001204 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001205 fu_util_display_current_message (priv);
1206 }
1207 return TRUE;
1208}
1209
1210static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001211fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001212{
1213 FwupdRelease *rel;
1214 g_autoptr(FuDevice) dev = NULL;
1215 g_autoptr(GPtrArray) rels = NULL;
1216
Mario Limonciello9917bb42020-04-20 13:41:27 -05001217 /* do not allow a partial device-id, lookup GUIDs */
1218 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001219 if (dev == NULL)
1220 return FALSE;
1221
1222 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001223 rels = fu_engine_get_upgrades (priv->engine,
1224 priv->request,
1225 fu_device_get_id (dev),
1226 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001227 if (rels == NULL)
1228 return FALSE;
1229 rel = g_ptr_array_index (rels, 0);
1230 if (!fu_util_install_release (priv, rel, error))
1231 return FALSE;
1232 fu_util_display_current_message (priv);
1233
1234 return TRUE;
1235}
1236
1237static gboolean
1238fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1239{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001240 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1241 g_set_error_literal (error,
1242 FWUPD_ERROR,
1243 FWUPD_ERROR_INVALID_ARGS,
1244 "--allow-older is not supported for this command");
1245 return FALSE;
1246 }
1247
1248 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1249 g_set_error_literal (error,
1250 FWUPD_ERROR,
1251 FWUPD_ERROR_INVALID_ARGS,
1252 "--allow-reinstall is not supported for this command");
1253 return FALSE;
1254 }
1255
Mario Limonciellofd734852019-08-01 16:41:42 -05001256 if (g_strv_length (values) > 1) {
1257 g_set_error_literal (error,
1258 FWUPD_ERROR,
1259 FWUPD_ERROR_INVALID_ARGS,
1260 "Invalid arguments");
1261 return FALSE;
1262 }
1263
Richard Hughesc7d870a2020-12-10 10:05:35 +00001264 if (!fu_util_start_engine (priv,
1265 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1266 FU_ENGINE_LOAD_FLAG_HWINFO |
1267 FU_ENGINE_LOAD_FLAG_REMOTES,
1268 error))
Mario Limonciellofd734852019-08-01 16:41:42 -05001269 return FALSE;
1270
1271 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1272 g_signal_connect (priv->engine, "device-changed",
1273 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1274
1275 if (g_strv_length (values) == 1) {
1276 if (!fu_util_update_by_id (priv, values[0], error))
1277 return FALSE;
1278 } else {
1279 if (!fu_util_update_all (priv, error))
1280 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001281 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001282
1283 /* we don't want to ask anything */
1284 if (priv->no_reboot_check) {
1285 g_debug ("skipping reboot check");
1286 return TRUE;
1287 }
1288
Mario Limonciello3143bad2019-02-27 07:31:00 -06001289 /* save the device state for other applications to see */
1290 if (!fu_util_save_current_state (priv, error))
1291 return FALSE;
1292
Mario Limonciello3f243a92019-01-21 22:05:23 -06001293 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001294}
1295
1296static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001297fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1298{
1299 g_autoptr(FwupdRelease) rel = NULL;
1300 g_autoptr(GPtrArray) rels = NULL;
1301 g_autoptr(FuDevice) dev = NULL;
1302
1303 if (g_strv_length (values) != 1) {
1304 g_set_error_literal (error,
1305 FWUPD_ERROR,
1306 FWUPD_ERROR_INVALID_ARGS,
1307 "Invalid arguments");
1308 return FALSE;
1309 }
1310
Richard Hughesc7d870a2020-12-10 10:05:35 +00001311 if (!fu_util_start_engine (priv,
1312 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1313 FU_ENGINE_LOAD_FLAG_HWINFO |
1314 FU_ENGINE_LOAD_FLAG_REMOTES,
1315 error))
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001316 return FALSE;
1317
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001318 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001319 if (dev == NULL)
1320 return FALSE;
1321
1322 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001323 rels = fu_engine_get_releases_for_device (priv->engine,
1324 priv->request,
1325 dev,
1326 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001327 if (rels == NULL)
1328 return FALSE;
1329
1330 for (guint j = 0; j < rels->len; j++) {
1331 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1332 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1333 fu_device_get_version (dev),
1334 fu_device_get_version_format (dev)) == 0) {
1335 rel = g_object_ref (rel_tmp);
1336 break;
1337 }
1338 }
1339 if (rel == NULL) {
1340 g_set_error (error,
1341 FWUPD_ERROR,
1342 FWUPD_ERROR_NOT_SUPPORTED,
1343 "Unable to locate release for %s version %s",
1344 fu_device_get_name (dev),
1345 fu_device_get_version (dev));
1346 return FALSE;
1347 }
1348
1349 /* update the console if composite devices are also updated */
1350 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1351 g_signal_connect (priv->engine, "device-changed",
1352 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1353 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1354 if (!fu_util_install_release (priv, rel, error))
1355 return FALSE;
1356 fu_util_display_current_message (priv);
1357
1358 /* we don't want to ask anything */
1359 if (priv->no_reboot_check) {
1360 g_debug ("skipping reboot check");
1361 return TRUE;
1362 }
1363
1364 /* save the device state for other applications to see */
1365 if (!fu_util_save_current_state (priv, error))
1366 return FALSE;
1367
1368 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1369}
1370
1371static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001372fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1373{
1374 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001375 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001376
1377 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001378 if (!fu_util_start_engine (priv,
1379 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1380 FU_ENGINE_LOAD_FLAG_HWINFO |
1381 FU_ENGINE_LOAD_FLAG_REMOTES,
1382 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001383 return FALSE;
1384
Richard Hughes98ca9932018-05-18 10:24:07 +01001385 /* get device */
1386 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001387 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001388 if (device == NULL)
1389 return FALSE;
1390 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001391 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001392 if (device == NULL)
1393 return FALSE;
1394 }
1395
1396 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001397 locker = fu_device_locker_new (device, error);
1398 if (locker == NULL)
1399 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001400 return fu_device_detach (device, error);
1401}
1402
1403static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001404fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1405{
1406 g_autoptr(FuDevice) device = NULL;
1407 g_autoptr(FuDeviceLocker) locker = NULL;
1408
1409 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001410 if (!fu_util_start_engine (priv,
1411 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1412 FU_ENGINE_LOAD_FLAG_HWINFO |
1413 FU_ENGINE_LOAD_FLAG_REMOTES,
1414 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001415 return FALSE;
1416
1417 /* get device */
1418 if (g_strv_length (values) == 1) {
1419 device = fu_util_get_device (priv, values[0], error);
1420 } else {
1421 device = fu_util_prompt_for_device (priv, NULL, error);
1422 }
1423 if (device == NULL)
1424 return FALSE;
1425
1426 /* run vfunc */
1427 locker = fu_device_locker_new (device, error);
1428 if (locker == NULL)
1429 return FALSE;
1430 return fu_device_unbind_driver (device, error);
1431}
1432
1433static gboolean
1434fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1435{
1436 g_autoptr(FuDevice) device = NULL;
1437 g_autoptr(FuDeviceLocker) locker = NULL;
1438
1439 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001440 if (!fu_util_start_engine (priv,
1441 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1442 FU_ENGINE_LOAD_FLAG_HWINFO |
1443 FU_ENGINE_LOAD_FLAG_REMOTES,
1444 error))
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001445 return FALSE;
1446
1447 /* get device */
1448 if (g_strv_length (values) == 3) {
1449 device = fu_util_get_device (priv, values[2], error);
1450 if (device == NULL)
1451 return FALSE;
1452 } else if (g_strv_length (values) == 2) {
1453 device = fu_util_prompt_for_device (priv, NULL, error);
1454 if (device == NULL)
1455 return FALSE;
1456 } else {
1457 g_set_error_literal (error,
1458 FWUPD_ERROR,
1459 FWUPD_ERROR_INVALID_ARGS,
1460 "Invalid arguments");
1461 return FALSE;
1462 }
1463
1464 /* run vfunc */
1465 locker = fu_device_locker_new (device, error);
1466 if (locker == NULL)
1467 return FALSE;
1468 return fu_device_bind_driver (device, values[0], values[1], error);
1469}
1470
1471static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001472fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1473{
1474 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001475 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001476
1477 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001478 if (!fu_util_start_engine (priv,
1479 FU_ENGINE_LOAD_FLAG_COLDPLUG |
1480 FU_ENGINE_LOAD_FLAG_HWINFO |
1481 FU_ENGINE_LOAD_FLAG_REMOTES,
1482 error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001483 return FALSE;
1484
Richard Hughes98ca9932018-05-18 10:24:07 +01001485 /* get device */
1486 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001487 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001488 if (device == NULL)
1489 return FALSE;
1490 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001491 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001492 if (device == NULL)
1493 return FALSE;
1494 }
1495
1496 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001497 locker = fu_device_locker_new (device, error);
1498 if (locker == NULL)
1499 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001500 return fu_device_attach (device, error);
1501}
1502
Richard Hughes1d894f12018-08-31 13:05:51 +01001503static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001504fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001505{
1506 gboolean has_pending = FALSE;
1507 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001508 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1509 if (devices == NULL)
1510 return FALSE;
1511
1512 /* only start up the plugins needed */
1513 for (guint i = 0; i < devices->len; i++) {
1514 FuDevice *dev = g_ptr_array_index (devices, i);
1515 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1516 fu_engine_add_plugin_filter (priv->engine,
1517 fu_device_get_plugin (dev));
1518 has_pending = TRUE;
1519 }
1520 }
1521
1522 if (!has_pending) {
1523 g_set_error_literal (error,
1524 FWUPD_ERROR,
1525 FWUPD_ERROR_NOTHING_TO_DO,
1526 "No devices to activate");
1527 return FALSE;
1528 }
1529
1530 return TRUE;
1531}
1532
1533static gboolean
1534fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1535{
1536 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001537 g_autoptr(GPtrArray) devices = NULL;
1538
1539 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001540 if (!fu_util_check_activation_needed (priv, error))
1541 return FALSE;
1542
1543 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001544 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY, error))
Mario Limonciello02085a02020-09-11 14:59:35 -05001545 return FALSE;
1546
1547 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001548 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001549 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001550 if (devices == NULL)
1551 return FALSE;
1552 } else if (g_strv_length (values) == 1) {
1553 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001554 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001555 if (device == NULL)
1556 return FALSE;
1557 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1558 g_ptr_array_add (devices, device);
1559 } else {
1560 g_set_error_literal (error,
1561 FWUPD_ERROR,
1562 FWUPD_ERROR_INVALID_ARGS,
1563 "Invalid arguments");
1564 return FALSE;
1565 }
1566
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001567 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001568 /* order by device priority */
1569 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001570 for (guint i = 0; i < devices->len; i++) {
1571 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001572 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1573 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001574 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1575 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001576 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001577 /* TRANSLATORS: shown when shutting down to switch to the new version */
1578 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1579 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1580 return FALSE;
1581 }
1582
Mario Limonciello02085a02020-09-11 14:59:35 -05001583 if (!has_pending) {
1584 g_set_error_literal (error,
1585 FWUPD_ERROR,
1586 FWUPD_ERROR_NOTHING_TO_DO,
1587 "No devices to activate");
1588 return FALSE;
1589 }
1590
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001591 return TRUE;
1592}
1593
1594static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001595fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1596{
1597 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1598 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1599 const gchar *hwid_keys[] = {
1600 FU_HWIDS_KEY_BIOS_VENDOR,
1601 FU_HWIDS_KEY_BIOS_VERSION,
1602 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1603 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1604 FU_HWIDS_KEY_MANUFACTURER,
1605 FU_HWIDS_KEY_FAMILY,
1606 FU_HWIDS_KEY_PRODUCT_NAME,
1607 FU_HWIDS_KEY_PRODUCT_SKU,
1608 FU_HWIDS_KEY_ENCLOSURE_KIND,
1609 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1610 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1611 NULL };
1612
1613 /* read DMI data */
1614 if (g_strv_length (values) == 0) {
1615 if (!fu_smbios_setup (smbios, error))
1616 return FALSE;
1617 } else if (g_strv_length (values) == 1) {
1618 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1619 return FALSE;
1620 } else {
1621 g_set_error_literal (error,
1622 FWUPD_ERROR,
1623 FWUPD_ERROR_INVALID_ARGS,
1624 "Invalid arguments");
1625 return FALSE;
1626 }
1627 if (!fu_hwids_setup (hwids, smbios, error))
1628 return FALSE;
1629
1630 /* show debug output */
1631 g_print ("Computer Information\n");
1632 g_print ("--------------------\n");
1633 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1634 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1635 if (tmp == NULL)
1636 continue;
1637 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1638 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1639 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1640 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1641 } else {
1642 g_print ("%s: %s\n", hwid_keys[i], tmp);
1643 }
1644 }
1645
1646 /* show GUIDs */
1647 g_print ("\nHardware IDs\n");
1648 g_print ("------------\n");
1649 for (guint i = 0; i < 15; i++) {
1650 const gchar *keys = NULL;
1651 g_autofree gchar *guid = NULL;
1652 g_autofree gchar *key = NULL;
1653 g_autofree gchar *keys_str = NULL;
1654 g_auto(GStrv) keysv = NULL;
1655 g_autoptr(GError) error_local = NULL;
1656
1657 /* get the GUID */
1658 key = g_strdup_printf ("HardwareID-%u", i);
1659 keys = fu_hwids_get_replace_keys (hwids, key);
1660 guid = fu_hwids_get_guid (hwids, key, &error_local);
1661 if (guid == NULL) {
1662 g_print ("%s\n", error_local->message);
1663 continue;
1664 }
1665
1666 /* show what makes up the GUID */
1667 keysv = g_strsplit (keys, "&", -1);
1668 keys_str = g_strjoinv (" + ", keysv);
1669 g_print ("{%s} <- %s\n", guid, keys_str);
1670 }
1671
1672 return TRUE;
1673}
1674
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001675static gboolean
1676fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1677{
1678 const gchar *script_fn = "startup.sh";
1679 const gchar *output_fn = "firmware.bin";
1680 g_autoptr(GBytes) archive_blob = NULL;
1681 g_autoptr(GBytes) firmware_blob = NULL;
1682 if (g_strv_length (values) < 2) {
1683 g_set_error_literal (error,
1684 FWUPD_ERROR,
1685 FWUPD_ERROR_INVALID_ARGS,
1686 "Invalid arguments");
1687 return FALSE;
1688 }
1689 archive_blob = fu_common_get_contents_bytes (values[0], error);
1690 if (archive_blob == NULL)
1691 return FALSE;
1692 if (g_strv_length (values) > 2)
1693 script_fn = values[2];
1694 if (g_strv_length (values) > 3)
1695 output_fn = values[3];
1696 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1697 if (firmware_blob == NULL)
1698 return FALSE;
1699 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1700}
1701
Richard Hughes3d607622019-03-07 16:59:27 +00001702static gboolean
1703fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1704{
1705 g_autofree gchar *sig = NULL;
1706
1707 /* check args */
1708 if (g_strv_length (values) != 1) {
1709 g_set_error_literal (error,
1710 FWUPD_ERROR,
1711 FWUPD_ERROR_INVALID_ARGS,
1712 "Invalid arguments: value expected");
1713 return FALSE;
1714 }
1715
1716 /* start engine */
1717 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1718 return FALSE;
1719 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001720 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1721 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001722 if (sig == NULL)
1723 return FALSE;
1724 g_print ("%s\n", sig);
1725 return TRUE;
1726}
1727
Mario Limonciello62f84862018-10-18 13:15:23 -05001728static void
1729fu_util_device_added_cb (FwupdClient *client,
1730 FwupdDevice *device,
1731 gpointer user_data)
1732{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001733 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001734 /* TRANSLATORS: this is when a device is hotplugged */
1735 g_print ("%s\n%s", _("Device added:"), tmp);
1736}
1737
1738static void
1739fu_util_device_removed_cb (FwupdClient *client,
1740 FwupdDevice *device,
1741 gpointer user_data)
1742{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001743 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001744 /* TRANSLATORS: this is when a device is hotplugged */
1745 g_print ("%s\n%s", _("Device removed:"), tmp);
1746}
1747
1748static void
1749fu_util_device_changed_cb (FwupdClient *client,
1750 FwupdDevice *device,
1751 gpointer user_data)
1752{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001753 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001754 /* TRANSLATORS: this is when a device has been updated */
1755 g_print ("%s\n%s", _("Device changed:"), tmp);
1756}
1757
1758static void
1759fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1760{
1761 /* TRANSLATORS: this is when the daemon state changes */
1762 g_print ("%s\n", _("Changed"));
1763}
1764
1765static gboolean
1766fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1767{
1768 g_autoptr(FwupdClient) client = fwupd_client_new ();
1769
1770 /* get all the devices */
1771 if (!fwupd_client_connect (client, priv->cancellable, error))
1772 return FALSE;
1773
1774 /* watch for any hotplugged device */
1775 g_signal_connect (client, "changed",
1776 G_CALLBACK (fu_util_changed_cb), priv);
1777 g_signal_connect (client, "device-added",
1778 G_CALLBACK (fu_util_device_added_cb), priv);
1779 g_signal_connect (client, "device-removed",
1780 G_CALLBACK (fu_util_device_removed_cb), priv);
1781 g_signal_connect (client, "device-changed",
1782 G_CALLBACK (fu_util_device_changed_cb), priv);
1783 g_signal_connect (priv->cancellable, "cancelled",
1784 G_CALLBACK (fu_util_cancelled_cb), priv);
1785 g_main_loop_run (priv->loop);
1786 return TRUE;
1787}
1788
Richard Hughes15684492019-03-15 16:27:50 +00001789static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001790fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1791{
1792 g_autoptr(GPtrArray) firmware_types = NULL;
1793
1794 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001795 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001796 return FALSE;
1797
1798 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1799 for (guint i = 0; i < firmware_types->len; i++) {
1800 const gchar *id = g_ptr_array_index (firmware_types, i);
1801 g_print ("%s\n", id);
1802 }
1803 if (firmware_types->len == 0) {
1804 /* TRANSLATORS: nothing found */
1805 g_print ("%s\n", _("No firmware IDs found"));
1806 return TRUE;
1807 }
1808
1809 return TRUE;
1810}
1811
1812static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001813fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001814{
1815 g_autoptr(GPtrArray) firmware_types = NULL;
1816 guint idx;
1817 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1818
1819 /* TRANSLATORS: get interactive prompt */
1820 g_print ("%s\n", _("Choose a firmware type:"));
1821 /* TRANSLATORS: this is to abort the interactive prompt */
1822 g_print ("0.\t%s\n", _("Cancel"));
1823 for (guint i = 0; i < firmware_types->len; i++) {
1824 const gchar *id = g_ptr_array_index (firmware_types, i);
1825 g_print ("%u.\t%s\n", i + 1, id);
1826 }
1827 idx = fu_util_prompt_for_number (firmware_types->len);
1828 if (idx == 0) {
1829 g_set_error_literal (error,
1830 FWUPD_ERROR,
1831 FWUPD_ERROR_NOTHING_TO_DO,
1832 "Request canceled");
1833 return NULL;
1834 }
1835
1836 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1837}
1838
1839static gboolean
1840fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1841{
1842 GType gtype;
1843 g_autoptr(GBytes) blob = NULL;
1844 g_autoptr(FuFirmware) firmware = NULL;
1845 g_autofree gchar *firmware_type = NULL;
1846 g_autofree gchar *str = NULL;
1847
1848 /* check args */
1849 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1850 g_set_error_literal (error,
1851 FWUPD_ERROR,
1852 FWUPD_ERROR_INVALID_ARGS,
1853 "Invalid arguments: filename required");
1854 return FALSE;
1855 }
1856
1857 if (g_strv_length (values) == 2)
1858 firmware_type = g_strdup (values[1]);
1859
1860 /* load file */
1861 blob = fu_common_get_contents_bytes (values[0], error);
1862 if (blob == NULL)
1863 return FALSE;
1864
1865 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001866 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes95c98a92019-10-22 16:03:15 +01001867 return FALSE;
1868
1869 /* find the GType to use */
1870 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001871 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001872 if (firmware_type == NULL)
1873 return FALSE;
1874 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1875 if (gtype == G_TYPE_INVALID) {
1876 g_set_error (error,
1877 G_IO_ERROR,
1878 G_IO_ERROR_NOT_FOUND,
1879 "GType %s not supported", firmware_type);
1880 return FALSE;
1881 }
1882 firmware = g_object_new (gtype, NULL);
1883 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1884 return FALSE;
1885 str = fu_firmware_to_string (firmware);
1886 g_print ("%s", str);
1887 return TRUE;
1888}
1889
Richard Hughesdd653442020-09-22 10:23:52 +01001890static gboolean
1891fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1892{
1893 GType gtype;
1894 g_autofree gchar *firmware_type = NULL;
1895 g_autofree gchar *str = NULL;
1896 g_autoptr(FuFirmware) firmware = NULL;
1897 g_autoptr(GBytes) blob = NULL;
1898 g_autoptr(GPtrArray) images = NULL;
1899
1900 /* check args */
1901 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1902 g_set_error_literal (error,
1903 FWUPD_ERROR,
1904 FWUPD_ERROR_INVALID_ARGS,
1905 "Invalid arguments: filename required");
1906 return FALSE;
1907 }
1908 if (g_strv_length (values) == 2)
1909 firmware_type = g_strdup (values[1]);
1910
1911 /* load file */
1912 blob = fu_common_get_contents_bytes (values[0], error);
1913 if (blob == NULL)
1914 return FALSE;
1915
1916 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001917 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesdd653442020-09-22 10:23:52 +01001918 return FALSE;
1919
1920 /* find the GType to use */
1921 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001922 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001923 if (firmware_type == NULL)
1924 return FALSE;
1925 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1926 if (gtype == G_TYPE_INVALID) {
1927 g_set_error (error,
1928 G_IO_ERROR,
1929 G_IO_ERROR_NOT_FOUND,
1930 "GType %s not supported", firmware_type);
1931 return FALSE;
1932 }
1933 firmware = g_object_new (gtype, NULL);
1934 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1935 return FALSE;
1936 str = fu_firmware_to_string (firmware);
1937 g_print ("%s", str);
1938 images = fu_firmware_get_images (firmware);
1939 for (guint i = 0; i < images->len; i++) {
1940 FuFirmwareImage *img = g_ptr_array_index (images, i);
1941 g_autofree gchar *fn = NULL;
1942 g_autoptr(GBytes) blob_img = NULL;
1943
Richard Hughes88dd7c42020-09-22 16:54:40 +01001944 /* get raw image without generated header, footer or crc */
1945 blob_img = fu_firmware_image_get_bytes (img);
1946 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1947 continue;
1948
Richard Hughesdd653442020-09-22 10:23:52 +01001949 /* use suitable filename */
1950 if (fu_firmware_image_get_filename (img) != NULL) {
1951 fn = g_strdup (fu_firmware_image_get_filename (img));
1952 } else if (fu_firmware_image_get_id (img) != NULL) {
1953 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1954 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1955 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1956 } else {
1957 fn = g_strdup_printf ("img-0x%x.fw", i);
1958 }
1959 /* TRANSLATORS: decompressing images from a container firmware */
1960 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01001961 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1962 return FALSE;
1963 }
1964
1965 /* success */
1966 return TRUE;
1967}
1968
Richard Hughes0924c932020-09-22 19:07:05 +01001969static gboolean
1970fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01001971{
Richard Hughes0924c932020-09-22 19:07:05 +01001972 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01001973 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01001974 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001975 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01001976 g_autoptr(FuFirmware) firmware_dst = NULL;
1977 g_autoptr(GBytes) blob_dst = NULL;
1978 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001979 g_autoptr(XbBuilder) builder = xb_builder_new ();
1980 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1981 g_autoptr(XbNode) n = NULL;
1982 g_autoptr(XbSilo) silo = NULL;
1983
Richard Hughes0924c932020-09-22 19:07:05 +01001984 /* check args */
1985 if (g_strv_length (values) != 2) {
1986 g_set_error_literal (error,
1987 FWUPD_ERROR,
1988 FWUPD_ERROR_INVALID_ARGS,
1989 "Invalid arguments: filename required");
1990 return FALSE;
1991 }
1992
1993 /* load file */
1994 blob_src = fu_common_get_contents_bytes (values[0], error);
1995 if (blob_src == NULL)
1996 return FALSE;
1997
1998 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00001999 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002000 return FALSE;
2001
Richard Hughes41400a82020-09-21 13:43:15 +01002002 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01002003 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01002004 XB_BUILDER_SOURCE_FLAG_NONE,
2005 error)) {
2006 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01002007 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002008 }
2009 xb_builder_import_source (builder, source);
2010 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2011 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002012 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002013
2014 /* create FuFirmware of specific GType */
2015 n = xb_silo_query_first (silo, "firmware", error);
2016 if (n == NULL)
2017 return FALSE;
2018 tmp = xb_node_get_attr (n, "gtype");
2019 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01002020 gtype = g_type_from_name (tmp);
2021 if (gtype == G_TYPE_INVALID) {
2022 g_set_error (error,
2023 G_IO_ERROR,
2024 G_IO_ERROR_NOT_FOUND,
2025 "GType %s not registered", tmp);
2026 return FALSE;
2027 }
2028 }
2029 tmp = xb_node_get_attr (n, "id");
2030 if (tmp != NULL) {
2031 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01002032 if (gtype == G_TYPE_INVALID) {
2033 g_set_error (error,
2034 G_IO_ERROR,
2035 G_IO_ERROR_NOT_FOUND,
2036 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01002037 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01002038 }
Richard Hughes41400a82020-09-21 13:43:15 +01002039 }
Richard Hughes0924c932020-09-22 19:07:05 +01002040 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01002041 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01002042 return FALSE;
2043
2044 /* write new file */
2045 blob_dst = fu_firmware_write (firmware, error);
2046 if (blob_dst == NULL)
2047 return FALSE;
2048 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2049 return FALSE;
2050
2051 /* show what we wrote */
2052 firmware_dst = g_object_new (gtype, NULL);
2053 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2054 return FALSE;
2055 str = fu_firmware_to_string (firmware_dst);
2056 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002057
2058 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002059 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002060}
2061
Richard Hughes95c98a92019-10-22 16:03:15 +01002062static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002063fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2064{
2065 GType gtype_dst;
2066 GType gtype_src;
2067 g_autofree gchar *firmware_type_dst = NULL;
2068 g_autofree gchar *firmware_type_src = NULL;
2069 g_autofree gchar *str_dst = NULL;
2070 g_autofree gchar *str_src = NULL;
2071 g_autoptr(FuFirmware) firmware_dst = NULL;
2072 g_autoptr(FuFirmware) firmware_src = NULL;
2073 g_autoptr(GBytes) blob_dst = NULL;
2074 g_autoptr(GBytes) blob_src = NULL;
2075 g_autoptr(GPtrArray) images = NULL;
2076
2077 /* check args */
2078 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2079 g_set_error_literal (error,
2080 FWUPD_ERROR,
2081 FWUPD_ERROR_INVALID_ARGS,
2082 "Invalid arguments: filename required");
2083 return FALSE;
2084 }
2085
2086 if (g_strv_length (values) > 2)
2087 firmware_type_src = g_strdup (values[2]);
2088 if (g_strv_length (values) > 3)
2089 firmware_type_dst = g_strdup (values[3]);
2090
2091 /* load file */
2092 blob_src = fu_common_get_contents_bytes (values[0], error);
2093 if (blob_src == NULL)
2094 return FALSE;
2095
2096 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002097 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_READONLY, error))
Richard Hughesbca63ed2020-03-09 20:20:03 +00002098 return FALSE;
2099
2100 /* find the GType to use */
2101 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002102 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002103 if (firmware_type_src == NULL)
2104 return FALSE;
2105 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002106 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002107 if (firmware_type_dst == NULL)
2108 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002109 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2110 if (gtype_src == G_TYPE_INVALID) {
2111 g_set_error (error,
2112 G_IO_ERROR,
2113 G_IO_ERROR_NOT_FOUND,
2114 "GType %s not supported", firmware_type_src);
2115 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002116 }
Richard Hughes0924c932020-09-22 19:07:05 +01002117 firmware_src = g_object_new (gtype_src, NULL);
2118 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2119 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002120 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2121 if (gtype_dst == G_TYPE_INVALID) {
2122 g_set_error (error,
2123 G_IO_ERROR,
2124 G_IO_ERROR_NOT_FOUND,
2125 "GType %s not supported", firmware_type_dst);
2126 return FALSE;
2127 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002128 str_src = fu_firmware_to_string (firmware_src);
2129 g_print ("%s", str_src);
2130
2131 /* copy images */
2132 firmware_dst = g_object_new (gtype_dst, NULL);
2133 images = fu_firmware_get_images (firmware_src);
2134 for (guint i = 0; i < images->len; i++) {
2135 FuFirmwareImage *img = g_ptr_array_index (images, i);
2136 fu_firmware_add_image (firmware_dst, img);
2137 }
2138
2139 /* write new file */
2140 blob_dst = fu_firmware_write (firmware_dst, error);
2141 if (blob_dst == NULL)
2142 return FALSE;
2143 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2144 return FALSE;
2145 str_dst = fu_firmware_to_string (firmware_dst);
2146 g_print ("%s", str_dst);
2147
2148 /* success */
2149 return TRUE;
2150}
2151
2152static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002153fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2154{
2155 g_autofree gchar *str = NULL;
2156 g_autoptr(FuDevice) dev = NULL;
2157
2158 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002159 if (!fu_util_start_engine (priv,
2160 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2161 FU_ENGINE_LOAD_FLAG_HWINFO |
2162 FU_ENGINE_LOAD_FLAG_REMOTES,
2163 error))
Richard Hughes15684492019-03-15 16:27:50 +00002164 return FALSE;
2165
2166 /* get device */
2167 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002168 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002169 if (dev == NULL)
2170 return FALSE;
2171 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002172 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002173 if (dev == NULL)
2174 return FALSE;
2175 }
2176
2177 /* add checksums */
2178 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2179 return FALSE;
2180
2181 /* show checksums */
2182 str = fu_device_to_string (dev);
2183 g_print ("%s\n", str);
2184 return TRUE;
2185}
2186
Mario Limonciellofe593942019-04-03 13:48:52 -05002187static gboolean
2188fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2189{
2190 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002191 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002192 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002193
2194 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002195 if (!fu_util_start_engine (priv,
2196 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2197 FU_ENGINE_LOAD_FLAG_HWINFO |
2198 FU_ENGINE_LOAD_FLAG_REMOTES,
2199 error))
Mario Limonciellofe593942019-04-03 13:48:52 -05002200 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002201 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002202
2203 /* get all devices from the history database */
2204 devices = fu_engine_get_history (priv->engine, error);
2205 if (devices == NULL)
2206 return FALSE;
2207
2208 /* show each device */
2209 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002210 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002211 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002212 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002213 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002214 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002215
Richard Hughes747f5702019-08-06 14:27:26 +01002216 if (!fu_util_filter_device (priv, dev))
2217 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002218 child = g_node_append_data (root, dev);
2219
2220 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002221 if (rel == NULL)
2222 continue;
2223 remote = fwupd_release_get_remote_id (rel);
2224
2225 /* doesn't actually map to remote */
2226 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002227 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002228 continue;
2229 }
2230
2231 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002232 rels = fu_engine_get_releases (priv->engine,
2233 priv->request,
2234 fwupd_device_get_id (dev),
2235 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002236 if (rels == NULL)
2237 return FALSE;
2238
2239 /* map to a release in client */
2240 for (guint j = 0; j < rels->len; j++) {
2241 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2242 if (g_strcmp0 (remote,
2243 fwupd_release_get_remote_id (rel2)) != 0)
2244 continue;
2245 if (g_strcmp0 (fwupd_release_get_version (rel),
2246 fwupd_release_get_version (rel2)) != 0)
2247 continue;
2248 g_node_append_data (child, g_object_ref (rel2));
2249 rel = NULL;
2250 break;
2251 }
2252
2253 /* didn't match anything */
2254 if (rels->len == 0 || rel != NULL) {
2255 g_node_append_data (child, rel);
2256 continue;
2257 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002258
Mario Limonciellofe593942019-04-03 13:48:52 -05002259 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002260 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002261
2262 return TRUE;
2263}
2264
Richard Hughesfd7e9942020-01-17 14:09:13 +00002265static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002266fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2267{
Richard Hughesc5710d92020-06-26 11:08:25 +01002268 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002269 g_autofree gchar *fn_raw = NULL;
2270 g_autofree gchar *fn_sig = NULL;
2271 g_autoptr(GBytes) bytes_raw = NULL;
2272 g_autoptr(GBytes) bytes_sig = NULL;
2273
Richard Hughes4959baa2020-01-17 14:33:00 +00002274 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002275 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2276 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002277 g_set_error (error,
2278 FWUPD_ERROR,
2279 FWUPD_ERROR_NOTHING_TO_DO,
2280 "no metadata signature URI available for %s",
2281 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002282 return FALSE;
2283 }
2284 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002285 if (!fu_common_mkdir_parent (fn_sig, error))
2286 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002287 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002288 return FALSE;
2289 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2290 if (bytes_sig == NULL)
2291 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002292 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2293 return FALSE;
2294
2295 /* payload */
2296 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2297 if (metadata_uri == NULL) {
2298 g_set_error (error,
2299 FWUPD_ERROR,
2300 FWUPD_ERROR_NOTHING_TO_DO,
2301 "no metadata URI available for %s",
2302 fwupd_remote_get_id (remote));
2303 return FALSE;
2304 }
2305 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2306 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2307 return FALSE;
2308 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2309 if (bytes_raw == NULL)
2310 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002311
2312 /* send to daemon */
2313 g_debug ("updating %s", fwupd_remote_get_id (remote));
2314 return fu_engine_update_metadata_bytes (priv->engine,
2315 fwupd_remote_get_id (remote),
2316 bytes_raw,
2317 bytes_sig,
2318 error);
2319
2320}
2321
2322static gboolean
2323fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2324{
2325 g_autoptr(GPtrArray) remotes = NULL;
2326
2327 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002328 if (!fu_util_start_engine (priv,
2329 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2330 FU_ENGINE_LOAD_FLAG_HWINFO |
2331 FU_ENGINE_LOAD_FLAG_REMOTES,
2332 error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002333 return FALSE;
2334
2335 /* download new metadata */
2336 remotes = fu_engine_get_remotes (priv->engine, error);
2337 if (remotes == NULL)
2338 return FALSE;
2339 for (guint i = 0; i < remotes->len; i++) {
2340 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2341 if (!fwupd_remote_get_enabled (remote))
2342 continue;
2343 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2344 continue;
2345 if (!fu_util_refresh_remote (priv, remote, error))
2346 return FALSE;
2347 }
2348 return TRUE;
2349}
2350
2351static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002352fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2353{
2354 g_autoptr(GNode) root = g_node_new (NULL);
2355 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002356 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002357
2358 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002359 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_REMOTES, error))
Richard Hughesfd7e9942020-01-17 14:09:13 +00002360 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002361 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002362
2363 /* list remotes */
2364 remotes = fu_engine_get_remotes (priv->engine, error);
2365 if (remotes == NULL)
2366 return FALSE;
2367 if (remotes->len == 0) {
2368 g_set_error_literal (error,
2369 FWUPD_ERROR,
2370 FWUPD_ERROR_NOTHING_TO_DO,
2371 "no remotes available");
2372 return FALSE;
2373 }
2374 for (guint i = 0; i < remotes->len; i++) {
2375 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2376 g_node_append_data (root, remote_tmp);
2377 }
2378 fu_util_print_tree (root, title);
2379
2380 return TRUE;
2381}
2382
Richard Hughes196c6c62020-05-11 19:42:47 +01002383static gboolean
2384fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2385{
Richard Hughes5c82b942020-09-14 12:24:06 +01002386 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002387 g_autoptr(FuSecurityAttrs) attrs = NULL;
2388 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002389 g_autofree gchar *str = NULL;
2390
2391 /* not ready yet */
2392 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2393 g_set_error_literal (error,
2394 FWUPD_ERROR,
2395 FWUPD_ERROR_NOT_SUPPORTED,
2396 "The HSI specification is not yet complete. "
2397 "To ignore this warning, use --force");
2398 return FALSE;
2399 }
2400
Richard Hughesc7d870a2020-12-10 10:05:35 +00002401 if (!fu_util_start_engine (priv,
2402 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2403 FU_ENGINE_LOAD_FLAG_HWINFO |
2404 FU_ENGINE_LOAD_FLAG_REMOTES,
2405 error))
Richard Hughes196c6c62020-05-11 19:42:47 +01002406 return FALSE;
2407
2408 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2409 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2410 fu_engine_get_host_security_id (priv->engine));
2411
Richard Hughes5c82b942020-09-14 12:24:06 +01002412 /* show or hide different elements */
2413 if (priv->show_all) {
2414 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2415 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2416 }
2417
Richard Hughes196c6c62020-05-11 19:42:47 +01002418 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002419 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002420 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002421 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002422 g_print ("%s\n", str);
2423 return TRUE;
2424}
2425
Richard Hughesa83deb42020-08-12 12:45:36 +01002426static FuVolume *
2427fu_util_prompt_for_volume (GError **error)
2428{
2429 FuVolume *volume;
2430 guint idx;
2431 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002432 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2433 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002434
2435 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002436 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2437 if (volumes == NULL) {
2438 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2439 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2440 if (volumes == NULL) {
2441 g_prefix_error (error, "%s: ", error_local->message);
2442 return NULL;
2443 }
2444 }
2445 /* only add internal vfat partitions */
2446 for (guint i = 0; i < volumes->len; i++) {
2447 FuVolume *vol = g_ptr_array_index (volumes, i);
2448 g_autofree gchar *type = fu_volume_get_id_type (vol);
2449 if (type == NULL)
2450 continue;
2451 if (!fu_volume_is_internal (vol))
2452 continue;
2453 if (g_strcmp0 (type, "vfat") == 0)
2454 g_ptr_array_add (volumes_vfat, vol);
2455 }
2456 if (volumes_vfat->len == 1) {
2457 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002458 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002459 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2460 return g_object_ref (volume);
2461 }
2462
2463 /* TRANSLATORS: get interactive prompt */
2464 g_print ("%s\n", _("Choose a volume:"));
2465 /* TRANSLATORS: this is to abort the interactive prompt */
2466 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002467 for (guint i = 0; i < volumes_vfat->len; i++) {
2468 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002469 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2470 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002471 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002472 if (idx == 0) {
2473 g_set_error_literal (error,
2474 FWUPD_ERROR,
2475 FWUPD_ERROR_NOTHING_TO_DO,
2476 "Request canceled");
2477 return NULL;
2478 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002479 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002480 return g_object_ref (volume);
2481
2482}
2483
2484static gboolean
2485fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2486{
2487 g_autoptr(FuVolume) volume = NULL;
2488 volume = fu_util_prompt_for_volume (error);
2489 if (volume == NULL)
2490 return FALSE;
2491 return fu_volume_mount (volume, error);
2492}
2493
2494static gboolean
2495fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2496{
2497 g_autoptr(FuVolume) volume = NULL;
2498 volume = fu_util_prompt_for_volume (error);
2499 if (volume == NULL)
2500 return FALSE;
2501 return fu_volume_unmount (volume, error);
2502}
2503
2504static gboolean
2505fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2506{
2507 g_autofree gchar *mount_point = NULL;
2508 g_autoptr(FuDeviceLocker) locker = NULL;
2509 g_autoptr(FuVolume) volume = NULL;
2510 g_autoptr(GPtrArray) files = NULL;
2511
2512 volume = fu_util_prompt_for_volume (error);
2513 if (volume == NULL)
2514 return FALSE;
2515 locker = fu_volume_locker (volume, error);
2516 if (locker == NULL)
2517 return FALSE;
2518 mount_point = fu_volume_get_mount_point (volume);
2519 files = fu_common_get_files_recursive (mount_point, error);
2520 if (files == NULL)
2521 return FALSE;
2522 for (guint i = 0; i < files->len; i++) {
2523 const gchar *fn = g_ptr_array_index (files, i);
2524 g_print ("%s\n", fn);
2525 }
2526 return TRUE;
2527}
2528
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002529
2530static gboolean
2531fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2532{
2533 const gchar *branch;
2534 g_autoptr(FwupdRelease) rel = NULL;
2535 g_autoptr(GPtrArray) rels = NULL;
2536 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2537 g_autoptr(FuDevice) dev = NULL;
2538
2539 /* load engine */
Richard Hughesc7d870a2020-12-10 10:05:35 +00002540 if (!fu_util_start_engine (priv,
2541 FU_ENGINE_LOAD_FLAG_COLDPLUG |
2542 FU_ENGINE_LOAD_FLAG_HWINFO |
2543 FU_ENGINE_LOAD_FLAG_REMOTES,
2544 error))
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002545 return FALSE;
2546
2547 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002548 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002549 if (g_strv_length (values) == 1)
2550 dev = fu_util_get_device (priv, values[1], error);
2551 else
2552 dev = fu_util_prompt_for_device (priv, NULL, error);
2553 if (dev == NULL)
2554 return FALSE;
2555 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2556 g_set_error_literal (error,
2557 FWUPD_ERROR,
2558 FWUPD_ERROR_NOT_SUPPORTED,
2559 "Multiple branches not available");
2560 return FALSE;
2561 }
2562
2563 /* get all releases, including the alternate branch versions */
2564 rels = fu_engine_get_releases (priv->engine,
2565 priv->request,
2566 fu_device_get_id (dev),
2567 error);
2568 if (rels == NULL)
2569 return FALSE;
2570
2571 /* get all the unique branches */
2572 for (guint i = 0; i < rels->len; i++) {
2573 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
2574 const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp);
2575 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
2576 g_str_equal, NULL))
2577 continue;
2578 g_ptr_array_add (branches, g_strdup (branch_tmp));
2579 }
2580
2581 /* branch name is optional */
2582 if (g_strv_length (values) > 1) {
2583 branch = values[1];
2584 } else if (branches->len == 1) {
2585 branch = g_ptr_array_index (branches, 0);
2586 } else {
2587 guint idx;
2588
2589 /* TRANSLATORS: get interactive prompt, where branch is the
2590 * supplier of the firmware, e.g. "non-free" or "free" */
2591 g_print ("%s\n", _("Choose a branch:"));
2592 /* TRANSLATORS: this is to abort the interactive prompt */
2593 g_print ("0.\t%s\n", _("Cancel"));
2594 for (guint i = 0; i < branches->len; i++) {
2595 const gchar *branch_tmp = g_ptr_array_index (branches, i);
2596 g_print ("%u.\t%s\n", i + 1, branch_tmp);
2597 }
2598 idx = fu_util_prompt_for_number (branches->len);
2599 if (idx == 0) {
2600 g_set_error_literal (error,
2601 FWUPD_ERROR,
2602 FWUPD_ERROR_NOTHING_TO_DO,
2603 "Request canceled");
2604 return FALSE;
2605 }
2606 branch = g_ptr_array_index (branches, idx - 1);
2607 }
2608
2609 /* sanity check */
2610 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2611 g_set_error (error,
2612 FWUPD_ERROR,
2613 FWUPD_ERROR_NOT_SUPPORTED,
2614 "Device %s is already on branch %s",
2615 fu_device_get_name (dev),
2616 branch);
2617 return FALSE;
2618 }
2619
2620 /* the releases are ordered by version */
2621 for (guint j = 0; j < rels->len; j++) {
2622 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2623 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2624 rel = g_object_ref (rel_tmp);
2625 break;
2626 }
2627 }
2628 if (rel == NULL) {
2629 g_set_error (error,
2630 FWUPD_ERROR,
2631 FWUPD_ERROR_NOT_SUPPORTED,
2632 "No releases for branch %s",
2633 branch);
2634 return FALSE;
2635 }
2636
2637 /* we're switching branch */
2638 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2639 return FALSE;
2640
2641 /* update the console if composite devices are also updated */
2642 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2643 g_signal_connect (priv->engine, "device-changed",
2644 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2645 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2646 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2647 if (!fu_util_install_release (priv, rel, error))
2648 return FALSE;
2649 fu_util_display_current_message (priv);
2650
2651 /* we don't want to ask anything */
2652 if (priv->no_reboot_check) {
2653 g_debug ("skipping reboot check");
2654 return TRUE;
2655 }
2656
2657 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2658}
2659
Richard Hughesb5976832018-05-18 10:02:09 +01002660int
2661main (int argc, char *argv[])
2662{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002663 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002664 gboolean allow_older = FALSE;
2665 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002666 gboolean force = FALSE;
2667 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002668 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002669 gboolean ignore_checksum = FALSE;
2670 gboolean ignore_power = FALSE;
2671 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002672 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002673 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002674 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2675 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002676 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002677 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002678 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002679 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002680 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2681 /* TRANSLATORS: command line option */
2682 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002683 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2684 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002685 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002686 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2687 /* TRANSLATORS: command line option */
2688 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002689 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2690 /* TRANSLATORS: command line option */
2691 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002692 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2693 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002694 _("Force the action by relaxing some runtime checks"), NULL },
2695 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2696 /* TRANSLATORS: command line option */
2697 _("Ignore firmware checksum failures"), NULL },
2698 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2699 /* TRANSLATORS: command line option */
2700 _("Ignore firmware hardware mismatch failures"), NULL },
2701 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2702 /* TRANSLATORS: command line option */
2703 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002704 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2705 /* TRANSLATORS: command line option */
2706 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002707 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2708 /* TRANSLATORS: command line option */
2709 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002710 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2711 /* TRANSLATORS: command line option */
2712 _("Show all results"), NULL },
2713 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002714 /* TRANSLATORS: command line option */
2715 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002716 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002717 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002718 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002719 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2720 /* TRANSLATORS: command line option */
2721 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002722 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002723 /* TRANSLATORS: command line option */
2724 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002725 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002726 /* TRANSLATORS: command line option */
2727 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002728 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2729 /* TRANSLATORS: command line option */
2730 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002731 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2732 /* TRANSLATORS: command line option */
2733 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002734 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2735 /* TRANSLATORS: command line option */
2736 _("Filter with a set of device flags using a ~ prefix to "
2737 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002738 { NULL}
2739 };
2740
Richard Hughes429f72b2020-01-16 12:18:19 +00002741#ifdef _WIN32
2742 /* workaround Windows setting the codepage to 1252 */
2743 g_setenv ("LANG", "C.UTF-8", FALSE);
2744#endif
2745
Richard Hughesb5976832018-05-18 10:02:09 +01002746 setlocale (LC_ALL, "");
2747
Richard Hughes668ee212019-11-22 09:17:46 +00002748 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002749 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2750 textdomain (GETTEXT_PACKAGE);
2751
Richard Hughes01c0bad2019-11-22 09:08:51 +00002752#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002753 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002754 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002755 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002756 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002757#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002758
2759 /* create helper object */
2760 priv->loop = g_main_loop_new (NULL, FALSE);
2761 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002762 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002763
2764 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002765 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002766 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002767 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2768 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002769 /* TRANSLATORS: command description */
2770 _("Build firmware using a sandbox"),
2771 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002772 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002773 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002774 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2775 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002776 /* TRANSLATORS: command description */
2777 _("Dump SMBIOS data from a file"),
2778 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002779 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002780 "get-plugins",
2781 NULL,
2782 /* TRANSLATORS: command description */
2783 _("Get all enabled plugins registered with the system"),
2784 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002785 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002786 "get-details",
2787 NULL,
2788 /* TRANSLATORS: command description */
2789 _("Gets details about a firmware file"),
2790 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002791 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002792 "get-history",
2793 NULL,
2794 /* TRANSLATORS: command description */
2795 _("Show history of firmware updates"),
2796 fu_util_get_history);
2797 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002798 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002799 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2800 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002801 /* TRANSLATORS: command description */
2802 _("Gets the list of updates for connected hardware"),
2803 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002804 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002805 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002806 NULL,
2807 /* TRANSLATORS: command description */
2808 _("Get all devices that support firmware updates"),
2809 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002810 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002811 "get-device-flags",
2812 NULL,
2813 /* TRANSLATORS: command description */
2814 _("Get all device flags supported by fwupd"),
2815 fu_util_get_device_flags);
2816 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002817 "watch",
2818 NULL,
2819 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002820 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002821 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002822 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002823 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002824 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2825 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002826 /* TRANSLATORS: command description */
2827 _("Install a firmware blob on a device"),
2828 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002829 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002830 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002831 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2832 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002833 /* TRANSLATORS: command description */
2834 _("Install a firmware file on this hardware"),
2835 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002836 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002837 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002838 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2839 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002840 /* TRANSLATORS: command description */
2841 _("Reinstall firmware on a device"),
2842 fu_util_reinstall);
2843 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002844 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002845 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2846 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002847 /* TRANSLATORS: command description */
2848 _("Attach to firmware mode"),
2849 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002850 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002851 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002852 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2853 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002854 /* TRANSLATORS: command description */
2855 _("Detach to bootloader mode"),
2856 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002857 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002858 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002859 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2860 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002861 /* TRANSLATORS: command description */
2862 _("Unbind current driver"),
2863 fu_util_unbind_driver);
2864 fu_util_cmd_array_add (cmd_array,
2865 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002866 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2867 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002868 /* TRANSLATORS: command description */
2869 _("Bind new kernel driver"),
2870 fu_util_bind_driver);
2871 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002872 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002873 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2874 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002875 /* TRANSLATORS: command description */
2876 _("Activate pending devices"),
2877 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002878 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002879 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002880 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2881 _("[FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002882 /* TRANSLATORS: command description */
2883 _("Return all the hardware IDs for the machine"),
2884 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002885 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002886 "monitor",
2887 NULL,
2888 /* TRANSLATORS: command description */
2889 _("Monitor the daemon for events"),
2890 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002891 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002892 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002893 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2894 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002895 /* TRANSLATORS: command description */
2896 _("Update all devices that match local metadata"),
2897 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002898 fu_util_cmd_array_add (cmd_array,
2899 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002900 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2901 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002902 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002903 C_("command-description",
2904 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002905 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002906 fu_util_cmd_array_add (cmd_array,
2907 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002908 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2909 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002910 /* TRANSLATORS: command description */
2911 _("Update the stored metadata with current contents"),
2912 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002913 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002914 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002915 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2916 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002917 /* TRANSLATORS: command description */
2918 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002919 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002920 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002921 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002922 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2923 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002924 /* TRANSLATORS: command description */
2925 _("Convert a firmware file"),
2926 fu_util_firmware_convert);
2927 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002928 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002929 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2930 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002931 /* TRANSLATORS: command description */
2932 _("Build a firmware file"),
2933 fu_util_firmware_build);
2934 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002935 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002936 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2937 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01002938 /* TRANSLATORS: command description */
2939 _("Parse and show details about a firmware file"),
2940 fu_util_firmware_parse);
2941 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002942 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002943 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2944 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01002945 /* TRANSLATORS: command description */
2946 _("Extract a firmware blob to images"),
2947 fu_util_firmware_extract);
2948 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002949 "get-firmware-types",
2950 NULL,
2951 /* TRANSLATORS: command description */
2952 _("List the available firmware types"),
2953 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002954 fu_util_cmd_array_add (cmd_array,
2955 "get-remotes",
2956 NULL,
2957 /* TRANSLATORS: command description */
2958 _("Gets the configured remotes"),
2959 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002960 fu_util_cmd_array_add (cmd_array,
2961 "refresh",
2962 NULL,
2963 /* TRANSLATORS: command description */
2964 _("Refresh metadata from remote server"),
2965 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002966 fu_util_cmd_array_add (cmd_array,
2967 "security",
2968 NULL,
2969 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002970 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01002971 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002972 fu_util_cmd_array_add (cmd_array,
2973 "esp-mount",
2974 NULL,
2975 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002976 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002977 fu_util_esp_mount);
2978 fu_util_cmd_array_add (cmd_array,
2979 "esp-unmount",
2980 NULL,
2981 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002982 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002983 fu_util_esp_unmount);
2984 fu_util_cmd_array_add (cmd_array,
2985 "esp-list",
2986 NULL,
2987 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002988 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002989 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002990 fu_util_cmd_array_add (cmd_array,
2991 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002992 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2993 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002994 /* TRANSLATORS: command description */
2995 _("Switch the firmware branch on the device"),
2996 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01002997
2998 /* do stuff on ctrl+c */
2999 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003000#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01003001 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3002 SIGINT, fu_util_sigint_cb,
3003 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003004#endif
Richard Hughesb5976832018-05-18 10:02:09 +01003005 g_signal_connect (priv->cancellable, "cancelled",
3006 G_CALLBACK (fu_util_cancelled_cb), priv);
3007
3008 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003009 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003010
Mario Limonciello3f243a92019-01-21 22:05:23 -06003011 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06003012 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06003013 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003014 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003015 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01003016 } else {
3017 /* set our implemented feature set */
3018 fu_engine_request_set_feature_flags (priv->request,
3019 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05003020 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003021 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06003022 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06003023
Richard Hughesb5976832018-05-18 10:02:09 +01003024 /* get a list of the commands */
3025 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003026 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01003027 g_option_context_set_summary (priv->context, cmd_descriptions);
3028 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003029 /* TRANSLATORS: CLI description */
3030 _("This tool allows an administrator to use the fwupd plugins "
3031 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01003032
3033 /* TRANSLATORS: program name */
3034 g_set_application_name (_("Firmware Utility"));
3035 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05003036 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01003037 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3038 if (!ret) {
3039 /* TRANSLATORS: the user didn't read the man page */
3040 g_print ("%s: %s\n", _("Failed to parse arguments"),
3041 error->message);
3042 return EXIT_FAILURE;
3043 }
3044
Richard Hughes0e46b222019-09-05 12:13:35 +01003045 /* allow disabling SSL strict mode for broken corporate proxies */
3046 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003047 g_autofree gchar *fmt = NULL;
3048 /* TRANSLATORS: this is a prefix on the console */
3049 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003050 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003051 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3052 "to do this automatically in the future "
3053 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003054 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3055 }
3056
Richard Hughes747f5702019-08-06 14:27:26 +01003057 /* parse filter flags */
3058 if (filter != NULL) {
3059 if (!fu_util_parse_filter_flags (filter,
3060 &priv->filter_include,
3061 &priv->filter_exclude,
3062 &error)) {
3063 /* TRANSLATORS: the user didn't read the man page */
3064 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3065 error->message);
3066 return EXIT_FAILURE;
3067 }
3068 }
3069
3070
Richard Hughes460226a2018-05-21 20:56:21 +01003071 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003072 if (allow_reinstall)
3073 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3074 if (allow_older)
3075 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003076 if (allow_branch_switch)
3077 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3078 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003079 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003080 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3081 }
3082 if (ignore_checksum)
3083 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3084 if (ignore_vid_pid)
3085 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3086 if (ignore_power)
3087 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003088
Richard Hughes98ca9932018-05-18 10:24:07 +01003089 /* load engine */
3090 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3091 g_signal_connect (priv->engine, "device-added",
3092 G_CALLBACK (fu_main_engine_device_added_cb),
3093 priv);
3094 g_signal_connect (priv->engine, "device-removed",
3095 G_CALLBACK (fu_main_engine_device_removed_cb),
3096 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003097 g_signal_connect (priv->engine, "status-changed",
3098 G_CALLBACK (fu_main_engine_status_changed_cb),
3099 priv);
3100 g_signal_connect (priv->engine, "percentage-changed",
3101 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3102 priv);
3103
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003104 /* just show versions and exit */
3105 if (version) {
3106 g_autofree gchar *version_str = fu_util_get_versions ();
3107 g_print ("%s\n", version_str);
3108 return EXIT_SUCCESS;
3109 }
3110
Richard Hughes85226fd2020-06-30 14:43:48 +01003111 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003112 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3113 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3114
Richard Hughesb5976832018-05-18 10:02:09 +01003115 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003116 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003117 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003118 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003119 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003120 /* TRANSLATORS: error message explaining command to run to how to get help */
3121 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3122 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3123 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003124 return EXIT_NOTHING_TO_DO;
3125 }
Richard Hughesb5976832018-05-18 10:02:09 +01003126 return EXIT_FAILURE;
3127 }
3128
3129 /* success */
3130 return EXIT_SUCCESS;
3131}