blob: 16eb16e6bca57bb1b8983fae16df247a4d51a504 [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 Limonciello20cc9ee2019-09-05 07:27:26 -0500317 g_autofree gchar *title = fu_util_get_tree_title (priv);
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;
322
323 /* get devices from daemon */
324 devices = fu_engine_get_devices (priv->engine, error);
325 if (devices == NULL)
326 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000327 fwupd_device_array_ensure_parents (devices);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600328 for (guint i = 0; i < devices->len; i++) {
329 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello7e4949c2019-12-09 10:21:20 -0600330 g_autofree gchar *upgrade_str = NULL;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600331 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 Limonciello1e35e4c2019-01-28 11:13:02 -0600336 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
337 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100338 if (!fu_util_filter_device (priv, dev))
339 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600340
341 /* get the releases for this device and filter for validity */
342 rels = fu_engine_get_upgrades (priv->engine,
343 fwupd_device_get_id (dev),
344 &error_local);
345 if (rels == NULL) {
Mario Limonciello7e4949c2019-12-09 10:21:20 -0600346 /* TRANSLATORS: message letting the user know no device upgrade available
347 * %1 is the device name */
348 upgrade_str = g_strdup_printf (_("No upgrades for %s"),
349 fwupd_device_get_name (dev));
350 g_printerr ("%s: %s\n", upgrade_str, error_local->message);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600351 continue;
352 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500353 child = g_node_append_data (root, dev);
354
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600355 for (guint j = 0; j < rels->len; j++) {
356 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500357 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600358 }
359 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500360 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500361 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600362 /* save the device state for other applications to see */
363 if (!fu_util_save_current_state (priv, error))
364 return FALSE;
365
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600366 /* success */
367 return TRUE;
368}
369
370static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500371fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
372{
373 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500374 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500375 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500376 gint fd;
377
378 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000379 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500380 return FALSE;
381
382 /* check args */
383 if (g_strv_length (values) != 1) {
384 g_set_error_literal (error,
385 FWUPD_ERROR,
386 FWUPD_ERROR_INVALID_ARGS,
387 "Invalid arguments");
388 return FALSE;
389 }
390
391 /* open file */
392 fd = open (values[0], O_RDONLY);
393 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500394 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500395 g_set_error (error,
396 FWUPD_ERROR,
397 FWUPD_ERROR_INVALID_FILE,
398 "failed to open %s",
399 values[0]);
400 return FALSE;
401 }
402 array = fu_engine_get_details (priv->engine, fd, error);
403 close (fd);
404
405 if (array == NULL)
406 return FALSE;
407 for (guint i = 0; i < array->len; i++) {
408 FwupdDevice *dev = g_ptr_array_index (array, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100409 if (!fu_util_filter_device (priv, dev))
410 continue;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500411 g_node_append_data (root, dev);
Mario Limonciello716ab272018-05-29 12:34:37 -0500412 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500413 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500414
Mario Limonciello716ab272018-05-29 12:34:37 -0500415 return TRUE;
416}
417
418static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100419fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
420{
421 g_autoptr(GString) str = g_string_new (NULL);
422
423 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
424 const gchar *tmp = fwupd_device_flag_to_string (i);
425 if (tmp == NULL)
426 break;
427 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
428 g_string_append (str, " ");
429 g_string_append (str, tmp);
430 g_string_append (str, " ~");
431 g_string_append (str, tmp);
432 }
433 g_print ("%s\n", str->str);
434
435 return TRUE;
436}
437
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500438static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100439fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500440{
441 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100442 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100443 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
444 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500445 if (!priv->show_all_devices &&
446 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500447 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100448 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500449 GNode *child = g_node_append_data (root, dev_tmp);
450 fu_util_build_device_tree (priv, child, devs, dev_tmp);
451 }
452 }
453}
454
455static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100456fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500457{
458 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500459 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500460 g_autoptr(GPtrArray) devs = NULL;
461
462 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000463 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500464 return FALSE;
465
466 /* print */
467 devs = fu_engine_get_devices (priv->engine, error);
468 if (devs == NULL)
469 return FALSE;
470
471 /* print */
472 if (devs->len == 0) {
473 /* TRANSLATORS: nothing attached that can be upgraded */
474 g_print ("%s\n", _("No hardware detected with firmware update capability"));
475 return TRUE;
476 }
Richard Hughes0ef47202020-01-06 13:59:09 +0000477 fwupd_device_array_ensure_parents (devs);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500478 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500479 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500480
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100481 /* save the device state for other applications to see */
482 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500483}
484
Richard Hughes98ca9932018-05-18 10:24:07 +0100485static FuDevice *
486fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
487{
488 FuDevice *dev;
489 guint idx;
490 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100491 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100492
493 /* get devices from daemon */
494 devices = fu_engine_get_devices (priv->engine, error);
495 if (devices == NULL)
496 return NULL;
Richard Hughes0ef47202020-01-06 13:59:09 +0000497 fwupd_device_array_ensure_parents (devices);
Richard Hughes98ca9932018-05-18 10:24:07 +0100498
Richard Hughes747f5702019-08-06 14:27:26 +0100499 /* filter results */
500 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
501 for (guint i = 0; i < devices->len; i++) {
502 dev = g_ptr_array_index (devices, i);
503 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
504 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100505 g_ptr_array_add (devices_filtered, g_object_ref (dev));
506 }
507
508 /* nothing */
509 if (devices_filtered->len == 0) {
510 g_set_error_literal (error,
511 FWUPD_ERROR,
512 FWUPD_ERROR_NOTHING_TO_DO,
513 "No supported devices");
514 return NULL;
515 }
516
Richard Hughes98ca9932018-05-18 10:24:07 +0100517 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100518 if (devices_filtered->len == 1) {
519 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500520 /* TRANSLATORS: Device has been chosen by the daemon for the user */
521 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100522 return g_object_ref (dev);
523 }
524
525 /* TRANSLATORS: get interactive prompt */
526 g_print ("%s\n", _("Choose a device:"));
527 /* TRANSLATORS: this is to abort the interactive prompt */
528 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100529 for (guint i = 0; i < devices_filtered->len; i++) {
530 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100531 g_print ("%u.\t%s (%s)\n",
532 i + 1,
533 fu_device_get_id (dev),
534 fu_device_get_name (dev));
535 }
Richard Hughes747f5702019-08-06 14:27:26 +0100536 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100537 if (idx == 0) {
538 g_set_error_literal (error,
539 FWUPD_ERROR,
540 FWUPD_ERROR_NOTHING_TO_DO,
541 "Request canceled");
542 return NULL;
543 }
Richard Hughes747f5702019-08-06 14:27:26 +0100544 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100545 return g_object_ref (dev);
546}
547
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500548static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600549fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500550 FwupdDevice *device,
551 FuUtilPrivate *priv)
552{
553 g_autofree gchar *str = NULL;
554
Richard Hughes809abea2019-03-23 11:06:18 +0000555 /* allowed to set whenever the device has changed */
556 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
557 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
558 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
559 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
560
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500561 /* same as last time, so ignore */
562 if (priv->current_device != NULL &&
563 fwupd_device_compare (priv->current_device, device) == 0)
564 return;
565
566 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600567 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
568 /* TRANSLATORS: %1 is a device name */
569 str = g_strdup_printf (_("Updating %s…"),
570 fwupd_device_get_name (device));
571 fu_progressbar_set_title (priv->progressbar, str);
572 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
573 /* TRANSLATORS: %1 is a device name */
574 str = g_strdup_printf (_("Installing on %s…"),
575 fwupd_device_get_name (device));
576 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000577 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
578 /* TRANSLATORS: %1 is a device name */
579 str = g_strdup_printf (_("Reading from %s…"),
580 fwupd_device_get_name (device));
581 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600582 } else {
583 g_warning ("no FuUtilOperation set");
584 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500585 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600586
Mario Limonciello32241f42019-01-24 10:12:41 -0600587 if (priv->current_message == NULL) {
588 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
589 if (tmp != NULL)
590 priv->current_message = g_strdup (tmp);
591 }
592}
593
594static void
595fu_util_display_current_message (FuUtilPrivate *priv)
596{
597 if (priv->current_message == NULL)
598 return;
599 g_print ("%s\n", priv->current_message);
600 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500601}
602
Richard Hughes98ca9932018-05-18 10:24:07 +0100603static gboolean
604fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
605{
606 g_autoptr(FuDevice) device = NULL;
607 g_autoptr(GBytes) blob_fw = NULL;
608
609 /* invalid args */
610 if (g_strv_length (values) == 0) {
611 g_set_error_literal (error,
612 FWUPD_ERROR,
613 FWUPD_ERROR_INVALID_ARGS,
614 "Invalid arguments");
615 return FALSE;
616 }
617
618 /* parse blob */
619 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500620 if (blob_fw == NULL) {
621 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100622 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500623 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100624
625 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000626 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100627 return FALSE;
628
629 /* get device */
630 if (g_strv_length (values) >= 2) {
631 device = fu_engine_get_device (priv->engine, values[1], error);
632 if (device == NULL)
633 return FALSE;
634 } else {
635 device = fu_util_prompt_for_device (priv, error);
636 if (device == NULL)
637 return FALSE;
638 }
639
Mario Limonciello3f243a92019-01-21 22:05:23 -0600640 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500641 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600642 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500643
Richard Hughes98ca9932018-05-18 10:24:07 +0100644 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000645 if (priv->prepare_blob) {
646 g_autoptr(GPtrArray) devices = NULL;
647 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
648 g_ptr_array_add (devices, g_object_ref (device));
649 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
650 g_prefix_error (error, "failed to prepare composite action: ");
651 return FALSE;
652 }
653 }
Mario Limonciello035818b2019-03-26 11:12:16 -0500654 priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000655 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600656 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000657 if (priv->cleanup_blob) {
658 g_autoptr(FuDevice) device_new = NULL;
659 g_autoptr(GError) error_local = NULL;
660
661 /* get the possibly new device from the old ID */
662 device_new = fu_engine_get_device (priv->engine,
663 fu_device_get_id (device),
664 &error_local);
665 if (device_new == NULL) {
666 g_debug ("failed to find new device: %s",
667 error_local->message);
668 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600669 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000670 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
671 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
672 g_prefix_error (error, "failed to cleanup composite action: ");
673 return FALSE;
674 }
675 }
676 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600677
Mario Limonciello32241f42019-01-24 10:12:41 -0600678 fu_util_display_current_message (priv);
679
Mario Limonciello3f243a92019-01-21 22:05:23 -0600680 /* success */
681 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100682}
683
Richard Hughesa58510b2019-10-30 10:03:12 +0000684static gboolean
685fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
686{
687 g_autoptr(FuDevice) device = NULL;
688 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
689 g_autoptr(GBytes) blob_fw = NULL;
690
691 /* invalid args */
692 if (g_strv_length (values) == 0) {
693 g_set_error_literal (error,
694 FWUPD_ERROR,
695 FWUPD_ERROR_INVALID_ARGS,
696 "Invalid arguments");
697 return FALSE;
698 }
699
700 /* file already exists */
701 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
702 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
703 g_set_error_literal (error,
704 FWUPD_ERROR,
705 FWUPD_ERROR_INVALID_ARGS,
706 "Filename already exists");
707 return FALSE;
708 }
709
Richard Hughes02792c02019-11-01 14:21:20 +0000710 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000711 * avoid failing at the end of a potentially lengthy operation */
712 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
713 return FALSE;
714
715 /* load engine */
716 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
717 return FALSE;
718
719 /* get device */
720 if (g_strv_length (values) >= 2) {
721 device = fu_engine_get_device (priv->engine, values[1], error);
722 if (device == NULL)
723 return FALSE;
724 } else {
725 device = fu_util_prompt_for_device (priv, error);
726 if (device == NULL)
727 return FALSE;
728 }
729 priv->current_operation = FU_UTIL_OPERATION_READ;
730 g_signal_connect (priv->engine, "device-changed",
731 G_CALLBACK (fu_util_update_device_changed_cb), priv);
732
733 /* dump firmware */
734 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
735 if (blob_fw == NULL)
736 return FALSE;
737 return fu_common_set_contents_bytes (values[0], blob_fw, error);
738}
739
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100740static gint
741fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
742{
743 FuInstallTask *task1 = *((FuInstallTask **) a);
744 FuInstallTask *task2 = *((FuInstallTask **) b);
745 return fu_install_task_compare (task1, task2);
746}
747
748static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100749fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
750{
Filipe Laínse0914272019-09-20 10:04:43 +0100751 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100752 { "curl", uri, "--output", fn, NULL },
753 { NULL } };
754 for (guint i = 0; argv[i][0] != NULL; i++) {
755 g_autoptr(GError) error_local = NULL;
756 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
757 g_debug ("%s", error_local->message);
758 continue;
759 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000760 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100761 }
762 g_set_error_literal (error,
763 FWUPD_ERROR,
764 FWUPD_ERROR_NOT_FOUND,
765 "no supported out-of-process downloaders found");
766 return FALSE;
767}
768
769static gchar *
770fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
771{
772 g_autofree gchar *filename = NULL;
773 g_autoptr(SoupURI) uri = NULL;
774
775 /* a local file */
776 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000777 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
778 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100779 if (uri == NULL)
780 return g_strdup (perhapsfn);
781
782 /* download the firmware to a cachedir */
783 filename = fu_util_get_user_cache_path (perhapsfn);
784 if (!fu_common_mkdir_parent (filename, error))
785 return NULL;
786 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
787 return NULL;
788 return g_steal_pointer (&filename);
789}
790
791static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100792fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
793{
Richard Hughes3d178be2018-08-30 11:14:24 +0100794 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100795 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100796 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100797 g_autoptr(GPtrArray) devices_possible = NULL;
798 g_autoptr(GPtrArray) errors = NULL;
799 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100800 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100801
Mario Limonciello8949e892018-05-25 08:03:06 -0500802 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000803 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500804 return FALSE;
805
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100806 /* handle both forms */
807 if (g_strv_length (values) == 1) {
808 devices_possible = fu_engine_get_devices (priv->engine, error);
809 if (devices_possible == NULL)
810 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000811 fwupd_device_array_ensure_parents (devices_possible);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100812 } else if (g_strv_length (values) == 2) {
813 FuDevice *device = fu_engine_get_device (priv->engine,
814 values[1],
815 error);
816 if (device == NULL)
817 return FALSE;
818 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
819 g_ptr_array_add (devices_possible, device);
820 } else {
821 g_set_error_literal (error,
822 FWUPD_ERROR,
823 FWUPD_ERROR_INVALID_ARGS,
824 "Invalid arguments");
825 return FALSE;
826 }
827
Richard Hughes3d178be2018-08-30 11:14:24 +0100828 /* download if required */
829 filename = fu_util_download_if_required (priv, values[0], error);
830 if (filename == NULL)
831 return FALSE;
832
Richard Hughes481aa2a2018-09-18 20:51:46 +0100833 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100834 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500835 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100836 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100837 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500838 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100839 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
840 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100841 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600842 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100843 if (components == NULL)
844 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100845
Richard Hughes481aa2a2018-09-18 20:51:46 +0100846 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100847 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
848 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100849 for (guint i = 0; i < components->len; i++) {
850 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100851
852 /* do any devices pass the requirements */
853 for (guint j = 0; j < devices_possible->len; j++) {
854 FuDevice *device = g_ptr_array_index (devices_possible, j);
855 g_autoptr(FuInstallTask) task = NULL;
856 g_autoptr(GError) error_local = NULL;
857
858 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100859 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100860 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100861 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100862 &error_local)) {
863 g_debug ("requirement on %s:%s failed: %s",
864 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100865 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100866 error_local->message);
867 g_ptr_array_add (errors, g_steal_pointer (&error_local));
868 continue;
869 }
870
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600871 /* if component should have an update message from CAB */
872 fu_device_incorporate_from_component (device, component);
873
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100874 /* success */
875 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
876 }
877 }
878
879 /* order the install tasks by the device priority */
880 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
881
882 /* nothing suitable */
883 if (install_tasks->len == 0) {
884 GError *error_tmp = fu_common_error_array_get_best (errors);
885 g_propagate_error (error, error_tmp);
886 return FALSE;
887 }
888
Mario Limonciello3f243a92019-01-21 22:05:23 -0600889 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500890 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600891 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500892
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100893 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100894 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
895 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100896
Mario Limonciello32241f42019-01-24 10:12:41 -0600897 fu_util_display_current_message (priv);
898
Mario Limonciello3f243a92019-01-21 22:05:23 -0600899 /* we don't want to ask anything */
900 if (priv->no_reboot_check) {
901 g_debug ("skipping reboot check");
902 return TRUE;
903 }
904
Mario Limonciello3143bad2019-02-27 07:31:00 -0600905 /* save the device state for other applications to see */
906 if (!fu_util_save_current_state (priv, error))
907 return FALSE;
908
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100909 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600910 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100911}
912
Richard Hughes98ca9932018-05-18 10:24:07 +0100913static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500914fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600915{
Mario Limonciellofd734852019-08-01 16:41:42 -0500916 FwupdRemote *remote;
917 const gchar *remote_id;
918 const gchar *uri_tmp;
919 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600920
Mario Limonciellofd734852019-08-01 16:41:42 -0500921 uri_tmp = fwupd_release_get_uri (rel);
922 if (uri_tmp == NULL) {
923 g_set_error_literal (error,
924 FWUPD_ERROR,
925 FWUPD_ERROR_INVALID_FILE,
926 "release missing URI");
927 return FALSE;
928 }
929 remote_id = fwupd_release_get_remote_id (rel);
930 if (remote_id == NULL) {
931 g_set_error (error,
932 FWUPD_ERROR,
933 FWUPD_ERROR_INVALID_FILE,
934 "failed to find remote for %s",
935 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +0100936 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -0500937 }
938
939 remote = fu_engine_get_remote_by_id (priv->engine,
940 remote_id,
941 error);
942 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600943 return FALSE;
944
Mario Limonciellofd734852019-08-01 16:41:42 -0500945 argv = g_new0 (gchar *, 2);
946 /* local remotes have the firmware already */
947 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
948 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
949 g_autofree gchar *path = g_path_get_dirname (fn_cache);
950 argv[0] = g_build_filename (path, uri_tmp, NULL);
951 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
952 argv[0] = g_strdup (uri_tmp + 7);
953 /* web remote, fu_util_install will download file */
954 } else {
955 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
956 }
957 return fu_util_install (priv, argv, error);
958}
959
960static gboolean
961fu_util_update_all (FuUtilPrivate *priv, GError **error)
962{
963 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600964
Mario Limonciello46aaee82019-01-10 12:58:00 -0600965 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +0000966 if (devices == NULL)
967 return FALSE;
Richard Hughes0ef47202020-01-06 13:59:09 +0000968 fwupd_device_array_ensure_parents (devices);
Mario Limonciello46aaee82019-01-10 12:58:00 -0600969 for (guint i = 0; i < devices->len; i++) {
970 FwupdDevice *dev = g_ptr_array_index (devices, i);
971 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600972 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600973 g_autoptr(GPtrArray) rels = NULL;
974 g_autoptr(GError) error_local = NULL;
975
976 if (!fu_util_is_interesting_device (dev))
977 continue;
978 /* only show stuff that has metadata available */
979 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
980 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100981 if (!fu_util_filter_device (priv, dev))
982 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600983
984 device_id = fu_device_get_id (dev);
985 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
986 if (rels == NULL) {
987 g_printerr ("%s\n", error_local->message);
988 continue;
989 }
990
Mario Limonciello98b95162019-10-30 09:20:43 -0500991 if (!priv->no_safety_check) {
992 if (!fu_util_prompt_warning (dev,
993 fu_util_get_tree_title (priv),
994 error))
995 return FALSE;
996 }
997
Mario Limonciello46aaee82019-01-10 12:58:00 -0600998 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -0500999 if (!fu_util_install_release (priv, rel, &error_local)) {
1000 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +01001001 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001002 }
Mario Limonciellofd734852019-08-01 16:41:42 -05001003 fu_util_display_current_message (priv);
1004 }
1005 return TRUE;
1006}
1007
1008static gboolean
1009fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
1010{
1011 FwupdRelease *rel;
1012 g_autoptr(FuDevice) dev = NULL;
1013 g_autoptr(GPtrArray) rels = NULL;
1014
1015 /* do not allow a partial device-id */
1016 dev = fu_engine_get_device (priv->engine, device_id, error);
1017 if (dev == NULL)
1018 return FALSE;
1019
1020 /* get the releases for this device and filter for validity */
1021 rels = fu_engine_get_upgrades (priv->engine, device_id, error);
1022 if (rels == NULL)
1023 return FALSE;
1024 rel = g_ptr_array_index (rels, 0);
1025 if (!fu_util_install_release (priv, rel, error))
1026 return FALSE;
1027 fu_util_display_current_message (priv);
1028
1029 return TRUE;
1030}
1031
1032static gboolean
1033fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1034{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001035 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1036 g_set_error_literal (error,
1037 FWUPD_ERROR,
1038 FWUPD_ERROR_INVALID_ARGS,
1039 "--allow-older is not supported for this command");
1040 return FALSE;
1041 }
1042
1043 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1044 g_set_error_literal (error,
1045 FWUPD_ERROR,
1046 FWUPD_ERROR_INVALID_ARGS,
1047 "--allow-reinstall is not supported for this command");
1048 return FALSE;
1049 }
1050
Mario Limonciellofd734852019-08-01 16:41:42 -05001051 if (g_strv_length (values) > 1) {
1052 g_set_error_literal (error,
1053 FWUPD_ERROR,
1054 FWUPD_ERROR_INVALID_ARGS,
1055 "Invalid arguments");
1056 return FALSE;
1057 }
1058
1059 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1060 return FALSE;
1061
1062 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1063 g_signal_connect (priv->engine, "device-changed",
1064 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1065
1066 if (g_strv_length (values) == 1) {
1067 if (!fu_util_update_by_id (priv, values[0], error))
1068 return FALSE;
1069 } else {
1070 if (!fu_util_update_all (priv, error))
1071 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001072 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001073
1074 /* we don't want to ask anything */
1075 if (priv->no_reboot_check) {
1076 g_debug ("skipping reboot check");
1077 return TRUE;
1078 }
1079
Mario Limonciello3143bad2019-02-27 07:31:00 -06001080 /* save the device state for other applications to see */
1081 if (!fu_util_save_current_state (priv, error))
1082 return FALSE;
1083
Mario Limonciello3f243a92019-01-21 22:05:23 -06001084 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001085}
1086
1087static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001088fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1089{
1090 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001091 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001092
1093 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001094 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001095 return FALSE;
1096
Richard Hughes98ca9932018-05-18 10:24:07 +01001097 /* get device */
1098 if (g_strv_length (values) >= 1) {
1099 device = fu_engine_get_device (priv->engine, values[0], error);
1100 if (device == NULL)
1101 return FALSE;
1102 } else {
1103 device = fu_util_prompt_for_device (priv, error);
1104 if (device == NULL)
1105 return FALSE;
1106 }
1107
1108 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001109 locker = fu_device_locker_new (device, error);
1110 if (locker == NULL)
1111 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001112 return fu_device_detach (device, error);
1113}
1114
1115static gboolean
1116fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1117{
1118 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001119 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001120
1121 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001122 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001123 return FALSE;
1124
Richard Hughes98ca9932018-05-18 10:24:07 +01001125 /* get device */
1126 if (g_strv_length (values) >= 1) {
1127 device = fu_engine_get_device (priv->engine, values[0], error);
1128 if (device == NULL)
1129 return FALSE;
1130 } else {
1131 device = fu_util_prompt_for_device (priv, error);
1132 if (device == NULL)
1133 return FALSE;
1134 }
1135
1136 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001137 locker = fu_device_locker_new (device, error);
1138 if (locker == NULL)
1139 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001140 return fu_device_attach (device, error);
1141}
1142
Richard Hughes1d894f12018-08-31 13:05:51 +01001143static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001144fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1145{
1146 gboolean has_pending = FALSE;
1147 g_autoptr(FuHistory) history = fu_history_new ();
1148 g_autoptr(GPtrArray) devices = NULL;
1149
1150 /* check the history database before starting the daemon */
1151 if (g_strv_length (values) == 0) {
1152 devices = fu_history_get_devices (history, error);
1153 if (devices == NULL)
1154 return FALSE;
1155 } else if (g_strv_length (values) == 1) {
1156 FuDevice *device;
1157 device = fu_history_get_device_by_id (history, values[0], error);
1158 if (device == NULL)
1159 return FALSE;
1160 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1161 g_ptr_array_add (devices, device);
1162 } else {
1163 g_set_error_literal (error,
1164 FWUPD_ERROR,
1165 FWUPD_ERROR_INVALID_ARGS,
1166 "Invalid arguments");
1167 return FALSE;
1168 }
1169
1170 /* nothing to do */
1171 for (guint i = 0; i < devices->len; i++) {
1172 FuDevice *dev = g_ptr_array_index (devices, i);
1173 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1174 fu_engine_add_plugin_filter (priv->engine,
1175 fu_device_get_plugin (dev));
1176 has_pending = TRUE;
1177 }
1178 }
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001179
dkadioglu1db7e982019-12-04 21:13:21 +01001180 if (!has_pending) {
1181 g_printerr ("No firmware to activate\n");
1182 return TRUE;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001183 }
1184
1185 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001186 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001187 return FALSE;
1188
1189 /* activate anything with _NEEDS_ACTIVATION */
1190 for (guint i = 0; i < devices->len; i++) {
1191 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001192 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1193 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001194 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1195 continue;
1196 /* TRANSLATORS: shown when shutting down to switch to the new version */
1197 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1198 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1199 return FALSE;
1200 }
1201
1202 return TRUE;
1203}
1204
1205static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001206fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1207{
1208 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1209 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1210 const gchar *hwid_keys[] = {
1211 FU_HWIDS_KEY_BIOS_VENDOR,
1212 FU_HWIDS_KEY_BIOS_VERSION,
1213 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1214 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1215 FU_HWIDS_KEY_MANUFACTURER,
1216 FU_HWIDS_KEY_FAMILY,
1217 FU_HWIDS_KEY_PRODUCT_NAME,
1218 FU_HWIDS_KEY_PRODUCT_SKU,
1219 FU_HWIDS_KEY_ENCLOSURE_KIND,
1220 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1221 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1222 NULL };
1223
1224 /* read DMI data */
1225 if (g_strv_length (values) == 0) {
1226 if (!fu_smbios_setup (smbios, error))
1227 return FALSE;
1228 } else if (g_strv_length (values) == 1) {
1229 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1230 return FALSE;
1231 } else {
1232 g_set_error_literal (error,
1233 FWUPD_ERROR,
1234 FWUPD_ERROR_INVALID_ARGS,
1235 "Invalid arguments");
1236 return FALSE;
1237 }
1238 if (!fu_hwids_setup (hwids, smbios, error))
1239 return FALSE;
1240
1241 /* show debug output */
1242 g_print ("Computer Information\n");
1243 g_print ("--------------------\n");
1244 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1245 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1246 if (tmp == NULL)
1247 continue;
1248 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1249 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1250 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1251 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1252 } else {
1253 g_print ("%s: %s\n", hwid_keys[i], tmp);
1254 }
1255 }
1256
1257 /* show GUIDs */
1258 g_print ("\nHardware IDs\n");
1259 g_print ("------------\n");
1260 for (guint i = 0; i < 15; i++) {
1261 const gchar *keys = NULL;
1262 g_autofree gchar *guid = NULL;
1263 g_autofree gchar *key = NULL;
1264 g_autofree gchar *keys_str = NULL;
1265 g_auto(GStrv) keysv = NULL;
1266 g_autoptr(GError) error_local = NULL;
1267
1268 /* get the GUID */
1269 key = g_strdup_printf ("HardwareID-%u", i);
1270 keys = fu_hwids_get_replace_keys (hwids, key);
1271 guid = fu_hwids_get_guid (hwids, key, &error_local);
1272 if (guid == NULL) {
1273 g_print ("%s\n", error_local->message);
1274 continue;
1275 }
1276
1277 /* show what makes up the GUID */
1278 keysv = g_strsplit (keys, "&", -1);
1279 keys_str = g_strjoinv (" + ", keysv);
1280 g_print ("{%s} <- %s\n", guid, keys_str);
1281 }
1282
1283 return TRUE;
1284}
1285
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001286static gboolean
1287fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1288{
1289 const gchar *script_fn = "startup.sh";
1290 const gchar *output_fn = "firmware.bin";
1291 g_autoptr(GBytes) archive_blob = NULL;
1292 g_autoptr(GBytes) firmware_blob = NULL;
1293 if (g_strv_length (values) < 2) {
1294 g_set_error_literal (error,
1295 FWUPD_ERROR,
1296 FWUPD_ERROR_INVALID_ARGS,
1297 "Invalid arguments");
1298 return FALSE;
1299 }
1300 archive_blob = fu_common_get_contents_bytes (values[0], error);
1301 if (archive_blob == NULL)
1302 return FALSE;
1303 if (g_strv_length (values) > 2)
1304 script_fn = values[2];
1305 if (g_strv_length (values) > 3)
1306 output_fn = values[3];
1307 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1308 if (firmware_blob == NULL)
1309 return FALSE;
1310 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1311}
1312
Richard Hughes3d607622019-03-07 16:59:27 +00001313static gboolean
1314fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1315{
1316 g_autofree gchar *sig = NULL;
1317
1318 /* check args */
1319 if (g_strv_length (values) != 1) {
1320 g_set_error_literal (error,
1321 FWUPD_ERROR,
1322 FWUPD_ERROR_INVALID_ARGS,
1323 "Invalid arguments: value expected");
1324 return FALSE;
1325 }
1326
1327 /* start engine */
1328 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1329 return FALSE;
1330 sig = fu_engine_self_sign (priv->engine, values[0],
1331 FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
1332 FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
1333 if (sig == NULL)
1334 return FALSE;
1335 g_print ("%s\n", sig);
1336 return TRUE;
1337}
1338
Mario Limonciello62f84862018-10-18 13:15:23 -05001339static void
1340fu_util_device_added_cb (FwupdClient *client,
1341 FwupdDevice *device,
1342 gpointer user_data)
1343{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001344 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001345 /* TRANSLATORS: this is when a device is hotplugged */
1346 g_print ("%s\n%s", _("Device added:"), tmp);
1347}
1348
1349static void
1350fu_util_device_removed_cb (FwupdClient *client,
1351 FwupdDevice *device,
1352 gpointer user_data)
1353{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001354 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001355 /* TRANSLATORS: this is when a device is hotplugged */
1356 g_print ("%s\n%s", _("Device removed:"), tmp);
1357}
1358
1359static void
1360fu_util_device_changed_cb (FwupdClient *client,
1361 FwupdDevice *device,
1362 gpointer user_data)
1363{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001364 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001365 /* TRANSLATORS: this is when a device has been updated */
1366 g_print ("%s\n%s", _("Device changed:"), tmp);
1367}
1368
1369static void
1370fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1371{
1372 /* TRANSLATORS: this is when the daemon state changes */
1373 g_print ("%s\n", _("Changed"));
1374}
1375
1376static gboolean
1377fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1378{
1379 g_autoptr(FwupdClient) client = fwupd_client_new ();
1380
1381 /* get all the devices */
1382 if (!fwupd_client_connect (client, priv->cancellable, error))
1383 return FALSE;
1384
1385 /* watch for any hotplugged device */
1386 g_signal_connect (client, "changed",
1387 G_CALLBACK (fu_util_changed_cb), priv);
1388 g_signal_connect (client, "device-added",
1389 G_CALLBACK (fu_util_device_added_cb), priv);
1390 g_signal_connect (client, "device-removed",
1391 G_CALLBACK (fu_util_device_removed_cb), priv);
1392 g_signal_connect (client, "device-changed",
1393 G_CALLBACK (fu_util_device_changed_cb), priv);
1394 g_signal_connect (priv->cancellable, "cancelled",
1395 G_CALLBACK (fu_util_cancelled_cb), priv);
1396 g_main_loop_run (priv->loop);
1397 return TRUE;
1398}
1399
Richard Hughes15684492019-03-15 16:27:50 +00001400static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001401fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1402{
1403 g_autoptr(GPtrArray) firmware_types = NULL;
1404
1405 /* load engine */
1406 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1407 return FALSE;
1408
1409 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1410 for (guint i = 0; i < firmware_types->len; i++) {
1411 const gchar *id = g_ptr_array_index (firmware_types, i);
1412 g_print ("%s\n", id);
1413 }
1414 if (firmware_types->len == 0) {
1415 /* TRANSLATORS: nothing found */
1416 g_print ("%s\n", _("No firmware IDs found"));
1417 return TRUE;
1418 }
1419
1420 return TRUE;
1421}
1422
1423static gchar *
1424fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1425{
1426 g_autoptr(GPtrArray) firmware_types = NULL;
1427 guint idx;
1428 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1429
1430 /* TRANSLATORS: get interactive prompt */
1431 g_print ("%s\n", _("Choose a firmware type:"));
1432 /* TRANSLATORS: this is to abort the interactive prompt */
1433 g_print ("0.\t%s\n", _("Cancel"));
1434 for (guint i = 0; i < firmware_types->len; i++) {
1435 const gchar *id = g_ptr_array_index (firmware_types, i);
1436 g_print ("%u.\t%s\n", i + 1, id);
1437 }
1438 idx = fu_util_prompt_for_number (firmware_types->len);
1439 if (idx == 0) {
1440 g_set_error_literal (error,
1441 FWUPD_ERROR,
1442 FWUPD_ERROR_NOTHING_TO_DO,
1443 "Request canceled");
1444 return NULL;
1445 }
1446
1447 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1448}
1449
1450static gboolean
1451fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1452{
1453 GType gtype;
1454 g_autoptr(GBytes) blob = NULL;
1455 g_autoptr(FuFirmware) firmware = NULL;
1456 g_autofree gchar *firmware_type = NULL;
1457 g_autofree gchar *str = NULL;
1458
1459 /* check args */
1460 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1461 g_set_error_literal (error,
1462 FWUPD_ERROR,
1463 FWUPD_ERROR_INVALID_ARGS,
1464 "Invalid arguments: filename required");
1465 return FALSE;
1466 }
1467
1468 if (g_strv_length (values) == 2)
1469 firmware_type = g_strdup (values[1]);
1470
1471 /* load file */
1472 blob = fu_common_get_contents_bytes (values[0], error);
1473 if (blob == NULL)
1474 return FALSE;
1475
1476 /* load engine */
1477 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1478 return FALSE;
1479
1480 /* find the GType to use */
1481 if (firmware_type == NULL)
1482 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1483 if (firmware_type == NULL)
1484 return FALSE;
1485 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1486 if (gtype == G_TYPE_INVALID) {
1487 g_set_error (error,
1488 G_IO_ERROR,
1489 G_IO_ERROR_NOT_FOUND,
1490 "GType %s not supported", firmware_type);
1491 return FALSE;
1492 }
1493 firmware = g_object_new (gtype, NULL);
1494 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1495 return FALSE;
1496 str = fu_firmware_to_string (firmware);
1497 g_print ("%s", str);
1498 return TRUE;
1499}
1500
1501static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001502fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1503{
1504 g_autofree gchar *str = NULL;
1505 g_autoptr(FuDevice) dev = NULL;
1506
1507 /* load engine */
1508 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1509 return FALSE;
1510
1511 /* get device */
1512 if (g_strv_length (values) == 1) {
1513 dev = fu_engine_get_device (priv->engine, values[1], error);
1514 if (dev == NULL)
1515 return FALSE;
1516 } else {
1517 dev = fu_util_prompt_for_device (priv, error);
1518 if (dev == NULL)
1519 return FALSE;
1520 }
1521
1522 /* add checksums */
1523 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1524 return FALSE;
1525
1526 /* show checksums */
1527 str = fu_device_to_string (dev);
1528 g_print ("%s\n", str);
1529 return TRUE;
1530}
1531
Mario Limonciellofe593942019-04-03 13:48:52 -05001532static gboolean
1533fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1534{
1535 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001536 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001537 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001538
1539 /* load engine */
1540 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1541 return FALSE;
1542
1543 /* get all devices from the history database */
1544 devices = fu_engine_get_history (priv->engine, error);
1545 if (devices == NULL)
1546 return FALSE;
1547
1548 /* show each device */
1549 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001550 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001551 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001552 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001553 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001554 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001555
Richard Hughes747f5702019-08-06 14:27:26 +01001556 if (!fu_util_filter_device (priv, dev))
1557 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001558 child = g_node_append_data (root, dev);
1559
1560 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001561 if (rel == NULL)
1562 continue;
1563 remote = fwupd_release_get_remote_id (rel);
1564
1565 /* doesn't actually map to remote */
1566 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001567 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001568 continue;
1569 }
1570
1571 /* try to lookup releases from client */
1572 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1573 if (rels == NULL)
1574 return FALSE;
1575
1576 /* map to a release in client */
1577 for (guint j = 0; j < rels->len; j++) {
1578 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1579 if (g_strcmp0 (remote,
1580 fwupd_release_get_remote_id (rel2)) != 0)
1581 continue;
1582 if (g_strcmp0 (fwupd_release_get_version (rel),
1583 fwupd_release_get_version (rel2)) != 0)
1584 continue;
1585 g_node_append_data (child, g_object_ref (rel2));
1586 rel = NULL;
1587 break;
1588 }
1589
1590 /* didn't match anything */
1591 if (rels->len == 0 || rel != NULL) {
1592 g_node_append_data (child, rel);
1593 continue;
1594 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001595
Mario Limonciellofe593942019-04-03 13:48:52 -05001596 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001597 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001598
1599 return TRUE;
1600}
1601
Richard Hughesfd7e9942020-01-17 14:09:13 +00001602static gboolean
1603fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
1604{
1605 g_autoptr(GNode) root = g_node_new (NULL);
1606 g_autoptr(GPtrArray) remotes = NULL;
1607 g_autofree gchar *title = fu_util_get_tree_title (priv);
1608
1609 /* load engine */
1610 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1611 return FALSE;
1612
1613 /* list remotes */
1614 remotes = fu_engine_get_remotes (priv->engine, error);
1615 if (remotes == NULL)
1616 return FALSE;
1617 if (remotes->len == 0) {
1618 g_set_error_literal (error,
1619 FWUPD_ERROR,
1620 FWUPD_ERROR_NOTHING_TO_DO,
1621 "no remotes available");
1622 return FALSE;
1623 }
1624 for (guint i = 0; i < remotes->len; i++) {
1625 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
1626 g_node_append_data (root, remote_tmp);
1627 }
1628 fu_util_print_tree (root, title);
1629
1630 return TRUE;
1631}
1632
Richard Hughesb5976832018-05-18 10:02:09 +01001633int
1634main (int argc, char *argv[])
1635{
Richard Hughes460226a2018-05-21 20:56:21 +01001636 gboolean allow_older = FALSE;
1637 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001638 gboolean force = FALSE;
1639 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001640 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001641 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001642 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001643 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1644 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001645 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001646 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001647 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001648 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001649 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1650 /* TRANSLATORS: command line option */
1651 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001652 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1653 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001654 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001655 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1656 /* TRANSLATORS: command line option */
1657 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001658 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1659 /* TRANSLATORS: command line option */
1660 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06001661 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
1662 /* TRANSLATORS: command line option */
1663 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05001664 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
1665 /* TRANSLATORS: command line option */
1666 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001667 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1668 /* TRANSLATORS: command line option */
1669 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001670 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1671 /* TRANSLATORS: command line option */
1672 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001673 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001674 /* TRANSLATORS: command line option */
1675 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001676 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001677 /* TRANSLATORS: command line option */
1678 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06001679 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
1680 /* TRANSLATORS: command line option */
1681 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01001682 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
1683 /* TRANSLATORS: command line option */
1684 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01001685 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
1686 /* TRANSLATORS: command line option */
1687 _("Filter with a set of device flags using a ~ prefix to "
1688 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001689 { NULL}
1690 };
1691
Richard Hughes429f72b2020-01-16 12:18:19 +00001692#ifdef _WIN32
1693 /* workaround Windows setting the codepage to 1252 */
1694 g_setenv ("LANG", "C.UTF-8", FALSE);
1695#endif
1696
Richard Hughesb5976832018-05-18 10:02:09 +01001697 setlocale (LC_ALL, "");
1698
Richard Hughes668ee212019-11-22 09:17:46 +00001699 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01001700 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1701 textdomain (GETTEXT_PACKAGE);
1702
Richard Hughes01c0bad2019-11-22 09:08:51 +00001703#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01001704 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001705 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01001706 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001707 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00001708#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001709
1710 /* create helper object */
1711 priv->loop = g_main_loop_new (NULL, FALSE);
1712 priv->progressbar = fu_progressbar_new ();
1713
1714 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00001715 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001716 "build-firmware",
1717 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1718 /* TRANSLATORS: command description */
1719 _("Build firmware using a sandbox"),
1720 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00001721 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001722 "smbios-dump",
1723 "FILE",
1724 /* TRANSLATORS: command description */
1725 _("Dump SMBIOS data from a file"),
1726 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00001727 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001728 "get-plugins",
1729 NULL,
1730 /* TRANSLATORS: command description */
1731 _("Get all enabled plugins registered with the system"),
1732 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00001733 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001734 "get-details",
1735 NULL,
1736 /* TRANSLATORS: command description */
1737 _("Gets details about a firmware file"),
1738 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00001739 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05001740 "get-history",
1741 NULL,
1742 /* TRANSLATORS: command description */
1743 _("Show history of firmware updates"),
1744 fu_util_get_history);
1745 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001746 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06001747 NULL,
1748 /* TRANSLATORS: command description */
1749 _("Gets the list of updates for connected hardware"),
1750 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00001751 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01001752 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01001753 NULL,
1754 /* TRANSLATORS: command description */
1755 _("Get all devices that support firmware updates"),
1756 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00001757 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01001758 "get-device-flags",
1759 NULL,
1760 /* TRANSLATORS: command description */
1761 _("Get all device flags supported by fwupd"),
1762 fu_util_get_device_flags);
1763 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001764 "watch",
1765 NULL,
1766 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02001767 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001768 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00001769 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001770 "install-blob",
1771 "FILENAME DEVICE-ID",
1772 /* TRANSLATORS: command description */
1773 _("Install a firmware blob on a device"),
1774 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00001775 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001776 "install",
Mario Limonciello234c8642020-01-08 20:07:29 -06001777 "FILE [DEVICE-ID]",
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001778 /* TRANSLATORS: command description */
1779 _("Install a firmware file on this hardware"),
1780 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00001781 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001782 "attach",
1783 "DEVICE-ID",
1784 /* TRANSLATORS: command description */
1785 _("Attach to firmware mode"),
1786 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001787 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001788 "detach",
1789 "DEVICE-ID",
1790 /* TRANSLATORS: command description */
1791 _("Detach to bootloader mode"),
1792 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001793 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001794 "activate",
1795 "[DEVICE-ID]",
1796 /* TRANSLATORS: command description */
1797 _("Activate pending devices"),
1798 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00001799 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01001800 "hwids",
1801 "[FILE]",
1802 /* TRANSLATORS: command description */
1803 _("Return all the hardware IDs for the machine"),
1804 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00001805 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05001806 "monitor",
1807 NULL,
1808 /* TRANSLATORS: command description */
1809 _("Monitor the daemon for events"),
1810 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00001811 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001812 "update,upgrade",
Mario Limonciello46aaee82019-01-10 12:58:00 -06001813 NULL,
1814 /* TRANSLATORS: command description */
1815 _("Update all devices that match local metadata"),
1816 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00001817 fu_util_cmd_array_add (cmd_array,
1818 "self-sign",
1819 "TEXT",
1820 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00001821 C_("command-description",
1822 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00001823 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00001824 fu_util_cmd_array_add (cmd_array,
1825 "verify-update",
Mario Limonciello234c8642020-01-08 20:07:29 -06001826 "[DEVICE-ID]",
Richard Hughes15684492019-03-15 16:27:50 +00001827 /* TRANSLATORS: command description */
1828 _("Update the stored metadata with current contents"),
1829 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01001830 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00001831 "firmware-read",
1832 "FILENAME [DEVICE-ID]",
1833 /* TRANSLATORS: command description */
1834 _("Read a firmware blob from a device"),
1835 fu_util_firmware_read);
1836 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01001837 "firmware-parse",
Mario Limonciello234c8642020-01-08 20:07:29 -06001838 "FILENAME [FIRMWARE-TYPE]",
Richard Hughes95c98a92019-10-22 16:03:15 +01001839 /* TRANSLATORS: command description */
1840 _("Parse and show details about a firmware file"),
1841 fu_util_firmware_parse);
1842 fu_util_cmd_array_add (cmd_array,
1843 "get-firmware-types",
1844 NULL,
1845 /* TRANSLATORS: command description */
1846 _("List the available firmware types"),
1847 fu_util_get_firmware_types);
Richard Hughesfd7e9942020-01-17 14:09:13 +00001848 fu_util_cmd_array_add (cmd_array,
1849 "get-remotes",
1850 NULL,
1851 /* TRANSLATORS: command description */
1852 _("Gets the configured remotes"),
1853 fu_util_get_remotes);
Richard Hughesb5976832018-05-18 10:02:09 +01001854
1855 /* do stuff on ctrl+c */
1856 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00001857#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01001858 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1859 SIGINT, fu_util_sigint_cb,
1860 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00001861#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001862 g_signal_connect (priv->cancellable, "cancelled",
1863 G_CALLBACK (fu_util_cancelled_cb), priv);
1864
1865 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00001866 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001867
Mario Limonciello3f243a92019-01-21 22:05:23 -06001868 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001869 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06001870 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05001871 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06001872 fu_progressbar_set_interactive (priv->progressbar, FALSE);
1873 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001874
Richard Hughesb5976832018-05-18 10:02:09 +01001875 /* get a list of the commands */
1876 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00001877 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001878 g_option_context_set_summary (priv->context, cmd_descriptions);
1879 g_option_context_set_description (priv->context,
1880 "This tool allows an administrator to use the fwupd plugins "
1881 "without being installed on the host system.");
1882
1883 /* TRANSLATORS: program name */
1884 g_set_application_name (_("Firmware Utility"));
1885 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05001886 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01001887 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
1888 if (!ret) {
1889 /* TRANSLATORS: the user didn't read the man page */
1890 g_print ("%s: %s\n", _("Failed to parse arguments"),
1891 error->message);
1892 return EXIT_FAILURE;
1893 }
1894
Richard Hughes0e46b222019-09-05 12:13:35 +01001895 /* allow disabling SSL strict mode for broken corporate proxies */
1896 if (priv->disable_ssl_strict) {
1897 /* TRANSLATORS: try to help */
1898 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
1899 "to do this automatically in the future "
1900 "export DISABLE_SSL_STRICT in your environment"));
1901 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
1902 }
1903
Richard Hughes747f5702019-08-06 14:27:26 +01001904 /* parse filter flags */
1905 if (filter != NULL) {
1906 if (!fu_util_parse_filter_flags (filter,
1907 &priv->filter_include,
1908 &priv->filter_exclude,
1909 &error)) {
1910 /* TRANSLATORS: the user didn't read the man page */
1911 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
1912 error->message);
1913 return EXIT_FAILURE;
1914 }
1915 }
1916
1917
Richard Hughes460226a2018-05-21 20:56:21 +01001918 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01001919 if (allow_reinstall)
1920 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1921 if (allow_older)
1922 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
1923 if (force)
1924 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
1925
Richard Hughes98ca9932018-05-18 10:24:07 +01001926 /* load engine */
1927 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
1928 g_signal_connect (priv->engine, "device-added",
1929 G_CALLBACK (fu_main_engine_device_added_cb),
1930 priv);
1931 g_signal_connect (priv->engine, "device-removed",
1932 G_CALLBACK (fu_main_engine_device_removed_cb),
1933 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01001934 g_signal_connect (priv->engine, "status-changed",
1935 G_CALLBACK (fu_main_engine_status_changed_cb),
1936 priv);
1937 g_signal_connect (priv->engine, "percentage-changed",
1938 G_CALLBACK (fu_main_engine_percentage_changed_cb),
1939 priv);
1940
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001941 /* just show versions and exit */
1942 if (version) {
1943 g_autofree gchar *version_str = fu_util_get_versions ();
1944 g_print ("%s\n", version_str);
1945 return EXIT_SUCCESS;
1946 }
1947
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001948 /* any plugin whitelist specified */
1949 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
1950 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
1951
Richard Hughesb5976832018-05-18 10:02:09 +01001952 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00001953 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01001954 if (!ret) {
1955 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
1956 g_autofree gchar *tmp = NULL;
1957 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
1958 g_print ("%s\n\n%s", error->message, tmp);
1959 return EXIT_FAILURE;
1960 }
1961 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
1962 g_print ("%s\n", error->message);
1963 return EXIT_NOTHING_TO_DO;
1964 }
1965 g_print ("%s\n", error->message);
1966 return EXIT_FAILURE;
1967 }
1968
1969 /* success */
1970 return EXIT_SUCCESS;
1971}