blob: b07a2192cc4f36704aa4190121a8cd936a6bb074 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughesb5976832018-05-18 10:02:09 +01002 * Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
3 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughesb5976832018-05-18 10:02:09 +01005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuMain"
8
Richard Hughesb5976832018-05-18 10:02:09 +01009#include "config.h"
10
11#include <fwupd.h>
Mario Limonciello3f243a92019-01-21 22:05:23 -060012#include <glib/gstdio.h>
Richard Hughesb5976832018-05-18 10:02:09 +010013#include <glib/gi18n.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000014#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +010015#include <glib-unix.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000016#endif
Richard Hughes7d82a092019-11-22 09:42:31 +000017#include <fcntl.h>
Richard Hughesb5976832018-05-18 10:02:09 +010018#include <locale.h>
19#include <stdlib.h>
20#include <unistd.h>
Richard Hughes3d178be2018-08-30 11:14:24 +010021#include <libsoup/soup.h>
Richard 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"
Mario Limonciello6b0e6632019-11-22 13:04:32 -060028#include "fu-smbios-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010029#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050030#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060031#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060032#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010033
Richard Hughes3d005222019-05-17 14:02:41 +010034#ifdef HAVE_SYSTEMD
35#include "fu-systemd.h"
36#endif
37
Richard Hughesb5976832018-05-18 10:02:09 +010038/* custom return code */
39#define EXIT_NOTHING_TO_DO 2
40
Mario Limonciello3f243a92019-01-21 22:05:23 -060041typedef enum {
42 FU_UTIL_OPERATION_UNKNOWN,
43 FU_UTIL_OPERATION_UPDATE,
44 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000045 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060046 FU_UTIL_OPERATION_LAST
47} FuUtilOperation;
48
Richard Hughesc77e1112019-03-01 10:16:26 +000049struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010050 GCancellable *cancellable;
51 GMainLoop *loop;
52 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010053 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010054 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060055 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050056 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000057 gboolean prepare_blob;
58 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060059 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010060 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050061 gboolean show_all_devices;
Richard Hughes0e46b222019-09-05 12:13:35 +010062 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050063 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060064 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050065 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060066 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060067 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010068 FwupdDeviceFlags filter_include;
69 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000070};
Richard Hughesb5976832018-05-18 10:02:09 +010071
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050072static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060073fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
74{
75 g_autoptr(JsonBuilder) builder = NULL;
76 g_autoptr(JsonGenerator) json_generator = NULL;
77 g_autoptr(JsonNode) json_root = NULL;
78 g_autoptr(GPtrArray) devices = NULL;
79 g_autofree gchar *state = NULL;
80 g_autofree gchar *dirname = NULL;
81 g_autofree gchar *filename = NULL;
82
83 if (!priv->enable_json_state)
84 return TRUE;
85
86 devices = fu_engine_get_devices (priv->engine, error);
87 if (devices == NULL)
88 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +000089 fwupd_device_array_ensure_parents (devices);
Mario Limonciello3143bad2019-02-27 07:31:00 -060090
91 /* create header */
92 builder = json_builder_new ();
93 json_builder_begin_object (builder);
94
95 /* add each device */
96 json_builder_set_member_name (builder, "Devices");
97 json_builder_begin_array (builder);
98 for (guint i = 0; i < devices->len; i++) {
99 FwupdDevice *dev = g_ptr_array_index (devices, i);
100 json_builder_begin_object (builder);
101 fwupd_device_to_json (dev, builder);
102 json_builder_end_object (builder);
103 }
104 json_builder_end_array (builder);
105 json_builder_end_object (builder);
106
107 /* export as a string */
108 json_root = json_builder_get_root (builder);
109 json_generator = json_generator_new ();
110 json_generator_set_pretty (json_generator, TRUE);
111 json_generator_set_root (json_generator, json_root);
112 state = json_generator_to_data (json_generator, NULL);
113 if (state == NULL)
114 return FALSE;
115 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
116 filename = g_build_filename (dirname, "state.json", NULL);
117 return g_file_set_contents (filename, state, -1, error);
118}
119
120static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000121fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600122{
123 g_autoptr(GError) error_local = NULL;
124
Richard Hughesd92ccca2019-05-20 11:28:31 +0100125#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100126 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500127 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100128#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000129 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000130 return FALSE;
131 if (fu_engine_get_tainted (priv->engine)) {
132 g_printerr ("WARNING: This tool has loaded 3rd party code and "
133 "is no longer supported by the upstream developers!\n");
134 }
135 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500136}
137
Richard Hughesb5976832018-05-18 10:02:09 +0100138static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500139fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
140{
141 g_autofree gchar *path = g_path_get_dirname (value);
142 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
143 g_prefix_error (error,
144 "Unable to access %s. You may need to copy %s to %s: ",
145 path, value, g_getenv ("HOME"));
146 }
147}
148
149static void
Richard Hughesb5976832018-05-18 10:02:09 +0100150fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
151{
152 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
153 /* TRANSLATORS: this is when a device ctrl+c's a watch */
154 g_print ("%s\n", _("Cancelled"));
155 g_main_loop_quit (priv->loop);
156}
157
158static gboolean
159fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
160{
161 g_autofree gchar *tmp = NULL;
162 g_autoptr(FuSmbios) smbios = NULL;
163 if (g_strv_length (values) < 1) {
164 g_set_error_literal (error,
165 FWUPD_ERROR,
166 FWUPD_ERROR_INVALID_ARGS,
167 "Invalid arguments");
168 return FALSE;
169 }
170 smbios = fu_smbios_new ();
171 if (!fu_smbios_setup_from_file (smbios, values[0], error))
172 return FALSE;
173 tmp = fu_smbios_to_string (smbios);
174 g_print ("%s\n", tmp);
175 return TRUE;
176}
177
Richard Hughes9e5675e2019-11-22 09:35:03 +0000178#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100179static gboolean
180fu_util_sigint_cb (gpointer user_data)
181{
182 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
183 g_debug ("Handling SIGINT");
184 g_cancellable_cancel (priv->cancellable);
185 return FALSE;
186}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000187#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100188
189static void
190fu_util_private_free (FuUtilPrivate *priv)
191{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500192 if (priv->current_device != NULL)
193 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100194 if (priv->engine != NULL)
195 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100196 if (priv->loop != NULL)
197 g_main_loop_unref (priv->loop);
198 if (priv->cancellable != NULL)
199 g_object_unref (priv->cancellable);
200 if (priv->progressbar != NULL)
201 g_object_unref (priv->progressbar);
202 if (priv->context != NULL)
203 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600204 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100205 g_free (priv);
206}
207
208#pragma clang diagnostic push
209#pragma clang diagnostic ignored "-Wunused-function"
210G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
211#pragma clang diagnostic pop
212
Richard Hughes98ca9932018-05-18 10:24:07 +0100213
214static void
215fu_main_engine_device_added_cb (FuEngine *engine,
216 FuDevice *device,
217 FuUtilPrivate *priv)
218{
219 g_autofree gchar *tmp = fu_device_to_string (device);
220 g_debug ("ADDED:\n%s", tmp);
221}
222
223static void
224fu_main_engine_device_removed_cb (FuEngine *engine,
225 FuDevice *device,
226 FuUtilPrivate *priv)
227{
228 g_autofree gchar *tmp = fu_device_to_string (device);
229 g_debug ("REMOVED:\n%s", tmp);
230}
231
232static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100233fu_main_engine_status_changed_cb (FuEngine *engine,
234 FwupdStatus status,
235 FuUtilPrivate *priv)
236{
237 fu_progressbar_update (priv->progressbar, status, 0);
238}
239
240static void
241fu_main_engine_percentage_changed_cb (FuEngine *engine,
242 guint percentage,
243 FuUtilPrivate *priv)
244{
245 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
246}
247
248static gboolean
249fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
250{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000251 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100252 return FALSE;
253 g_main_loop_run (priv->loop);
254 return TRUE;
255}
256
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100257static gint
258fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
259{
260 return fu_plugin_name_compare (*item1, *item2);
261}
262
263static gboolean
264fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
265{
266 GPtrArray *plugins;
267 guint cnt = 0;
268
269 /* load engine */
270 if (!fu_engine_load_plugins (priv->engine, error))
271 return FALSE;
272
273 /* print */
274 plugins = fu_engine_get_plugins (priv->engine);
275 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
276 for (guint i = 0; i < plugins->len; i++) {
277 FuPlugin *plugin = g_ptr_array_index (plugins, i);
278 if (!fu_plugin_get_enabled (plugin))
279 continue;
280 g_print ("%s\n", fu_plugin_get_name (plugin));
281 cnt++;
282 }
283 if (cnt == 0) {
284 /* TRANSLATORS: nothing found */
285 g_print ("%s\n", _("No plugins found"));
286 return TRUE;
287 }
288
289 return TRUE;
290}
291
Richard Hughes98ca9932018-05-18 10:24:07 +0100292static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100293fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
294{
295 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
296 if (!fwupd_device_has_flag (dev, priv->filter_include))
297 return FALSE;
298 }
299 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
300 if (fwupd_device_has_flag (dev, priv->filter_exclude))
301 return FALSE;
302 }
303 return TRUE;
304}
305
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500306static gchar *
307fu_util_get_tree_title (FuUtilPrivate *priv)
308{
309 return g_strdup (fu_engine_get_host_product (priv->engine));
310}
311
Richard Hughes747f5702019-08-06 14:27:26 +0100312static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600313fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
314{
315 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500316 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600317 g_autofree gchar *title = NULL;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600318
319 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000320 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600321 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600322 title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600323
324 /* get devices from daemon */
325 devices = fu_engine_get_devices (priv->engine, error);
326 if (devices == NULL)
327 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000328 fwupd_device_array_ensure_parents (devices);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600329 for (guint i = 0; i < devices->len; i++) {
330 FwupdDevice *dev = g_ptr_array_index (devices, i);
331 g_autoptr(GPtrArray) rels = NULL;
332 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500333 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600334
Richard Hughes747f5702019-08-06 14:27:26 +0100335 /* not going to have results, so save a engine round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -0600336 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600337 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -0600338 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
339 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
340 * %1 is the device name */
341 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
342 fwupd_device_get_name (dev));
343 g_printerr ("%s\n", tmp);
344 continue;
345 }
Richard Hughes747f5702019-08-06 14:27:26 +0100346 if (!fu_util_filter_device (priv, dev))
347 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600348
349 /* get the releases for this device and filter for validity */
350 rels = fu_engine_get_upgrades (priv->engine,
351 fwupd_device_get_id (dev),
352 &error_local);
353 if (rels == NULL) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -0600354 /* TRANSLATORS: message letting the user know no device upgrade available
355 * %1 is the device name */
Mario Limonciello27164b72020-02-17 23:19:36 -0600356 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
357 fwupd_device_get_name (dev));
358 g_printerr ("%s\n", tmp);
359 /* discard the actual reason from user, but leave for debugging */
360 g_debug ("%s", error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600361 continue;
362 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500363 child = g_node_append_data (root, dev);
364
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600365 for (guint j = 0; j < rels->len; j++) {
366 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500367 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600368 }
369 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500370 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500371 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600372 /* save the device state for other applications to see */
373 if (!fu_util_save_current_state (priv, error))
374 return FALSE;
375
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600376 /* success */
377 return TRUE;
378}
379
380static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500381fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
382{
383 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500384 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600385 g_autofree gchar *title = NULL;
Mario Limonciello716ab272018-05-29 12:34:37 -0500386 gint fd;
387
388 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000389 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500390 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600391 title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500392
393 /* check args */
394 if (g_strv_length (values) != 1) {
395 g_set_error_literal (error,
396 FWUPD_ERROR,
397 FWUPD_ERROR_INVALID_ARGS,
398 "Invalid arguments");
399 return FALSE;
400 }
401
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600402 /* implied, important for get-details on a device not in your system */
403 priv->show_all_devices = TRUE;
404
Mario Limonciello716ab272018-05-29 12:34:37 -0500405 /* open file */
406 fd = open (values[0], O_RDONLY);
407 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500408 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500409 g_set_error (error,
410 FWUPD_ERROR,
411 FWUPD_ERROR_INVALID_FILE,
412 "failed to open %s",
413 values[0]);
414 return FALSE;
415 }
416 array = fu_engine_get_details (priv->engine, fd, error);
417 close (fd);
418
419 if (array == NULL)
420 return FALSE;
421 for (guint i = 0; i < array->len; i++) {
422 FwupdDevice *dev = g_ptr_array_index (array, i);
Mario Limonciello984e29c2020-02-25 11:30:28 -0600423 FwupdRelease *rel;
424 GNode *child;
Richard Hughes747f5702019-08-06 14:27:26 +0100425 if (!fu_util_filter_device (priv, dev))
426 continue;
Mario Limonciello984e29c2020-02-25 11:30:28 -0600427 child = g_node_append_data (root, dev);
428 rel = fwupd_device_get_release_default (dev);
429 if (rel != NULL)
430 g_node_append_data (child, rel);
431
Mario Limonciello716ab272018-05-29 12:34:37 -0500432 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500433 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500434
Mario Limonciello716ab272018-05-29 12:34:37 -0500435 return TRUE;
436}
437
438static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100439fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
440{
441 g_autoptr(GString) str = g_string_new (NULL);
442
443 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
444 const gchar *tmp = fwupd_device_flag_to_string (i);
445 if (tmp == NULL)
446 break;
447 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
448 g_string_append (str, " ");
449 g_string_append (str, tmp);
450 g_string_append (str, " ~");
451 g_string_append (str, tmp);
452 }
453 g_print ("%s\n", str->str);
454
455 return TRUE;
456}
457
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500458static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100459fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500460{
461 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100462 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100463 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
464 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500465 if (!priv->show_all_devices &&
466 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500467 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100468 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500469 GNode *child = g_node_append_data (root, dev_tmp);
470 fu_util_build_device_tree (priv, child, devs, dev_tmp);
471 }
472 }
473}
474
475static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100476fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500477{
478 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600479 g_autofree gchar *title = NULL;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500480 g_autoptr(GPtrArray) devs = NULL;
481
482 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000483 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500484 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -0600485 title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500486
487 /* print */
488 devs = fu_engine_get_devices (priv->engine, error);
489 if (devs == NULL)
490 return FALSE;
491
492 /* print */
493 if (devs->len == 0) {
494 /* TRANSLATORS: nothing attached that can be upgraded */
495 g_print ("%s\n", _("No hardware detected with firmware update capability"));
496 return TRUE;
497 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000498 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500499 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500500 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500501
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100502 /* save the device state for other applications to see */
503 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500504}
505
Richard Hughes98ca9932018-05-18 10:24:07 +0100506static FuDevice *
507fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
508{
509 FuDevice *dev;
510 guint idx;
511 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100512 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100513
514 /* get devices from daemon */
515 devices = fu_engine_get_devices (priv->engine, error);
516 if (devices == NULL)
517 return NULL;
Richard Hughes0ef47202020-01-06 13:59:09 +0000518 fwupd_device_array_ensure_parents (devices);
Richard Hughes98ca9932018-05-18 10:24:07 +0100519
Richard Hughes747f5702019-08-06 14:27:26 +0100520 /* filter results */
521 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
522 for (guint i = 0; i < devices->len; i++) {
523 dev = g_ptr_array_index (devices, i);
524 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
525 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100526 g_ptr_array_add (devices_filtered, g_object_ref (dev));
527 }
528
529 /* nothing */
530 if (devices_filtered->len == 0) {
531 g_set_error_literal (error,
532 FWUPD_ERROR,
533 FWUPD_ERROR_NOTHING_TO_DO,
534 "No supported devices");
535 return NULL;
536 }
537
Richard Hughes98ca9932018-05-18 10:24:07 +0100538 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100539 if (devices_filtered->len == 1) {
540 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500541 /* TRANSLATORS: Device has been chosen by the daemon for the user */
542 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100543 return g_object_ref (dev);
544 }
545
546 /* TRANSLATORS: get interactive prompt */
547 g_print ("%s\n", _("Choose a device:"));
548 /* TRANSLATORS: this is to abort the interactive prompt */
549 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100550 for (guint i = 0; i < devices_filtered->len; i++) {
551 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100552 g_print ("%u.\t%s (%s)\n",
553 i + 1,
554 fu_device_get_id (dev),
555 fu_device_get_name (dev));
556 }
Richard Hughes747f5702019-08-06 14:27:26 +0100557 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100558 if (idx == 0) {
559 g_set_error_literal (error,
560 FWUPD_ERROR,
561 FWUPD_ERROR_NOTHING_TO_DO,
562 "Request canceled");
563 return NULL;
564 }
Richard Hughes747f5702019-08-06 14:27:26 +0100565 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100566 return g_object_ref (dev);
567}
568
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500569static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600570fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500571 FwupdDevice *device,
572 FuUtilPrivate *priv)
573{
574 g_autofree gchar *str = NULL;
575
Richard Hughes809abea2019-03-23 11:06:18 +0000576 /* allowed to set whenever the device has changed */
577 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
578 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
579 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
580 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
581
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500582 /* same as last time, so ignore */
583 if (priv->current_device != NULL &&
584 fwupd_device_compare (priv->current_device, device) == 0)
585 return;
586
587 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600588 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
589 /* TRANSLATORS: %1 is a device name */
590 str = g_strdup_printf (_("Updating %s…"),
591 fwupd_device_get_name (device));
592 fu_progressbar_set_title (priv->progressbar, str);
593 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
594 /* TRANSLATORS: %1 is a device name */
595 str = g_strdup_printf (_("Installing on %s…"),
596 fwupd_device_get_name (device));
597 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000598 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
599 /* TRANSLATORS: %1 is a device name */
600 str = g_strdup_printf (_("Reading from %s…"),
601 fwupd_device_get_name (device));
602 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600603 } else {
604 g_warning ("no FuUtilOperation set");
605 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500606 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600607
Mario Limonciello32241f42019-01-24 10:12:41 -0600608 if (priv->current_message == NULL) {
609 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
610 if (tmp != NULL)
611 priv->current_message = g_strdup (tmp);
612 }
613}
614
615static void
616fu_util_display_current_message (FuUtilPrivate *priv)
617{
618 if (priv->current_message == NULL)
619 return;
620 g_print ("%s\n", priv->current_message);
621 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500622}
623
Richard Hughes98ca9932018-05-18 10:24:07 +0100624static gboolean
625fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
626{
627 g_autoptr(FuDevice) device = NULL;
628 g_autoptr(GBytes) blob_fw = NULL;
629
630 /* invalid args */
631 if (g_strv_length (values) == 0) {
632 g_set_error_literal (error,
633 FWUPD_ERROR,
634 FWUPD_ERROR_INVALID_ARGS,
635 "Invalid arguments");
636 return FALSE;
637 }
638
639 /* parse blob */
640 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500641 if (blob_fw == NULL) {
642 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100643 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500644 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100645
646 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000647 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100648 return FALSE;
649
650 /* get device */
651 if (g_strv_length (values) >= 2) {
652 device = fu_engine_get_device (priv->engine, values[1], error);
653 if (device == NULL)
654 return FALSE;
655 } else {
656 device = fu_util_prompt_for_device (priv, error);
657 if (device == NULL)
658 return FALSE;
659 }
660
Mario Limonciello3f243a92019-01-21 22:05:23 -0600661 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500662 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600663 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500664
Richard Hughes98ca9932018-05-18 10:24:07 +0100665 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000666 if (priv->prepare_blob) {
667 g_autoptr(GPtrArray) devices = NULL;
668 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
669 g_ptr_array_add (devices, g_object_ref (device));
670 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
671 g_prefix_error (error, "failed to prepare composite action: ");
672 return FALSE;
673 }
674 }
Mario Limonciello035818b2019-03-26 11:12:16 -0500675 priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000676 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600677 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000678 if (priv->cleanup_blob) {
679 g_autoptr(FuDevice) device_new = NULL;
680 g_autoptr(GError) error_local = NULL;
681
682 /* get the possibly new device from the old ID */
683 device_new = fu_engine_get_device (priv->engine,
684 fu_device_get_id (device),
685 &error_local);
686 if (device_new == NULL) {
687 g_debug ("failed to find new device: %s",
688 error_local->message);
689 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600690 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000691 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
692 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
693 g_prefix_error (error, "failed to cleanup composite action: ");
694 return FALSE;
695 }
696 }
697 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600698
Mario Limonciello32241f42019-01-24 10:12:41 -0600699 fu_util_display_current_message (priv);
700
Mario Limonciello3f243a92019-01-21 22:05:23 -0600701 /* success */
702 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100703}
704
Richard Hughesa58510b2019-10-30 10:03:12 +0000705static gboolean
706fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
707{
708 g_autoptr(FuDevice) device = NULL;
709 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
710 g_autoptr(GBytes) blob_fw = NULL;
711
712 /* invalid args */
713 if (g_strv_length (values) == 0) {
714 g_set_error_literal (error,
715 FWUPD_ERROR,
716 FWUPD_ERROR_INVALID_ARGS,
717 "Invalid arguments");
718 return FALSE;
719 }
720
721 /* file already exists */
722 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
723 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
724 g_set_error_literal (error,
725 FWUPD_ERROR,
726 FWUPD_ERROR_INVALID_ARGS,
727 "Filename already exists");
728 return FALSE;
729 }
730
Richard Hughes02792c02019-11-01 14:21:20 +0000731 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000732 * avoid failing at the end of a potentially lengthy operation */
733 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
734 return FALSE;
735
736 /* load engine */
737 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
738 return FALSE;
739
740 /* get device */
741 if (g_strv_length (values) >= 2) {
742 device = fu_engine_get_device (priv->engine, values[1], error);
743 if (device == NULL)
744 return FALSE;
745 } else {
746 device = fu_util_prompt_for_device (priv, error);
747 if (device == NULL)
748 return FALSE;
749 }
750 priv->current_operation = FU_UTIL_OPERATION_READ;
751 g_signal_connect (priv->engine, "device-changed",
752 G_CALLBACK (fu_util_update_device_changed_cb), priv);
753
754 /* dump firmware */
755 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
756 if (blob_fw == NULL)
757 return FALSE;
758 return fu_common_set_contents_bytes (values[0], blob_fw, error);
759}
760
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100761static gint
762fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
763{
764 FuInstallTask *task1 = *((FuInstallTask **) a);
765 FuInstallTask *task2 = *((FuInstallTask **) b);
766 return fu_install_task_compare (task1, task2);
767}
768
769static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100770fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
771{
Filipe Laínse0914272019-09-20 10:04:43 +0100772 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100773 { "curl", uri, "--output", fn, NULL },
774 { NULL } };
775 for (guint i = 0; argv[i][0] != NULL; i++) {
776 g_autoptr(GError) error_local = NULL;
777 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
778 g_debug ("%s", error_local->message);
779 continue;
780 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000781 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100782 }
783 g_set_error_literal (error,
784 FWUPD_ERROR,
785 FWUPD_ERROR_NOT_FOUND,
786 "no supported out-of-process downloaders found");
787 return FALSE;
788}
789
790static gchar *
791fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
792{
793 g_autofree gchar *filename = NULL;
794 g_autoptr(SoupURI) uri = NULL;
795
796 /* a local file */
797 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000798 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
799 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100800 if (uri == NULL)
801 return g_strdup (perhapsfn);
802
803 /* download the firmware to a cachedir */
804 filename = fu_util_get_user_cache_path (perhapsfn);
805 if (!fu_common_mkdir_parent (filename, error))
806 return NULL;
807 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
808 return NULL;
809 return g_steal_pointer (&filename);
810}
811
812static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100813fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
814{
Richard Hughes3d178be2018-08-30 11:14:24 +0100815 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100816 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100817 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100818 g_autoptr(GPtrArray) devices_possible = NULL;
819 g_autoptr(GPtrArray) errors = NULL;
820 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100821 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100822
Mario Limonciello8949e892018-05-25 08:03:06 -0500823 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000824 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500825 return FALSE;
826
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100827 /* handle both forms */
828 if (g_strv_length (values) == 1) {
829 devices_possible = fu_engine_get_devices (priv->engine, error);
830 if (devices_possible == NULL)
831 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000832 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100833 } else if (g_strv_length (values) == 2) {
834 FuDevice *device = fu_engine_get_device (priv->engine,
835 values[1],
836 error);
837 if (device == NULL)
838 return FALSE;
839 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
840 g_ptr_array_add (devices_possible, device);
841 } else {
842 g_set_error_literal (error,
843 FWUPD_ERROR,
844 FWUPD_ERROR_INVALID_ARGS,
845 "Invalid arguments");
846 return FALSE;
847 }
848
Richard Hughes3d178be2018-08-30 11:14:24 +0100849 /* download if required */
850 filename = fu_util_download_if_required (priv, values[0], error);
851 if (filename == NULL)
852 return FALSE;
853
Richard Hughes481aa2a2018-09-18 20:51:46 +0100854 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100855 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500856 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100857 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100858 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500859 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100860 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
861 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100862 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600863 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100864 if (components == NULL)
865 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100866
Richard Hughes481aa2a2018-09-18 20:51:46 +0100867 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100868 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
869 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100870 for (guint i = 0; i < components->len; i++) {
871 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100872
873 /* do any devices pass the requirements */
874 for (guint j = 0; j < devices_possible->len; j++) {
875 FuDevice *device = g_ptr_array_index (devices_possible, j);
876 g_autoptr(FuInstallTask) task = NULL;
877 g_autoptr(GError) error_local = NULL;
878
879 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100880 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100881 if (!fu_engine_check_requirements (priv->engine,
Mario Limonciello537da0e2020-03-09 15:38:17 -0500882 task, priv->flags | FWUPD_INSTALL_FLAG_FORCE,
883 &error_local)) {
884 g_debug ("first pass requirement on %s:%s failed: %s",
885 fu_device_get_id (device),
886 xb_node_query_text (component, "id", NULL),
887 error_local->message);
888 g_ptr_array_add (errors, g_steal_pointer (&error_local));
889 continue;
890 }
891
892 /* make a second pass using possibly updated version format now */
893 fu_engine_md_refresh_device_from_component (priv->engine, device, component);
894 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100895 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100896 &error_local)) {
Mario Limonciello537da0e2020-03-09 15:38:17 -0500897 g_debug ("second pass requirement on %s:%s failed: %s",
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100898 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100899 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100900 error_local->message);
901 g_ptr_array_add (errors, g_steal_pointer (&error_local));
902 continue;
903 }
904
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600905 /* if component should have an update message from CAB */
906 fu_device_incorporate_from_component (device, component);
907
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100908 /* success */
909 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
910 }
911 }
912
913 /* order the install tasks by the device priority */
914 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
915
916 /* nothing suitable */
917 if (install_tasks->len == 0) {
918 GError *error_tmp = fu_common_error_array_get_best (errors);
919 g_propagate_error (error, error_tmp);
920 return FALSE;
921 }
922
Mario Limonciello3f243a92019-01-21 22:05:23 -0600923 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500924 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600925 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500926
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100927 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100928 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
929 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100930
Mario Limonciello32241f42019-01-24 10:12:41 -0600931 fu_util_display_current_message (priv);
932
Mario Limonciello3f243a92019-01-21 22:05:23 -0600933 /* we don't want to ask anything */
934 if (priv->no_reboot_check) {
935 g_debug ("skipping reboot check");
936 return TRUE;
937 }
938
Mario Limonciello3143bad2019-02-27 07:31:00 -0600939 /* save the device state for other applications to see */
940 if (!fu_util_save_current_state (priv, error))
941 return FALSE;
942
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100943 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600944 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100945}
946
Richard Hughes98ca9932018-05-18 10:24:07 +0100947static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500948fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600949{
Mario Limonciellofd734852019-08-01 16:41:42 -0500950 FwupdRemote *remote;
951 const gchar *remote_id;
952 const gchar *uri_tmp;
953 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600954
Mario Limonciellofd734852019-08-01 16:41:42 -0500955 uri_tmp = fwupd_release_get_uri (rel);
956 if (uri_tmp == NULL) {
957 g_set_error_literal (error,
958 FWUPD_ERROR,
959 FWUPD_ERROR_INVALID_FILE,
960 "release missing URI");
961 return FALSE;
962 }
963 remote_id = fwupd_release_get_remote_id (rel);
964 if (remote_id == NULL) {
965 g_set_error (error,
966 FWUPD_ERROR,
967 FWUPD_ERROR_INVALID_FILE,
968 "failed to find remote for %s",
969 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +0100970 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -0500971 }
972
973 remote = fu_engine_get_remote_by_id (priv->engine,
974 remote_id,
975 error);
976 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600977 return FALSE;
978
Mario Limonciellofd734852019-08-01 16:41:42 -0500979 argv = g_new0 (gchar *, 2);
980 /* local remotes have the firmware already */
981 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
982 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
983 g_autofree gchar *path = g_path_get_dirname (fn_cache);
984 argv[0] = g_build_filename (path, uri_tmp, NULL);
985 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
986 argv[0] = g_strdup (uri_tmp + 7);
987 /* web remote, fu_util_install will download file */
988 } else {
989 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
990 }
991 return fu_util_install (priv, argv, error);
992}
993
994static gboolean
995fu_util_update_all (FuUtilPrivate *priv, GError **error)
996{
997 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600998
Mario Limonciello46aaee82019-01-10 12:58:00 -0600999 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +00001000 if (devices == NULL)
1001 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +00001002 fwupd_device_array_ensure_parents (devices);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001003 for (guint i = 0; i < devices->len; i++) {
1004 FwupdDevice *dev = g_ptr_array_index (devices, i);
1005 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001006 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001007 g_autoptr(GPtrArray) rels = NULL;
1008 g_autoptr(GError) error_local = NULL;
1009
1010 if (!fu_util_is_interesting_device (dev))
1011 continue;
1012 /* only show stuff that has metadata available */
Mario Limonciello27164b72020-02-17 23:19:36 -06001013 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Mario Limonciello46aaee82019-01-10 12:58:00 -06001014 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001015 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
1016 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS
1017 * %1 is the device name */
1018 g_autofree gchar *tmp = g_strdup_printf (_("• %s has no available firmware updates"),
1019 fwupd_device_get_name (dev));
1020 g_printerr ("%s\n", tmp);
1021 continue;
1022 }
Richard Hughes747f5702019-08-06 14:27:26 +01001023 if (!fu_util_filter_device (priv, dev))
1024 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001025
1026 device_id = fu_device_get_id (dev);
1027 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
1028 if (rels == NULL) {
Mario Limonciello27164b72020-02-17 23:19:36 -06001029 /* TRANSLATORS: message letting the user know no device upgrade available
1030 * %1 is the device name */
1031 g_autofree gchar *tmp = g_strdup_printf (_("• %s has the latest available firmware version"),
1032 fwupd_device_get_name (dev));
1033 g_printerr ("%s\n", tmp);
1034 /* discard the actual reason from user, but leave for debugging */
1035 g_debug ("%s", error_local->message);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001036 continue;
1037 }
1038
Mario Limonciello98b95162019-10-30 09:20:43 -05001039 if (!priv->no_safety_check) {
1040 if (!fu_util_prompt_warning (dev,
1041 fu_util_get_tree_title (priv),
1042 error))
1043 return FALSE;
1044 }
1045
Mario Limonciello46aaee82019-01-10 12:58:00 -06001046 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -05001047 if (!fu_util_install_release (priv, rel, &error_local)) {
1048 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001049 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001050 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001051 fu_util_display_current_message (priv);
1052 }
1053 return TRUE;
1054}
1055
1056static gboolean
1057fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
1058{
1059 FwupdRelease *rel;
1060 g_autoptr(FuDevice) dev = NULL;
1061 g_autoptr(GPtrArray) rels = NULL;
1062
1063 /* do not allow a partial device-id */
1064 dev = fu_engine_get_device (priv->engine, device_id, error);
1065 if (dev == NULL)
1066 return FALSE;
1067
1068 /* get the releases for this device and filter for validity */
1069 rels = fu_engine_get_upgrades (priv->engine, device_id, error);
1070 if (rels == NULL)
1071 return FALSE;
1072 rel = g_ptr_array_index (rels, 0);
1073 if (!fu_util_install_release (priv, rel, error))
1074 return FALSE;
1075 fu_util_display_current_message (priv);
1076
1077 return TRUE;
1078}
1079
1080static gboolean
1081fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1082{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001083 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1084 g_set_error_literal (error,
1085 FWUPD_ERROR,
1086 FWUPD_ERROR_INVALID_ARGS,
1087 "--allow-older is not supported for this command");
1088 return FALSE;
1089 }
1090
1091 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1092 g_set_error_literal (error,
1093 FWUPD_ERROR,
1094 FWUPD_ERROR_INVALID_ARGS,
1095 "--allow-reinstall is not supported for this command");
1096 return FALSE;
1097 }
1098
Mario Limonciellofd734852019-08-01 16:41:42 -05001099 if (g_strv_length (values) > 1) {
1100 g_set_error_literal (error,
1101 FWUPD_ERROR,
1102 FWUPD_ERROR_INVALID_ARGS,
1103 "Invalid arguments");
1104 return FALSE;
1105 }
1106
1107 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1108 return FALSE;
1109
1110 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1111 g_signal_connect (priv->engine, "device-changed",
1112 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1113
1114 if (g_strv_length (values) == 1) {
1115 if (!fu_util_update_by_id (priv, values[0], error))
1116 return FALSE;
1117 } else {
1118 if (!fu_util_update_all (priv, error))
1119 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001120 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001121
1122 /* we don't want to ask anything */
1123 if (priv->no_reboot_check) {
1124 g_debug ("skipping reboot check");
1125 return TRUE;
1126 }
1127
Mario Limonciello3143bad2019-02-27 07:31:00 -06001128 /* save the device state for other applications to see */
1129 if (!fu_util_save_current_state (priv, error))
1130 return FALSE;
1131
Mario Limonciello3f243a92019-01-21 22:05:23 -06001132 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001133}
1134
1135static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001136fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1137{
1138 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001139 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001140
1141 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001142 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001143 return FALSE;
1144
Richard Hughes98ca9932018-05-18 10:24:07 +01001145 /* get device */
1146 if (g_strv_length (values) >= 1) {
1147 device = fu_engine_get_device (priv->engine, values[0], error);
1148 if (device == NULL)
1149 return FALSE;
1150 } else {
1151 device = fu_util_prompt_for_device (priv, error);
1152 if (device == NULL)
1153 return FALSE;
1154 }
1155
1156 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001157 locker = fu_device_locker_new (device, error);
1158 if (locker == NULL)
1159 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001160 return fu_device_detach (device, error);
1161}
1162
1163static gboolean
1164fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1165{
1166 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001167 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001168
1169 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001170 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001171 return FALSE;
1172
Richard Hughes98ca9932018-05-18 10:24:07 +01001173 /* get device */
1174 if (g_strv_length (values) >= 1) {
1175 device = fu_engine_get_device (priv->engine, values[0], error);
1176 if (device == NULL)
1177 return FALSE;
1178 } else {
1179 device = fu_util_prompt_for_device (priv, error);
1180 if (device == NULL)
1181 return FALSE;
1182 }
1183
1184 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001185 locker = fu_device_locker_new (device, error);
1186 if (locker == NULL)
1187 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001188 return fu_device_attach (device, error);
1189}
1190
Richard Hughes1d894f12018-08-31 13:05:51 +01001191static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001192fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1193{
1194 gboolean has_pending = FALSE;
1195 g_autoptr(FuHistory) history = fu_history_new ();
1196 g_autoptr(GPtrArray) devices = NULL;
1197
1198 /* check the history database before starting the daemon */
1199 if (g_strv_length (values) == 0) {
1200 devices = fu_history_get_devices (history, error);
1201 if (devices == NULL)
1202 return FALSE;
1203 } else if (g_strv_length (values) == 1) {
1204 FuDevice *device;
1205 device = fu_history_get_device_by_id (history, values[0], error);
1206 if (device == NULL)
1207 return FALSE;
1208 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1209 g_ptr_array_add (devices, device);
1210 } else {
1211 g_set_error_literal (error,
1212 FWUPD_ERROR,
1213 FWUPD_ERROR_INVALID_ARGS,
1214 "Invalid arguments");
1215 return FALSE;
1216 }
1217
1218 /* nothing to do */
1219 for (guint i = 0; i < devices->len; i++) {
1220 FuDevice *dev = g_ptr_array_index (devices, i);
1221 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1222 fu_engine_add_plugin_filter (priv->engine,
1223 fu_device_get_plugin (dev));
1224 has_pending = TRUE;
1225 }
1226 }
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001227
dkadioglu1db7e982019-12-04 21:13:21 +01001228 if (!has_pending) {
1229 g_printerr ("No firmware to activate\n");
1230 return TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001231 }
1232
1233 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001234 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001235 return FALSE;
1236
1237 /* activate anything with _NEEDS_ACTIVATION */
1238 for (guint i = 0; i < devices->len; i++) {
1239 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001240 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1241 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001242 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1243 continue;
1244 /* TRANSLATORS: shown when shutting down to switch to the new version */
1245 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1246 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1247 return FALSE;
1248 }
1249
1250 return TRUE;
1251}
1252
1253static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001254fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1255{
1256 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1257 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1258 const gchar *hwid_keys[] = {
1259 FU_HWIDS_KEY_BIOS_VENDOR,
1260 FU_HWIDS_KEY_BIOS_VERSION,
1261 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1262 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1263 FU_HWIDS_KEY_MANUFACTURER,
1264 FU_HWIDS_KEY_FAMILY,
1265 FU_HWIDS_KEY_PRODUCT_NAME,
1266 FU_HWIDS_KEY_PRODUCT_SKU,
1267 FU_HWIDS_KEY_ENCLOSURE_KIND,
1268 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1269 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1270 NULL };
1271
1272 /* read DMI data */
1273 if (g_strv_length (values) == 0) {
1274 if (!fu_smbios_setup (smbios, error))
1275 return FALSE;
1276 } else if (g_strv_length (values) == 1) {
1277 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1278 return FALSE;
1279 } else {
1280 g_set_error_literal (error,
1281 FWUPD_ERROR,
1282 FWUPD_ERROR_INVALID_ARGS,
1283 "Invalid arguments");
1284 return FALSE;
1285 }
1286 if (!fu_hwids_setup (hwids, smbios, error))
1287 return FALSE;
1288
1289 /* show debug output */
1290 g_print ("Computer Information\n");
1291 g_print ("--------------------\n");
1292 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1293 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1294 if (tmp == NULL)
1295 continue;
1296 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1297 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1298 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1299 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1300 } else {
1301 g_print ("%s: %s\n", hwid_keys[i], tmp);
1302 }
1303 }
1304
1305 /* show GUIDs */
1306 g_print ("\nHardware IDs\n");
1307 g_print ("------------\n");
1308 for (guint i = 0; i < 15; i++) {
1309 const gchar *keys = NULL;
1310 g_autofree gchar *guid = NULL;
1311 g_autofree gchar *key = NULL;
1312 g_autofree gchar *keys_str = NULL;
1313 g_auto(GStrv) keysv = NULL;
1314 g_autoptr(GError) error_local = NULL;
1315
1316 /* get the GUID */
1317 key = g_strdup_printf ("HardwareID-%u", i);
1318 keys = fu_hwids_get_replace_keys (hwids, key);
1319 guid = fu_hwids_get_guid (hwids, key, &error_local);
1320 if (guid == NULL) {
1321 g_print ("%s\n", error_local->message);
1322 continue;
1323 }
1324
1325 /* show what makes up the GUID */
1326 keysv = g_strsplit (keys, "&", -1);
1327 keys_str = g_strjoinv (" + ", keysv);
1328 g_print ("{%s} <- %s\n", guid, keys_str);
1329 }
1330
1331 return TRUE;
1332}
1333
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001334static gboolean
1335fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1336{
1337 const gchar *script_fn = "startup.sh";
1338 const gchar *output_fn = "firmware.bin";
1339 g_autoptr(GBytes) archive_blob = NULL;
1340 g_autoptr(GBytes) firmware_blob = NULL;
1341 if (g_strv_length (values) < 2) {
1342 g_set_error_literal (error,
1343 FWUPD_ERROR,
1344 FWUPD_ERROR_INVALID_ARGS,
1345 "Invalid arguments");
1346 return FALSE;
1347 }
1348 archive_blob = fu_common_get_contents_bytes (values[0], error);
1349 if (archive_blob == NULL)
1350 return FALSE;
1351 if (g_strv_length (values) > 2)
1352 script_fn = values[2];
1353 if (g_strv_length (values) > 3)
1354 output_fn = values[3];
1355 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1356 if (firmware_blob == NULL)
1357 return FALSE;
1358 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1359}
1360
Richard Hughes3d607622019-03-07 16:59:27 +00001361static gboolean
1362fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1363{
1364 g_autofree gchar *sig = NULL;
1365
1366 /* check args */
1367 if (g_strv_length (values) != 1) {
1368 g_set_error_literal (error,
1369 FWUPD_ERROR,
1370 FWUPD_ERROR_INVALID_ARGS,
1371 "Invalid arguments: value expected");
1372 return FALSE;
1373 }
1374
1375 /* start engine */
1376 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1377 return FALSE;
1378 sig = fu_engine_self_sign (priv->engine, values[0],
1379 FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
1380 FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
1381 if (sig == NULL)
1382 return FALSE;
1383 g_print ("%s\n", sig);
1384 return TRUE;
1385}
1386
Mario Limonciello62f84862018-10-18 13:15:23 -05001387static void
1388fu_util_device_added_cb (FwupdClient *client,
1389 FwupdDevice *device,
1390 gpointer user_data)
1391{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001392 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001393 /* TRANSLATORS: this is when a device is hotplugged */
1394 g_print ("%s\n%s", _("Device added:"), tmp);
1395}
1396
1397static void
1398fu_util_device_removed_cb (FwupdClient *client,
1399 FwupdDevice *device,
1400 gpointer user_data)
1401{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001402 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001403 /* TRANSLATORS: this is when a device is hotplugged */
1404 g_print ("%s\n%s", _("Device removed:"), tmp);
1405}
1406
1407static void
1408fu_util_device_changed_cb (FwupdClient *client,
1409 FwupdDevice *device,
1410 gpointer user_data)
1411{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001412 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001413 /* TRANSLATORS: this is when a device has been updated */
1414 g_print ("%s\n%s", _("Device changed:"), tmp);
1415}
1416
1417static void
1418fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1419{
1420 /* TRANSLATORS: this is when the daemon state changes */
1421 g_print ("%s\n", _("Changed"));
1422}
1423
1424static gboolean
1425fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1426{
1427 g_autoptr(FwupdClient) client = fwupd_client_new ();
1428
1429 /* get all the devices */
1430 if (!fwupd_client_connect (client, priv->cancellable, error))
1431 return FALSE;
1432
1433 /* watch for any hotplugged device */
1434 g_signal_connect (client, "changed",
1435 G_CALLBACK (fu_util_changed_cb), priv);
1436 g_signal_connect (client, "device-added",
1437 G_CALLBACK (fu_util_device_added_cb), priv);
1438 g_signal_connect (client, "device-removed",
1439 G_CALLBACK (fu_util_device_removed_cb), priv);
1440 g_signal_connect (client, "device-changed",
1441 G_CALLBACK (fu_util_device_changed_cb), priv);
1442 g_signal_connect (priv->cancellable, "cancelled",
1443 G_CALLBACK (fu_util_cancelled_cb), priv);
1444 g_main_loop_run (priv->loop);
1445 return TRUE;
1446}
1447
Richard Hughes15684492019-03-15 16:27:50 +00001448static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001449fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1450{
1451 g_autoptr(GPtrArray) firmware_types = NULL;
1452
1453 /* load engine */
1454 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1455 return FALSE;
1456
1457 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1458 for (guint i = 0; i < firmware_types->len; i++) {
1459 const gchar *id = g_ptr_array_index (firmware_types, i);
1460 g_print ("%s\n", id);
1461 }
1462 if (firmware_types->len == 0) {
1463 /* TRANSLATORS: nothing found */
1464 g_print ("%s\n", _("No firmware IDs found"));
1465 return TRUE;
1466 }
1467
1468 return TRUE;
1469}
1470
1471static gchar *
1472fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1473{
1474 g_autoptr(GPtrArray) firmware_types = NULL;
1475 guint idx;
1476 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1477
1478 /* TRANSLATORS: get interactive prompt */
1479 g_print ("%s\n", _("Choose a firmware type:"));
1480 /* TRANSLATORS: this is to abort the interactive prompt */
1481 g_print ("0.\t%s\n", _("Cancel"));
1482 for (guint i = 0; i < firmware_types->len; i++) {
1483 const gchar *id = g_ptr_array_index (firmware_types, i);
1484 g_print ("%u.\t%s\n", i + 1, id);
1485 }
1486 idx = fu_util_prompt_for_number (firmware_types->len);
1487 if (idx == 0) {
1488 g_set_error_literal (error,
1489 FWUPD_ERROR,
1490 FWUPD_ERROR_NOTHING_TO_DO,
1491 "Request canceled");
1492 return NULL;
1493 }
1494
1495 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1496}
1497
1498static gboolean
1499fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1500{
1501 GType gtype;
1502 g_autoptr(GBytes) blob = NULL;
1503 g_autoptr(FuFirmware) firmware = NULL;
1504 g_autofree gchar *firmware_type = NULL;
1505 g_autofree gchar *str = NULL;
1506
1507 /* check args */
1508 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1509 g_set_error_literal (error,
1510 FWUPD_ERROR,
1511 FWUPD_ERROR_INVALID_ARGS,
1512 "Invalid arguments: filename required");
1513 return FALSE;
1514 }
1515
1516 if (g_strv_length (values) == 2)
1517 firmware_type = g_strdup (values[1]);
1518
1519 /* load file */
1520 blob = fu_common_get_contents_bytes (values[0], error);
1521 if (blob == NULL)
1522 return FALSE;
1523
1524 /* load engine */
1525 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1526 return FALSE;
1527
1528 /* find the GType to use */
1529 if (firmware_type == NULL)
1530 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1531 if (firmware_type == NULL)
1532 return FALSE;
1533 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1534 if (gtype == G_TYPE_INVALID) {
1535 g_set_error (error,
1536 G_IO_ERROR,
1537 G_IO_ERROR_NOT_FOUND,
1538 "GType %s not supported", firmware_type);
1539 return FALSE;
1540 }
1541 firmware = g_object_new (gtype, NULL);
1542 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1543 return FALSE;
1544 str = fu_firmware_to_string (firmware);
1545 g_print ("%s", str);
1546 return TRUE;
1547}
1548
1549static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001550fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1551{
1552 g_autofree gchar *str = NULL;
1553 g_autoptr(FuDevice) dev = NULL;
1554
1555 /* load engine */
1556 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1557 return FALSE;
1558
1559 /* get device */
1560 if (g_strv_length (values) == 1) {
1561 dev = fu_engine_get_device (priv->engine, values[1], error);
1562 if (dev == NULL)
1563 return FALSE;
1564 } else {
1565 dev = fu_util_prompt_for_device (priv, error);
1566 if (dev == NULL)
1567 return FALSE;
1568 }
1569
1570 /* add checksums */
1571 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1572 return FALSE;
1573
1574 /* show checksums */
1575 str = fu_device_to_string (dev);
1576 g_print ("%s\n", str);
1577 return TRUE;
1578}
1579
Mario Limonciellofe593942019-04-03 13:48:52 -05001580static gboolean
1581fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1582{
1583 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001584 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001585 g_autofree gchar *title = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001586
1587 /* load engine */
1588 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1589 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001590 title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001591
1592 /* get all devices from the history database */
1593 devices = fu_engine_get_history (priv->engine, error);
1594 if (devices == NULL)
1595 return FALSE;
1596
1597 /* show each device */
1598 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001599 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001600 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001601 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001602 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001603 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001604
Richard Hughes747f5702019-08-06 14:27:26 +01001605 if (!fu_util_filter_device (priv, dev))
1606 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001607 child = g_node_append_data (root, dev);
1608
1609 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001610 if (rel == NULL)
1611 continue;
1612 remote = fwupd_release_get_remote_id (rel);
1613
1614 /* doesn't actually map to remote */
1615 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001616 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001617 continue;
1618 }
1619
1620 /* try to lookup releases from client */
1621 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1622 if (rels == NULL)
1623 return FALSE;
1624
1625 /* map to a release in client */
1626 for (guint j = 0; j < rels->len; j++) {
1627 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1628 if (g_strcmp0 (remote,
1629 fwupd_release_get_remote_id (rel2)) != 0)
1630 continue;
1631 if (g_strcmp0 (fwupd_release_get_version (rel),
1632 fwupd_release_get_version (rel2)) != 0)
1633 continue;
1634 g_node_append_data (child, g_object_ref (rel2));
1635 rel = NULL;
1636 break;
1637 }
1638
1639 /* didn't match anything */
1640 if (rels->len == 0 || rel != NULL) {
1641 g_node_append_data (child, rel);
1642 continue;
1643 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001644
Mario Limonciellofe593942019-04-03 13:48:52 -05001645 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001646 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001647
1648 return TRUE;
1649}
1650
Richard Hughesfd7e9942020-01-17 14:09:13 +00001651static gboolean
Richard Hughes4959baa2020-01-17 14:33:00 +00001652fu_util_refresh_remote (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
1653{
1654 g_autofree gchar *fn_raw = NULL;
1655 g_autofree gchar *fn_sig = NULL;
1656 g_autoptr(GBytes) bytes_raw = NULL;
1657 g_autoptr(GBytes) bytes_sig = NULL;
1658
1659 /* payload */
1660 fn_raw = fu_util_get_user_cache_path (fwupd_remote_get_metadata_uri (remote));
1661 if (!fu_common_mkdir_parent (fn_raw, error))
1662 return FALSE;
1663 if (!fu_util_download_out_of_process (fwupd_remote_get_metadata_uri (remote),
1664 fn_raw, error))
1665 return FALSE;
1666 bytes_raw = fu_common_get_contents_bytes (fn_raw, error);
1667 if (bytes_raw == NULL)
1668 return FALSE;
1669
1670 /* signature */
1671 fn_sig = fu_util_get_user_cache_path (fwupd_remote_get_metadata_uri_sig (remote));
1672 if (!fu_util_download_out_of_process (fwupd_remote_get_metadata_uri_sig (remote),
1673 fn_sig, error))
1674 return FALSE;
1675 bytes_sig = fu_common_get_contents_bytes (fn_sig, error);
1676 if (bytes_sig == NULL)
1677 return FALSE;
1678
1679 /* send to daemon */
1680 g_debug ("updating %s", fwupd_remote_get_id (remote));
1681 return fu_engine_update_metadata_bytes (priv->engine,
1682 fwupd_remote_get_id (remote),
1683 bytes_raw,
1684 bytes_sig,
1685 error);
1686
1687}
1688
1689static gboolean
1690fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
1691{
1692 g_autoptr(GPtrArray) remotes = NULL;
1693
1694 /* load engine */
1695 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1696 return FALSE;
1697
1698 /* download new metadata */
1699 remotes = fu_engine_get_remotes (priv->engine, error);
1700 if (remotes == NULL)
1701 return FALSE;
1702 for (guint i = 0; i < remotes->len; i++) {
1703 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1704 if (!fwupd_remote_get_enabled (remote))
1705 continue;
1706 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
1707 continue;
1708 if (!fu_util_refresh_remote (priv, remote, error))
1709 return FALSE;
1710 }
1711 return TRUE;
1712}
1713
1714static gboolean
Richard Hughesfd7e9942020-01-17 14:09:13 +00001715fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
1716{
1717 g_autoptr(GNode) root = g_node_new (NULL);
1718 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001719 g_autofree gchar *title = NULL;
Richard Hughesfd7e9942020-01-17 14:09:13 +00001720
1721 /* load engine */
1722 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1723 return FALSE;
Mario Limonciellodc9a1a82020-02-20 14:20:20 -06001724 title = fu_util_get_tree_title (priv);
Richard Hughesfd7e9942020-01-17 14:09:13 +00001725
1726 /* list remotes */
1727 remotes = fu_engine_get_remotes (priv->engine, error);
1728 if (remotes == NULL)
1729 return FALSE;
1730 if (remotes->len == 0) {
1731 g_set_error_literal (error,
1732 FWUPD_ERROR,
1733 FWUPD_ERROR_NOTHING_TO_DO,
1734 "no remotes available");
1735 return FALSE;
1736 }
1737 for (guint i = 0; i < remotes->len; i++) {
1738 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
1739 g_node_append_data (root, remote_tmp);
1740 }
1741 fu_util_print_tree (root, title);
1742
1743 return TRUE;
1744}
1745
Richard Hughesb5976832018-05-18 10:02:09 +01001746int
1747main (int argc, char *argv[])
1748{
Richard Hughes460226a2018-05-21 20:56:21 +01001749 gboolean allow_older = FALSE;
1750 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001751 gboolean force = FALSE;
1752 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001753 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001754 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001755 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001756 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1757 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001758 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001759 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001760 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001761 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001762 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1763 /* TRANSLATORS: command line option */
1764 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001765 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1766 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001767 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001768 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1769 /* TRANSLATORS: command line option */
1770 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001771 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1772 /* TRANSLATORS: command line option */
1773 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06001774 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
1775 /* TRANSLATORS: command line option */
1776 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05001777 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
1778 /* TRANSLATORS: command line option */
1779 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001780 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1781 /* TRANSLATORS: command line option */
1782 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001783 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1784 /* TRANSLATORS: command line option */
1785 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001786 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001787 /* TRANSLATORS: command line option */
1788 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001789 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001790 /* TRANSLATORS: command line option */
1791 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06001792 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
1793 /* TRANSLATORS: command line option */
1794 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01001795 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
1796 /* TRANSLATORS: command line option */
1797 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01001798 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
1799 /* TRANSLATORS: command line option */
1800 _("Filter with a set of device flags using a ~ prefix to "
1801 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001802 { NULL}
1803 };
1804
Richard Hughes429f72b2020-01-16 12:18:19 +00001805#ifdef _WIN32
1806 /* workaround Windows setting the codepage to 1252 */
1807 g_setenv ("LANG", "C.UTF-8", FALSE);
1808#endif
1809
Richard Hughesb5976832018-05-18 10:02:09 +01001810 setlocale (LC_ALL, "");
1811
Richard Hughes668ee212019-11-22 09:17:46 +00001812 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01001813 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1814 textdomain (GETTEXT_PACKAGE);
1815
Richard Hughes01c0bad2019-11-22 09:08:51 +00001816#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01001817 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001818 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01001819 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001820 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00001821#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001822
1823 /* create helper object */
1824 priv->loop = g_main_loop_new (NULL, FALSE);
1825 priv->progressbar = fu_progressbar_new ();
1826
1827 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00001828 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001829 "build-firmware",
1830 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1831 /* TRANSLATORS: command description */
1832 _("Build firmware using a sandbox"),
1833 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00001834 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001835 "smbios-dump",
1836 "FILE",
1837 /* TRANSLATORS: command description */
1838 _("Dump SMBIOS data from a file"),
1839 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00001840 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001841 "get-plugins",
1842 NULL,
1843 /* TRANSLATORS: command description */
1844 _("Get all enabled plugins registered with the system"),
1845 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00001846 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001847 "get-details",
1848 NULL,
1849 /* TRANSLATORS: command description */
1850 _("Gets details about a firmware file"),
1851 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00001852 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05001853 "get-history",
1854 NULL,
1855 /* TRANSLATORS: command description */
1856 _("Show history of firmware updates"),
1857 fu_util_get_history);
1858 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001859 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06001860 NULL,
1861 /* TRANSLATORS: command description */
1862 _("Gets the list of updates for connected hardware"),
1863 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00001864 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01001865 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01001866 NULL,
1867 /* TRANSLATORS: command description */
1868 _("Get all devices that support firmware updates"),
1869 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00001870 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01001871 "get-device-flags",
1872 NULL,
1873 /* TRANSLATORS: command description */
1874 _("Get all device flags supported by fwupd"),
1875 fu_util_get_device_flags);
1876 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001877 "watch",
1878 NULL,
1879 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02001880 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001881 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00001882 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001883 "install-blob",
1884 "FILENAME DEVICE-ID",
1885 /* TRANSLATORS: command description */
1886 _("Install a firmware blob on a device"),
1887 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00001888 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001889 "install",
Mario Limonciello234c8642020-01-08 20:07:29 -06001890 "FILE [DEVICE-ID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001891 /* TRANSLATORS: command description */
1892 _("Install a firmware file on this hardware"),
1893 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00001894 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001895 "attach",
1896 "DEVICE-ID",
1897 /* TRANSLATORS: command description */
1898 _("Attach to firmware mode"),
1899 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001900 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001901 "detach",
1902 "DEVICE-ID",
1903 /* TRANSLATORS: command description */
1904 _("Detach to bootloader mode"),
1905 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001906 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001907 "activate",
1908 "[DEVICE-ID]",
1909 /* TRANSLATORS: command description */
1910 _("Activate pending devices"),
1911 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00001912 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01001913 "hwids",
1914 "[FILE]",
1915 /* TRANSLATORS: command description */
1916 _("Return all the hardware IDs for the machine"),
1917 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00001918 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05001919 "monitor",
1920 NULL,
1921 /* TRANSLATORS: command description */
1922 _("Monitor the daemon for events"),
1923 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00001924 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001925 "update,upgrade",
Mario Limonciello46aaee82019-01-10 12:58:00 -06001926 NULL,
1927 /* TRANSLATORS: command description */
1928 _("Update all devices that match local metadata"),
1929 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00001930 fu_util_cmd_array_add (cmd_array,
1931 "self-sign",
1932 "TEXT",
1933 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00001934 C_("command-description",
1935 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00001936 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00001937 fu_util_cmd_array_add (cmd_array,
1938 "verify-update",
Mario Limonciello234c8642020-01-08 20:07:29 -06001939 "[DEVICE-ID]",
Richard Hughes15684492019-03-15 16:27:50 +00001940 /* TRANSLATORS: command description */
1941 _("Update the stored metadata with current contents"),
1942 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01001943 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00001944 "firmware-read",
1945 "FILENAME [DEVICE-ID]",
1946 /* TRANSLATORS: command description */
1947 _("Read a firmware blob from a device"),
1948 fu_util_firmware_read);
1949 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01001950 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06001951 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01001952 /* TRANSLATORS: command description */
1953 _("Parse and show details about a firmware file"),
1954 fu_util_firmware_parse);
1955 fu_util_cmd_array_add (cmd_array,
1956 "get-firmware-types",
1957 NULL,
1958 /* TRANSLATORS: command description */
1959 _("List the available firmware types"),
1960 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00001961 fu_util_cmd_array_add (cmd_array,
1962 "get-remotes",
1963 NULL,
1964 /* TRANSLATORS: command description */
1965 _("Gets the configured remotes"),
1966 fu_util_get_remotes);
Richard Hughes4959baa2020-01-17 14:33:00 +00001967 fu_util_cmd_array_add (cmd_array,
1968 "refresh",
1969 NULL,
1970 /* TRANSLATORS: command description */
1971 _("Refresh metadata from remote server"),
1972 fu_util_refresh);
Richard Hughesb5976832018-05-18 10:02:09 +01001973
1974 /* do stuff on ctrl+c */
1975 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00001976#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01001977 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1978 SIGINT, fu_util_sigint_cb,
1979 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00001980#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001981 g_signal_connect (priv->cancellable, "cancelled",
1982 G_CALLBACK (fu_util_cancelled_cb), priv);
1983
1984 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00001985 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001986
Mario Limonciello3f243a92019-01-21 22:05:23 -06001987 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001988 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06001989 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05001990 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06001991 fu_progressbar_set_interactive (priv->progressbar, FALSE);
1992 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001993
Richard Hughesb5976832018-05-18 10:02:09 +01001994 /* get a list of the commands */
1995 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00001996 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001997 g_option_context_set_summary (priv->context, cmd_descriptions);
1998 g_option_context_set_description (priv->context,
1999 "This tool allows an administrator to use the fwupd plugins "
2000 "without being installed on the host system.");
2001
2002 /* TRANSLATORS: program name */
2003 g_set_application_name (_("Firmware Utility"));
2004 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05002005 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01002006 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
2007 if (!ret) {
2008 /* TRANSLATORS: the user didn't read the man page */
2009 g_print ("%s: %s\n", _("Failed to parse arguments"),
2010 error->message);
2011 return EXIT_FAILURE;
2012 }
2013
Richard Hughes0e46b222019-09-05 12:13:35 +01002014 /* allow disabling SSL strict mode for broken corporate proxies */
2015 if (priv->disable_ssl_strict) {
2016 /* TRANSLATORS: try to help */
2017 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
2018 "to do this automatically in the future "
2019 "export DISABLE_SSL_STRICT in your environment"));
2020 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
2021 }
2022
Richard Hughes747f5702019-08-06 14:27:26 +01002023 /* parse filter flags */
2024 if (filter != NULL) {
2025 if (!fu_util_parse_filter_flags (filter,
2026 &priv->filter_include,
2027 &priv->filter_exclude,
2028 &error)) {
2029 /* TRANSLATORS: the user didn't read the man page */
2030 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
2031 error->message);
2032 return EXIT_FAILURE;
2033 }
2034 }
2035
2036
Richard Hughes460226a2018-05-21 20:56:21 +01002037 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01002038 if (allow_reinstall)
2039 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
2040 if (allow_older)
2041 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
2042 if (force)
2043 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
2044
Richard Hughes98ca9932018-05-18 10:24:07 +01002045 /* load engine */
2046 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
2047 g_signal_connect (priv->engine, "device-added",
2048 G_CALLBACK (fu_main_engine_device_added_cb),
2049 priv);
2050 g_signal_connect (priv->engine, "device-removed",
2051 G_CALLBACK (fu_main_engine_device_removed_cb),
2052 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01002053 g_signal_connect (priv->engine, "status-changed",
2054 G_CALLBACK (fu_main_engine_status_changed_cb),
2055 priv);
2056 g_signal_connect (priv->engine, "percentage-changed",
2057 G_CALLBACK (fu_main_engine_percentage_changed_cb),
2058 priv);
2059
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05002060 /* just show versions and exit */
2061 if (version) {
2062 g_autofree gchar *version_str = fu_util_get_versions ();
2063 g_print ("%s\n", version_str);
2064 return EXIT_SUCCESS;
2065 }
2066
Richard Hughesc02ee4d2018-05-22 15:46:03 +01002067 /* any plugin whitelist specified */
2068 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
2069 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
2070
Richard Hughesb5976832018-05-18 10:02:09 +01002071 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00002072 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01002073 if (!ret) {
2074 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
2075 g_autofree gchar *tmp = NULL;
2076 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
2077 g_print ("%s\n\n%s", error->message, tmp);
2078 return EXIT_FAILURE;
2079 }
2080 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
2081 g_print ("%s\n", error->message);
2082 return EXIT_NOTHING_TO_DO;
2083 }
2084 g_print ("%s\n", error->message);
2085 return EXIT_FAILURE;
2086 }
2087
2088 /* success */
2089 return EXIT_SUCCESS;
2090}