blob: a486486d6cf53f1e421b3de1187481322a26cf85 [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;
89
90 /* create header */
91 builder = json_builder_new ();
92 json_builder_begin_object (builder);
93
94 /* add each device */
95 json_builder_set_member_name (builder, "Devices");
96 json_builder_begin_array (builder);
97 for (guint i = 0; i < devices->len; i++) {
98 FwupdDevice *dev = g_ptr_array_index (devices, i);
99 json_builder_begin_object (builder);
100 fwupd_device_to_json (dev, builder);
101 json_builder_end_object (builder);
102 }
103 json_builder_end_array (builder);
104 json_builder_end_object (builder);
105
106 /* export as a string */
107 json_root = json_builder_get_root (builder);
108 json_generator = json_generator_new ();
109 json_generator_set_pretty (json_generator, TRUE);
110 json_generator_set_root (json_generator, json_root);
111 state = json_generator_to_data (json_generator, NULL);
112 if (state == NULL)
113 return FALSE;
114 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
115 filename = g_build_filename (dirname, "state.json", NULL);
116 return g_file_set_contents (filename, state, -1, error);
117}
118
119static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000120fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600121{
122 g_autoptr(GError) error_local = NULL;
123
Richard Hughesd92ccca2019-05-20 11:28:31 +0100124#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100125 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500126 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100127#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000128 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000129 return FALSE;
130 if (fu_engine_get_tainted (priv->engine)) {
131 g_printerr ("WARNING: This tool has loaded 3rd party code and "
132 "is no longer supported by the upstream developers!\n");
133 }
134 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500135}
136
Richard Hughesb5976832018-05-18 10:02:09 +0100137static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500138fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
139{
140 g_autofree gchar *path = g_path_get_dirname (value);
141 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
142 g_prefix_error (error,
143 "Unable to access %s. You may need to copy %s to %s: ",
144 path, value, g_getenv ("HOME"));
145 }
146}
147
148static void
Richard Hughesb5976832018-05-18 10:02:09 +0100149fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
150{
151 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
152 /* TRANSLATORS: this is when a device ctrl+c's a watch */
153 g_print ("%s\n", _("Cancelled"));
154 g_main_loop_quit (priv->loop);
155}
156
157static gboolean
158fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
159{
160 g_autofree gchar *tmp = NULL;
161 g_autoptr(FuSmbios) smbios = NULL;
162 if (g_strv_length (values) < 1) {
163 g_set_error_literal (error,
164 FWUPD_ERROR,
165 FWUPD_ERROR_INVALID_ARGS,
166 "Invalid arguments");
167 return FALSE;
168 }
169 smbios = fu_smbios_new ();
170 if (!fu_smbios_setup_from_file (smbios, values[0], error))
171 return FALSE;
172 tmp = fu_smbios_to_string (smbios);
173 g_print ("%s\n", tmp);
174 return TRUE;
175}
176
Richard Hughes9e5675e2019-11-22 09:35:03 +0000177#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +0100178static gboolean
179fu_util_sigint_cb (gpointer user_data)
180{
181 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
182 g_debug ("Handling SIGINT");
183 g_cancellable_cancel (priv->cancellable);
184 return FALSE;
185}
Richard Hughes9e5675e2019-11-22 09:35:03 +0000186#endif
Richard Hughesb5976832018-05-18 10:02:09 +0100187
188static void
189fu_util_private_free (FuUtilPrivate *priv)
190{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500191 if (priv->current_device != NULL)
192 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100193 if (priv->engine != NULL)
194 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100195 if (priv->loop != NULL)
196 g_main_loop_unref (priv->loop);
197 if (priv->cancellable != NULL)
198 g_object_unref (priv->cancellable);
199 if (priv->progressbar != NULL)
200 g_object_unref (priv->progressbar);
201 if (priv->context != NULL)
202 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600203 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100204 g_free (priv);
205}
206
207#pragma clang diagnostic push
208#pragma clang diagnostic ignored "-Wunused-function"
209G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
210#pragma clang diagnostic pop
211
Richard Hughes98ca9932018-05-18 10:24:07 +0100212
213static void
214fu_main_engine_device_added_cb (FuEngine *engine,
215 FuDevice *device,
216 FuUtilPrivate *priv)
217{
218 g_autofree gchar *tmp = fu_device_to_string (device);
219 g_debug ("ADDED:\n%s", tmp);
220}
221
222static void
223fu_main_engine_device_removed_cb (FuEngine *engine,
224 FuDevice *device,
225 FuUtilPrivate *priv)
226{
227 g_autofree gchar *tmp = fu_device_to_string (device);
228 g_debug ("REMOVED:\n%s", tmp);
229}
230
231static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100232fu_main_engine_status_changed_cb (FuEngine *engine,
233 FwupdStatus status,
234 FuUtilPrivate *priv)
235{
236 fu_progressbar_update (priv->progressbar, status, 0);
237}
238
239static void
240fu_main_engine_percentage_changed_cb (FuEngine *engine,
241 guint percentage,
242 FuUtilPrivate *priv)
243{
244 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
245}
246
247static gboolean
248fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
249{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000250 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100251 return FALSE;
252 g_main_loop_run (priv->loop);
253 return TRUE;
254}
255
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100256static gint
257fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
258{
259 return fu_plugin_name_compare (*item1, *item2);
260}
261
262static gboolean
263fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
264{
265 GPtrArray *plugins;
266 guint cnt = 0;
267
268 /* load engine */
269 if (!fu_engine_load_plugins (priv->engine, error))
270 return FALSE;
271
272 /* print */
273 plugins = fu_engine_get_plugins (priv->engine);
274 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
275 for (guint i = 0; i < plugins->len; i++) {
276 FuPlugin *plugin = g_ptr_array_index (plugins, i);
277 if (!fu_plugin_get_enabled (plugin))
278 continue;
279 g_print ("%s\n", fu_plugin_get_name (plugin));
280 cnt++;
281 }
282 if (cnt == 0) {
283 /* TRANSLATORS: nothing found */
284 g_print ("%s\n", _("No plugins found"));
285 return TRUE;
286 }
287
288 return TRUE;
289}
290
Richard Hughes98ca9932018-05-18 10:24:07 +0100291static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100292fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
293{
294 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
295 if (!fwupd_device_has_flag (dev, priv->filter_include))
296 return FALSE;
297 }
298 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
299 if (fwupd_device_has_flag (dev, priv->filter_exclude))
300 return FALSE;
301 }
302 return TRUE;
303}
304
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500305static gchar *
306fu_util_get_tree_title (FuUtilPrivate *priv)
307{
308 return g_strdup (fu_engine_get_host_product (priv->engine));
309}
310
Richard Hughes747f5702019-08-06 14:27:26 +0100311static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600312fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
313{
314 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500315 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500316 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600317
318 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000319 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600320 return FALSE;
321
322 /* get devices from daemon */
323 devices = fu_engine_get_devices (priv->engine, error);
324 if (devices == NULL)
325 return FALSE;
326 for (guint i = 0; i < devices->len; i++) {
327 FwupdDevice *dev = g_ptr_array_index (devices, i);
328 g_autoptr(GPtrArray) rels = NULL;
329 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500330 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600331
Richard Hughes747f5702019-08-06 14:27:26 +0100332 /* not going to have results, so save a engine round-trip */
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600333 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
334 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100335 if (!fu_util_filter_device (priv, dev))
336 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600337
338 /* get the releases for this device and filter for validity */
339 rels = fu_engine_get_upgrades (priv->engine,
340 fwupd_device_get_id (dev),
341 &error_local);
342 if (rels == NULL) {
343 g_printerr ("%s\n", error_local->message);
344 continue;
345 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500346 child = g_node_append_data (root, dev);
347
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600348 for (guint j = 0; j < rels->len; j++) {
349 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500350 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600351 }
352 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500353 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500354 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600355 /* save the device state for other applications to see */
356 if (!fu_util_save_current_state (priv, error))
357 return FALSE;
358
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600359 /* success */
360 return TRUE;
361}
362
363static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500364fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
365{
366 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500367 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500368 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500369 gint fd;
370
371 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000372 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500373 return FALSE;
374
375 /* check args */
376 if (g_strv_length (values) != 1) {
377 g_set_error_literal (error,
378 FWUPD_ERROR,
379 FWUPD_ERROR_INVALID_ARGS,
380 "Invalid arguments");
381 return FALSE;
382 }
383
384 /* open file */
385 fd = open (values[0], O_RDONLY);
386 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500387 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500388 g_set_error (error,
389 FWUPD_ERROR,
390 FWUPD_ERROR_INVALID_FILE,
391 "failed to open %s",
392 values[0]);
393 return FALSE;
394 }
395 array = fu_engine_get_details (priv->engine, fd, error);
396 close (fd);
397
398 if (array == NULL)
399 return FALSE;
400 for (guint i = 0; i < array->len; i++) {
401 FwupdDevice *dev = g_ptr_array_index (array, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100402 if (!fu_util_filter_device (priv, dev))
403 continue;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500404 g_node_append_data (root, dev);
Mario Limonciello716ab272018-05-29 12:34:37 -0500405 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500406 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500407
Mario Limonciello716ab272018-05-29 12:34:37 -0500408 return TRUE;
409}
410
411static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100412fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
413{
414 g_autoptr(GString) str = g_string_new (NULL);
415
416 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
417 const gchar *tmp = fwupd_device_flag_to_string (i);
418 if (tmp == NULL)
419 break;
420 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
421 g_string_append (str, " ");
422 g_string_append (str, tmp);
423 g_string_append (str, " ~");
424 g_string_append (str, tmp);
425 }
426 g_print ("%s\n", str->str);
427
428 return TRUE;
429}
430
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500431static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100432fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500433{
434 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100435 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100436 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
437 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500438 if (!priv->show_all_devices &&
439 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500440 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100441 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500442 GNode *child = g_node_append_data (root, dev_tmp);
443 fu_util_build_device_tree (priv, child, devs, dev_tmp);
444 }
445 }
446}
447
448static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100449fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500450{
451 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500452 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500453 g_autoptr(GPtrArray) devs = NULL;
454
455 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000456 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500457 return FALSE;
458
459 /* print */
460 devs = fu_engine_get_devices (priv->engine, error);
461 if (devs == NULL)
462 return FALSE;
463
464 /* print */
465 if (devs->len == 0) {
466 /* TRANSLATORS: nothing attached that can be upgraded */
467 g_print ("%s\n", _("No hardware detected with firmware update capability"));
468 return TRUE;
469 }
470 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500471 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500472
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100473 /* save the device state for other applications to see */
474 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500475}
476
Richard Hughes98ca9932018-05-18 10:24:07 +0100477static FuDevice *
478fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
479{
480 FuDevice *dev;
481 guint idx;
482 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100483 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100484
485 /* get devices from daemon */
486 devices = fu_engine_get_devices (priv->engine, error);
487 if (devices == NULL)
488 return NULL;
489
Richard Hughes747f5702019-08-06 14:27:26 +0100490 /* filter results */
491 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
492 for (guint i = 0; i < devices->len; i++) {
493 dev = g_ptr_array_index (devices, i);
494 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
495 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100496 g_ptr_array_add (devices_filtered, g_object_ref (dev));
497 }
498
499 /* nothing */
500 if (devices_filtered->len == 0) {
501 g_set_error_literal (error,
502 FWUPD_ERROR,
503 FWUPD_ERROR_NOTHING_TO_DO,
504 "No supported devices");
505 return NULL;
506 }
507
Richard Hughes98ca9932018-05-18 10:24:07 +0100508 /* exactly one */
Richard Hughes747f5702019-08-06 14:27:26 +0100509 if (devices_filtered->len == 1) {
510 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limoncielloa61b4d82019-10-22 08:37:45 -0500511 /* TRANSLATORS: Device has been chosen by the daemon for the user */
512 g_print ("%s: %s\n", _("Selected device"), fu_device_get_name (dev));
Richard Hughes98ca9932018-05-18 10:24:07 +0100513 return g_object_ref (dev);
514 }
515
516 /* TRANSLATORS: get interactive prompt */
517 g_print ("%s\n", _("Choose a device:"));
518 /* TRANSLATORS: this is to abort the interactive prompt */
519 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100520 for (guint i = 0; i < devices_filtered->len; i++) {
521 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100522 g_print ("%u.\t%s (%s)\n",
523 i + 1,
524 fu_device_get_id (dev),
525 fu_device_get_name (dev));
526 }
Richard Hughes747f5702019-08-06 14:27:26 +0100527 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100528 if (idx == 0) {
529 g_set_error_literal (error,
530 FWUPD_ERROR,
531 FWUPD_ERROR_NOTHING_TO_DO,
532 "Request canceled");
533 return NULL;
534 }
Richard Hughes747f5702019-08-06 14:27:26 +0100535 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100536 return g_object_ref (dev);
537}
538
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500539static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600540fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500541 FwupdDevice *device,
542 FuUtilPrivate *priv)
543{
544 g_autofree gchar *str = NULL;
545
Richard Hughes809abea2019-03-23 11:06:18 +0000546 /* allowed to set whenever the device has changed */
547 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
548 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
549 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
550 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
551
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500552 /* same as last time, so ignore */
553 if (priv->current_device != NULL &&
554 fwupd_device_compare (priv->current_device, device) == 0)
555 return;
556
557 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600558 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
559 /* TRANSLATORS: %1 is a device name */
560 str = g_strdup_printf (_("Updating %s…"),
561 fwupd_device_get_name (device));
562 fu_progressbar_set_title (priv->progressbar, str);
563 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
564 /* TRANSLATORS: %1 is a device name */
565 str = g_strdup_printf (_("Installing on %s…"),
566 fwupd_device_get_name (device));
567 fu_progressbar_set_title (priv->progressbar, str);
Richard Hughesa58510b2019-10-30 10:03:12 +0000568 } else if (priv->current_operation == FU_UTIL_OPERATION_READ) {
569 /* TRANSLATORS: %1 is a device name */
570 str = g_strdup_printf (_("Reading from %s…"),
571 fwupd_device_get_name (device));
572 fu_progressbar_set_title (priv->progressbar, str);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600573 } else {
574 g_warning ("no FuUtilOperation set");
575 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500576 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600577
Mario Limonciello32241f42019-01-24 10:12:41 -0600578 if (priv->current_message == NULL) {
579 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
580 if (tmp != NULL)
581 priv->current_message = g_strdup (tmp);
582 }
583}
584
585static void
586fu_util_display_current_message (FuUtilPrivate *priv)
587{
588 if (priv->current_message == NULL)
589 return;
590 g_print ("%s\n", priv->current_message);
591 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500592}
593
Richard Hughes98ca9932018-05-18 10:24:07 +0100594static gboolean
595fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
596{
597 g_autoptr(FuDevice) device = NULL;
598 g_autoptr(GBytes) blob_fw = NULL;
599
600 /* invalid args */
601 if (g_strv_length (values) == 0) {
602 g_set_error_literal (error,
603 FWUPD_ERROR,
604 FWUPD_ERROR_INVALID_ARGS,
605 "Invalid arguments");
606 return FALSE;
607 }
608
609 /* parse blob */
610 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500611 if (blob_fw == NULL) {
612 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100613 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500614 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100615
616 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000617 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100618 return FALSE;
619
620 /* get device */
621 if (g_strv_length (values) >= 2) {
622 device = fu_engine_get_device (priv->engine, values[1], error);
623 if (device == NULL)
624 return FALSE;
625 } else {
626 device = fu_util_prompt_for_device (priv, error);
627 if (device == NULL)
628 return FALSE;
629 }
630
Mario Limonciello3f243a92019-01-21 22:05:23 -0600631 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500632 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600633 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500634
Richard Hughes98ca9932018-05-18 10:24:07 +0100635 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000636 if (priv->prepare_blob) {
637 g_autoptr(GPtrArray) devices = NULL;
638 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
639 g_ptr_array_add (devices, g_object_ref (device));
640 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
641 g_prefix_error (error, "failed to prepare composite action: ");
642 return FALSE;
643 }
644 }
Mario Limonciello035818b2019-03-26 11:12:16 -0500645 priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000646 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600647 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000648 if (priv->cleanup_blob) {
649 g_autoptr(FuDevice) device_new = NULL;
650 g_autoptr(GError) error_local = NULL;
651
652 /* get the possibly new device from the old ID */
653 device_new = fu_engine_get_device (priv->engine,
654 fu_device_get_id (device),
655 &error_local);
656 if (device_new == NULL) {
657 g_debug ("failed to find new device: %s",
658 error_local->message);
659 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600660 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000661 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
662 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
663 g_prefix_error (error, "failed to cleanup composite action: ");
664 return FALSE;
665 }
666 }
667 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600668
Mario Limonciello32241f42019-01-24 10:12:41 -0600669 fu_util_display_current_message (priv);
670
Mario Limonciello3f243a92019-01-21 22:05:23 -0600671 /* success */
672 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100673}
674
Richard Hughesa58510b2019-10-30 10:03:12 +0000675static gboolean
676fu_util_firmware_read (FuUtilPrivate *priv, gchar **values, GError **error)
677{
678 g_autoptr(FuDevice) device = NULL;
679 g_autoptr(GBytes) blob_empty = g_bytes_new (NULL, 0);
680 g_autoptr(GBytes) blob_fw = NULL;
681
682 /* invalid args */
683 if (g_strv_length (values) == 0) {
684 g_set_error_literal (error,
685 FWUPD_ERROR,
686 FWUPD_ERROR_INVALID_ARGS,
687 "Invalid arguments");
688 return FALSE;
689 }
690
691 /* file already exists */
692 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
693 g_file_test (values[0], G_FILE_TEST_EXISTS)) {
694 g_set_error_literal (error,
695 FWUPD_ERROR,
696 FWUPD_ERROR_INVALID_ARGS,
697 "Filename already exists");
698 return FALSE;
699 }
700
Richard Hughes02792c02019-11-01 14:21:20 +0000701 /* write a zero length file to ensure the destination is writable to
Richard Hughesa58510b2019-10-30 10:03:12 +0000702 * avoid failing at the end of a potentially lengthy operation */
703 if (!fu_common_set_contents_bytes (values[0], blob_empty, error))
704 return FALSE;
705
706 /* load engine */
707 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
708 return FALSE;
709
710 /* get device */
711 if (g_strv_length (values) >= 2) {
712 device = fu_engine_get_device (priv->engine, values[1], error);
713 if (device == NULL)
714 return FALSE;
715 } else {
716 device = fu_util_prompt_for_device (priv, error);
717 if (device == NULL)
718 return FALSE;
719 }
720 priv->current_operation = FU_UTIL_OPERATION_READ;
721 g_signal_connect (priv->engine, "device-changed",
722 G_CALLBACK (fu_util_update_device_changed_cb), priv);
723
724 /* dump firmware */
725 blob_fw = fu_engine_firmware_read (priv->engine, device, priv->flags, error);
726 if (blob_fw == NULL)
727 return FALSE;
728 return fu_common_set_contents_bytes (values[0], blob_fw, error);
729}
730
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100731static gint
732fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
733{
734 FuInstallTask *task1 = *((FuInstallTask **) a);
735 FuInstallTask *task2 = *((FuInstallTask **) b);
736 return fu_install_task_compare (task1, task2);
737}
738
739static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100740fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
741{
Filipe Laínse0914272019-09-20 10:04:43 +0100742 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100743 { "curl", uri, "--output", fn, NULL },
744 { NULL } };
745 for (guint i = 0; argv[i][0] != NULL; i++) {
746 g_autoptr(GError) error_local = NULL;
747 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
748 g_debug ("%s", error_local->message);
749 continue;
750 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000751 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100752 }
753 g_set_error_literal (error,
754 FWUPD_ERROR,
755 FWUPD_ERROR_NOT_FOUND,
756 "no supported out-of-process downloaders found");
757 return FALSE;
758}
759
760static gchar *
761fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
762{
763 g_autofree gchar *filename = NULL;
764 g_autoptr(SoupURI) uri = NULL;
765
766 /* a local file */
767 uri = soup_uri_new (perhapsfn);
Richard Hughes45a00732019-11-22 16:57:14 +0000768 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
769 return g_strdup (perhapsfn);
Richard Hughes3d178be2018-08-30 11:14:24 +0100770 if (uri == NULL)
771 return g_strdup (perhapsfn);
772
773 /* download the firmware to a cachedir */
774 filename = fu_util_get_user_cache_path (perhapsfn);
775 if (!fu_common_mkdir_parent (filename, error))
776 return NULL;
777 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
778 return NULL;
779 return g_steal_pointer (&filename);
780}
781
782static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100783fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
784{
Richard Hughes3d178be2018-08-30 11:14:24 +0100785 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100786 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100787 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100788 g_autoptr(GPtrArray) devices_possible = NULL;
789 g_autoptr(GPtrArray) errors = NULL;
790 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100791 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100792
Mario Limonciello8949e892018-05-25 08:03:06 -0500793 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000794 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500795 return FALSE;
796
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100797 /* handle both forms */
798 if (g_strv_length (values) == 1) {
799 devices_possible = fu_engine_get_devices (priv->engine, error);
800 if (devices_possible == NULL)
801 return FALSE;
802 } else if (g_strv_length (values) == 2) {
803 FuDevice *device = fu_engine_get_device (priv->engine,
804 values[1],
805 error);
806 if (device == NULL)
807 return FALSE;
808 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
809 g_ptr_array_add (devices_possible, device);
810 } else {
811 g_set_error_literal (error,
812 FWUPD_ERROR,
813 FWUPD_ERROR_INVALID_ARGS,
814 "Invalid arguments");
815 return FALSE;
816 }
817
Richard Hughes3d178be2018-08-30 11:14:24 +0100818 /* download if required */
819 filename = fu_util_download_if_required (priv, values[0], error);
820 if (filename == NULL)
821 return FALSE;
822
Richard Hughes481aa2a2018-09-18 20:51:46 +0100823 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100824 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500825 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100826 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100827 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500828 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100829 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
830 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100831 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600832 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100833 if (components == NULL)
834 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100835
Richard Hughes481aa2a2018-09-18 20:51:46 +0100836 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100837 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
838 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100839 for (guint i = 0; i < components->len; i++) {
840 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100841
842 /* do any devices pass the requirements */
843 for (guint j = 0; j < devices_possible->len; j++) {
844 FuDevice *device = g_ptr_array_index (devices_possible, j);
845 g_autoptr(FuInstallTask) task = NULL;
846 g_autoptr(GError) error_local = NULL;
847
848 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100849 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100850 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100851 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100852 &error_local)) {
853 g_debug ("requirement on %s:%s failed: %s",
854 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100855 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100856 error_local->message);
857 g_ptr_array_add (errors, g_steal_pointer (&error_local));
858 continue;
859 }
860
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600861 /* if component should have an update message from CAB */
862 fu_device_incorporate_from_component (device, component);
863
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100864 /* success */
865 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
866 }
867 }
868
869 /* order the install tasks by the device priority */
870 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
871
872 /* nothing suitable */
873 if (install_tasks->len == 0) {
874 GError *error_tmp = fu_common_error_array_get_best (errors);
875 g_propagate_error (error, error_tmp);
876 return FALSE;
877 }
878
Mario Limonciello3f243a92019-01-21 22:05:23 -0600879 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500880 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600881 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500882
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100883 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100884 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
885 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100886
Mario Limonciello32241f42019-01-24 10:12:41 -0600887 fu_util_display_current_message (priv);
888
Mario Limonciello3f243a92019-01-21 22:05:23 -0600889 /* we don't want to ask anything */
890 if (priv->no_reboot_check) {
891 g_debug ("skipping reboot check");
892 return TRUE;
893 }
894
Mario Limonciello3143bad2019-02-27 07:31:00 -0600895 /* save the device state for other applications to see */
896 if (!fu_util_save_current_state (priv, error))
897 return FALSE;
898
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100899 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600900 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100901}
902
Richard Hughes98ca9932018-05-18 10:24:07 +0100903static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500904fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600905{
Mario Limonciellofd734852019-08-01 16:41:42 -0500906 FwupdRemote *remote;
907 const gchar *remote_id;
908 const gchar *uri_tmp;
909 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600910
Mario Limonciellofd734852019-08-01 16:41:42 -0500911 uri_tmp = fwupd_release_get_uri (rel);
912 if (uri_tmp == NULL) {
913 g_set_error_literal (error,
914 FWUPD_ERROR,
915 FWUPD_ERROR_INVALID_FILE,
916 "release missing URI");
917 return FALSE;
918 }
919 remote_id = fwupd_release_get_remote_id (rel);
920 if (remote_id == NULL) {
921 g_set_error (error,
922 FWUPD_ERROR,
923 FWUPD_ERROR_INVALID_FILE,
924 "failed to find remote for %s",
925 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +0100926 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -0500927 }
928
929 remote = fu_engine_get_remote_by_id (priv->engine,
930 remote_id,
931 error);
932 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600933 return FALSE;
934
Mario Limonciellofd734852019-08-01 16:41:42 -0500935 argv = g_new0 (gchar *, 2);
936 /* local remotes have the firmware already */
937 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
938 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
939 g_autofree gchar *path = g_path_get_dirname (fn_cache);
940 argv[0] = g_build_filename (path, uri_tmp, NULL);
941 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
942 argv[0] = g_strdup (uri_tmp + 7);
943 /* web remote, fu_util_install will download file */
944 } else {
945 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
946 }
947 return fu_util_install (priv, argv, error);
948}
949
950static gboolean
951fu_util_update_all (FuUtilPrivate *priv, GError **error)
952{
953 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600954
Mario Limonciello46aaee82019-01-10 12:58:00 -0600955 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +0000956 if (devices == NULL)
957 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600958 for (guint i = 0; i < devices->len; i++) {
959 FwupdDevice *dev = g_ptr_array_index (devices, i);
960 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600961 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600962 g_autoptr(GPtrArray) rels = NULL;
963 g_autoptr(GError) error_local = NULL;
964
965 if (!fu_util_is_interesting_device (dev))
966 continue;
967 /* only show stuff that has metadata available */
968 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
969 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100970 if (!fu_util_filter_device (priv, dev))
971 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600972
973 device_id = fu_device_get_id (dev);
974 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
975 if (rels == NULL) {
976 g_printerr ("%s\n", error_local->message);
977 continue;
978 }
979
Mario Limonciello98b95162019-10-30 09:20:43 -0500980 if (!priv->no_safety_check) {
981 if (!fu_util_prompt_warning (dev,
982 fu_util_get_tree_title (priv),
983 error))
984 return FALSE;
985 }
986
Mario Limonciello46aaee82019-01-10 12:58:00 -0600987 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -0500988 if (!fu_util_install_release (priv, rel, &error_local)) {
989 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +0100990 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600991 }
Mario Limonciellofd734852019-08-01 16:41:42 -0500992 fu_util_display_current_message (priv);
993 }
994 return TRUE;
995}
996
997static gboolean
998fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
999{
1000 FwupdRelease *rel;
1001 g_autoptr(FuDevice) dev = NULL;
1002 g_autoptr(GPtrArray) rels = NULL;
1003
1004 /* do not allow a partial device-id */
1005 dev = fu_engine_get_device (priv->engine, device_id, error);
1006 if (dev == NULL)
1007 return FALSE;
1008
1009 /* get the releases for this device and filter for validity */
1010 rels = fu_engine_get_upgrades (priv->engine, device_id, error);
1011 if (rels == NULL)
1012 return FALSE;
1013 rel = g_ptr_array_index (rels, 0);
1014 if (!fu_util_install_release (priv, rel, error))
1015 return FALSE;
1016 fu_util_display_current_message (priv);
1017
1018 return TRUE;
1019}
1020
1021static gboolean
1022fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1023{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001024 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1025 g_set_error_literal (error,
1026 FWUPD_ERROR,
1027 FWUPD_ERROR_INVALID_ARGS,
1028 "--allow-older is not supported for this command");
1029 return FALSE;
1030 }
1031
1032 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1033 g_set_error_literal (error,
1034 FWUPD_ERROR,
1035 FWUPD_ERROR_INVALID_ARGS,
1036 "--allow-reinstall is not supported for this command");
1037 return FALSE;
1038 }
1039
Mario Limonciellofd734852019-08-01 16:41:42 -05001040 if (g_strv_length (values) > 1) {
1041 g_set_error_literal (error,
1042 FWUPD_ERROR,
1043 FWUPD_ERROR_INVALID_ARGS,
1044 "Invalid arguments");
1045 return FALSE;
1046 }
1047
1048 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1049 return FALSE;
1050
1051 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1052 g_signal_connect (priv->engine, "device-changed",
1053 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1054
1055 if (g_strv_length (values) == 1) {
1056 if (!fu_util_update_by_id (priv, values[0], error))
1057 return FALSE;
1058 } else {
1059 if (!fu_util_update_all (priv, error))
1060 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -06001061 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001062
1063 /* we don't want to ask anything */
1064 if (priv->no_reboot_check) {
1065 g_debug ("skipping reboot check");
1066 return TRUE;
1067 }
1068
Mario Limonciello3143bad2019-02-27 07:31:00 -06001069 /* save the device state for other applications to see */
1070 if (!fu_util_save_current_state (priv, error))
1071 return FALSE;
1072
Mario Limonciello3f243a92019-01-21 22:05:23 -06001073 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -06001074}
1075
1076static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +01001077fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
1078{
1079 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001080 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001081
1082 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001083 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001084 return FALSE;
1085
Richard Hughes98ca9932018-05-18 10:24:07 +01001086 /* get device */
1087 if (g_strv_length (values) >= 1) {
1088 device = fu_engine_get_device (priv->engine, values[0], error);
1089 if (device == NULL)
1090 return FALSE;
1091 } else {
1092 device = fu_util_prompt_for_device (priv, error);
1093 if (device == NULL)
1094 return FALSE;
1095 }
1096
1097 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001098 locker = fu_device_locker_new (device, error);
1099 if (locker == NULL)
1100 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001101 return fu_device_detach (device, error);
1102}
1103
1104static gboolean
1105fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1106{
1107 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001108 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001109
1110 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001111 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001112 return FALSE;
1113
Richard Hughes98ca9932018-05-18 10:24:07 +01001114 /* get device */
1115 if (g_strv_length (values) >= 1) {
1116 device = fu_engine_get_device (priv->engine, values[0], error);
1117 if (device == NULL)
1118 return FALSE;
1119 } else {
1120 device = fu_util_prompt_for_device (priv, error);
1121 if (device == NULL)
1122 return FALSE;
1123 }
1124
1125 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001126 locker = fu_device_locker_new (device, error);
1127 if (locker == NULL)
1128 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001129 return fu_device_attach (device, error);
1130}
1131
Richard Hughes1d894f12018-08-31 13:05:51 +01001132static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001133fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1134{
1135 gboolean has_pending = FALSE;
1136 g_autoptr(FuHistory) history = fu_history_new ();
1137 g_autoptr(GPtrArray) devices = NULL;
1138
1139 /* check the history database before starting the daemon */
1140 if (g_strv_length (values) == 0) {
1141 devices = fu_history_get_devices (history, error);
1142 if (devices == NULL)
1143 return FALSE;
1144 } else if (g_strv_length (values) == 1) {
1145 FuDevice *device;
1146 device = fu_history_get_device_by_id (history, values[0], error);
1147 if (device == NULL)
1148 return FALSE;
1149 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1150 g_ptr_array_add (devices, device);
1151 } else {
1152 g_set_error_literal (error,
1153 FWUPD_ERROR,
1154 FWUPD_ERROR_INVALID_ARGS,
1155 "Invalid arguments");
1156 return FALSE;
1157 }
1158
1159 /* nothing to do */
1160 for (guint i = 0; i < devices->len; i++) {
1161 FuDevice *dev = g_ptr_array_index (devices, i);
1162 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1163 fu_engine_add_plugin_filter (priv->engine,
1164 fu_device_get_plugin (dev));
1165 has_pending = TRUE;
1166 }
1167 }
1168 if (!has_pending) {
1169 g_set_error_literal (error,
1170 FWUPD_ERROR,
1171 FWUPD_ERROR_NOTHING_TO_DO,
1172 "No firmware to activate");
1173 return FALSE;
1174
1175 }
1176
1177 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001178 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001179 return FALSE;
1180
1181 /* activate anything with _NEEDS_ACTIVATION */
1182 for (guint i = 0; i < devices->len; i++) {
1183 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001184 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1185 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001186 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1187 continue;
1188 /* TRANSLATORS: shown when shutting down to switch to the new version */
1189 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1190 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1191 return FALSE;
1192 }
1193
1194 return TRUE;
1195}
1196
1197static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001198fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1199{
1200 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1201 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1202 const gchar *hwid_keys[] = {
1203 FU_HWIDS_KEY_BIOS_VENDOR,
1204 FU_HWIDS_KEY_BIOS_VERSION,
1205 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1206 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1207 FU_HWIDS_KEY_MANUFACTURER,
1208 FU_HWIDS_KEY_FAMILY,
1209 FU_HWIDS_KEY_PRODUCT_NAME,
1210 FU_HWIDS_KEY_PRODUCT_SKU,
1211 FU_HWIDS_KEY_ENCLOSURE_KIND,
1212 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1213 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1214 NULL };
1215
1216 /* read DMI data */
1217 if (g_strv_length (values) == 0) {
1218 if (!fu_smbios_setup (smbios, error))
1219 return FALSE;
1220 } else if (g_strv_length (values) == 1) {
1221 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1222 return FALSE;
1223 } else {
1224 g_set_error_literal (error,
1225 FWUPD_ERROR,
1226 FWUPD_ERROR_INVALID_ARGS,
1227 "Invalid arguments");
1228 return FALSE;
1229 }
1230 if (!fu_hwids_setup (hwids, smbios, error))
1231 return FALSE;
1232
1233 /* show debug output */
1234 g_print ("Computer Information\n");
1235 g_print ("--------------------\n");
1236 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1237 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1238 if (tmp == NULL)
1239 continue;
1240 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1241 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1242 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1243 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1244 } else {
1245 g_print ("%s: %s\n", hwid_keys[i], tmp);
1246 }
1247 }
1248
1249 /* show GUIDs */
1250 g_print ("\nHardware IDs\n");
1251 g_print ("------------\n");
1252 for (guint i = 0; i < 15; i++) {
1253 const gchar *keys = NULL;
1254 g_autofree gchar *guid = NULL;
1255 g_autofree gchar *key = NULL;
1256 g_autofree gchar *keys_str = NULL;
1257 g_auto(GStrv) keysv = NULL;
1258 g_autoptr(GError) error_local = NULL;
1259
1260 /* get the GUID */
1261 key = g_strdup_printf ("HardwareID-%u", i);
1262 keys = fu_hwids_get_replace_keys (hwids, key);
1263 guid = fu_hwids_get_guid (hwids, key, &error_local);
1264 if (guid == NULL) {
1265 g_print ("%s\n", error_local->message);
1266 continue;
1267 }
1268
1269 /* show what makes up the GUID */
1270 keysv = g_strsplit (keys, "&", -1);
1271 keys_str = g_strjoinv (" + ", keysv);
1272 g_print ("{%s} <- %s\n", guid, keys_str);
1273 }
1274
1275 return TRUE;
1276}
1277
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001278static gboolean
1279fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1280{
1281 const gchar *script_fn = "startup.sh";
1282 const gchar *output_fn = "firmware.bin";
1283 g_autoptr(GBytes) archive_blob = NULL;
1284 g_autoptr(GBytes) firmware_blob = NULL;
1285 if (g_strv_length (values) < 2) {
1286 g_set_error_literal (error,
1287 FWUPD_ERROR,
1288 FWUPD_ERROR_INVALID_ARGS,
1289 "Invalid arguments");
1290 return FALSE;
1291 }
1292 archive_blob = fu_common_get_contents_bytes (values[0], error);
1293 if (archive_blob == NULL)
1294 return FALSE;
1295 if (g_strv_length (values) > 2)
1296 script_fn = values[2];
1297 if (g_strv_length (values) > 3)
1298 output_fn = values[3];
1299 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1300 if (firmware_blob == NULL)
1301 return FALSE;
1302 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1303}
1304
Richard Hughes3d607622019-03-07 16:59:27 +00001305static gboolean
1306fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1307{
1308 g_autofree gchar *sig = NULL;
1309
1310 /* check args */
1311 if (g_strv_length (values) != 1) {
1312 g_set_error_literal (error,
1313 FWUPD_ERROR,
1314 FWUPD_ERROR_INVALID_ARGS,
1315 "Invalid arguments: value expected");
1316 return FALSE;
1317 }
1318
1319 /* start engine */
1320 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1321 return FALSE;
1322 sig = fu_engine_self_sign (priv->engine, values[0],
1323 FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
1324 FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
1325 if (sig == NULL)
1326 return FALSE;
1327 g_print ("%s\n", sig);
1328 return TRUE;
1329}
1330
Mario Limonciello62f84862018-10-18 13:15:23 -05001331static void
1332fu_util_device_added_cb (FwupdClient *client,
1333 FwupdDevice *device,
1334 gpointer user_data)
1335{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001336 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001337 /* TRANSLATORS: this is when a device is hotplugged */
1338 g_print ("%s\n%s", _("Device added:"), tmp);
1339}
1340
1341static void
1342fu_util_device_removed_cb (FwupdClient *client,
1343 FwupdDevice *device,
1344 gpointer user_data)
1345{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001346 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001347 /* TRANSLATORS: this is when a device is hotplugged */
1348 g_print ("%s\n%s", _("Device removed:"), tmp);
1349}
1350
1351static void
1352fu_util_device_changed_cb (FwupdClient *client,
1353 FwupdDevice *device,
1354 gpointer user_data)
1355{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001356 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001357 /* TRANSLATORS: this is when a device has been updated */
1358 g_print ("%s\n%s", _("Device changed:"), tmp);
1359}
1360
1361static void
1362fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1363{
1364 /* TRANSLATORS: this is when the daemon state changes */
1365 g_print ("%s\n", _("Changed"));
1366}
1367
1368static gboolean
1369fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1370{
1371 g_autoptr(FwupdClient) client = fwupd_client_new ();
1372
1373 /* get all the devices */
1374 if (!fwupd_client_connect (client, priv->cancellable, error))
1375 return FALSE;
1376
1377 /* watch for any hotplugged device */
1378 g_signal_connect (client, "changed",
1379 G_CALLBACK (fu_util_changed_cb), priv);
1380 g_signal_connect (client, "device-added",
1381 G_CALLBACK (fu_util_device_added_cb), priv);
1382 g_signal_connect (client, "device-removed",
1383 G_CALLBACK (fu_util_device_removed_cb), priv);
1384 g_signal_connect (client, "device-changed",
1385 G_CALLBACK (fu_util_device_changed_cb), priv);
1386 g_signal_connect (priv->cancellable, "cancelled",
1387 G_CALLBACK (fu_util_cancelled_cb), priv);
1388 g_main_loop_run (priv->loop);
1389 return TRUE;
1390}
1391
Richard Hughes15684492019-03-15 16:27:50 +00001392static gboolean
Richard Hughes95c98a92019-10-22 16:03:15 +01001393fu_util_get_firmware_types (FuUtilPrivate *priv, gchar **values, GError **error)
1394{
1395 g_autoptr(GPtrArray) firmware_types = NULL;
1396
1397 /* load engine */
1398 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1399 return FALSE;
1400
1401 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1402 for (guint i = 0; i < firmware_types->len; i++) {
1403 const gchar *id = g_ptr_array_index (firmware_types, i);
1404 g_print ("%s\n", id);
1405 }
1406 if (firmware_types->len == 0) {
1407 /* TRANSLATORS: nothing found */
1408 g_print ("%s\n", _("No firmware IDs found"));
1409 return TRUE;
1410 }
1411
1412 return TRUE;
1413}
1414
1415static gchar *
1416fu_util_prompt_for_firmware_type (FuUtilPrivate *priv, GError **error)
1417{
1418 g_autoptr(GPtrArray) firmware_types = NULL;
1419 guint idx;
1420 firmware_types = fu_engine_get_firmware_gtype_ids (priv->engine);
1421
1422 /* TRANSLATORS: get interactive prompt */
1423 g_print ("%s\n", _("Choose a firmware type:"));
1424 /* TRANSLATORS: this is to abort the interactive prompt */
1425 g_print ("0.\t%s\n", _("Cancel"));
1426 for (guint i = 0; i < firmware_types->len; i++) {
1427 const gchar *id = g_ptr_array_index (firmware_types, i);
1428 g_print ("%u.\t%s\n", i + 1, id);
1429 }
1430 idx = fu_util_prompt_for_number (firmware_types->len);
1431 if (idx == 0) {
1432 g_set_error_literal (error,
1433 FWUPD_ERROR,
1434 FWUPD_ERROR_NOTHING_TO_DO,
1435 "Request canceled");
1436 return NULL;
1437 }
1438
1439 return g_strdup (g_ptr_array_index (firmware_types, idx - 1));
1440}
1441
1442static gboolean
1443fu_util_firmware_parse (FuUtilPrivate *priv, gchar **values, GError **error)
1444{
1445 GType gtype;
1446 g_autoptr(GBytes) blob = NULL;
1447 g_autoptr(FuFirmware) firmware = NULL;
1448 g_autofree gchar *firmware_type = NULL;
1449 g_autofree gchar *str = NULL;
1450
1451 /* check args */
1452 if (g_strv_length (values) == 0 || g_strv_length (values) > 2) {
1453 g_set_error_literal (error,
1454 FWUPD_ERROR,
1455 FWUPD_ERROR_INVALID_ARGS,
1456 "Invalid arguments: filename required");
1457 return FALSE;
1458 }
1459
1460 if (g_strv_length (values) == 2)
1461 firmware_type = g_strdup (values[1]);
1462
1463 /* load file */
1464 blob = fu_common_get_contents_bytes (values[0], error);
1465 if (blob == NULL)
1466 return FALSE;
1467
1468 /* load engine */
1469 if (!fu_engine_load (priv->engine, FU_ENGINE_LOAD_FLAG_NO_ENUMERATE, error))
1470 return FALSE;
1471
1472 /* find the GType to use */
1473 if (firmware_type == NULL)
1474 firmware_type = fu_util_prompt_for_firmware_type (priv, error);
1475 if (firmware_type == NULL)
1476 return FALSE;
1477 gtype = fu_engine_get_firmware_gtype_by_id (priv->engine, firmware_type);
1478 if (gtype == G_TYPE_INVALID) {
1479 g_set_error (error,
1480 G_IO_ERROR,
1481 G_IO_ERROR_NOT_FOUND,
1482 "GType %s not supported", firmware_type);
1483 return FALSE;
1484 }
1485 firmware = g_object_new (gtype, NULL);
1486 if (!fu_firmware_parse (firmware, blob, priv->flags, error))
1487 return FALSE;
1488 str = fu_firmware_to_string (firmware);
1489 g_print ("%s", str);
1490 return TRUE;
1491}
1492
1493static gboolean
Richard Hughes15684492019-03-15 16:27:50 +00001494fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1495{
1496 g_autofree gchar *str = NULL;
1497 g_autoptr(FuDevice) dev = NULL;
1498
1499 /* load engine */
1500 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1501 return FALSE;
1502
1503 /* get device */
1504 if (g_strv_length (values) == 1) {
1505 dev = fu_engine_get_device (priv->engine, values[1], error);
1506 if (dev == NULL)
1507 return FALSE;
1508 } else {
1509 dev = fu_util_prompt_for_device (priv, error);
1510 if (dev == NULL)
1511 return FALSE;
1512 }
1513
1514 /* add checksums */
1515 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1516 return FALSE;
1517
1518 /* show checksums */
1519 str = fu_device_to_string (dev);
1520 g_print ("%s\n", str);
1521 return TRUE;
1522}
1523
Mario Limonciellofe593942019-04-03 13:48:52 -05001524static gboolean
1525fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1526{
1527 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001528 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001529 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001530
1531 /* load engine */
1532 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1533 return FALSE;
1534
1535 /* get all devices from the history database */
1536 devices = fu_engine_get_history (priv->engine, error);
1537 if (devices == NULL)
1538 return FALSE;
1539
1540 /* show each device */
1541 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001542 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001543 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001544 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001545 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001546 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001547
Richard Hughes747f5702019-08-06 14:27:26 +01001548 if (!fu_util_filter_device (priv, dev))
1549 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001550 child = g_node_append_data (root, dev);
1551
1552 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001553 if (rel == NULL)
1554 continue;
1555 remote = fwupd_release_get_remote_id (rel);
1556
1557 /* doesn't actually map to remote */
1558 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001559 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001560 continue;
1561 }
1562
1563 /* try to lookup releases from client */
1564 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1565 if (rels == NULL)
1566 return FALSE;
1567
1568 /* map to a release in client */
1569 for (guint j = 0; j < rels->len; j++) {
1570 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1571 if (g_strcmp0 (remote,
1572 fwupd_release_get_remote_id (rel2)) != 0)
1573 continue;
1574 if (g_strcmp0 (fwupd_release_get_version (rel),
1575 fwupd_release_get_version (rel2)) != 0)
1576 continue;
1577 g_node_append_data (child, g_object_ref (rel2));
1578 rel = NULL;
1579 break;
1580 }
1581
1582 /* didn't match anything */
1583 if (rels->len == 0 || rel != NULL) {
1584 g_node_append_data (child, rel);
1585 continue;
1586 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001587
Mario Limonciellofe593942019-04-03 13:48:52 -05001588 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001589 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001590
1591 return TRUE;
1592}
1593
Richard Hughesb5976832018-05-18 10:02:09 +01001594int
1595main (int argc, char *argv[])
1596{
Richard Hughes460226a2018-05-21 20:56:21 +01001597 gboolean allow_older = FALSE;
1598 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001599 gboolean force = FALSE;
1600 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001601 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001602 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001603 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001604 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1605 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001606 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001607 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001608 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001609 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001610 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1611 /* TRANSLATORS: command line option */
1612 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001613 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1614 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001615 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001616 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1617 /* TRANSLATORS: command line option */
1618 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001619 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1620 /* TRANSLATORS: command line option */
1621 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06001622 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
1623 /* TRANSLATORS: command line option */
1624 _("Do not check for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05001625 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
1626 /* TRANSLATORS: command line option */
1627 _("Do not perform device safety checks"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001628 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1629 /* TRANSLATORS: command line option */
1630 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001631 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1632 /* TRANSLATORS: command line option */
1633 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001634 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001635 /* TRANSLATORS: command line option */
1636 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001637 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001638 /* TRANSLATORS: command line option */
1639 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06001640 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
1641 /* TRANSLATORS: command line option */
1642 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01001643 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
1644 /* TRANSLATORS: command line option */
1645 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01001646 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
1647 /* TRANSLATORS: command line option */
1648 _("Filter with a set of device flags using a ~ prefix to "
1649 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001650 { NULL}
1651 };
1652
1653 setlocale (LC_ALL, "");
1654
Richard Hughes668ee212019-11-22 09:17:46 +00001655 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughesb5976832018-05-18 10:02:09 +01001656 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1657 textdomain (GETTEXT_PACKAGE);
1658
Richard Hughes01c0bad2019-11-22 09:08:51 +00001659#ifdef HAVE_GETUID
Richard Hughesb5976832018-05-18 10:02:09 +01001660 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001661 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01001662 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001663 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughes01c0bad2019-11-22 09:08:51 +00001664#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001665
1666 /* create helper object */
1667 priv->loop = g_main_loop_new (NULL, FALSE);
1668 priv->progressbar = fu_progressbar_new ();
1669
1670 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00001671 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001672 "build-firmware",
1673 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1674 /* TRANSLATORS: command description */
1675 _("Build firmware using a sandbox"),
1676 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00001677 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001678 "smbios-dump",
1679 "FILE",
1680 /* TRANSLATORS: command description */
1681 _("Dump SMBIOS data from a file"),
1682 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00001683 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001684 "get-plugins",
1685 NULL,
1686 /* TRANSLATORS: command description */
1687 _("Get all enabled plugins registered with the system"),
1688 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00001689 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001690 "get-details",
1691 NULL,
1692 /* TRANSLATORS: command description */
1693 _("Gets details about a firmware file"),
1694 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00001695 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05001696 "get-history",
1697 NULL,
1698 /* TRANSLATORS: command description */
1699 _("Show history of firmware updates"),
1700 fu_util_get_history);
1701 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001702 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06001703 NULL,
1704 /* TRANSLATORS: command description */
1705 _("Gets the list of updates for connected hardware"),
1706 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00001707 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01001708 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01001709 NULL,
1710 /* TRANSLATORS: command description */
1711 _("Get all devices that support firmware updates"),
1712 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00001713 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01001714 "get-device-flags",
1715 NULL,
1716 /* TRANSLATORS: command description */
1717 _("Get all device flags supported by fwupd"),
1718 fu_util_get_device_flags);
1719 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001720 "watch",
1721 NULL,
1722 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02001723 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001724 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00001725 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001726 "install-blob",
1727 "FILENAME DEVICE-ID",
1728 /* TRANSLATORS: command description */
1729 _("Install a firmware blob on a device"),
1730 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00001731 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001732 "install",
1733 "FILE [ID]",
1734 /* TRANSLATORS: command description */
1735 _("Install a firmware file on this hardware"),
1736 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00001737 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001738 "attach",
1739 "DEVICE-ID",
1740 /* TRANSLATORS: command description */
1741 _("Attach to firmware mode"),
1742 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001743 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001744 "detach",
1745 "DEVICE-ID",
1746 /* TRANSLATORS: command description */
1747 _("Detach to bootloader mode"),
1748 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001749 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001750 "activate",
1751 "[DEVICE-ID]",
1752 /* TRANSLATORS: command description */
1753 _("Activate pending devices"),
1754 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00001755 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01001756 "hwids",
1757 "[FILE]",
1758 /* TRANSLATORS: command description */
1759 _("Return all the hardware IDs for the machine"),
1760 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00001761 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05001762 "monitor",
1763 NULL,
1764 /* TRANSLATORS: command description */
1765 _("Monitor the daemon for events"),
1766 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00001767 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001768 "update,upgrade",
Mario Limonciello46aaee82019-01-10 12:58:00 -06001769 NULL,
1770 /* TRANSLATORS: command description */
1771 _("Update all devices that match local metadata"),
1772 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00001773 fu_util_cmd_array_add (cmd_array,
1774 "self-sign",
1775 "TEXT",
1776 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00001777 C_("command-description",
1778 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00001779 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00001780 fu_util_cmd_array_add (cmd_array,
1781 "verify-update",
1782 "[DEVICE_ID]",
1783 /* TRANSLATORS: command description */
1784 _("Update the stored metadata with current contents"),
1785 fu_util_verify_update);
Richard Hughes95c98a92019-10-22 16:03:15 +01001786 fu_util_cmd_array_add (cmd_array,
Richard Hughesa58510b2019-10-30 10:03:12 +00001787 "firmware-read",
1788 "FILENAME [DEVICE-ID]",
1789 /* TRANSLATORS: command description */
1790 _("Read a firmware blob from a device"),
1791 fu_util_firmware_read);
1792 fu_util_cmd_array_add (cmd_array,
Richard Hughes95c98a92019-10-22 16:03:15 +01001793 "firmware-parse",
1794 "FILENAME [FIRMWARE_TYPE]",
1795 /* TRANSLATORS: command description */
1796 _("Parse and show details about a firmware file"),
1797 fu_util_firmware_parse);
1798 fu_util_cmd_array_add (cmd_array,
1799 "get-firmware-types",
1800 NULL,
1801 /* TRANSLATORS: command description */
1802 _("List the available firmware types"),
1803 fu_util_get_firmware_types);
Richard Hughesb5976832018-05-18 10:02:09 +01001804
1805 /* do stuff on ctrl+c */
1806 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00001807#ifdef HAVE_GIO_UNIX
Richard Hughesb5976832018-05-18 10:02:09 +01001808 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1809 SIGINT, fu_util_sigint_cb,
1810 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00001811#endif
Richard Hughesb5976832018-05-18 10:02:09 +01001812 g_signal_connect (priv->cancellable, "cancelled",
1813 G_CALLBACK (fu_util_cancelled_cb), priv);
1814
1815 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00001816 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001817
Mario Limonciello3f243a92019-01-21 22:05:23 -06001818 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001819 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06001820 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05001821 priv->no_safety_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06001822 fu_progressbar_set_interactive (priv->progressbar, FALSE);
1823 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001824
Richard Hughesb5976832018-05-18 10:02:09 +01001825 /* get a list of the commands */
1826 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00001827 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001828 g_option_context_set_summary (priv->context, cmd_descriptions);
1829 g_option_context_set_description (priv->context,
1830 "This tool allows an administrator to use the fwupd plugins "
1831 "without being installed on the host system.");
1832
1833 /* TRANSLATORS: program name */
1834 g_set_application_name (_("Firmware Utility"));
1835 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05001836 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01001837 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
1838 if (!ret) {
1839 /* TRANSLATORS: the user didn't read the man page */
1840 g_print ("%s: %s\n", _("Failed to parse arguments"),
1841 error->message);
1842 return EXIT_FAILURE;
1843 }
1844
Richard Hughes0e46b222019-09-05 12:13:35 +01001845 /* allow disabling SSL strict mode for broken corporate proxies */
1846 if (priv->disable_ssl_strict) {
1847 /* TRANSLATORS: try to help */
1848 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
1849 "to do this automatically in the future "
1850 "export DISABLE_SSL_STRICT in your environment"));
1851 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
1852 }
1853
Richard Hughes747f5702019-08-06 14:27:26 +01001854 /* parse filter flags */
1855 if (filter != NULL) {
1856 if (!fu_util_parse_filter_flags (filter,
1857 &priv->filter_include,
1858 &priv->filter_exclude,
1859 &error)) {
1860 /* TRANSLATORS: the user didn't read the man page */
1861 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
1862 error->message);
1863 return EXIT_FAILURE;
1864 }
1865 }
1866
1867
Richard Hughes460226a2018-05-21 20:56:21 +01001868 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01001869 if (allow_reinstall)
1870 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1871 if (allow_older)
1872 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
1873 if (force)
1874 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
1875
Richard Hughes98ca9932018-05-18 10:24:07 +01001876 /* load engine */
1877 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
1878 g_signal_connect (priv->engine, "device-added",
1879 G_CALLBACK (fu_main_engine_device_added_cb),
1880 priv);
1881 g_signal_connect (priv->engine, "device-removed",
1882 G_CALLBACK (fu_main_engine_device_removed_cb),
1883 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01001884 g_signal_connect (priv->engine, "status-changed",
1885 G_CALLBACK (fu_main_engine_status_changed_cb),
1886 priv);
1887 g_signal_connect (priv->engine, "percentage-changed",
1888 G_CALLBACK (fu_main_engine_percentage_changed_cb),
1889 priv);
1890
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001891 /* just show versions and exit */
1892 if (version) {
1893 g_autofree gchar *version_str = fu_util_get_versions ();
1894 g_print ("%s\n", version_str);
1895 return EXIT_SUCCESS;
1896 }
1897
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001898 /* any plugin whitelist specified */
1899 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
1900 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
1901
Richard Hughesb5976832018-05-18 10:02:09 +01001902 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00001903 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01001904 if (!ret) {
1905 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
1906 g_autofree gchar *tmp = NULL;
1907 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
1908 g_print ("%s\n\n%s", error->message, tmp);
1909 return EXIT_FAILURE;
1910 }
1911 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
1912 g_print ("%s\n", error->message);
1913 return EXIT_NOTHING_TO_DO;
1914 }
1915 g_print ("%s\n", error->message);
1916 return EXIT_FAILURE;
1917 }
1918
1919 /* success */
1920 return EXIT_SUCCESS;
1921}