blob: 37f2b17639fae2306c04edb692e6559a5bbdcbc1 [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 Hughesc8cc77c2019-03-07 11:57:24 +0000307 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, 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 Hughesc8cc77c2019-03-07 11:57:24 +0000465 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600466 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600467 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600468
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600469 /* parse arguments */
470 if (g_strv_length (values) == 0) {
471 devices = fu_engine_get_devices (priv->engine, error);
472 if (devices == NULL)
473 return FALSE;
474 } else if (g_strv_length (values) == 1) {
475 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -0600476 device = fu_util_get_device (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600477 if (device == NULL)
478 return FALSE;
479 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
480 g_ptr_array_add (devices, device);
481 } else {
482 g_set_error_literal (error,
483 FWUPD_ERROR,
484 FWUPD_ERROR_INVALID_ARGS,
485 "Invalid arguments");
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600486 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -0600487 }
488
Richard Hughes0ef47202020-01-06 13:59:09 +0000489 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500490 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600491 for (guint i = 0; i < devices->len; i++) {
492 FwupdDevice *dev = g_ptr_array_index (devices, i);
493 g_autoptr(GPtrArray) rels = NULL;
494 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500495 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600496
Richard Hughes747f5702019-08-06 14:27:26 +0100497 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600498 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600499 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600500 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500501 if (!no_updates_header) {
502 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
503 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
504 no_updates_header = TRUE;
505 }
506 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600507 continue;
508 }
Richard Hughes747f5702019-08-06 14:27:26 +0100509 if (!fu_util_filter_device (priv, dev))
510 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600511
512 /* get the releases for this device and filter for validity */
513 rels = fu_engine_get_upgrades (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100514 priv->request,
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600515 fwupd_device_get_id (dev),
516 &error_local);
517 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -0500518 if (!latest_header) {
519 /* TRANSLATORS: message letting the user know no device upgrade available */
520 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
521 latest_header = TRUE;
522 }
523 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -0600524 /* discard the actual reason from user, but leave for debugging */
525 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600526 continue;
527 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500528 child = g_node_append_data (root, dev);
529
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600530 for (guint j = 0; j < rels->len; j++) {
531 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500532 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600533 }
534 }
Mario Limonciello3143bad2019-02-27 07:31:00 -0600535 /* save the device state for other applications to see */
536 if (!fu_util_save_current_state (priv, error))
537 return FALSE;
538
Mario Limoncielloe8946a72020-09-30 16:31:03 -0500539 /* updates */
540 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
541 g_set_error_literal (error,
542 FWUPD_ERROR,
543 FWUPD_ERROR_NOTHING_TO_DO,
544 "No updates available for remaining devices");
545 return FALSE;
546 }
547
548 fu_util_print_tree (root, title);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600549 return TRUE;
550}
551
552static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500553fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
554{
555 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500556 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600557 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500558 gint fd;
559
560 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000561 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500562 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600563 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500564
565 /* check args */
566 if (g_strv_length (values) != 1) {
567 g_set_error_literal (error,
568 FWUPD_ERROR,
569 FWUPD_ERROR_INVALID_ARGS,
570 "Invalid arguments");
571 return FALSE;
572 }
573
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600574 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100575 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600576
Mario Limonciello716ab272018-05-29 12:34:37 -0500577 /* open file */
578 fd = open (values[0], O_RDONLY);
579 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500580 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500581 g_set_error (error,
582 FWUPD_ERROR,
583 FWUPD_ERROR_INVALID_FILE,
584 "failed to open %s",
585 values[0]);
586 return FALSE;
587 }
Richard Hughesdf89cd52020-06-26 20:25:18 +0100588 array = fu_engine_get_details (priv->engine, priv->request, fd, error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500589 close (fd);
590
591 if (array == NULL)
592 return FALSE;
593 for (guint i = 0; i < array->len; i++) {
594 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600595 FwupdRelease *rel;
596 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100597 if (!fu_util_filter_device (priv, dev))
598 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600599 child = g_node_append_data (root, dev);
600 rel = fwupd_device_get_release_default (dev);
601 if (rel != NULL)
602 g_node_append_data (child, rel);
603
Mario Limonciello716ab272018-05-29 12:34:37 -0500604 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500605 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500606
Mario Limonciello716ab272018-05-29 12:34:37 -0500607 return TRUE;
608}
609
610static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100611fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
612{
613 g_autoptr(GString) str = g_string_new (NULL);
614
615 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
616 const gchar *tmp = fwupd_device_flag_to_string (i);
617 if (tmp == NULL)
618 break;
619 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
620 g_string_append (str, " ");
621 g_string_append (str, tmp);
622 g_string_append (str, " ~");
623 g_string_append (str, tmp);
624 }
625 g_print ("%s\n", str->str);
626
627 return TRUE;
628}
629
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500630static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100631fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500632{
633 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100634 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100635 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
636 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100637 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500638 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500639 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100640 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500641 GNode *child = g_node_append_data (root, dev_tmp);
642 fu_util_build_device_tree (priv, child, devs, dev_tmp);
643 }
644 }
645}
646
647static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100648fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500649{
650 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600651 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500652 g_autoptr(GPtrArray) devs = NULL;
653
654 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000655 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500656 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600657 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500658
659 /* print */
660 devs = fu_engine_get_devices (priv->engine, error);
661 if (devs == NULL)
662 return FALSE;
663
664 /* print */
665 if (devs->len == 0) {
666 /* TRANSLATORS: nothing attached that can be upgraded */
667 g_print ("%s\n", _("No hardware detected with firmware update capability"));
668 return TRUE;
669 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000670 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500671 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500672 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500673
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100674 /* save the device state for other applications to see */
675 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500676}
677
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500678static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600679fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500680 FwupdDevice *device,
681 FuUtilPrivate *priv)
682{
683 g_autofree gchar *str = NULL;
684
Richard Hughes809abea2019-03-23 11:06:18 +0000685 /* allowed to set whenever the device has changed */
686 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
687 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
688 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
689 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
690
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500691 /* same as last time, so ignore */
692 if (priv->current_device != NULL &&
693 fwupd_device_compare (priv->current_device, device) == 0)
694 return;
695
Richard Hughesee562b52020-04-07 14:32:52 +0100696 /* ignore indirect devices that might have changed */
697 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
698 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
699 g_debug ("ignoring %s with status %s",
700 fwupd_device_get_name (device),
701 fwupd_status_to_string (fwupd_device_get_status (device)));
702 return;
703 }
704
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500705 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600706 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
707 /* TRANSLATORS: %1 is a device name */
708 str = g_strdup_printf (_("Updating %s…"),
709 fwupd_device_get_name (device));
710 fu_progressbar_set_title (priv->progressbar, str);
711 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
712 /* TRANSLATORS: %1 is a device name */
713 str = g_strdup_printf (_("Installing on %s…"),
714 fwupd_device_get_name (device));
715 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000716 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
717 /* TRANSLATORS: %1 is a device name */
718 str = g_strdup_printf (_("Reading from %s…"),
719 fwupd_device_get_name (device));
720 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600721 } else {
722 g_warning ("no FuUtilOperation set");
723 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500724 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600725
Mario Limonciello32241f42019-01-24 10:12:41 -0600726 if (priv->current_message == NULL) {
727 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
728 if (tmp != NULL)
729 priv->current_message = g_strdup (tmp);
730 }
731}
732
733static void
734fu_util_display_current_message (FuUtilPrivate *priv)
735{
736 if (priv->current_message == NULL)
737 return;
738 g_print ("%s\n", priv->current_message);
739 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500740}
741
Richard Hughes98ca9932018-05-18 10:24:07 +0100742static gboolean
743fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
744{
745 g_autoptr(FuDevice) device = NULL;
746 g_autoptr(GBytes) blob_fw = NULL;
747
748 /* invalid args */
749 if (g_strv_length (values) == 0) {
750 g_set_error_literal (error,
751 FWUPD_ERROR,
752 FWUPD_ERROR_INVALID_ARGS,
753 "Invalid arguments");
754 return FALSE;
755 }
756
757 /* parse blob */
758 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500759 if (blob_fw == NULL) {
760 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100761 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500762 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100763
764 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000765 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100766 return FALSE;
767
768 /* get device */
769 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100770 device = fu_util_get_device (priv, values[1], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100771 if (device == NULL)
772 return FALSE;
773 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100774 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100775 if (device == NULL)
776 return FALSE;
777 }
778
Mario Limonciello3f243a92019-01-21 22:05:23 -0600779 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500780 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600781 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500782
Richard Hughes98ca9932018-05-18 10:24:07 +0100783 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000784 if (priv->prepare_blob) {
785 g_autoptr(GPtrArray) devices = NULL;
786 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
787 g_ptr_array_add (devices, g_object_ref (device));
788 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
789 g_prefix_error (error, "failed to prepare composite action: ");
790 return FALSE;
791 }
792 }
Richard Hughesf1fa73e2020-04-06 16:19:04 +0100793 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000794 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600795 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000796 if (priv->cleanup_blob) {
797 g_autoptr(FuDevice) device_new = NULL;
798 g_autoptr(GError) error_local = NULL;
799
800 /* get the possibly new device from the old ID */
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100801 device_new = fu_util_get_device (priv,
802 fu_device_get_id (device),
803 &error_local);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000804 if (device_new == NULL) {
805 g_debug ("failed to find new device: %s",
806 error_local->message);
807 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600808 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000809 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
810 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
811 g_prefix_error (error, "failed to cleanup composite action: ");
812 return FALSE;
813 }
814 }
815 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600816
Mario Limonciello32241f42019-01-24 10:12:41 -0600817 fu_util_display_current_message (priv);
818
Mario Limonciello3f243a92019-01-21 22:05:23 -0600819 /* success */
820 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100821}
822
Richard Hughesa58510b2019-10-30 10:03:12 +0000823static gboolean
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100824fu_util_firmware_dump (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa58510b2019-10-30 10:03:12 +0000825{
826 g_autoptr(FuDevice) device = NULL;
827 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
828 g_autoptr(GBytes) blob_fw = NULL;
829
830 /* invalid args */
831 if (g_strv_length (values) == 0) {
832 g_set_error_literal (error,
833 FWUPD_ERROR,
834 FWUPD_ERROR_INVALID_ARGS,
835 "Invalid arguments");
836 return FALSE;
837 }
838
839 /* file already exists */
840 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
841 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
842 g_set_error_literal (error,
843 FWUPD_ERROR,
844 FWUPD_ERROR_INVALID_ARGS,
845 "Filename already exists");
846 return FALSE;
847 }
848
Richard Hughes02792c02019-11-01 14:21:20 +0000849 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000850 * avoid failing at the end of a potentially lengthy operation */
851 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
852 return FALSE;
853
854 /* load engine */
855 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
856 return FALSE;
857
858 /* get device */
859 if (g_strv_length (values) >= 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100860 device = fu_util_get_device (priv, values[1], error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000861 if (device == NULL)
862 return FALSE;
863 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100864 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000865 if (device == NULL)
866 return FALSE;
867 }
868 priv->current_operation = FU_UTIL_OPERATION_READ;
869 g_signal_connect (priv->engine, "device-changed",
870 G_CALLBACK (fu_util_update_device_changed_cb), priv);
871
872 /* dump firmware */
Richard Hughesfbd8b5d2020-09-24 11:48:59 +0100873 blob_fw = fu_engine_firmware_dump (priv->engine, device, priv->flags, error);
Richard Hughesa58510b2019-10-30 10:03:12 +0000874 if (blob_fw == NULL)
875 return FALSE;
876 return fu_common_set_contents_bytes (values[0], blob_fw, error);
877}
878
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100879static gint
880fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
881{
882 FuInstallTask *task1 = *((FuInstallTask **) a);
883 FuInstallTask *task2 = *((FuInstallTask **) b);
884 return fu_install_task_compare (task1, task2);
885}
886
887static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100888fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
889{
Filipe Laínse0914272019-09-20 10:04:43 +0100890 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100891 { "curl", uri, "--output", fn, NULL },
892 { NULL } };
893 for (guint i = 0; argv[i][0] != NULL; i++) {
894 g_autoptr(GError) error_local = NULL;
Richard Hughesdb344d52020-09-09 19:42:27 +0100895 g_autofree gchar *fn_tmp = NULL;
896 fn_tmp = fu_common_find_program_in_path (argv[i][0], &error_local);
897 if (fn_tmp == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100898 g_debug ("%s", error_local->message);
899 continue;
900 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000901 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100902 }
903 g_set_error_literal (error,
904 FWUPD_ERROR,
905 FWUPD_ERROR_NOT_FOUND,
906 "no supported out-of-process downloaders found");
907 return FALSE;
908}
909
910static gchar *
911fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
912{
913 g_autofree gchar *filename = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100914
915 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000916 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
917 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000918 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100919 return g_strdup (perhapsfn);
920
921 /* download the firmware to a cachedir */
922 filename = fu_util_get_user_cache_path (perhapsfn);
923 if (!fu_common_mkdir_parent (filename, error))
924 return NULL;
925 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
926 return NULL;
927 return g_steal_pointer (&filename);
928}
929
930static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100931fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
932{
Richard Hughes3d178be2018-08-30 11:14:24 +0100933 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100934 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100935 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100936 g_autoptr(GPtrArray) devices_possible = NULL;
937 g_autoptr(GPtrArray) errors = NULL;
938 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100939 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100940
Mario Limonciello8949e892018-05-25 08:03:06 -0500941 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000942 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500943 return FALSE;
944
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100945 /* handle both forms */
946 if (g_strv_length (values) == 1) {
947 devices_possible = fu_engine_get_devices (priv->engine, error);
948 if (devices_possible == NULL)
949 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000950 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100951 } else if (g_strv_length (values) == 2) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100952 FuDevice *device = fu_util_get_device (priv, values[1], error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100953 if (device == NULL)
954 return FALSE;
955 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
956 g_ptr_array_add (devices_possible, device);
957 } else {
958 g_set_error_literal (error,
959 FWUPD_ERROR,
960 FWUPD_ERROR_INVALID_ARGS,
961 "Invalid arguments");
962 return FALSE;
963 }
964
Richard Hughes3d178be2018-08-30 11:14:24 +0100965 /* download if required */
966 filename = fu_util_download_if_required (priv, values[0], error);
967 if (filename == NULL)
968 return FALSE;
969
Richard Hughes481aa2a2018-09-18 20:51:46 +0100970 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100971 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500972 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100973 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100974 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500975 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100976 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
977 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100978 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600979 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100980 if (components == NULL)
981 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100982
Richard Hughes481aa2a2018-09-18 20:51:46 +0100983 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100984 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
985 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100986 for (guint i = 0; i < components->len; i++) {
987 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100988
989 /* do any devices pass the requirements */
990 for (guint j = 0; j < devices_possible->len; j++) {
991 FuDevice *device = g_ptr_array_index (devices_possible, j);
992 g_autoptr(FuInstallTask) task = NULL;
993 g_autoptr(GError) error_local = NULL;
994
995 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100996 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100997 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +0100998 priv->request,
999 task,
1000 priv->flags | FWUPD_INSTALL_FLAG_FORCE,
Mario Limonciello537da0e2020-03-09 15:38:17 -05001001 &error_local)) {
1002 g_debug ("first pass requirement on %s:%s failed: %s",
1003 fu_device_get_id (device),
1004 xb_node_query_text (component, "id", NULL),
1005 error_local->message);
1006 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1007 continue;
1008 }
1009
1010 /* make a second pass using possibly updated version format now */
1011 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
1012 if (!fu_engine_check_requirements (priv->engine,
Richard Hughesdf89cd52020-06-26 20:25:18 +01001013 priv->request,
1014 task,
1015 priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001016 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -05001017 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001018 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +01001019 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001020 error_local->message);
1021 g_ptr_array_add (errors, g_steal_pointer (&error_local));
1022 continue;
1023 }
1024
Mario Limonciello7a3df4b2019-01-31 10:27:22 -06001025 /* if component should have an update message from CAB */
1026 fu_device_incorporate_from_component (device, component);
1027
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001028 /* success */
1029 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
1030 }
1031 }
1032
1033 /* order the install tasks by the device priority */
1034 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
1035
1036 /* nothing suitable */
1037 if (install_tasks->len == 0) {
1038 GError *error_tmp = fu_common_error_array_get_best (errors);
1039 g_propagate_error (error, error_tmp);
1040 return FALSE;
1041 }
1042
Mario Limonciello3f243a92019-01-21 22:05:23 -06001043 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001044 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -06001045 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -05001046
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001047 /* install all the tasks */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001048 if (!fu_engine_install_tasks (priv->engine,
1049 priv->request,
1050 install_tasks,
1051 blob_cab,
1052 priv->flags,
1053 error))
Richard Hughesdbd8c762018-06-15 20:31:40 +01001054 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001055
Mario Limonciello32241f42019-01-24 10:12:41 -06001056 fu_util_display_current_message (priv);
1057
Mario Limonciello3f243a92019-01-21 22:05:23 -06001058 /* we don't want to ask anything */
1059 if (priv->no_reboot_check) {
1060 g_debug ("skipping reboot check");
1061 return TRUE;
1062 }
1063
Mario Limonciello3143bad2019-02-27 07:31:00 -06001064 /* save the device state for other applications to see */
1065 if (!fu_util_save_current_state (priv, error))
1066 return FALSE;
1067
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001068 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -06001069 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001070}
1071
Richard Hughes98ca9932018-05-18 10:24:07 +01001072static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -05001073fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001074{
Mario Limonciellofd734852019-08-01 16:41:42 -05001075 FwupdRemote *remote;
1076 const gchar *remote_id;
1077 const gchar *uri_tmp;
1078 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001079
Mario Limonciellofd734852019-08-01 16:41:42 -05001080 uri_tmp = fwupd_release_get_uri (rel);
1081 if (uri_tmp == NULL) {
1082 g_set_error_literal (error,
1083 FWUPD_ERROR,
1084 FWUPD_ERROR_INVALID_FILE,
1085 "release missing URI");
1086 return FALSE;
1087 }
1088 remote_id = fwupd_release_get_remote_id (rel);
1089 if (remote_id == NULL) {
1090 g_set_error (error,
1091 FWUPD_ERROR,
1092 FWUPD_ERROR_INVALID_FILE,
1093 "failed to find remote for %s",
1094 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +01001095 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -05001096 }
1097
1098 remote = fu_engine_get_remote_by_id (priv->engine,
1099 remote_id,
1100 error);
1101 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -06001102 return FALSE;
1103
Mario Limonciellofd734852019-08-01 16:41:42 -05001104 argv = g_new0 (gchar *, 2);
Daniel Campello722f5322020-08-12 11:27:38 -06001105 /* local remotes may have the firmware already */
Richard Hughes3a73c342020-11-13 13:25:22 +00001106 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL &&
1107 !fu_util_is_url (uri_tmp)) {
Mario Limonciellofd734852019-08-01 16:41:42 -05001108 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
1109 g_autofree gchar *path = g_path_get_dirname (fn_cache);
1110 argv[0] = g_build_filename (path, uri_tmp, NULL);
1111 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
1112 argv[0] = g_strdup (uri_tmp + 7);
1113 /* web remote, fu_util_install will download file */
1114 } else {
1115 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
1116 }
1117 return fu_util_install (priv, argv, error);
1118}
1119
1120static gboolean
1121fu_util_update_all (FuUtilPrivate *priv, GError **error)
1122{
1123 g_autoptr(GPtrArray) devices = NULL;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001124 gboolean no_updates_header = FALSE;
1125 gboolean latest_header = FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -06001126
Mario Limonciello46aaee82019-01-10 12:58:00 -06001127 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001128 if (devices == NULL)
1129 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001130 fwupd_device_array_ensure_parents (devices);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001131 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001132 for (guint i = 0; i < devices->len; i++) {
1133 FwupdDevice *dev = g_ptr_array_index (devices, i);
1134 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001135 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001136 g_autoptr(GPtrArray) rels = NULL;
1137 g_autoptr(GError) error_local = NULL;
1138
1139 if (!fu_util_is_interesting_device (dev))
1140 continue;
1141 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001142 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001143 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001144 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001145 if (!no_updates_header) {
1146 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1147 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1148 no_updates_header = TRUE;
1149 }
1150 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001151 continue;
1152 }
Richard Hughes747f5702019-08-06 14:27:26 +01001153 if (!fu_util_filter_device (priv, dev))
1154 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001155
1156 device_id = fu_device_get_id (dev);
Richard Hughesdf89cd52020-06-26 20:25:18 +01001157 rels = fu_engine_get_upgrades (priv->engine,
1158 priv->request,
1159 device_id,
1160 &error_local);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001161 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001162 if (!latest_header) {
1163 /* TRANSLATORS: message letting the user know no device upgrade available */
1164 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1165 latest_header = TRUE;
1166 }
1167 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001168 /* discard the actual reason from user, but leave for debugging */
1169 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001170 continue;
1171 }
1172
Mario Limonciello98b95162019-10-30 09:20:43 -05001173 if (!priv->no_safety_check) {
1174 if (!fu_util_prompt_warning (dev,
1175 fu_util_get_tree_title (priv),
1176 error))
1177 return FALSE;
1178 }
1179
Mario Limonciello46aaee82019-01-10 12:58:00 -06001180 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001181 if (!fu_util_install_release (priv, rel, &error_local)) {
1182 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001183 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001184 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001185 fu_util_display_current_message (priv);
1186 }
1187 return TRUE;
1188}
1189
1190static gboolean
Mario Limonciello9917bb42020-04-20 13:41:27 -05001191fu_util_update_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
Mario Limonciellofd734852019-08-01 16:41:42 -05001192{
1193 FwupdRelease *rel;
1194 g_autoptr(FuDevice) dev = NULL;
1195 g_autoptr(GPtrArray) rels = NULL;
1196
Mario Limonciello9917bb42020-04-20 13:41:27 -05001197 /* do not allow a partial device-id, lookup GUIDs */
1198 dev = fu_util_get_device (priv, id, error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001199 if (dev == NULL)
1200 return FALSE;
1201
1202 /* get the releases for this device and filter for validity */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001203 rels = fu_engine_get_upgrades (priv->engine,
1204 priv->request,
1205 fu_device_get_id (dev),
1206 error);
Mario Limonciellofd734852019-08-01 16:41:42 -05001207 if (rels == NULL)
1208 return FALSE;
1209 rel = g_ptr_array_index (rels, 0);
1210 if (!fu_util_install_release (priv, rel, error))
1211 return FALSE;
1212 fu_util_display_current_message (priv);
1213
1214 return TRUE;
1215}
1216
1217static gboolean
1218fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1219{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001220 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1221 g_set_error_literal (error,
1222 FWUPD_ERROR,
1223 FWUPD_ERROR_INVALID_ARGS,
1224 "--allow-older is not supported for this command");
1225 return FALSE;
1226 }
1227
1228 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1229 g_set_error_literal (error,
1230 FWUPD_ERROR,
1231 FWUPD_ERROR_INVALID_ARGS,
1232 "--allow-reinstall is not supported for this command");
1233 return FALSE;
1234 }
1235
Mario Limonciellofd734852019-08-01 16:41:42 -05001236 if (g_strv_length (values) > 1) {
1237 g_set_error_literal (error,
1238 FWUPD_ERROR,
1239 FWUPD_ERROR_INVALID_ARGS,
1240 "Invalid arguments");
1241 return FALSE;
1242 }
1243
1244 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1245 return FALSE;
1246
1247 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1248 g_signal_connect (priv->engine, "device-changed",
1249 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1250
1251 if (g_strv_length (values) == 1) {
1252 if (!fu_util_update_by_id (priv, values[0], error))
1253 return FALSE;
1254 } else {
1255 if (!fu_util_update_all (priv, error))
1256 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001257 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001258
1259 /* we don't want to ask anything */
1260 if (priv->no_reboot_check) {
1261 g_debug ("skipping reboot check");
1262 return TRUE;
1263 }
1264
Mario Limonciello3143bad2019-02-27 07:31:00 -06001265 /* save the device state for other applications to see */
1266 if (!fu_util_save_current_state (priv, error))
1267 return FALSE;
1268
Mario Limonciello3f243a92019-01-21 22:05:23 -06001269 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001270}
1271
1272static gboolean
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001273fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1274{
1275 g_autoptr(FwupdRelease) rel = NULL;
1276 g_autoptr(GPtrArray) rels = NULL;
1277 g_autoptr(FuDevice) dev = NULL;
1278
1279 if (g_strv_length (values) != 1) {
1280 g_set_error_literal (error,
1281 FWUPD_ERROR,
1282 FWUPD_ERROR_INVALID_ARGS,
1283 "Invalid arguments");
1284 return FALSE;
1285 }
1286
1287 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1288 return FALSE;
1289
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001290 dev = fu_util_get_device (priv, values[0], error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001291 if (dev == NULL)
1292 return FALSE;
1293
1294 /* try to lookup/match release from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01001295 rels = fu_engine_get_releases_for_device (priv->engine,
1296 priv->request,
1297 dev,
1298 error);
Filipe Laínsb2f377a2020-03-30 21:05:50 +01001299 if (rels == NULL)
1300 return FALSE;
1301
1302 for (guint j = 0; j < rels->len; j++) {
1303 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1304 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1305 fu_device_get_version (dev),
1306 fu_device_get_version_format (dev)) == 0) {
1307 rel = g_object_ref (rel_tmp);
1308 break;
1309 }
1310 }
1311 if (rel == NULL) {
1312 g_set_error (error,
1313 FWUPD_ERROR,
1314 FWUPD_ERROR_NOT_SUPPORTED,
1315 "Unable to locate release for %s version %s",
1316 fu_device_get_name (dev),
1317 fu_device_get_version (dev));
1318 return FALSE;
1319 }
1320
1321 /* update the console if composite devices are also updated */
1322 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1323 g_signal_connect (priv->engine, "device-changed",
1324 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1325 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1326 if (!fu_util_install_release (priv, rel, error))
1327 return FALSE;
1328 fu_util_display_current_message (priv);
1329
1330 /* we don't want to ask anything */
1331 if (priv->no_reboot_check) {
1332 g_debug ("skipping reboot check");
1333 return TRUE;
1334 }
1335
1336 /* save the device state for other applications to see */
1337 if (!fu_util_save_current_state (priv, error))
1338 return FALSE;
1339
1340 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1341}
1342
1343static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001344fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1345{
1346 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001347 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001348
1349 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001350 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001351 return FALSE;
1352
Richard Hughes98ca9932018-05-18 10:24:07 +01001353 /* get device */
1354 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001355 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001356 if (device == NULL)
1357 return FALSE;
1358 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001359 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001360 if (device == NULL)
1361 return FALSE;
1362 }
1363
1364 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001365 locker = fu_device_locker_new (device, error);
1366 if (locker == NULL)
1367 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001368 return fu_device_detach (device, error);
1369}
1370
1371static gboolean
Richard Hughes34f7d9d2020-09-19 15:28:11 +01001372fu_util_unbind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1373{
1374 g_autoptr(FuDevice) device = NULL;
1375 g_autoptr(FuDeviceLocker) locker = NULL;
1376
1377 /* load engine */
1378 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1379 return FALSE;
1380
1381 /* get device */
1382 if (g_strv_length (values) == 1) {
1383 device = fu_util_get_device (priv, values[0], error);
1384 } else {
1385 device = fu_util_prompt_for_device (priv, NULL, error);
1386 }
1387 if (device == NULL)
1388 return FALSE;
1389
1390 /* run vfunc */
1391 locker = fu_device_locker_new (device, error);
1392 if (locker == NULL)
1393 return FALSE;
1394 return fu_device_unbind_driver (device, error);
1395}
1396
1397static gboolean
1398fu_util_bind_driver (FuUtilPrivate *priv, gchar **values, GError **error)
1399{
1400 g_autoptr(FuDevice) device = NULL;
1401 g_autoptr(FuDeviceLocker) locker = NULL;
1402
1403 /* load engine */
1404 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1405 return FALSE;
1406
1407 /* get device */
1408 if (g_strv_length (values) == 3) {
1409 device = fu_util_get_device (priv, values[2], error);
1410 if (device == NULL)
1411 return FALSE;
1412 } else if (g_strv_length (values) == 2) {
1413 device = fu_util_prompt_for_device (priv, NULL, error);
1414 if (device == NULL)
1415 return FALSE;
1416 } else {
1417 g_set_error_literal (error,
1418 FWUPD_ERROR,
1419 FWUPD_ERROR_INVALID_ARGS,
1420 "Invalid arguments");
1421 return FALSE;
1422 }
1423
1424 /* run vfunc */
1425 locker = fu_device_locker_new (device, error);
1426 if (locker == NULL)
1427 return FALSE;
1428 return fu_device_bind_driver (device, values[0], values[1], error);
1429}
1430
1431static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001432fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1433{
1434 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001435 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001436
1437 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001438 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001439 return FALSE;
1440
Richard Hughes98ca9932018-05-18 10:24:07 +01001441 /* get device */
1442 if (g_strv_length (values) >= 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001443 device = fu_util_get_device (priv, values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001444 if (device == NULL)
1445 return FALSE;
1446 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01001447 device = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes98ca9932018-05-18 10:24:07 +01001448 if (device == NULL)
1449 return FALSE;
1450 }
1451
1452 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001453 locker = fu_device_locker_new (device, error);
1454 if (locker == NULL)
1455 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001456 return fu_device_attach (device, error);
1457}
1458
Richard Hughes1d894f12018-08-31 13:05:51 +01001459static gboolean
Mario Limonciello02085a02020-09-11 14:59:35 -05001460fu_util_check_activation_needed (FuUtilPrivate *priv, GError **error)
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001461{
1462 gboolean has_pending = FALSE;
1463 g_autoptr(FuHistory) history = fu_history_new ();
Mario Limonciello02085a02020-09-11 14:59:35 -05001464 g_autoptr(GPtrArray) devices = fu_history_get_devices (history, error);
1465 if (devices == NULL)
1466 return FALSE;
1467
1468 /* only start up the plugins needed */
1469 for (guint i = 0; i < devices->len; i++) {
1470 FuDevice *dev = g_ptr_array_index (devices, i);
1471 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1472 fu_engine_add_plugin_filter (priv->engine,
1473 fu_device_get_plugin (dev));
1474 has_pending = TRUE;
1475 }
1476 }
1477
1478 if (!has_pending) {
1479 g_set_error_literal (error,
1480 FWUPD_ERROR,
1481 FWUPD_ERROR_NOTHING_TO_DO,
1482 "No devices to activate");
1483 return FALSE;
1484 }
1485
1486 return TRUE;
1487}
1488
1489static gboolean
1490fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1491{
1492 gboolean has_pending = FALSE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001493 g_autoptr(GPtrArray) devices = NULL;
1494
1495 /* check the history database before starting the daemon */
Mario Limonciello02085a02020-09-11 14:59:35 -05001496 if (!fu_util_check_activation_needed (priv, error))
1497 return FALSE;
1498
1499 /* load engine */
1500 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
1501 return FALSE;
1502
1503 /* parse arguments */
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001504 if (g_strv_length (values) == 0) {
Mario Limonciello02085a02020-09-11 14:59:35 -05001505 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001506 if (devices == NULL)
1507 return FALSE;
1508 } else if (g_strv_length (values) == 1) {
1509 FuDevice *device;
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001510 device = fu_util_get_device (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001511 if (device == NULL)
1512 return FALSE;
1513 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1514 g_ptr_array_add (devices, device);
1515 } else {
1516 g_set_error_literal (error,
1517 FWUPD_ERROR,
1518 FWUPD_ERROR_INVALID_ARGS,
1519 "Invalid arguments");
1520 return FALSE;
1521 }
1522
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001523 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05001524 /* order by device priority */
1525 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001526 for (guint i = 0; i < devices->len; i++) {
1527 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001528 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1529 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001530 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1531 continue;
Mario Limonciello02085a02020-09-11 14:59:35 -05001532 has_pending = TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001533 /* TRANSLATORS: shown when shutting down to switch to the new version */
1534 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1535 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1536 return FALSE;
1537 }
1538
Mario Limonciello02085a02020-09-11 14:59:35 -05001539 if (!has_pending) {
1540 g_set_error_literal (error,
1541 FWUPD_ERROR,
1542 FWUPD_ERROR_NOTHING_TO_DO,
1543 "No devices to activate");
1544 return FALSE;
1545 }
1546
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001547 return TRUE;
1548}
1549
1550static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001551fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1552{
1553 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1554 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1555 const gchar *hwid_keys[] = {
1556 FU_HWIDS_KEY_BIOS_VENDOR,
1557 FU_HWIDS_KEY_BIOS_VERSION,
1558 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1559 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1560 FU_HWIDS_KEY_MANUFACTURER,
1561 FU_HWIDS_KEY_FAMILY,
1562 FU_HWIDS_KEY_PRODUCT_NAME,
1563 FU_HWIDS_KEY_PRODUCT_SKU,
1564 FU_HWIDS_KEY_ENCLOSURE_KIND,
1565 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1566 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1567 NULL };
1568
1569 /* read DMI data */
1570 if (g_strv_length (values) == 0) {
1571 if (!fu_smbios_setup (smbios, error))
1572 return FALSE;
1573 } else if (g_strv_length (values) == 1) {
1574 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1575 return FALSE;
1576 } else {
1577 g_set_error_literal (error,
1578 FWUPD_ERROR,
1579 FWUPD_ERROR_INVALID_ARGS,
1580 "Invalid arguments");
1581 return FALSE;
1582 }
1583 if (!fu_hwids_setup (hwids, smbios, error))
1584 return FALSE;
1585
1586 /* show debug output */
1587 g_print ("Computer Information\n");
1588 g_print ("--------------------\n");
1589 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1590 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1591 if (tmp == NULL)
1592 continue;
1593 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1594 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1595 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1596 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1597 } else {
1598 g_print ("%s: %s\n", hwid_keys[i], tmp);
1599 }
1600 }
1601
1602 /* show GUIDs */
1603 g_print ("\nHardware IDs\n");
1604 g_print ("------------\n");
1605 for (guint i = 0; i < 15; i++) {
1606 const gchar *keys = NULL;
1607 g_autofree gchar *guid = NULL;
1608 g_autofree gchar *key = NULL;
1609 g_autofree gchar *keys_str = NULL;
1610 g_auto(GStrv) keysv = NULL;
1611 g_autoptr(GError) error_local = NULL;
1612
1613 /* get the GUID */
1614 key = g_strdup_printf ("HardwareID-%u", i);
1615 keys = fu_hwids_get_replace_keys (hwids, key);
1616 guid = fu_hwids_get_guid (hwids, key, &error_local);
1617 if (guid == NULL) {
1618 g_print ("%s\n", error_local->message);
1619 continue;
1620 }
1621
1622 /* show what makes up the GUID */
1623 keysv = g_strsplit (keys, "&", -1);
1624 keys_str = g_strjoinv (" + ", keysv);
1625 g_print ("{%s} <- %s\n", guid, keys_str);
1626 }
1627
1628 return TRUE;
1629}
1630
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001631static gboolean
1632fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1633{
1634 const gchar *script_fn = "startup.sh";
1635 const gchar *output_fn = "firmware.bin";
1636 g_autoptr(GBytes) archive_blob = NULL;
1637 g_autoptr(GBytes) firmware_blob = NULL;
1638 if (g_strv_length (values) < 2) {
1639 g_set_error_literal (error,
1640 FWUPD_ERROR,
1641 FWUPD_ERROR_INVALID_ARGS,
1642 "Invalid arguments");
1643 return FALSE;
1644 }
1645 archive_blob = fu_common_get_contents_bytes (values[0], error);
1646 if (archive_blob == NULL)
1647 return FALSE;
1648 if (g_strv_length (values) > 2)
1649 script_fn = values[2];
1650 if (g_strv_length (values) > 3)
1651 output_fn = values[3];
1652 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1653 if (firmware_blob == NULL)
1654 return FALSE;
1655 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1656}
1657
Richard Hughes3d607622019-03-07 16:59:27 +00001658static gboolean
1659fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1660{
1661 g_autofree gchar *sig = NULL;
1662
1663 /* check args */
1664 if (g_strv_length (values) != 1) {
1665 g_set_error_literal (error,
1666 FWUPD_ERROR,
1667 FWUPD_ERROR_INVALID_ARGS,
1668 "Invalid arguments: value expected");
1669 return FALSE;
1670 }
1671
1672 /* start engine */
1673 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1674 return FALSE;
1675 sig = fu_engine_self_sign (priv->engine, values[0],
Richard Hughesd5aab652020-02-25 12:47:50 +00001676 JCAT_SIGN_FLAG_ADD_TIMESTAMP |
1677 JCAT_SIGN_FLAG_ADD_CERT, error);
Richard Hughes3d607622019-03-07 16:59:27 +00001678 if (sig == NULL)
1679 return FALSE;
1680 g_print ("%s\n", sig);
1681 return TRUE;
1682}
1683
Mario Limonciello62f84862018-10-18 13:15:23 -05001684static void
1685fu_util_device_added_cb (FwupdClient *client,
1686 FwupdDevice *device,
1687 gpointer user_data)
1688{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001689 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001690 /* TRANSLATORS: this is when a device is hotplugged */
1691 g_print ("%s\n%s", _("Device added:"), tmp);
1692}
1693
1694static void
1695fu_util_device_removed_cb (FwupdClient *client,
1696 FwupdDevice *device,
1697 gpointer user_data)
1698{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001699 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001700 /* TRANSLATORS: this is when a device is hotplugged */
1701 g_print ("%s\n%s", _("Device removed:"), tmp);
1702}
1703
1704static void
1705fu_util_device_changed_cb (FwupdClient *client,
1706 FwupdDevice *device,
1707 gpointer user_data)
1708{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001709 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001710 /* TRANSLATORS: this is when a device has been updated */
1711 g_print ("%s\n%s", _("Device changed:"), tmp);
1712}
1713
1714static void
1715fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1716{
1717 /* TRANSLATORS: this is when the daemon state changes */
1718 g_print ("%s\n", _("Changed"));
1719}
1720
1721static gboolean
1722fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1723{
1724 g_autoptr(FwupdClient) client = fwupd_client_new ();
1725
1726 /* get all the devices */
1727 if (!fwupd_client_connect (client, priv->cancellable, error))
1728 return FALSE;
1729
1730 /* watch for any hotplugged device */
1731 g_signal_connect (client, "changed",
1732 G_CALLBACK (fu_util_changed_cb), priv);
1733 g_signal_connect (client, "device-added",
1734 G_CALLBACK (fu_util_device_added_cb), priv);
1735 g_signal_connect (client, "device-removed",
1736 G_CALLBACK (fu_util_device_removed_cb), priv);
1737 g_signal_connect (client, "device-changed",
1738 G_CALLBACK (fu_util_device_changed_cb), priv);
1739 g_signal_connect (priv->cancellable, "cancelled",
1740 G_CALLBACK (fu_util_cancelled_cb), priv);
1741 g_main_loop_run (priv->loop);
1742 return TRUE;
1743}
1744
Richard Hughes15684492019-03-15 16:27:50 +00001745static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001746fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1747{
1748 g_autoptr(GPtrArray) firmware_types = NULL;
1749
1750 /* load engine */
1751 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1752 return FALSE;
1753
1754 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1755 for (guint i = 0; i < firmware_types->len; i++) {
1756 const gchar *id = g_ptr_array_index (firmware_types, i);
1757 g_print ("%s\n", id);
1758 }
1759 if (firmware_types->len == 0) {
1760 /* TRANSLATORS: nothing found */
1761 g_print ("%s\n", _("No firmware IDs found"));
1762 return TRUE;
1763 }
1764
1765 return TRUE;
1766}
1767
1768static gchar *
Richard Hughes0924c932020-09-22 19:07:05 +01001769fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
Richard Hughes95c98a92019-10-22 16:03:15 +01001770{
1771 g_autoptr(GPtrArray) firmware_types = NULL;
1772 guint idx;
1773 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1774
1775 /* TRANSLATORS: get interactive prompt */
1776 g_print ("%s\n", _("Choose a firmware type:"));
1777 /* TRANSLATORS: this is to abort the interactive prompt */
1778 g_print ("0.\t%s\n", _("Cancel"));
1779 for (guint i = 0; i < firmware_types->len; i++) {
1780 const gchar *id = g_ptr_array_index (firmware_types, i);
1781 g_print ("%u.\t%s\n", i + 1, id);
1782 }
1783 idx = fu_util_prompt_for_number (firmware_types->len);
1784 if (idx == 0) {
1785 g_set_error_literal (error,
1786 FWUPD_ERROR,
1787 FWUPD_ERROR_NOTHING_TO_DO,
1788 "Request canceled");
1789 return NULL;
1790 }
1791
1792 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1793}
1794
1795static gboolean
1796fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1797{
1798 GType gtype;
1799 g_autoptr(GBytes) blob = NULL;
1800 g_autoptr(FuFirmware) firmware = NULL;
1801 g_autofree gchar *firmware_type = NULL;
1802 g_autofree gchar *str = NULL;
1803
1804 /* check args */
1805 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1806 g_set_error_literal (error,
1807 FWUPD_ERROR,
1808 FWUPD_ERROR_INVALID_ARGS,
1809 "Invalid arguments: filename required");
1810 return FALSE;
1811 }
1812
1813 if (g_strv_length (values) == 2)
1814 firmware_type = g_strdup (values[1]);
1815
1816 /* load file */
1817 blob = fu_common_get_contents_bytes (values[0], error);
1818 if (blob == NULL)
1819 return FALSE;
1820
1821 /* load engine */
1822 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1823 return FALSE;
1824
1825 /* find the GType to use */
1826 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001827 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughes95c98a92019-10-22 16:03:15 +01001828 if (firmware_type == NULL)
1829 return FALSE;
1830 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1831 if (gtype == G_TYPE_INVALID) {
1832 g_set_error (error,
1833 G_IO_ERROR,
1834 G_IO_ERROR_NOT_FOUND,
1835 "GType %s not supported", firmware_type);
1836 return FALSE;
1837 }
1838 firmware = g_object_new (gtype, NULL);
1839 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1840 return FALSE;
1841 str = fu_firmware_to_string (firmware);
1842 g_print ("%s", str);
1843 return TRUE;
1844}
1845
Richard Hughesdd653442020-09-22 10:23:52 +01001846static gboolean
1847fu_util_firmware_extract (FuUtilPrivate *priv, gchar **values, GError **error)
1848{
1849 GType gtype;
1850 g_autofree gchar *firmware_type = NULL;
1851 g_autofree gchar *str = NULL;
1852 g_autoptr(FuFirmware) firmware = NULL;
1853 g_autoptr(GBytes) blob = NULL;
1854 g_autoptr(GPtrArray) images = NULL;
1855
1856 /* check args */
1857 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1858 g_set_error_literal (error,
1859 FWUPD_ERROR,
1860 FWUPD_ERROR_INVALID_ARGS,
1861 "Invalid arguments: filename required");
1862 return FALSE;
1863 }
1864 if (g_strv_length (values) == 2)
1865 firmware_type = g_strdup (values[1]);
1866
1867 /* load file */
1868 blob = fu_common_get_contents_bytes (values[0], error);
1869 if (blob == NULL)
1870 return FALSE;
1871
1872 /* load engine */
1873 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1874 return FALSE;
1875
1876 /* find the GType to use */
1877 if (firmware_type == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001878 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesdd653442020-09-22 10:23:52 +01001879 if (firmware_type == NULL)
1880 return FALSE;
1881 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1882 if (gtype == G_TYPE_INVALID) {
1883 g_set_error (error,
1884 G_IO_ERROR,
1885 G_IO_ERROR_NOT_FOUND,
1886 "GType %s not supported", firmware_type);
1887 return FALSE;
1888 }
1889 firmware = g_object_new (gtype, NULL);
1890 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1891 return FALSE;
1892 str = fu_firmware_to_string (firmware);
1893 g_print ("%s", str);
1894 images = fu_firmware_get_images (firmware);
1895 for (guint i = 0; i < images->len; i++) {
1896 FuFirmwareImage *img = g_ptr_array_index (images, i);
1897 g_autofree gchar *fn = NULL;
1898 g_autoptr(GBytes) blob_img = NULL;
1899
Richard Hughes88dd7c42020-09-22 16:54:40 +01001900 /* get raw image without generated header, footer or crc */
1901 blob_img = fu_firmware_image_get_bytes (img);
1902 if (blob_img == NULL || g_bytes_get_size (blob_img) == 0)
1903 continue;
1904
Richard Hughesdd653442020-09-22 10:23:52 +01001905 /* use suitable filename */
1906 if (fu_firmware_image_get_filename (img) != NULL) {
1907 fn = g_strdup (fu_firmware_image_get_filename (img));
1908 } else if (fu_firmware_image_get_id (img) != NULL) {
1909 fn = g_strdup_printf ("id-%s.fw", fu_firmware_image_get_id (img));
1910 } else if (fu_firmware_image_get_idx (img) != 0x0) {
1911 fn = g_strdup_printf ("idx-0x%x.fw", (guint) fu_firmware_image_get_idx (img));
1912 } else {
1913 fn = g_strdup_printf ("img-0x%x.fw", i);
1914 }
1915 /* TRANSLATORS: decompressing images from a container firmware */
1916 g_print ("%s : %s\n", _("Writing file:"), fn);
Richard Hughesdd653442020-09-22 10:23:52 +01001917 if (!fu_common_set_contents_bytes (fn, blob_img, error))
1918 return FALSE;
1919 }
1920
1921 /* success */
1922 return TRUE;
1923}
1924
Richard Hughes0924c932020-09-22 19:07:05 +01001925static gboolean
1926fu_util_firmware_build (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes41400a82020-09-21 13:43:15 +01001927{
Richard Hughes0924c932020-09-22 19:07:05 +01001928 GType gtype = FU_TYPE_FIRMWARE;
Richard Hughes41400a82020-09-21 13:43:15 +01001929 const gchar *tmp;
Richard Hughes0924c932020-09-22 19:07:05 +01001930 g_autofree gchar *str = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001931 g_autoptr(FuFirmware) firmware = NULL;
Richard Hughes0924c932020-09-22 19:07:05 +01001932 g_autoptr(FuFirmware) firmware_dst = NULL;
1933 g_autoptr(GBytes) blob_dst = NULL;
1934 g_autoptr(GBytes) blob_src = NULL;
Richard Hughes41400a82020-09-21 13:43:15 +01001935 g_autoptr(XbBuilder) builder = xb_builder_new ();
1936 g_autoptr(XbBuilderSource) source = xb_builder_source_new ();
1937 g_autoptr(XbNode) n = NULL;
1938 g_autoptr(XbSilo) silo = NULL;
1939
Richard Hughes0924c932020-09-22 19:07:05 +01001940 /* check args */
1941 if (g_strv_length (values) != 2) {
1942 g_set_error_literal (error,
1943 FWUPD_ERROR,
1944 FWUPD_ERROR_INVALID_ARGS,
1945 "Invalid arguments: filename required");
1946 return FALSE;
1947 }
1948
1949 /* load file */
1950 blob_src = fu_common_get_contents_bytes (values[0], error);
1951 if (blob_src == NULL)
1952 return FALSE;
1953
1954 /* load engine */
1955 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1956 return FALSE;
1957
Richard Hughes41400a82020-09-21 13:43:15 +01001958 /* parse XML */
Richard Hughes0924c932020-09-22 19:07:05 +01001959 if (!xb_builder_source_load_bytes (source, blob_src,
Richard Hughes41400a82020-09-21 13:43:15 +01001960 XB_BUILDER_SOURCE_FLAG_NONE,
1961 error)) {
1962 g_prefix_error (error, "could not parse XML: ");
Richard Hughes0924c932020-09-22 19:07:05 +01001963 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001964 }
1965 xb_builder_import_source (builder, source);
1966 silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
1967 if (silo == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01001968 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001969
1970 /* create FuFirmware of specific GType */
1971 n = xb_silo_query_first (silo, "firmware", error);
1972 if (n == NULL)
1973 return FALSE;
1974 tmp = xb_node_get_attr (n, "gtype");
1975 if (tmp != NULL) {
Richard Hughes0924c932020-09-22 19:07:05 +01001976 gtype = g_type_from_name (tmp);
1977 if (gtype == G_TYPE_INVALID) {
1978 g_set_error (error,
1979 G_IO_ERROR,
1980 G_IO_ERROR_NOT_FOUND,
1981 "GType %s not registered", tmp);
1982 return FALSE;
1983 }
1984 }
1985 tmp = xb_node_get_attr (n, "id");
1986 if (tmp != NULL) {
1987 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, tmp);
Richard Hughes41400a82020-09-21 13:43:15 +01001988 if (gtype == G_TYPE_INVALID) {
1989 g_set_error (error,
1990 G_IO_ERROR,
1991 G_IO_ERROR_NOT_FOUND,
1992 "GType %s not supported", tmp);
Richard Hughes0924c932020-09-22 19:07:05 +01001993 return FALSE;
Richard Hughes41400a82020-09-21 13:43:15 +01001994 }
Richard Hughes41400a82020-09-21 13:43:15 +01001995 }
Richard Hughes0924c932020-09-22 19:07:05 +01001996 firmware = g_object_new (gtype, NULL);
Richard Hughes41400a82020-09-21 13:43:15 +01001997 if (!fu_firmware_build (firmware, n, error))
Richard Hughes0924c932020-09-22 19:07:05 +01001998 return FALSE;
1999
2000 /* write new file */
2001 blob_dst = fu_firmware_write (firmware, error);
2002 if (blob_dst == NULL)
2003 return FALSE;
2004 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2005 return FALSE;
2006
2007 /* show what we wrote */
2008 firmware_dst = g_object_new (gtype, NULL);
2009 if (!fu_firmware_parse (firmware_dst, blob_dst, priv->flags, error))
2010 return FALSE;
2011 str = fu_firmware_to_string (firmware_dst);
2012 g_print ("%s", str);
Richard Hughes41400a82020-09-21 13:43:15 +01002013
2014 /* success */
Richard Hughes0924c932020-09-22 19:07:05 +01002015 return TRUE;
Richard Hughes41400a82020-09-21 13:43:15 +01002016}
2017
Richard Hughes95c98a92019-10-22 16:03:15 +01002018static gboolean
Richard Hughesbca63ed2020-03-09 20:20:03 +00002019fu_util_firmware_convert (FuUtilPrivate *priv, gchar **values, GError **error)
2020{
2021 GType gtype_dst;
2022 GType gtype_src;
2023 g_autofree gchar *firmware_type_dst = NULL;
2024 g_autofree gchar *firmware_type_src = NULL;
2025 g_autofree gchar *str_dst = NULL;
2026 g_autofree gchar *str_src = NULL;
2027 g_autoptr(FuFirmware) firmware_dst = NULL;
2028 g_autoptr(FuFirmware) firmware_src = NULL;
2029 g_autoptr(GBytes) blob_dst = NULL;
2030 g_autoptr(GBytes) blob_src = NULL;
2031 g_autoptr(GPtrArray) images = NULL;
2032
2033 /* check args */
2034 if (g_strv_length (values) < 2 || g_strv_length (values) > 4) {
2035 g_set_error_literal (error,
2036 FWUPD_ERROR,
2037 FWUPD_ERROR_INVALID_ARGS,
2038 "Invalid arguments: filename required");
2039 return FALSE;
2040 }
2041
2042 if (g_strv_length (values) > 2)
2043 firmware_type_src = g_strdup (values[2]);
2044 if (g_strv_length (values) > 3)
2045 firmware_type_dst = g_strdup (values[3]);
2046
2047 /* load file */
2048 blob_src = fu_common_get_contents_bytes (values[0], error);
2049 if (blob_src == NULL)
2050 return FALSE;
2051
2052 /* load engine */
2053 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
2054 return FALSE;
2055
2056 /* find the GType to use */
2057 if (firmware_type_src == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002058 firmware_type_src = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002059 if (firmware_type_src == NULL)
2060 return FALSE;
2061 if (firmware_type_dst == NULL)
Richard Hughes0924c932020-09-22 19:07:05 +01002062 firmware_type_dst = fu_util_prompt_for_firmware_type (priv, error);
Richard Hughesbca63ed2020-03-09 20:20:03 +00002063 if (firmware_type_dst == NULL)
2064 return FALSE;
Richard Hughes0924c932020-09-22 19:07:05 +01002065 gtype_src = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_src);
2066 if (gtype_src == G_TYPE_INVALID) {
2067 g_set_error (error,
2068 G_IO_ERROR,
2069 G_IO_ERROR_NOT_FOUND,
2070 "GType %s not supported", firmware_type_src);
2071 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002072 }
Richard Hughes0924c932020-09-22 19:07:05 +01002073 firmware_src = g_object_new (gtype_src, NULL);
2074 if (!fu_firmware_parse (firmware_src, blob_src, priv->flags, error))
2075 return FALSE;
Richard Hughesbca63ed2020-03-09 20:20:03 +00002076 gtype_dst = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type_dst);
2077 if (gtype_dst == G_TYPE_INVALID) {
2078 g_set_error (error,
2079 G_IO_ERROR,
2080 G_IO_ERROR_NOT_FOUND,
2081 "GType %s not supported", firmware_type_dst);
2082 return FALSE;
2083 }
Richard Hughesbca63ed2020-03-09 20:20:03 +00002084 str_src = fu_firmware_to_string (firmware_src);
2085 g_print ("%s", str_src);
2086
2087 /* copy images */
2088 firmware_dst = g_object_new (gtype_dst, NULL);
2089 images = fu_firmware_get_images (firmware_src);
2090 for (guint i = 0; i < images->len; i++) {
2091 FuFirmwareImage *img = g_ptr_array_index (images, i);
2092 fu_firmware_add_image (firmware_dst, img);
2093 }
2094
2095 /* write new file */
2096 blob_dst = fu_firmware_write (firmware_dst, error);
2097 if (blob_dst == NULL)
2098 return FALSE;
2099 if (!fu_common_set_contents_bytes (values[1], blob_dst, error))
2100 return FALSE;
2101 str_dst = fu_firmware_to_string (firmware_dst);
2102 g_print ("%s", str_dst);
2103
2104 /* success */
2105 return TRUE;
2106}
2107
2108static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00002109fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
2110{
2111 g_autofree gchar *str = NULL;
2112 g_autoptr(FuDevice) dev = NULL;
2113
2114 /* load engine */
2115 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2116 return FALSE;
2117
2118 /* get device */
2119 if (g_strv_length (values) == 1) {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002120 dev = fu_util_get_device (priv, values[1], error);
Richard Hughes15684492019-03-15 16:27:50 +00002121 if (dev == NULL)
2122 return FALSE;
2123 } else {
Richard Hughes3aaf53c2020-04-20 09:39:46 +01002124 dev = fu_util_prompt_for_device (priv, NULL, error);
Richard Hughes15684492019-03-15 16:27:50 +00002125 if (dev == NULL)
2126 return FALSE;
2127 }
2128
2129 /* add checksums */
2130 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
2131 return FALSE;
2132
2133 /* show checksums */
2134 str = fu_device_to_string (dev);
2135 g_print ("%s\n", str);
2136 return TRUE;
2137}
2138
Mario Limonciellofe593942019-04-03 13:48:52 -05002139static gboolean
2140fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
2141{
2142 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05002143 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002144 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002145
2146 /* load engine */
2147 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2148 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002149 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05002150
2151 /* get all devices from the history database */
2152 devices = fu_engine_get_history (priv->engine, error);
2153 if (devices == NULL)
2154 return FALSE;
2155
2156 /* show each device */
2157 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05002158 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05002159 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05002160 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002161 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002162 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05002163
Richard Hughes747f5702019-08-06 14:27:26 +01002164 if (!fu_util_filter_device (priv, dev))
2165 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05002166 child = g_node_append_data (root, dev);
2167
2168 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002169 if (rel == NULL)
2170 continue;
2171 remote = fwupd_release_get_remote_id (rel);
2172
2173 /* doesn't actually map to remote */
2174 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05002175 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002176 continue;
2177 }
2178
2179 /* try to lookup releases from client */
Richard Hughesdf89cd52020-06-26 20:25:18 +01002180 rels = fu_engine_get_releases (priv->engine,
2181 priv->request,
2182 fwupd_device_get_id (dev),
2183 error);
Mario Limoncielloce94d162019-09-20 13:37:36 -05002184 if (rels == NULL)
2185 return FALSE;
2186
2187 /* map to a release in client */
2188 for (guint j = 0; j < rels->len; j++) {
2189 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
2190 if (g_strcmp0 (remote,
2191 fwupd_release_get_remote_id (rel2)) != 0)
2192 continue;
2193 if (g_strcmp0 (fwupd_release_get_version (rel),
2194 fwupd_release_get_version (rel2)) != 0)
2195 continue;
2196 g_node_append_data (child, g_object_ref (rel2));
2197 rel = NULL;
2198 break;
2199 }
2200
2201 /* didn't match anything */
2202 if (rels->len == 0 || rel != NULL) {
2203 g_node_append_data (child, rel);
2204 continue;
2205 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05002206
Mario Limonciellofe593942019-04-03 13:48:52 -05002207 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05002208 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05002209
2210 return TRUE;
2211}
2212
Richard Hughesfd7e9942020-01-17 14:09:13 +00002213static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00002214fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
2215{
Richard Hughesc5710d92020-06-26 11:08:25 +01002216 const gchar *metadata_uri = NULL;
Richard Hughes4959baa2020-01-17 14:33:00 +00002217 g_autofree gchar *fn_raw = NULL;
2218 g_autofree gchar *fn_sig = NULL;
2219 g_autoptr(GBytes) bytes_raw = NULL;
2220 g_autoptr(GBytes) bytes_sig = NULL;
2221
Richard Hughes4959baa2020-01-17 14:33:00 +00002222 /* signature */
Richard Hughesc5710d92020-06-26 11:08:25 +01002223 metadata_uri = fwupd_remote_get_metadata_uri_sig (remote);
2224 if (metadata_uri == NULL) {
Richard Hughesfb6315f2020-09-09 17:05:20 +01002225 g_set_error (error,
2226 FWUPD_ERROR,
2227 FWUPD_ERROR_NOTHING_TO_DO,
2228 "no metadata signature URI available for %s",
2229 fwupd_remote_get_id (remote));
Richard Hughesc5710d92020-06-26 11:08:25 +01002230 return FALSE;
2231 }
2232 fn_sig = fu_util_get_user_cache_path (metadata_uri);
Richard Hughesf083af32020-10-07 13:59:09 +01002233 if (!fu_common_mkdir_parent (fn_sig, error))
2234 return FALSE;
Richard Hughesc5710d92020-06-26 11:08:25 +01002235 if (!fu_util_download_out_of_process (metadata_uri, fn_sig, error))
Richard Hughes4959baa2020-01-17 14:33:00 +00002236 return FALSE;
2237 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
2238 if (bytes_sig == NULL)
2239 return FALSE;
Richard Hughesf083af32020-10-07 13:59:09 +01002240 if (!fwupd_remote_load_signature_bytes (remote, bytes_sig, error))
2241 return FALSE;
2242
2243 /* payload */
2244 metadata_uri = fwupd_remote_get_metadata_uri (remote);
2245 if (metadata_uri == NULL) {
2246 g_set_error (error,
2247 FWUPD_ERROR,
2248 FWUPD_ERROR_NOTHING_TO_DO,
2249 "no metadata URI available for %s",
2250 fwupd_remote_get_id (remote));
2251 return FALSE;
2252 }
2253 fn_raw = fu_util_get_user_cache_path (metadata_uri);
2254 if (!fu_util_download_out_of_process (metadata_uri, fn_raw, error))
2255 return FALSE;
2256 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
2257 if (bytes_raw == NULL)
2258 return FALSE;
Richard Hughes4959baa2020-01-17 14:33:00 +00002259
2260 /* send to daemon */
2261 g_debug ("updating %s", fwupd_remote_get_id (remote));
2262 return fu_engine_update_metadata_bytes (priv->engine,
2263 fwupd_remote_get_id (remote),
2264 bytes_raw,
2265 bytes_sig,
2266 error);
2267
2268}
2269
2270static gboolean
2271fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
2272{
2273 g_autoptr(GPtrArray) remotes = NULL;
2274
2275 /* load engine */
2276 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2277 return FALSE;
2278
2279 /* download new metadata */
2280 remotes = fu_engine_get_remotes (priv->engine, error);
2281 if (remotes == NULL)
2282 return FALSE;
2283 for (guint i = 0; i < remotes->len; i++) {
2284 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2285 if (!fwupd_remote_get_enabled (remote))
2286 continue;
2287 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
2288 continue;
2289 if (!fu_util_refresh_remote (priv, remote, error))
2290 return FALSE;
2291 }
2292 return TRUE;
2293}
2294
2295static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00002296fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
2297{
2298 g_autoptr(GNode) root = g_node_new (NULL);
2299 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002300 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00002301
2302 /* load engine */
2303 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2304 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06002305 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002306
2307 /* list remotes */
2308 remotes = fu_engine_get_remotes (priv->engine, error);
2309 if (remotes == NULL)
2310 return FALSE;
2311 if (remotes->len == 0) {
2312 g_set_error_literal (error,
2313 FWUPD_ERROR,
2314 FWUPD_ERROR_NOTHING_TO_DO,
2315 "no remotes available");
2316 return FALSE;
2317 }
2318 for (guint i = 0; i < remotes->len; i++) {
2319 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
2320 g_node_append_data (root, remote_tmp);
2321 }
2322 fu_util_print_tree (root, title);
2323
2324 return TRUE;
2325}
2326
Richard Hughes196c6c62020-05-11 19:42:47 +01002327static gboolean
2328fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2329{
Richard Hughes5c82b942020-09-14 12:24:06 +01002330 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughesf58ac732020-05-12 15:23:44 +01002331 g_autoptr(FuSecurityAttrs) attrs = NULL;
2332 g_autoptr(GPtrArray) items = NULL;
Richard Hughes196c6c62020-05-11 19:42:47 +01002333 g_autofree gchar *str = NULL;
2334
2335 /* not ready yet */
2336 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2337 g_set_error_literal (error,
2338 FWUPD_ERROR,
2339 FWUPD_ERROR_NOT_SUPPORTED,
2340 "The HSI specification is not yet complete. "
2341 "To ignore this warning, use --force");
2342 return FALSE;
2343 }
2344
2345 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2346 return FALSE;
2347
2348 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2349 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2350 fu_engine_get_host_security_id (priv->engine));
2351
Richard Hughes5c82b942020-09-14 12:24:06 +01002352 /* show or hide different elements */
2353 if (priv->show_all) {
2354 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2355 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2356 }
2357
Richard Hughes196c6c62020-05-11 19:42:47 +01002358 /* print the "why" */
Richard Hughes56e7ae52020-05-17 21:00:23 +01002359 attrs = fu_engine_get_host_security_attrs (priv->engine);
Richard Hughesf58ac732020-05-12 15:23:44 +01002360 items = fu_security_attrs_get_all (attrs);
Richard Hughes5c82b942020-09-14 12:24:06 +01002361 str = fu_util_security_attrs_to_string (items, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002362 g_print ("%s\n", str);
2363 return TRUE;
2364}
2365
Richard Hughesa83deb42020-08-12 12:45:36 +01002366static FuVolume *
2367fu_util_prompt_for_volume (GError **error)
2368{
2369 FuVolume *volume;
2370 guint idx;
2371 g_autoptr(GPtrArray) volumes = NULL;
Mario Limonciello56d816a2020-11-11 16:59:30 -06002372 g_autoptr(GPtrArray) volumes_vfat = g_ptr_array_new ();
2373 g_autoptr(GError) error_local = NULL;
Richard Hughesa83deb42020-08-12 12:45:36 +01002374
2375 /* exactly one */
Mario Limonciello56d816a2020-11-11 16:59:30 -06002376 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_ESP, &error_local);
2377 if (volumes == NULL) {
2378 g_debug ("%s, falling back to %s", error_local->message, FU_VOLUME_KIND_BDP);
2379 volumes = fu_common_get_volumes_by_kind (FU_VOLUME_KIND_BDP, error);
2380 if (volumes == NULL) {
2381 g_prefix_error (error, "%s: ", error_local->message);
2382 return NULL;
2383 }
2384 }
2385 /* only add internal vfat partitions */
2386 for (guint i = 0; i < volumes->len; i++) {
2387 FuVolume *vol = g_ptr_array_index (volumes, i);
2388 g_autofree gchar *type = fu_volume_get_id_type (vol);
2389 if (type == NULL)
2390 continue;
2391 if (!fu_volume_is_internal (vol))
2392 continue;
2393 if (g_strcmp0 (type, "vfat") == 0)
2394 g_ptr_array_add (volumes_vfat, vol);
2395 }
2396 if (volumes_vfat->len == 1) {
2397 volume = g_ptr_array_index (volumes_vfat, 0);
Richard Hughes59761252020-08-20 07:51:36 +01002398 /* TRANSLATORS: Volume has been chosen by the user */
Richard Hughesa83deb42020-08-12 12:45:36 +01002399 g_print ("%s: %s\n", _("Selected volume"), fu_volume_get_id (volume));
2400 return g_object_ref (volume);
2401 }
2402
2403 /* TRANSLATORS: get interactive prompt */
2404 g_print ("%s\n", _("Choose a volume:"));
2405 /* TRANSLATORS: this is to abort the interactive prompt */
2406 g_print ("0.\t%s\n", _("Cancel"));
Mario Limonciello56d816a2020-11-11 16:59:30 -06002407 for (guint i = 0; i < volumes_vfat->len; i++) {
2408 volume = g_ptr_array_index (volumes_vfat, i);
Richard Hughesa83deb42020-08-12 12:45:36 +01002409 g_print ("%u.\t%s\n", i + 1, fu_volume_get_id (volume));
2410 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002411 idx = fu_util_prompt_for_number (volumes_vfat->len);
Richard Hughesa83deb42020-08-12 12:45:36 +01002412 if (idx == 0) {
2413 g_set_error_literal (error,
2414 FWUPD_ERROR,
2415 FWUPD_ERROR_NOTHING_TO_DO,
2416 "Request canceled");
2417 return NULL;
2418 }
Mario Limonciello56d816a2020-11-11 16:59:30 -06002419 volume = g_ptr_array_index (volumes_vfat, idx - 1);
Richard Hughesa83deb42020-08-12 12:45:36 +01002420 return g_object_ref (volume);
2421
2422}
2423
2424static gboolean
2425fu_util_esp_mount (FuUtilPrivate *priv, gchar **values, GError **error)
2426{
2427 g_autoptr(FuVolume) volume = NULL;
2428 volume = fu_util_prompt_for_volume (error);
2429 if (volume == NULL)
2430 return FALSE;
2431 return fu_volume_mount (volume, error);
2432}
2433
2434static gboolean
2435fu_util_esp_unmount (FuUtilPrivate *priv, gchar **values, GError **error)
2436{
2437 g_autoptr(FuVolume) volume = NULL;
2438 volume = fu_util_prompt_for_volume (error);
2439 if (volume == NULL)
2440 return FALSE;
2441 return fu_volume_unmount (volume, error);
2442}
2443
2444static gboolean
2445fu_util_esp_list (FuUtilPrivate *priv, gchar **values, GError **error)
2446{
2447 g_autofree gchar *mount_point = NULL;
2448 g_autoptr(FuDeviceLocker) locker = NULL;
2449 g_autoptr(FuVolume) volume = NULL;
2450 g_autoptr(GPtrArray) files = NULL;
2451
2452 volume = fu_util_prompt_for_volume (error);
2453 if (volume == NULL)
2454 return FALSE;
2455 locker = fu_volume_locker (volume, error);
2456 if (locker == NULL)
2457 return FALSE;
2458 mount_point = fu_volume_get_mount_point (volume);
2459 files = fu_common_get_files_recursive (mount_point, error);
2460 if (files == NULL)
2461 return FALSE;
2462 for (guint i = 0; i < files->len; i++) {
2463 const gchar *fn = g_ptr_array_index (files, i);
2464 g_print ("%s\n", fn);
2465 }
2466 return TRUE;
2467}
2468
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002469
2470static gboolean
2471fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
2472{
2473 const gchar *branch;
2474 g_autoptr(FwupdRelease) rel = NULL;
2475 g_autoptr(GPtrArray) rels = NULL;
2476 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
2477 g_autoptr(FuDevice) dev = NULL;
2478
2479 /* load engine */
2480 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
2481 return FALSE;
2482
2483 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01002484 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002485 if (g_strv_length (values) == 1)
2486 dev = fu_util_get_device (priv, values[1], error);
2487 else
2488 dev = fu_util_prompt_for_device (priv, NULL, error);
2489 if (dev == NULL)
2490 return FALSE;
2491 if (!fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
2492 g_set_error_literal (error,
2493 FWUPD_ERROR,
2494 FWUPD_ERROR_NOT_SUPPORTED,
2495 "Multiple branches not available");
2496 return FALSE;
2497 }
2498
2499 /* get all releases, including the alternate branch versions */
2500 rels = fu_engine_get_releases (priv->engine,
2501 priv->request,
2502 fu_device_get_id (dev),
2503 error);
2504 if (rels == NULL)
2505 return FALSE;
2506
2507 /* get all the unique branches */
2508 for (guint i = 0; i < rels->len; i++) {
2509 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
2510 const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp);
2511 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
2512 g_str_equal, NULL))
2513 continue;
2514 g_ptr_array_add (branches, g_strdup (branch_tmp));
2515 }
2516
2517 /* branch name is optional */
2518 if (g_strv_length (values) > 1) {
2519 branch = values[1];
2520 } else if (branches->len == 1) {
2521 branch = g_ptr_array_index (branches, 0);
2522 } else {
2523 guint idx;
2524
2525 /* TRANSLATORS: get interactive prompt, where branch is the
2526 * supplier of the firmware, e.g. "non-free" or "free" */
2527 g_print ("%s\n", _("Choose a branch:"));
2528 /* TRANSLATORS: this is to abort the interactive prompt */
2529 g_print ("0.\t%s\n", _("Cancel"));
2530 for (guint i = 0; i < branches->len; i++) {
2531 const gchar *branch_tmp = g_ptr_array_index (branches, i);
2532 g_print ("%u.\t%s\n", i + 1, branch_tmp);
2533 }
2534 idx = fu_util_prompt_for_number (branches->len);
2535 if (idx == 0) {
2536 g_set_error_literal (error,
2537 FWUPD_ERROR,
2538 FWUPD_ERROR_NOTHING_TO_DO,
2539 "Request canceled");
2540 return FALSE;
2541 }
2542 branch = g_ptr_array_index (branches, idx - 1);
2543 }
2544
2545 /* sanity check */
2546 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
2547 g_set_error (error,
2548 FWUPD_ERROR,
2549 FWUPD_ERROR_NOT_SUPPORTED,
2550 "Device %s is already on branch %s",
2551 fu_device_get_name (dev),
2552 branch);
2553 return FALSE;
2554 }
2555
2556 /* the releases are ordered by version */
2557 for (guint j = 0; j < rels->len; j++) {
2558 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
2559 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
2560 rel = g_object_ref (rel_tmp);
2561 break;
2562 }
2563 }
2564 if (rel == NULL) {
2565 g_set_error (error,
2566 FWUPD_ERROR,
2567 FWUPD_ERROR_NOT_SUPPORTED,
2568 "No releases for branch %s",
2569 branch);
2570 return FALSE;
2571 }
2572
2573 /* we're switching branch */
2574 if (!fu_util_switch_branch_warning (FWUPD_DEVICE (dev), rel, FALSE, error))
2575 return FALSE;
2576
2577 /* update the console if composite devices are also updated */
2578 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2579 g_signal_connect (priv->engine, "device-changed",
2580 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2581 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2582 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
2583 if (!fu_util_install_release (priv, rel, error))
2584 return FALSE;
2585 fu_util_display_current_message (priv);
2586
2587 /* we don't want to ask anything */
2588 if (priv->no_reboot_check) {
2589 g_debug ("skipping reboot check");
2590 return TRUE;
2591 }
2592
2593 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2594}
2595
Richard Hughesb5976832018-05-18 10:02:09 +01002596int
2597main (int argc, char *argv[])
2598{
Richard Hughes6450d0d2020-10-06 16:05:24 +01002599 gboolean allow_branch_switch = FALSE;
Richard Hughes460226a2018-05-21 20:56:21 +01002600 gboolean allow_older = FALSE;
2601 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01002602 gboolean force = FALSE;
2603 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002604 gboolean version = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002605 gboolean ignore_checksum = FALSE;
2606 gboolean ignore_power = FALSE;
2607 gboolean ignore_vid_pid = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002608 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002609 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002610 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
2611 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002612 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002613 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002614 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01002615 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002616 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2617 /* TRANSLATORS: command line option */
2618 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002619 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2620 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002621 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01002622 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2623 /* TRANSLATORS: command line option */
2624 _("Allow downgrading firmware versions"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002625 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2626 /* TRANSLATORS: command line option */
2627 _("Allow switching firmware branch"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002628 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2629 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002630 _("Force the action by relaxing some runtime checks"), NULL },
2631 { "ignore-checksum", '\0', 0, G_OPTION_ARG_NONE, &ignore_checksum,
2632 /* TRANSLATORS: command line option */
2633 _("Ignore firmware checksum failures"), NULL },
2634 { "ignore-vid-pid", '\0', 0, G_OPTION_ARG_NONE, &ignore_vid_pid,
2635 /* TRANSLATORS: command line option */
2636 _("Ignore firmware hardware mismatch failures"), NULL },
2637 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2638 /* TRANSLATORS: command line option */
2639 _("Ignore requirement of external power source"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06002640 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2641 /* TRANSLATORS: command line option */
2642 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002643 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2644 /* TRANSLATORS: command line option */
2645 _("Do not perform device safety checks"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002646 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2647 /* TRANSLATORS: command line option */
2648 _("Show all results"), NULL },
2649 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05002650 /* TRANSLATORS: command line option */
2651 _("Show devices that are not updatable"), NULL },
Richard Hughesf8c10c22020-07-20 21:01:39 +01002652 { "plugins", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002653 /* TRANSLATORS: command line option */
Richard Hughes85226fd2020-06-30 14:43:48 +01002654 _("Manually enable specific plugins"), NULL },
Richard Hughesa1ebcbf2020-09-14 15:27:43 +01002655 { "plugin-whitelist", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
2656 /* TRANSLATORS: command line option */
2657 _("Manually enable specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002658 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002659 /* TRANSLATORS: command line option */
2660 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06002661 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00002662 /* TRANSLATORS: command line option */
2663 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06002664 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
2665 /* TRANSLATORS: command line option */
2666 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002667 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2668 /* TRANSLATORS: command line option */
2669 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002670 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2671 /* TRANSLATORS: command line option */
2672 _("Filter with a set of device flags using a ~ prefix to "
2673 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01002674 { NULL}
2675 };
2676
Richard Hughes429f72b2020-01-16 12:18:19 +00002677#ifdef _WIN32
2678 /* workaround Windows setting the codepage to 1252 */
2679 g_setenv ("LANG", "C.UTF-8", FALSE);
2680#endif
2681
Richard Hughesb5976832018-05-18 10:02:09 +01002682 setlocale (LC_ALL, "");
2683
Richard Hughes668ee212019-11-22 09:17:46 +00002684 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01002685 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2686 textdomain (GETTEXT_PACKAGE);
2687
Richard Hughes01c0bad2019-11-22 09:08:51 +00002688#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01002689 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002690 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01002691 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05002692 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00002693#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002694
2695 /* create helper object */
2696 priv->loop = g_main_loop_new (NULL, FALSE);
2697 priv->progressbar = fu_progressbar_new ();
Richard Hughesdf89cd52020-06-26 20:25:18 +01002698 priv->request = fu_engine_request_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01002699
2700 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002701 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002702 "build-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002703 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2704 _("FILE-IN FILE-OUT [SCRIPT] [OUTPUT]"),
Mario Limonciellof6d01b12018-10-18 12:57:10 -05002705 /* TRANSLATORS: command description */
2706 _("Build firmware using a sandbox"),
2707 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00002708 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01002709 "smbios-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002710 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2711 _("FILE"),
Richard Hughesb5976832018-05-18 10:02:09 +01002712 /* TRANSLATORS: command description */
2713 _("Dump SMBIOS data from a file"),
2714 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00002715 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01002716 "get-plugins",
2717 NULL,
2718 /* TRANSLATORS: command description */
2719 _("Get all enabled plugins registered with the system"),
2720 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00002721 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05002722 "get-details",
2723 NULL,
2724 /* TRANSLATORS: command description */
2725 _("Gets details about a firmware file"),
2726 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002727 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05002728 "get-history",
2729 NULL,
2730 /* TRANSLATORS: command description */
2731 _("Show history of firmware updates"),
2732 fu_util_get_history);
2733 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002734 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002735 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2736 _("[DEVICE-ID|GUID]"),
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06002737 /* TRANSLATORS: command description */
2738 _("Gets the list of updates for connected hardware"),
2739 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002740 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002741 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01002742 NULL,
2743 /* TRANSLATORS: command description */
2744 _("Get all devices that support firmware updates"),
2745 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002746 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01002747 "get-device-flags",
2748 NULL,
2749 /* TRANSLATORS: command description */
2750 _("Get all device flags supported by fwupd"),
2751 fu_util_get_device_flags);
2752 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002753 "watch",
2754 NULL,
2755 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02002756 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002757 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00002758 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002759 "install-blob",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002760 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2761 _("FILENAME DEVICE-ID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002762 /* TRANSLATORS: command description */
2763 _("Install a firmware blob on a device"),
2764 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00002765 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002766 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002767 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2768 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesa36c9cf2018-05-20 10:41:44 +01002769 /* TRANSLATORS: command description */
2770 _("Install a firmware file on this hardware"),
2771 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002772 fu_util_cmd_array_add (cmd_array,
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002773 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002774 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2775 _("DEVICE-ID|GUID"),
Filipe Laínsb2f377a2020-03-30 21:05:50 +01002776 /* TRANSLATORS: command description */
2777 _("Reinstall firmware on a device"),
2778 fu_util_reinstall);
2779 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002780 "attach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002781 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2782 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002783 /* TRANSLATORS: command description */
2784 _("Attach to firmware mode"),
2785 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002786 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01002787 "detach",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002788 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2789 _("DEVICE-ID|GUID"),
Richard Hughes98ca9932018-05-18 10:24:07 +01002790 /* TRANSLATORS: command description */
2791 _("Detach to bootloader mode"),
2792 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00002793 fu_util_cmd_array_add (cmd_array,
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002794 "unbind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002795 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2796 _("[DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002797 /* TRANSLATORS: command description */
2798 _("Unbind current driver"),
2799 fu_util_unbind_driver);
2800 fu_util_cmd_array_add (cmd_array,
2801 "bind-driver",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002802 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2803 _("SUBSYSTEM DRIVER [DEVICE-ID|GUID]"),
Richard Hughes34f7d9d2020-09-19 15:28:11 +01002804 /* TRANSLATORS: command description */
2805 _("Bind new kernel driver"),
2806 fu_util_bind_driver);
2807 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002808 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002809 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2810 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002811 /* TRANSLATORS: command description */
2812 _("Activate pending devices"),
2813 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00002814 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01002815 "hwids",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002816 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2817 _("[FILE]"),
Richard Hughes1d894f12018-08-31 13:05:51 +01002818 /* TRANSLATORS: command description */
2819 _("Return all the hardware IDs for the machine"),
2820 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00002821 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05002822 "monitor",
2823 NULL,
2824 /* TRANSLATORS: command description */
2825 _("Monitor the daemon for events"),
2826 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00002827 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002828 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002829 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2830 _("[DEVICE-ID|GUID]"),
Mario Limonciello46aaee82019-01-10 12:58:00 -06002831 /* TRANSLATORS: command description */
2832 _("Update all devices that match local metadata"),
2833 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00002834 fu_util_cmd_array_add (cmd_array,
2835 "self-sign",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002836 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2837 _("TEXT"),
Richard Hughes3d607622019-03-07 16:59:27 +00002838 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00002839 C_("command-description",
2840 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00002841 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00002842 fu_util_cmd_array_add (cmd_array,
2843 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002844 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2845 _("[DEVICE-ID|GUID]"),
Richard Hughes15684492019-03-15 16:27:50 +00002846 /* TRANSLATORS: command description */
2847 _("Update the stored metadata with current contents"),
2848 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01002849 fu_util_cmd_array_add (cmd_array,
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002850 "firmware-dump",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002851 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2852 _("FILENAME [DEVICE-ID|GUID]"),
Richard Hughesa58510b2019-10-30 10:03:12 +00002853 /* TRANSLATORS: command description */
2854 _("Read a firmware blob from a device"),
Richard Hughesfbd8b5d2020-09-24 11:48:59 +01002855 fu_util_firmware_dump);
Richard Hughesa58510b2019-10-30 10:03:12 +00002856 fu_util_cmd_array_add (cmd_array,
Richard Hughesbca63ed2020-03-09 20:20:03 +00002857 "firmware-convert",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002858 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2859 _("FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]"),
Richard Hughesbca63ed2020-03-09 20:20:03 +00002860 /* TRANSLATORS: command description */
2861 _("Convert a firmware file"),
2862 fu_util_firmware_convert);
2863 fu_util_cmd_array_add (cmd_array,
Richard Hughes0924c932020-09-22 19:07:05 +01002864 "firmware-build",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002865 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2866 _("BUILDER-XML FILENAME-DST"),
Richard Hughes0924c932020-09-22 19:07:05 +01002867 /* TRANSLATORS: command description */
2868 _("Build a firmware file"),
2869 fu_util_firmware_build);
2870 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002871 "firmware-parse",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002872 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2873 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughes95c98a92019-10-22 16:03:15 +01002874 /* TRANSLATORS: command description */
2875 _("Parse and show details about a firmware file"),
2876 fu_util_firmware_parse);
2877 fu_util_cmd_array_add (cmd_array,
Richard Hughesdd653442020-09-22 10:23:52 +01002878 "firmware-extract",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002879 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2880 _("FILENAME [FIRMWARE-TYPE]"),
Richard Hughesdd653442020-09-22 10:23:52 +01002881 /* TRANSLATORS: command description */
2882 _("Extract a firmware blob to images"),
2883 fu_util_firmware_extract);
2884 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01002885 "get-firmware-types",
2886 NULL,
2887 /* TRANSLATORS: command description */
2888 _("List the available firmware types"),
2889 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00002890 fu_util_cmd_array_add (cmd_array,
2891 "get-remotes",
2892 NULL,
2893 /* TRANSLATORS: command description */
2894 _("Gets the configured remotes"),
2895 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00002896 fu_util_cmd_array_add (cmd_array,
2897 "refresh",
2898 NULL,
2899 /* TRANSLATORS: command description */
2900 _("Refresh metadata from remote server"),
2901 fu_util_refresh);
Richard Hughes196c6c62020-05-11 19:42:47 +01002902 fu_util_cmd_array_add (cmd_array,
2903 "security",
2904 NULL,
2905 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002906 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01002907 fu_util_security);
Richard Hughesa83deb42020-08-12 12:45:36 +01002908 fu_util_cmd_array_add (cmd_array,
2909 "esp-mount",
2910 NULL,
2911 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002912 _("Mounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002913 fu_util_esp_mount);
2914 fu_util_cmd_array_add (cmd_array,
2915 "esp-unmount",
2916 NULL,
2917 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002918 _("Unmounts the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002919 fu_util_esp_unmount);
2920 fu_util_cmd_array_add (cmd_array,
2921 "esp-list",
2922 NULL,
2923 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002924 _("Lists files on the ESP"),
Richard Hughesa83deb42020-08-12 12:45:36 +01002925 fu_util_esp_list);
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002926 fu_util_cmd_array_add (cmd_array,
2927 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002928 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2929 _("[DEVICE-ID|GUID] [BRANCH]"),
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002930 /* TRANSLATORS: command description */
2931 _("Switch the firmware branch on the device"),
2932 fu_util_switch_branch);
Richard Hughesb5976832018-05-18 10:02:09 +01002933
2934 /* do stuff on ctrl+c */
2935 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00002936#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01002937 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
2938 SIGINT, fu_util_sigint_cb,
2939 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00002940#endif
Richard Hughesb5976832018-05-18 10:02:09 +01002941 g_signal_connect (priv->cancellable, "cancelled",
2942 G_CALLBACK (fu_util_cancelled_cb), priv);
2943
2944 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00002945 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002946
Mario Limonciello3f243a92019-01-21 22:05:23 -06002947 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06002948 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06002949 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05002950 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002951 fu_progressbar_set_interactive (priv->progressbar, FALSE);
Richard Hughesdf89cd52020-06-26 20:25:18 +01002952 } else {
2953 /* set our implemented feature set */
2954 fu_engine_request_set_feature_flags (priv->request,
2955 FWUPD_FEATURE_FLAG_DETACH_ACTION |
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002956 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01002957 FWUPD_FEATURE_FLAG_UPDATE_ACTION);
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06002958 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06002959
Richard Hughesb5976832018-05-18 10:02:09 +01002960 /* get a list of the commands */
2961 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00002962 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01002963 g_option_context_set_summary (priv->context, cmd_descriptions);
2964 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00002965 /* TRANSLATORS: CLI description */
2966 _("This tool allows an administrator to use the fwupd plugins "
2967 "without being installed on the host system."));
Richard Hughesb5976832018-05-18 10:02:09 +01002968
2969 /* TRANSLATORS: program name */
2970 g_set_application_name (_("Firmware Utility"));
2971 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002972 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002973 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2974 if (!ret) {
2975 /* TRANSLATORS: the user didn't read the man page */
2976 g_print ("%s: %s\n", _("Failed to parse arguments"),
2977 error->message);
2978 return EXIT_FAILURE;
2979 }
2980
Richard Hughes0e46b222019-09-05 12:13:35 +01002981 /* allow disabling SSL strict mode for broken corporate proxies */
2982 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002983 g_autofree gchar *fmt = NULL;
2984 /* TRANSLATORS: this is a prefix on the console */
2985 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01002986 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002987 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
2988 "to do this automatically in the future "
2989 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01002990 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2991 }
2992
Richard Hughes747f5702019-08-06 14:27:26 +01002993 /* parse filter flags */
2994 if (filter != NULL) {
2995 if (!fu_util_parse_filter_flags (filter,
2996 &priv->filter_include,
2997 &priv->filter_exclude,
2998 &error)) {
2999 /* TRANSLATORS: the user didn't read the man page */
3000 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3001 error->message);
3002 return EXIT_FAILURE;
3003 }
3004 }
3005
3006
Richard Hughes460226a2018-05-21 20:56:21 +01003007 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01003008 if (allow_reinstall)
3009 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
3010 if (allow_older)
3011 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003012 if (allow_branch_switch)
3013 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
3014 if (force) {
Richard Hughes460226a2018-05-21 20:56:21 +01003015 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003016 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3017 }
3018 if (ignore_checksum)
3019 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM;
3020 if (ignore_vid_pid)
3021 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID;
3022 if (ignore_power)
3023 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughes460226a2018-05-21 20:56:21 +01003024
Richard Hughes98ca9932018-05-18 10:24:07 +01003025 /* load engine */
3026 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
3027 g_signal_connect (priv->engine, "device-added",
3028 G_CALLBACK (fu_main_engine_device_added_cb),
3029 priv);
3030 g_signal_connect (priv->engine, "device-removed",
3031 G_CALLBACK (fu_main_engine_device_removed_cb),
3032 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01003033 g_signal_connect (priv->engine, "status-changed",
3034 G_CALLBACK (fu_main_engine_status_changed_cb),
3035 priv);
3036 g_signal_connect (priv->engine, "percentage-changed",
3037 G_CALLBACK (fu_main_engine_percentage_changed_cb),
3038 priv);
3039
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003040 /* just show versions and exit */
3041 if (version) {
3042 g_autofree gchar *version_str = fu_util_get_versions ();
3043 g_print ("%s\n", version_str);
3044 return EXIT_SUCCESS;
3045 }
3046
Richard Hughes85226fd2020-06-30 14:43:48 +01003047 /* any plugin allowlist specified */
Richard Hughesc02ee4d2018-05-22 15:46:03 +01003048 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
3049 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
3050
Richard Hughesb5976832018-05-18 10:02:09 +01003051 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003052 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01003053 if (!ret) {
Mario Limonciello89096312020-04-30 09:51:14 -05003054 g_printerr ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003055 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
Mario Limonciello89096312020-04-30 09:51:14 -05003056 /* TRANSLATORS: error message explaining command to run to how to get help */
3057 g_printerr ("\n%s\n", _("Use fwupdtool --help for help"));
3058 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3059 g_debug ("%s\n", error->message);
Richard Hughesb5976832018-05-18 10:02:09 +01003060 return EXIT_NOTHING_TO_DO;
3061 }
Richard Hughesb5976832018-05-18 10:02:09 +01003062 return EXIT_FAILURE;
3063 }
3064
3065 /* success */
3066 return EXIT_SUCCESS;
3067}