blob: 9bf54ada61da067b099f7ac14e6f17db222af67d [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>
14#include <glib-unix.h>
15#include <locale.h>
16#include <stdlib.h>
17#include <unistd.h>
Richard Hughes3d178be2018-08-30 11:14:24 +010018#include <libsoup/soup.h>
Richard Hughesb5976832018-05-18 10:02:09 +010019
Mario Limonciello7a3df4b2019-01-31 10:27:22 -060020#include "fu-device-private.h"
Richard Hughes98ca9932018-05-18 10:24:07 +010021#include "fu-engine.h"
Mario Limonciello96a0dd52019-02-25 13:50:03 -060022#include "fu-history.h"
Richard Hughes8c71a3f2018-05-22 19:19:52 +010023#include "fu-plugin-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010024#include "fu-progressbar.h"
25#include "fu-smbios.h"
26#include "fu-util-common.h"
Mario Limonciellofde47732018-09-11 12:20:58 -050027#include "fu-debug.h"
Mario Limonciello1e35e4c2019-01-28 11:13:02 -060028#include "fwupd-common-private.h"
Mario Limonciello3143bad2019-02-27 07:31:00 -060029#include "fwupd-device-private.h"
Richard Hughesb5976832018-05-18 10:02:09 +010030
Richard Hughes3d005222019-05-17 14:02:41 +010031#ifdef HAVE_SYSTEMD
32#include "fu-systemd.h"
33#endif
34
Richard Hughesb5976832018-05-18 10:02:09 +010035/* custom return code */
36#define EXIT_NOTHING_TO_DO 2
37
Mario Limonciello3f243a92019-01-21 22:05:23 -060038typedef enum {
39 FU_UTIL_OPERATION_UNKNOWN,
40 FU_UTIL_OPERATION_UPDATE,
41 FU_UTIL_OPERATION_INSTALL,
Richard Hughesa58510b2019-10-30 10:03:12 +000042 FU_UTIL_OPERATION_READ,
Mario Limonciello3f243a92019-01-21 22:05:23 -060043 FU_UTIL_OPERATION_LAST
44} FuUtilOperation;
45
Richard Hughesc77e1112019-03-01 10:16:26 +000046struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010047 GCancellable *cancellable;
48 GMainLoop *loop;
49 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010050 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010051 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060052 gboolean no_reboot_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050053 gboolean no_safety_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000054 gboolean prepare_blob;
55 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060056 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010057 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050058 gboolean show_all_devices;
Richard Hughes0e46b222019-09-05 12:13:35 +010059 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050060 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060061 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050062 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060063 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060064 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010065 FwupdDeviceFlags filter_include;
66 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000067};
Richard Hughesb5976832018-05-18 10:02:09 +010068
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050069static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060070fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
71{
72 g_autoptr(JsonBuilder) builder = NULL;
73 g_autoptr(JsonGenerator) json_generator = NULL;
74 g_autoptr(JsonNode) json_root = NULL;
75 g_autoptr(GPtrArray) devices = NULL;
76 g_autofree gchar *state = NULL;
77 g_autofree gchar *dirname = NULL;
78 g_autofree gchar *filename = NULL;
79
80 if (!priv->enable_json_state)
81 return TRUE;
82
83 devices = fu_engine_get_devices (priv->engine, error);
84 if (devices == NULL)
85 return FALSE;
86
87 /* create header */
88 builder = json_builder_new ();
89 json_builder_begin_object (builder);
90
91 /* add each device */
92 json_builder_set_member_name (builder, "Devices");
93 json_builder_begin_array (builder);
94 for (guint i = 0; i < devices->len; i++) {
95 FwupdDevice *dev = g_ptr_array_index (devices, i);
96 json_builder_begin_object (builder);
97 fwupd_device_to_json (dev, builder);
98 json_builder_end_object (builder);
99 }
100 json_builder_end_array (builder);
101 json_builder_end_object (builder);
102
103 /* export as a string */
104 json_root = json_builder_get_root (builder);
105 json_generator = json_generator_new ();
106 json_generator_set_pretty (json_generator, TRUE);
107 json_generator_set_root (json_generator, json_root);
108 state = json_generator_to_data (json_generator, NULL);
109 if (state == NULL)
110 return FALSE;
111 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
112 filename = g_build_filename (dirname, "state.json", NULL);
113 return g_file_set_contents (filename, state, -1, error);
114}
115
116static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000117fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600118{
119 g_autoptr(GError) error_local = NULL;
120
Richard Hughesd92ccca2019-05-20 11:28:31 +0100121#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100122 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500123 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100124#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000125 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000126 return FALSE;
127 if (fu_engine_get_tainted (priv->engine)) {
128 g_printerr ("WARNING: This tool has loaded 3rd party code and "
129 "is no longer supported by the upstream developers!\n");
130 }
131 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500132}
133
Richard Hughesb5976832018-05-18 10:02:09 +0100134static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500135fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
136{
137 g_autofree gchar *path = g_path_get_dirname (value);
138 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
139 g_prefix_error (error,
140 "Unable to access %s. You may need to copy %s to %s: ",
141 path, value, g_getenv ("HOME"));
142 }
143}
144
145static void
Richard Hughesb5976832018-05-18 10:02:09 +0100146fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
147{
148 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
149 /* TRANSLATORS: this is when a device ctrl+c's a watch */
150 g_print ("%s\n", _("Cancelled"));
151 g_main_loop_quit (priv->loop);
152}
153
154static gboolean
155fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
156{
157 g_autofree gchar *tmp = NULL;
158 g_autoptr(FuSmbios) smbios = NULL;
159 if (g_strv_length (values) < 1) {
160 g_set_error_literal (error,
161 FWUPD_ERROR,
162 FWUPD_ERROR_INVALID_ARGS,
163 "Invalid arguments");
164 return FALSE;
165 }
166 smbios = fu_smbios_new ();
167 if (!fu_smbios_setup_from_file (smbios, values[0], error))
168 return FALSE;
169 tmp = fu_smbios_to_string (smbios);
170 g_print ("%s\n", tmp);
171 return TRUE;
172}
173
Richard Hughesb5976832018-05-18 10:02:09 +0100174static gboolean
175fu_util_sigint_cb (gpointer user_data)
176{
177 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
178 g_debug ("Handling SIGINT");
179 g_cancellable_cancel (priv->cancellable);
180 return FALSE;
181}
182
183static void
184fu_util_private_free (FuUtilPrivate *priv)
185{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500186 if (priv->current_device != NULL)
187 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100188 if (priv->engine != NULL)
189 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100190 if (priv->loop != NULL)
191 g_main_loop_unref (priv->loop);
192 if (priv->cancellable != NULL)
193 g_object_unref (priv->cancellable);
194 if (priv->progressbar != NULL)
195 g_object_unref (priv->progressbar);
196 if (priv->context != NULL)
197 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600198 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100199 g_free (priv);
200}
201
202#pragma clang diagnostic push
203#pragma clang diagnostic ignored "-Wunused-function"
204G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
205#pragma clang diagnostic pop
206
Richard Hughes98ca9932018-05-18 10:24:07 +0100207
208static void
209fu_main_engine_device_added_cb (FuEngine *engine,
210 FuDevice *device,
211 FuUtilPrivate *priv)
212{
213 g_autofree gchar *tmp = fu_device_to_string (device);
214 g_debug ("ADDED:\n%s", tmp);
215}
216
217static void
218fu_main_engine_device_removed_cb (FuEngine *engine,
219 FuDevice *device,
220 FuUtilPrivate *priv)
221{
222 g_autofree gchar *tmp = fu_device_to_string (device);
223 g_debug ("REMOVED:\n%s", tmp);
224}
225
226static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100227fu_main_engine_status_changed_cb (FuEngine *engine,
228 FwupdStatus status,
229 FuUtilPrivate *priv)
230{
231 fu_progressbar_update (priv->progressbar, status, 0);
232}
233
234static void
235fu_main_engine_percentage_changed_cb (FuEngine *engine,
236 guint percentage,
237 FuUtilPrivate *priv)
238{
239 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
240}
241
242static gboolean
243fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
244{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000245 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100246 return FALSE;
247 g_main_loop_run (priv->loop);
248 return TRUE;
249}
250
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100251static gint
252fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
253{
254 return fu_plugin_name_compare (*item1, *item2);
255}
256
257static gboolean
258fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
259{
260 GPtrArray *plugins;
261 guint cnt = 0;
262
263 /* load engine */
264 if (!fu_engine_load_plugins (priv->engine, error))
265 return FALSE;
266
267 /* print */
268 plugins = fu_engine_get_plugins (priv->engine);
269 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
270 for (guint i = 0; i < plugins->len; i++) {
271 FuPlugin *plugin = g_ptr_array_index (plugins, i);
272 if (!fu_plugin_get_enabled (plugin))
273 continue;
274 g_print ("%s\n", fu_plugin_get_name (plugin));
275 cnt++;
276 }
277 if (cnt == 0) {
278 /* TRANSLATORS: nothing found */
279 g_print ("%s\n", _("No plugins found"));
280 return TRUE;
281 }
282
283 return TRUE;
284}
285
Richard Hughes98ca9932018-05-18 10:24:07 +0100286static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100287fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
288{
289 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
290 if (!fwupd_device_has_flag (dev, priv->filter_include))
291 return FALSE;
292 }
293 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
294 if (fwupd_device_has_flag (dev, priv->filter_exclude))
295 return FALSE;
296 }
297 return TRUE;
298}
299
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500300static gchar *
301fu_util_get_tree_title (FuUtilPrivate *priv)
302{
303 return g_strdup (fu_engine_get_host_product (priv->engine));
304}
305
Richard Hughes747f5702019-08-06 14:27:26 +0100306static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600307fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
308{
309 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500310 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500311 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600312
313 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000314 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600315 return FALSE;
316
317 /* get devices from daemon */
318 devices = fu_engine_get_devices (priv->engine, error);
319 if (devices == NULL)
320 return FALSE;
321 for (guint i = 0; i < devices->len; i++) {
322 FwupdDevice *dev = g_ptr_array_index (devices, i);
323 g_autoptr(GPtrArray) rels = NULL;
324 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500325 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600326
Richard Hughes747f5702019-08-06 14:27:26 +0100327 /* not going to have results, so save a engine round-trip */
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600328 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
329 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100330 if (!fu_util_filter_device (priv, dev))
331 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600332
333 /* get the releases for this device and filter for validity */
334 rels = fu_engine_get_upgrades (priv->engine,
335 fwupd_device_get_id (dev),
336 &error_local);
337 if (rels == NULL) {
338 g_printerr ("%s\n", error_local->message);
339 continue;
340 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500341 child = g_node_append_data (root, dev);
342
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600343 for (guint j = 0; j < rels->len; j++) {
344 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500345 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600346 }
347 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500348 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500349 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600350 /* save the device state for other applications to see */
351 if (!fu_util_save_current_state (priv, error))
352 return FALSE;
353
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600354 /* success */
355 return TRUE;
356}
357
358static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500359fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
360{
361 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500362 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500363 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500364 gint fd;
365
366 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000367 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500368 return FALSE;
369
370 /* check args */
371 if (g_strv_length (values) != 1) {
372 g_set_error_literal (error,
373 FWUPD_ERROR,
374 FWUPD_ERROR_INVALID_ARGS,
375 "Invalid arguments");
376 return FALSE;
377 }
378
379 /* open file */
380 fd = open (values[0], O_RDONLY);
381 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500382 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500383 g_set_error (error,
384 FWUPD_ERROR,
385 FWUPD_ERROR_INVALID_FILE,
386 "failed to open %s",
387 values[0]);
388 return FALSE;
389 }
390 array = fu_engine_get_details (priv->engine, fd, error);
391 close (fd);
392
393 if (array == NULL)
394 return FALSE;
395 for (guint i = 0; i < array->len; i++) {
396 FwupdDevice *dev = g_ptr_array_index (array, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100397 if (!fu_util_filter_device (priv, dev))
398 continue;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500399 g_node_append_data (root, dev);
Mario Limonciello716ab272018-05-29 12:34:37 -0500400 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500401 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500402
Mario Limonciello716ab272018-05-29 12:34:37 -0500403 return TRUE;
404}
405
406static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100407fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
408{
409 g_autoptr(GString) str = g_string_new (NULL);
410
411 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
412 const gchar *tmp = fwupd_device_flag_to_string (i);
413 if (tmp == NULL)
414 break;
415 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
416 g_string_append (str, " ");
417 g_string_append (str, tmp);
418 g_string_append (str, " ~");
419 g_string_append (str, tmp);
420 }
421 g_print ("%s\n", str->str);
422
423 return TRUE;
424}
425
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500426static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100427fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500428{
429 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100430 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100431 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
432 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500433 if (!priv->show_all_devices &&
434 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500435 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100436 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500437 GNode *child = g_node_append_data (root, dev_tmp);
438 fu_util_build_device_tree (priv, child, devs, dev_tmp);
439 }
440 }
441}
442
443static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100444fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500445{
446 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500447 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500448 g_autoptr(GPtrArray) devs = NULL;
449
450 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000451 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500452 return FALSE;
453
454 /* print */
455 devs = fu_engine_get_devices (priv->engine, error);
456 if (devs == NULL)
457 return FALSE;
458
459 /* print */
460 if (devs->len == 0) {
461 /* TRANSLATORS: nothing attached that can be upgraded */
462 g_print ("%s\n", _("No hardware detected with firmware update capability"));
463 return TRUE;
464 }
465 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500466 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500467
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100468 /* save the device state for other applications to see */
469 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500470}
471
Richard Hughes98ca9932018-05-18 10:24:07 +0100472static FuDevice *
473fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
474{
475 FuDevice *dev;
476 guint idx;
477 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100478 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100479
480 /* get devices from daemon */
481 devices = fu_engine_get_devices (priv->engine, error);
482 if (devices == NULL)
483 return NULL;
484
Richard Hughes747f5702019-08-06 14:27:26 +0100485 /* filter results */
486 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
487 for (guint i = 0; i < devices->len; i++) {
488 dev = g_ptr_array_index (devices, i);
489 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
490 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100491 g_ptr_array_add (devices_filtered, g_object_ref (dev));
492 }
493
494 /* nothing */
495 if (devices_filtered->len == 0) {
496 g_set_error_literal (error,
497 FWUPD_ERROR,
498 FWUPD_ERROR_NOTHING_TO_DO,
499 "No supported devices");
500 return NULL;
501 }
502
Richard Hughes98ca9932018-05-18 10:24:07 +0100503 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100504 if (devices_filtered->len == 1) {
505 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500506 /* TRANSLATORS: Device has been chosen by the daemon for the user */
507 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100508 return g_object_ref (dev);
509 }
510
511 /* TRANSLATORS: get interactive prompt */
512 g_print ("%s\n", _("Choose a device:"));
513 /* TRANSLATORS: this is to abort the interactive prompt */
514 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100515 for (guint i = 0; i < devices_filtered->len; i++) {
516 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100517 g_print ("%u.\t%s (%s)\n",
518 i + 1,
519 fu_device_get_id (dev),
520 fu_device_get_name (dev));
521 }
Richard Hughes747f5702019-08-06 14:27:26 +0100522 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100523 if (idx == 0) {
524 g_set_error_literal (error,
525 FWUPD_ERROR,
526 FWUPD_ERROR_NOTHING_TO_DO,
527 "Request canceled");
528 return NULL;
529 }
Richard Hughes747f5702019-08-06 14:27:26 +0100530 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100531 return g_object_ref (dev);
532}
533
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500534static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600535fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500536 FwupdDevice *device,
537 FuUtilPrivate *priv)
538{
539 g_autofree gchar *str = NULL;
540
Richard Hughes809abea2019-03-23 11:06:18 +0000541 /* allowed to set whenever the device has changed */
542 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
543 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
544 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
545 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
546
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500547 /* same as last time, so ignore */
548 if (priv->current_device != NULL &&
549 fwupd_device_compare (priv->current_device, device) == 0)
550 return;
551
552 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600553 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
554 /* TRANSLATORS: %1 is a device name */
555 str = g_strdup_printf (_("Updating %s…"),
556 fwupd_device_get_name (device));
557 fu_progressbar_set_title (priv->progressbar, str);
558 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
559 /* TRANSLATORS: %1 is a device name */
560 str = g_strdup_printf (_("Installing on %s…"),
561 fwupd_device_get_name (device));
562 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000563 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
564 /* TRANSLATORS: %1 is a device name */
565 str = g_strdup_printf (_("Reading from %s…"),
566 fwupd_device_get_name (device));
567 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600568 } else {
569 g_warning ("no FuUtilOperation set");
570 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500571 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600572
Mario Limonciello32241f42019-01-24 10:12:41 -0600573 if (priv->current_message == NULL) {
574 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
575 if (tmp != NULL)
576 priv->current_message = g_strdup (tmp);
577 }
578}
579
580static void
581fu_util_display_current_message (FuUtilPrivate *priv)
582{
583 if (priv->current_message == NULL)
584 return;
585 g_print ("%s\n", priv->current_message);
586 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500587}
588
Richard Hughes98ca9932018-05-18 10:24:07 +0100589static gboolean
590fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
591{
592 g_autoptr(FuDevice) device = NULL;
593 g_autoptr(GBytes) blob_fw = NULL;
594
595 /* invalid args */
596 if (g_strv_length (values) == 0) {
597 g_set_error_literal (error,
598 FWUPD_ERROR,
599 FWUPD_ERROR_INVALID_ARGS,
600 "Invalid arguments");
601 return FALSE;
602 }
603
604 /* parse blob */
605 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500606 if (blob_fw == NULL) {
607 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100608 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500609 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100610
611 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000612 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100613 return FALSE;
614
615 /* get device */
616 if (g_strv_length (values) >= 2) {
617 device = fu_engine_get_device (priv->engine, values[1], error);
618 if (device == NULL)
619 return FALSE;
620 } else {
621 device = fu_util_prompt_for_device (priv, error);
622 if (device == NULL)
623 return FALSE;
624 }
625
Mario Limonciello3f243a92019-01-21 22:05:23 -0600626 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500627 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600628 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500629
Richard Hughes98ca9932018-05-18 10:24:07 +0100630 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000631 if (priv->prepare_blob) {
632 g_autoptr(GPtrArray) devices = NULL;
633 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
634 g_ptr_array_add (devices, g_object_ref (device));
635 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
636 g_prefix_error (error, "failed to prepare composite action: ");
637 return FALSE;
638 }
639 }
Mario Limonciello035818b2019-03-26 11:12:16 -0500640 priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000641 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600642 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000643 if (priv->cleanup_blob) {
644 g_autoptr(FuDevice) device_new = NULL;
645 g_autoptr(GError) error_local = NULL;
646
647 /* get the possibly new device from the old ID */
648 device_new = fu_engine_get_device (priv->engine,
649 fu_device_get_id (device),
650 &error_local);
651 if (device_new == NULL) {
652 g_debug ("failed to find new device: %s",
653 error_local->message);
654 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600655 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000656 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
657 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
658 g_prefix_error (error, "failed to cleanup composite action: ");
659 return FALSE;
660 }
661 }
662 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600663
Mario Limonciello32241f42019-01-24 10:12:41 -0600664 fu_util_display_current_message (priv);
665
Mario Limonciello3f243a92019-01-21 22:05:23 -0600666 /* success */
667 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100668}
669
Richard Hughesa58510b2019-10-30 10:03:12 +0000670static gboolean
671fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
672{
673 g_autoptr(FuDevice) device = NULL;
674 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
675 g_autoptr(GBytes) blob_fw = NULL;
676
677 /* invalid args */
678 if (g_strv_length (values) == 0) {
679 g_set_error_literal (error,
680 FWUPD_ERROR,
681 FWUPD_ERROR_INVALID_ARGS,
682 "Invalid arguments");
683 return FALSE;
684 }
685
686 /* file already exists */
687 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
688 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
689 g_set_error_literal (error,
690 FWUPD_ERROR,
691 FWUPD_ERROR_INVALID_ARGS,
692 "Filename already exists");
693 return FALSE;
694 }
695
Richard Hughes02792c02019-11-01 14:21:20 +0000696 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000697 * avoid failing at the end of a potentially lengthy operation */
698 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
699 return FALSE;
700
701 /* load engine */
702 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
703 return FALSE;
704
705 /* get device */
706 if (g_strv_length (values) >= 2) {
707 device = fu_engine_get_device (priv->engine, values[1], error);
708 if (device == NULL)
709 return FALSE;
710 } else {
711 device = fu_util_prompt_for_device (priv, error);
712 if (device == NULL)
713 return FALSE;
714 }
715 priv->current_operation = FU_UTIL_OPERATION_READ;
716 g_signal_connect (priv->engine, "device-changed",
717 G_CALLBACK (fu_util_update_device_changed_cb), priv);
718
719 /* dump firmware */
720 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
721 if (blob_fw == NULL)
722 return FALSE;
723 return fu_common_set_contents_bytes (values[0], blob_fw, error);
724}
725
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100726static gint
727fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
728{
729 FuInstallTask *task1 = *((FuInstallTask **) a);
730 FuInstallTask *task2 = *((FuInstallTask **) b);
731 return fu_install_task_compare (task1, task2);
732}
733
734static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100735fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
736{
Filipe Laínse0914272019-09-20 10:04:43 +0100737 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100738 { "curl", uri, "--output", fn, NULL },
739 { NULL } };
740 for (guint i = 0; argv[i][0] != NULL; i++) {
741 g_autoptr(GError) error_local = NULL;
742 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
743 g_debug ("%s", error_local->message);
744 continue;
745 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000746 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100747 }
748 g_set_error_literal (error,
749 FWUPD_ERROR,
750 FWUPD_ERROR_NOT_FOUND,
751 "no supported out-of-process downloaders found");
752 return FALSE;
753}
754
755static gchar *
756fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
757{
758 g_autofree gchar *filename = NULL;
759 g_autoptr(SoupURI) uri = NULL;
760
761 /* a local file */
762 uri = soup_uri_new (perhapsfn);
763 if (uri == NULL)
764 return g_strdup (perhapsfn);
765
766 /* download the firmware to a cachedir */
767 filename = fu_util_get_user_cache_path (perhapsfn);
768 if (!fu_common_mkdir_parent (filename, error))
769 return NULL;
770 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
771 return NULL;
772 return g_steal_pointer (&filename);
773}
774
775static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100776fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
777{
Richard Hughes3d178be2018-08-30 11:14:24 +0100778 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100779 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100780 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100781 g_autoptr(GPtrArray) devices_possible = NULL;
782 g_autoptr(GPtrArray) errors = NULL;
783 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100784 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100785
Mario Limonciello8949e892018-05-25 08:03:06 -0500786 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000787 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500788 return FALSE;
789
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100790 /* handle both forms */
791 if (g_strv_length (values) == 1) {
792 devices_possible = fu_engine_get_devices (priv->engine, error);
793 if (devices_possible == NULL)
794 return FALSE;
795 } else if (g_strv_length (values) == 2) {
796 FuDevice *device = fu_engine_get_device (priv->engine,
797 values[1],
798 error);
799 if (device == NULL)
800 return FALSE;
801 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
802 g_ptr_array_add (devices_possible, device);
803 } else {
804 g_set_error_literal (error,
805 FWUPD_ERROR,
806 FWUPD_ERROR_INVALID_ARGS,
807 "Invalid arguments");
808 return FALSE;
809 }
810
Richard Hughes3d178be2018-08-30 11:14:24 +0100811 /* download if required */
812 filename = fu_util_download_if_required (priv, values[0], error);
813 if (filename == NULL)
814 return FALSE;
815
Richard Hughes481aa2a2018-09-18 20:51:46 +0100816 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100817 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500818 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100819 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100820 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500821 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100822 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
823 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100824 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600825 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100826 if (components == NULL)
827 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100828
Richard Hughes481aa2a2018-09-18 20:51:46 +0100829 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100830 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
831 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100832 for (guint i = 0; i < components->len; i++) {
833 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100834
835 /* do any devices pass the requirements */
836 for (guint j = 0; j < devices_possible->len; j++) {
837 FuDevice *device = g_ptr_array_index (devices_possible, j);
838 g_autoptr(FuInstallTask) task = NULL;
839 g_autoptr(GError) error_local = NULL;
840
841 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100842 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100843 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100844 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100845 &error_local)) {
846 g_debug ("requirement on %s:%s failed: %s",
847 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100848 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100849 error_local->message);
850 g_ptr_array_add (errors, g_steal_pointer (&error_local));
851 continue;
852 }
853
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600854 /* if component should have an update message from CAB */
855 fu_device_incorporate_from_component (device, component);
856
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100857 /* success */
858 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
859 }
860 }
861
862 /* order the install tasks by the device priority */
863 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
864
865 /* nothing suitable */
866 if (install_tasks->len == 0) {
867 GError *error_tmp = fu_common_error_array_get_best (errors);
868 g_propagate_error (error, error_tmp);
869 return FALSE;
870 }
871
Mario Limonciello3f243a92019-01-21 22:05:23 -0600872 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500873 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600874 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500875
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100876 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100877 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
878 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100879
Mario Limonciello32241f42019-01-24 10:12:41 -0600880 fu_util_display_current_message (priv);
881
Mario Limonciello3f243a92019-01-21 22:05:23 -0600882 /* we don't want to ask anything */
883 if (priv->no_reboot_check) {
884 g_debug ("skipping reboot check");
885 return TRUE;
886 }
887
Mario Limonciello3143bad2019-02-27 07:31:00 -0600888 /* save the device state for other applications to see */
889 if (!fu_util_save_current_state (priv, error))
890 return FALSE;
891
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100892 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600893 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100894}
895
Richard Hughes98ca9932018-05-18 10:24:07 +0100896static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500897fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600898{
Mario Limonciellofd734852019-08-01 16:41:42 -0500899 FwupdRemote *remote;
900 const gchar *remote_id;
901 const gchar *uri_tmp;
902 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600903
Mario Limonciellofd734852019-08-01 16:41:42 -0500904 uri_tmp = fwupd_release_get_uri (rel);
905 if (uri_tmp == NULL) {
906 g_set_error_literal (error,
907 FWUPD_ERROR,
908 FWUPD_ERROR_INVALID_FILE,
909 "release missing URI");
910 return FALSE;
911 }
912 remote_id = fwupd_release_get_remote_id (rel);
913 if (remote_id == NULL) {
914 g_set_error (error,
915 FWUPD_ERROR,
916 FWUPD_ERROR_INVALID_FILE,
917 "failed to find remote for %s",
918 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +0100919 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -0500920 }
921
922 remote = fu_engine_get_remote_by_id (priv->engine,
923 remote_id,
924 error);
925 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600926 return FALSE;
927
Mario Limonciellofd734852019-08-01 16:41:42 -0500928 argv = g_new0 (gchar *, 2);
929 /* local remotes have the firmware already */
930 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
931 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
932 g_autofree gchar *path = g_path_get_dirname (fn_cache);
933 argv[0] = g_build_filename (path, uri_tmp, NULL);
934 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
935 argv[0] = g_strdup (uri_tmp + 7);
936 /* web remote, fu_util_install will download file */
937 } else {
938 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
939 }
940 return fu_util_install (priv, argv, error);
941}
942
943static gboolean
944fu_util_update_all (FuUtilPrivate *priv, GError **error)
945{
946 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600947
Mario Limonciello46aaee82019-01-10 12:58:00 -0600948 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +0000949 if (devices == NULL)
950 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600951 for (guint i = 0; i < devices->len; i++) {
952 FwupdDevice *dev = g_ptr_array_index (devices, i);
953 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600954 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600955 g_autoptr(GPtrArray) rels = NULL;
956 g_autoptr(GError) error_local = NULL;
957
958 if (!fu_util_is_interesting_device (dev))
959 continue;
960 /* only show stuff that has metadata available */
961 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
962 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100963 if (!fu_util_filter_device (priv, dev))
964 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600965
966 device_id = fu_device_get_id (dev);
967 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
968 if (rels == NULL) {
969 g_printerr ("%s\n", error_local->message);
970 continue;
971 }
972
Mario Limonciello98b95162019-10-30 09:20:43 -0500973 if (!priv->no_safety_check) {
974 if (!fu_util_prompt_warning (dev,
975 fu_util_get_tree_title (priv),
976 error))
977 return FALSE;
978 }
979
Mario Limonciello46aaee82019-01-10 12:58:00 -0600980 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -0500981 if (!fu_util_install_release (priv, rel, &error_local)) {
982 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +0100983 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600984 }
Mario Limonciellofd734852019-08-01 16:41:42 -0500985 fu_util_display_current_message (priv);
986 }
987 return TRUE;
988}
989
990static gboolean
991fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
992{
993 FwupdRelease *rel;
994 g_autoptr(FuDevice) dev = NULL;
995 g_autoptr(GPtrArray) rels = NULL;
996
997 /* do not allow a partial device-id */
998 dev = fu_engine_get_device (priv->engine, device_id, error);
999 if (dev == NULL)
1000 return FALSE;
1001
1002 /* get the releases for this device and filter for validity */
1003 rels = fu_engine_get_upgrades (priv->engine, device_id, error);
1004 if (rels == NULL)
1005 return FALSE;
1006 rel = g_ptr_array_index (rels, 0);
1007 if (!fu_util_install_release (priv, rel, error))
1008 return FALSE;
1009 fu_util_display_current_message (priv);
1010
1011 return TRUE;
1012}
1013
1014static gboolean
1015fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1016{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001017 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1018 g_set_error_literal (error,
1019 FWUPD_ERROR,
1020 FWUPD_ERROR_INVALID_ARGS,
1021 "--allow-older is not supported for this command");
1022 return FALSE;
1023 }
1024
1025 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1026 g_set_error_literal (error,
1027 FWUPD_ERROR,
1028 FWUPD_ERROR_INVALID_ARGS,
1029 "--allow-reinstall is not supported for this command");
1030 return FALSE;
1031 }
1032
Mario Limonciellofd734852019-08-01 16:41:42 -05001033 if (g_strv_length (values) > 1) {
1034 g_set_error_literal (error,
1035 FWUPD_ERROR,
1036 FWUPD_ERROR_INVALID_ARGS,
1037 "Invalid arguments");
1038 return FALSE;
1039 }
1040
1041 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1042 return FALSE;
1043
1044 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1045 g_signal_connect (priv->engine, "device-changed",
1046 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1047
1048 if (g_strv_length (values) == 1) {
1049 if (!fu_util_update_by_id (priv, values[0], error))
1050 return FALSE;
1051 } else {
1052 if (!fu_util_update_all (priv, error))
1053 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001054 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001055
1056 /* we don't want to ask anything */
1057 if (priv->no_reboot_check) {
1058 g_debug ("skipping reboot check");
1059 return TRUE;
1060 }
1061
Mario Limonciello3143bad2019-02-27 07:31:00 -06001062 /* save the device state for other applications to see */
1063 if (!fu_util_save_current_state (priv, error))
1064 return FALSE;
1065
Mario Limonciello3f243a92019-01-21 22:05:23 -06001066 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001067}
1068
1069static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001070fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1071{
1072 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001073 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001074
1075 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001076 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001077 return FALSE;
1078
Richard Hughes98ca9932018-05-18 10:24:07 +01001079 /* get device */
1080 if (g_strv_length (values) >= 1) {
1081 device = fu_engine_get_device (priv->engine, values[0], error);
1082 if (device == NULL)
1083 return FALSE;
1084 } else {
1085 device = fu_util_prompt_for_device (priv, error);
1086 if (device == NULL)
1087 return FALSE;
1088 }
1089
1090 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001091 locker = fu_device_locker_new (device, error);
1092 if (locker == NULL)
1093 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001094 return fu_device_detach (device, error);
1095}
1096
1097static gboolean
1098fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1099{
1100 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001101 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001102
1103 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001104 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001105 return FALSE;
1106
Richard Hughes98ca9932018-05-18 10:24:07 +01001107 /* get device */
1108 if (g_strv_length (values) >= 1) {
1109 device = fu_engine_get_device (priv->engine, values[0], error);
1110 if (device == NULL)
1111 return FALSE;
1112 } else {
1113 device = fu_util_prompt_for_device (priv, error);
1114 if (device == NULL)
1115 return FALSE;
1116 }
1117
1118 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001119 locker = fu_device_locker_new (device, error);
1120 if (locker == NULL)
1121 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001122 return fu_device_attach (device, error);
1123}
1124
Richard Hughes1d894f12018-08-31 13:05:51 +01001125static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001126fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1127{
1128 gboolean has_pending = FALSE;
1129 g_autoptr(FuHistory) history = fu_history_new ();
1130 g_autoptr(GPtrArray) devices = NULL;
1131
1132 /* check the history database before starting the daemon */
1133 if (g_strv_length (values) == 0) {
1134 devices = fu_history_get_devices (history, error);
1135 if (devices == NULL)
1136 return FALSE;
1137 } else if (g_strv_length (values) == 1) {
1138 FuDevice *device;
1139 device = fu_history_get_device_by_id (history, values[0], error);
1140 if (device == NULL)
1141 return FALSE;
1142 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1143 g_ptr_array_add (devices, device);
1144 } else {
1145 g_set_error_literal (error,
1146 FWUPD_ERROR,
1147 FWUPD_ERROR_INVALID_ARGS,
1148 "Invalid arguments");
1149 return FALSE;
1150 }
1151
1152 /* nothing to do */
1153 for (guint i = 0; i < devices->len; i++) {
1154 FuDevice *dev = g_ptr_array_index (devices, i);
1155 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1156 fu_engine_add_plugin_filter (priv->engine,
1157 fu_device_get_plugin (dev));
1158 has_pending = TRUE;
1159 }
1160 }
1161 if (!has_pending) {
1162 g_set_error_literal (error,
1163 FWUPD_ERROR,
1164 FWUPD_ERROR_NOTHING_TO_DO,
1165 "No firmware to activate");
1166 return FALSE;
1167
1168 }
1169
1170 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001171 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001172 return FALSE;
1173
1174 /* activate anything with _NEEDS_ACTIVATION */
1175 for (guint i = 0; i < devices->len; i++) {
1176 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001177 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1178 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001179 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1180 continue;
1181 /* TRANSLATORS: shown when shutting down to switch to the new version */
1182 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1183 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1184 return FALSE;
1185 }
1186
1187 return TRUE;
1188}
1189
1190static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001191fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1192{
1193 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1194 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1195 const gchar *hwid_keys[] = {
1196 FU_HWIDS_KEY_BIOS_VENDOR,
1197 FU_HWIDS_KEY_BIOS_VERSION,
1198 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1199 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1200 FU_HWIDS_KEY_MANUFACTURER,
1201 FU_HWIDS_KEY_FAMILY,
1202 FU_HWIDS_KEY_PRODUCT_NAME,
1203 FU_HWIDS_KEY_PRODUCT_SKU,
1204 FU_HWIDS_KEY_ENCLOSURE_KIND,
1205 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1206 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1207 NULL };
1208
1209 /* read DMI data */
1210 if (g_strv_length (values) == 0) {
1211 if (!fu_smbios_setup (smbios, error))
1212 return FALSE;
1213 } else if (g_strv_length (values) == 1) {
1214 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1215 return FALSE;
1216 } else {
1217 g_set_error_literal (error,
1218 FWUPD_ERROR,
1219 FWUPD_ERROR_INVALID_ARGS,
1220 "Invalid arguments");
1221 return FALSE;
1222 }
1223 if (!fu_hwids_setup (hwids, smbios, error))
1224 return FALSE;
1225
1226 /* show debug output */
1227 g_print ("Computer Information\n");
1228 g_print ("--------------------\n");
1229 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1230 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1231 if (tmp == NULL)
1232 continue;
1233 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1234 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1235 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1236 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1237 } else {
1238 g_print ("%s: %s\n", hwid_keys[i], tmp);
1239 }
1240 }
1241
1242 /* show GUIDs */
1243 g_print ("\nHardware IDs\n");
1244 g_print ("------------\n");
1245 for (guint i = 0; i < 15; i++) {
1246 const gchar *keys = NULL;
1247 g_autofree gchar *guid = NULL;
1248 g_autofree gchar *key = NULL;
1249 g_autofree gchar *keys_str = NULL;
1250 g_auto(GStrv) keysv = NULL;
1251 g_autoptr(GError) error_local = NULL;
1252
1253 /* get the GUID */
1254 key = g_strdup_printf ("HardwareID-%u", i);
1255 keys = fu_hwids_get_replace_keys (hwids, key);
1256 guid = fu_hwids_get_guid (hwids, key, &error_local);
1257 if (guid == NULL) {
1258 g_print ("%s\n", error_local->message);
1259 continue;
1260 }
1261
1262 /* show what makes up the GUID */
1263 keysv = g_strsplit (keys, "&", -1);
1264 keys_str = g_strjoinv (" + ", keysv);
1265 g_print ("{%s} <- %s\n", guid, keys_str);
1266 }
1267
1268 return TRUE;
1269}
1270
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001271static gboolean
1272fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1273{
1274 const gchar *script_fn = "startup.sh";
1275 const gchar *output_fn = "firmware.bin";
1276 g_autoptr(GBytes) archive_blob = NULL;
1277 g_autoptr(GBytes) firmware_blob = NULL;
1278 if (g_strv_length (values) < 2) {
1279 g_set_error_literal (error,
1280 FWUPD_ERROR,
1281 FWUPD_ERROR_INVALID_ARGS,
1282 "Invalid arguments");
1283 return FALSE;
1284 }
1285 archive_blob = fu_common_get_contents_bytes (values[0], error);
1286 if (archive_blob == NULL)
1287 return FALSE;
1288 if (g_strv_length (values) > 2)
1289 script_fn = values[2];
1290 if (g_strv_length (values) > 3)
1291 output_fn = values[3];
1292 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1293 if (firmware_blob == NULL)
1294 return FALSE;
1295 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1296}
1297
Richard Hughes3d607622019-03-07 16:59:27 +00001298static gboolean
1299fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1300{
1301 g_autofree gchar *sig = NULL;
1302
1303 /* check args */
1304 if (g_strv_length (values) != 1) {
1305 g_set_error_literal (error,
1306 FWUPD_ERROR,
1307 FWUPD_ERROR_INVALID_ARGS,
1308 "Invalid arguments: value expected");
1309 return FALSE;
1310 }
1311
1312 /* start engine */
1313 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1314 return FALSE;
1315 sig = fu_engine_self_sign (priv->engine, values[0],
1316 FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
1317 FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
1318 if (sig == NULL)
1319 return FALSE;
1320 g_print ("%s\n", sig);
1321 return TRUE;
1322}
1323
Mario Limonciello62f84862018-10-18 13:15:23 -05001324static void
1325fu_util_device_added_cb (FwupdClient *client,
1326 FwupdDevice *device,
1327 gpointer user_data)
1328{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001329 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001330 /* TRANSLATORS: this is when a device is hotplugged */
1331 g_print ("%s\n%s", _("Device added:"), tmp);
1332}
1333
1334static void
1335fu_util_device_removed_cb (FwupdClient *client,
1336 FwupdDevice *device,
1337 gpointer user_data)
1338{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001339 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001340 /* TRANSLATORS: this is when a device is hotplugged */
1341 g_print ("%s\n%s", _("Device removed:"), tmp);
1342}
1343
1344static void
1345fu_util_device_changed_cb (FwupdClient *client,
1346 FwupdDevice *device,
1347 gpointer user_data)
1348{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001349 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001350 /* TRANSLATORS: this is when a device has been updated */
1351 g_print ("%s\n%s", _("Device changed:"), tmp);
1352}
1353
1354static void
1355fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1356{
1357 /* TRANSLATORS: this is when the daemon state changes */
1358 g_print ("%s\n", _("Changed"));
1359}
1360
1361static gboolean
1362fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1363{
1364 g_autoptr(FwupdClient) client = fwupd_client_new ();
1365
1366 /* get all the devices */
1367 if (!fwupd_client_connect (client, priv->cancellable, error))
1368 return FALSE;
1369
1370 /* watch for any hotplugged device */
1371 g_signal_connect (client, "changed",
1372 G_CALLBACK (fu_util_changed_cb), priv);
1373 g_signal_connect (client, "device-added",
1374 G_CALLBACK (fu_util_device_added_cb), priv);
1375 g_signal_connect (client, "device-removed",
1376 G_CALLBACK (fu_util_device_removed_cb), priv);
1377 g_signal_connect (client, "device-changed",
1378 G_CALLBACK (fu_util_device_changed_cb), priv);
1379 g_signal_connect (priv->cancellable, "cancelled",
1380 G_CALLBACK (fu_util_cancelled_cb), priv);
1381 g_main_loop_run (priv->loop);
1382 return TRUE;
1383}
1384
Richard Hughes15684492019-03-15 16:27:50 +00001385static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001386fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1387{
1388 g_autoptr(GPtrArray) firmware_types = NULL;
1389
1390 /* load engine */
1391 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1392 return FALSE;
1393
1394 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1395 for (guint i = 0; i < firmware_types->len; i++) {
1396 const gchar *id = g_ptr_array_index (firmware_types, i);
1397 g_print ("%s\n", id);
1398 }
1399 if (firmware_types->len == 0) {
1400 /* TRANSLATORS: nothing found */
1401 g_print ("%s\n", _("No firmware IDs found"));
1402 return TRUE;
1403 }
1404
1405 return TRUE;
1406}
1407
1408static gchar *
1409fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1410{
1411 g_autoptr(GPtrArray) firmware_types = NULL;
1412 guint idx;
1413 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1414
1415 /* TRANSLATORS: get interactive prompt */
1416 g_print ("%s\n", _("Choose a firmware type:"));
1417 /* TRANSLATORS: this is to abort the interactive prompt */
1418 g_print ("0.\t%s\n", _("Cancel"));
1419 for (guint i = 0; i < firmware_types->len; i++) {
1420 const gchar *id = g_ptr_array_index (firmware_types, i);
1421 g_print ("%u.\t%s\n", i + 1, id);
1422 }
1423 idx = fu_util_prompt_for_number (firmware_types->len);
1424 if (idx == 0) {
1425 g_set_error_literal (error,
1426 FWUPD_ERROR,
1427 FWUPD_ERROR_NOTHING_TO_DO,
1428 "Request canceled");
1429 return NULL;
1430 }
1431
1432 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1433}
1434
1435static gboolean
1436fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1437{
1438 GType gtype;
1439 g_autoptr(GBytes) blob = NULL;
1440 g_autoptr(FuFirmware) firmware = NULL;
1441 g_autofree gchar *firmware_type = NULL;
1442 g_autofree gchar *str = NULL;
1443
1444 /* check args */
1445 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1446 g_set_error_literal (error,
1447 FWUPD_ERROR,
1448 FWUPD_ERROR_INVALID_ARGS,
1449 "Invalid arguments: filename required");
1450 return FALSE;
1451 }
1452
1453 if (g_strv_length (values) == 2)
1454 firmware_type = g_strdup (values[1]);
1455
1456 /* load file */
1457 blob = fu_common_get_contents_bytes (values[0], error);
1458 if (blob == NULL)
1459 return FALSE;
1460
1461 /* load engine */
1462 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1463 return FALSE;
1464
1465 /* find the GType to use */
1466 if (firmware_type == NULL)
1467 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1468 if (firmware_type == NULL)
1469 return FALSE;
1470 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1471 if (gtype == G_TYPE_INVALID) {
1472 g_set_error (error,
1473 G_IO_ERROR,
1474 G_IO_ERROR_NOT_FOUND,
1475 "GType %s not supported", firmware_type);
1476 return FALSE;
1477 }
1478 firmware = g_object_new (gtype, NULL);
1479 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1480 return FALSE;
1481 str = fu_firmware_to_string (firmware);
1482 g_print ("%s", str);
1483 return TRUE;
1484}
1485
1486static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001487fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1488{
1489 g_autofree gchar *str = NULL;
1490 g_autoptr(FuDevice) dev = NULL;
1491
1492 /* load engine */
1493 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1494 return FALSE;
1495
1496 /* get device */
1497 if (g_strv_length (values) == 1) {
1498 dev = fu_engine_get_device (priv->engine, values[1], error);
1499 if (dev == NULL)
1500 return FALSE;
1501 } else {
1502 dev = fu_util_prompt_for_device (priv, error);
1503 if (dev == NULL)
1504 return FALSE;
1505 }
1506
1507 /* add checksums */
1508 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1509 return FALSE;
1510
1511 /* show checksums */
1512 str = fu_device_to_string (dev);
1513 g_print ("%s\n", str);
1514 return TRUE;
1515}
1516
Mario Limonciellofe593942019-04-03 13:48:52 -05001517static gboolean
1518fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1519{
1520 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001521 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001522 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001523
1524 /* load engine */
1525 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1526 return FALSE;
1527
1528 /* get all devices from the history database */
1529 devices = fu_engine_get_history (priv->engine, error);
1530 if (devices == NULL)
1531 return FALSE;
1532
1533 /* show each device */
1534 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001535 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001536 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001537 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001538 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001539 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001540
Richard Hughes747f5702019-08-06 14:27:26 +01001541 if (!fu_util_filter_device (priv, dev))
1542 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001543 child = g_node_append_data (root, dev);
1544
1545 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001546 if (rel == NULL)
1547 continue;
1548 remote = fwupd_release_get_remote_id (rel);
1549
1550 /* doesn't actually map to remote */
1551 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001552 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001553 continue;
1554 }
1555
1556 /* try to lookup releases from client */
1557 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1558 if (rels == NULL)
1559 return FALSE;
1560
1561 /* map to a release in client */
1562 for (guint j = 0; j < rels->len; j++) {
1563 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1564 if (g_strcmp0 (remote,
1565 fwupd_release_get_remote_id (rel2)) != 0)
1566 continue;
1567 if (g_strcmp0 (fwupd_release_get_version (rel),
1568 fwupd_release_get_version (rel2)) != 0)
1569 continue;
1570 g_node_append_data (child, g_object_ref (rel2));
1571 rel = NULL;
1572 break;
1573 }
1574
1575 /* didn't match anything */
1576 if (rels->len == 0 || rel != NULL) {
1577 g_node_append_data (child, rel);
1578 continue;
1579 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001580
Mario Limonciellofe593942019-04-03 13:48:52 -05001581 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001582 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001583
1584 return TRUE;
1585}
1586
Richard Hughesb5976832018-05-18 10:02:09 +01001587int
1588main (int argc, char *argv[])
1589{
Richard Hughes460226a2018-05-21 20:56:21 +01001590 gboolean allow_older = FALSE;
1591 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001592 gboolean force = FALSE;
1593 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001594 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001595 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001596 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001597 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1598 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001599 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001600 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001601 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001602 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001603 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1604 /* TRANSLATORS: command line option */
1605 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001606 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1607 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001608 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001609 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1610 /* TRANSLATORS: command line option */
1611 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001612 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1613 /* TRANSLATORS: command line option */
1614 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06001615 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
1616 /* TRANSLATORS: command line option */
1617 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05001618 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
1619 /* TRANSLATORS: command line option */
1620 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001621 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1622 /* TRANSLATORS: command line option */
1623 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001624 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1625 /* TRANSLATORS: command line option */
1626 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001627 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001628 /* TRANSLATORS: command line option */
1629 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001630 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001631 /* TRANSLATORS: command line option */
1632 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06001633 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
1634 /* TRANSLATORS: command line option */
1635 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01001636 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
1637 /* TRANSLATORS: command line option */
1638 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01001639 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
1640 /* TRANSLATORS: command line option */
1641 _("Filter with a set of device flags using a ~ prefix to "
1642 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001643 { NULL}
1644 };
1645
1646 setlocale (LC_ALL, "");
1647
1648 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1649 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1650 textdomain (GETTEXT_PACKAGE);
1651
1652 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001653 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01001654 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001655 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughesb5976832018-05-18 10:02:09 +01001656
1657 /* create helper object */
1658 priv->loop = g_main_loop_new (NULL, FALSE);
1659 priv->progressbar = fu_progressbar_new ();
1660
1661 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00001662 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001663 "build-firmware",
1664 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1665 /* TRANSLATORS: command description */
1666 _("Build firmware using a sandbox"),
1667 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00001668 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001669 "smbios-dump",
1670 "FILE",
1671 /* TRANSLATORS: command description */
1672 _("Dump SMBIOS data from a file"),
1673 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00001674 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001675 "get-plugins",
1676 NULL,
1677 /* TRANSLATORS: command description */
1678 _("Get all enabled plugins registered with the system"),
1679 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00001680 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001681 "get-details",
1682 NULL,
1683 /* TRANSLATORS: command description */
1684 _("Gets details about a firmware file"),
1685 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00001686 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05001687 "get-history",
1688 NULL,
1689 /* TRANSLATORS: command description */
1690 _("Show history of firmware updates"),
1691 fu_util_get_history);
1692 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001693 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06001694 NULL,
1695 /* TRANSLATORS: command description */
1696 _("Gets the list of updates for connected hardware"),
1697 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00001698 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01001699 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01001700 NULL,
1701 /* TRANSLATORS: command description */
1702 _("Get all devices that support firmware updates"),
1703 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00001704 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01001705 "get-device-flags",
1706 NULL,
1707 /* TRANSLATORS: command description */
1708 _("Get all device flags supported by fwupd"),
1709 fu_util_get_device_flags);
1710 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001711 "watch",
1712 NULL,
1713 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02001714 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001715 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00001716 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001717 "install-blob",
1718 "FILENAME DEVICE-ID",
1719 /* TRANSLATORS: command description */
1720 _("Install a firmware blob on a device"),
1721 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00001722 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001723 "install",
1724 "FILE [ID]",
1725 /* TRANSLATORS: command description */
1726 _("Install a firmware file on this hardware"),
1727 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00001728 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001729 "attach",
1730 "DEVICE-ID",
1731 /* TRANSLATORS: command description */
1732 _("Attach to firmware mode"),
1733 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001734 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001735 "detach",
1736 "DEVICE-ID",
1737 /* TRANSLATORS: command description */
1738 _("Detach to bootloader mode"),
1739 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001740 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001741 "activate",
1742 "[DEVICE-ID]",
1743 /* TRANSLATORS: command description */
1744 _("Activate pending devices"),
1745 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00001746 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01001747 "hwids",
1748 "[FILE]",
1749 /* TRANSLATORS: command description */
1750 _("Return all the hardware IDs for the machine"),
1751 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00001752 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05001753 "monitor",
1754 NULL,
1755 /* TRANSLATORS: command description */
1756 _("Monitor the daemon for events"),
1757 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00001758 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001759 "update,upgrade",
Mario Limonciello46aaee82019-01-10 12:58:00 -06001760 NULL,
1761 /* TRANSLATORS: command description */
1762 _("Update all devices that match local metadata"),
1763 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00001764 fu_util_cmd_array_add (cmd_array,
1765 "self-sign",
1766 "TEXT",
1767 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00001768 C_("command-description",
1769 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00001770 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00001771 fu_util_cmd_array_add (cmd_array,
1772 "verify-update",
1773 "[DEVICE_ID]",
1774 /* TRANSLATORS: command description */
1775 _("Update the stored metadata with current contents"),
1776 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01001777 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00001778 "firmware-read",
1779 "FILENAME [DEVICE-ID]",
1780 /* TRANSLATORS: command description */
1781 _("Read a firmware blob from a device"),
1782 fu_util_firmware_read);
1783 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01001784 "firmware-parse",
1785 "FILENAME [FIRMWARE_TYPE]",
1786 /* TRANSLATORS: command description */
1787 _("Parse and show details about a firmware file"),
1788 fu_util_firmware_parse);
1789 fu_util_cmd_array_add (cmd_array,
1790 "get-firmware-types",
1791 NULL,
1792 /* TRANSLATORS: command description */
1793 _("List the available firmware types"),
1794 fu_util_get_firmware_types);
Richard Hughesb5976832018-05-18 10:02:09 +01001795
1796 /* do stuff on ctrl+c */
1797 priv->cancellable = g_cancellable_new ();
1798 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1799 SIGINT, fu_util_sigint_cb,
1800 priv, NULL);
1801 g_signal_connect (priv->cancellable, "cancelled",
1802 G_CALLBACK (fu_util_cancelled_cb), priv);
1803
1804 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00001805 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001806
Mario Limonciello3f243a92019-01-21 22:05:23 -06001807 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001808 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06001809 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05001810 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06001811 fu_progressbar_set_interactive (priv->progressbar, FALSE);
1812 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001813
Richard Hughesb5976832018-05-18 10:02:09 +01001814 /* get a list of the commands */
1815 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00001816 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001817 g_option_context_set_summary (priv->context, cmd_descriptions);
1818 g_option_context_set_description (priv->context,
1819 "This tool allows an administrator to use the fwupd plugins "
1820 "without being installed on the host system.");
1821
1822 /* TRANSLATORS: program name */
1823 g_set_application_name (_("Firmware Utility"));
1824 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05001825 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01001826 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
1827 if (!ret) {
1828 /* TRANSLATORS: the user didn't read the man page */
1829 g_print ("%s: %s\n", _("Failed to parse arguments"),
1830 error->message);
1831 return EXIT_FAILURE;
1832 }
1833
Richard Hughes0e46b222019-09-05 12:13:35 +01001834 /* allow disabling SSL strict mode for broken corporate proxies */
1835 if (priv->disable_ssl_strict) {
1836 /* TRANSLATORS: try to help */
1837 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
1838 "to do this automatically in the future "
1839 "export DISABLE_SSL_STRICT in your environment"));
1840 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
1841 }
1842
Richard Hughes747f5702019-08-06 14:27:26 +01001843 /* parse filter flags */
1844 if (filter != NULL) {
1845 if (!fu_util_parse_filter_flags (filter,
1846 &priv->filter_include,
1847 &priv->filter_exclude,
1848 &error)) {
1849 /* TRANSLATORS: the user didn't read the man page */
1850 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
1851 error->message);
1852 return EXIT_FAILURE;
1853 }
1854 }
1855
1856
Richard Hughes460226a2018-05-21 20:56:21 +01001857 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01001858 if (allow_reinstall)
1859 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1860 if (allow_older)
1861 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
1862 if (force)
1863 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
1864
Richard Hughes98ca9932018-05-18 10:24:07 +01001865 /* load engine */
1866 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
1867 g_signal_connect (priv->engine, "device-added",
1868 G_CALLBACK (fu_main_engine_device_added_cb),
1869 priv);
1870 g_signal_connect (priv->engine, "device-removed",
1871 G_CALLBACK (fu_main_engine_device_removed_cb),
1872 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01001873 g_signal_connect (priv->engine, "status-changed",
1874 G_CALLBACK (fu_main_engine_status_changed_cb),
1875 priv);
1876 g_signal_connect (priv->engine, "percentage-changed",
1877 G_CALLBACK (fu_main_engine_percentage_changed_cb),
1878 priv);
1879
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001880 /* just show versions and exit */
1881 if (version) {
1882 g_autofree gchar *version_str = fu_util_get_versions ();
1883 g_print ("%s\n", version_str);
1884 return EXIT_SUCCESS;
1885 }
1886
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001887 /* any plugin whitelist specified */
1888 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
1889 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
1890
Richard Hughesb5976832018-05-18 10:02:09 +01001891 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00001892 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01001893 if (!ret) {
1894 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
1895 g_autofree gchar *tmp = NULL;
1896 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
1897 g_print ("%s\n\n%s", error->message, tmp);
1898 return EXIT_FAILURE;
1899 }
1900 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
1901 g_print ("%s\n", error->message);
1902 return EXIT_NOTHING_TO_DO;
1903 }
1904 g_print ("%s\n", error->message);
1905 return EXIT_FAILURE;
1906 }
1907
1908 /* success */
1909 return EXIT_SUCCESS;
1910}