blob: de05cdc874c4a41710434ed4161eee96982531bd [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,
42 FU_UTIL_OPERATION_LAST
43} FuUtilOperation;
44
Richard Hughesc77e1112019-03-01 10:16:26 +000045struct FuUtilPrivate {
Richard Hughesb5976832018-05-18 10:02:09 +010046 GCancellable *cancellable;
47 GMainLoop *loop;
48 GOptionContext *context;
Richard Hughes98ca9932018-05-18 10:24:07 +010049 FuEngine *engine;
Richard Hughesb5976832018-05-18 10:02:09 +010050 FuProgressbar *progressbar;
Mario Limonciello3f243a92019-01-21 22:05:23 -060051 gboolean no_reboot_check;
Mario Limonciello53ce25d2019-02-01 16:09:03 +000052 gboolean prepare_blob;
53 gboolean cleanup_blob;
Mario Limonciello3143bad2019-02-27 07:31:00 -060054 gboolean enable_json_state;
Richard Hughes460226a2018-05-21 20:56:21 +010055 FwupdInstallFlags flags;
Mario Limoncielloba9e5b92018-05-21 16:02:32 -050056 gboolean show_all_devices;
Richard Hughes0e46b222019-09-05 12:13:35 +010057 gboolean disable_ssl_strict;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050058 /* only valid in update and downgrade */
Mario Limonciello3f243a92019-01-21 22:05:23 -060059 FuUtilOperation current_operation;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -050060 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060061 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060062 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010063 FwupdDeviceFlags filter_include;
64 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000065};
Richard Hughesb5976832018-05-18 10:02:09 +010066
Mario Limoncielloe61c94d2018-10-11 10:49:55 -050067static gboolean
Mario Limonciello3143bad2019-02-27 07:31:00 -060068fu_util_save_current_state (FuUtilPrivate *priv, GError **error)
69{
70 g_autoptr(JsonBuilder) builder = NULL;
71 g_autoptr(JsonGenerator) json_generator = NULL;
72 g_autoptr(JsonNode) json_root = NULL;
73 g_autoptr(GPtrArray) devices = NULL;
74 g_autofree gchar *state = NULL;
75 g_autofree gchar *dirname = NULL;
76 g_autofree gchar *filename = NULL;
77
78 if (!priv->enable_json_state)
79 return TRUE;
80
81 devices = fu_engine_get_devices (priv->engine, error);
82 if (devices == NULL)
83 return FALSE;
84
85 /* create header */
86 builder = json_builder_new ();
87 json_builder_begin_object (builder);
88
89 /* add each device */
90 json_builder_set_member_name (builder, "Devices");
91 json_builder_begin_array (builder);
92 for (guint i = 0; i < devices->len; i++) {
93 FwupdDevice *dev = g_ptr_array_index (devices, i);
94 json_builder_begin_object (builder);
95 fwupd_device_to_json (dev, builder);
96 json_builder_end_object (builder);
97 }
98 json_builder_end_array (builder);
99 json_builder_end_object (builder);
100
101 /* export as a string */
102 json_root = json_builder_get_root (builder);
103 json_generator = json_generator_new ();
104 json_generator_set_pretty (json_generator, TRUE);
105 json_generator_set_root (json_generator, json_root);
106 state = json_generator_to_data (json_generator, NULL);
107 if (state == NULL)
108 return FALSE;
109 dirname = fu_common_get_path (FU_PATH_KIND_LOCALSTATEDIR_PKG);
110 filename = g_build_filename (dirname, "state.json", NULL);
111 return g_file_set_contents (filename, state, -1, error);
112}
113
114static gboolean
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000115fu_util_start_engine (FuUtilPrivate *priv, FuEngineLoadFlags flags, GError **error)
Mario Limoncielloe8dd4d72019-02-26 15:28:04 -0600116{
117 g_autoptr(GError) error_local = NULL;
118
Richard Hughesd92ccca2019-05-20 11:28:31 +0100119#ifdef HAVE_SYSTEMD
Richard Hughes3d005222019-05-17 14:02:41 +0100120 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), &error_local))
Mario Limonciello8692d012019-10-12 18:01:55 -0500121 g_debug ("Failed to stop daemon: %s", error_local->message);
Richard Hughesd92ccca2019-05-20 11:28:31 +0100122#endif
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000123 if (!fu_engine_load (priv->engine, flags, error))
Richard Hughesf425d292019-01-18 17:57:39 +0000124 return FALSE;
125 if (fu_engine_get_tainted (priv->engine)) {
126 g_printerr ("WARNING: This tool has loaded 3rd party code and "
127 "is no longer supported by the upstream developers!\n");
128 }
129 return TRUE;
Mario Limoncielloe61c94d2018-10-11 10:49:55 -0500130}
131
Richard Hughesb5976832018-05-18 10:02:09 +0100132static void
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500133fu_util_maybe_prefix_sandbox_error (const gchar *value, GError **error)
134{
135 g_autofree gchar *path = g_path_get_dirname (value);
136 if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
137 g_prefix_error (error,
138 "Unable to access %s. You may need to copy %s to %s: ",
139 path, value, g_getenv ("HOME"));
140 }
141}
142
143static void
Richard Hughesb5976832018-05-18 10:02:09 +0100144fu_util_cancelled_cb (GCancellable *cancellable, gpointer user_data)
145{
146 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
147 /* TRANSLATORS: this is when a device ctrl+c's a watch */
148 g_print ("%s\n", _("Cancelled"));
149 g_main_loop_quit (priv->loop);
150}
151
152static gboolean
153fu_util_smbios_dump (FuUtilPrivate *priv, gchar **values, GError **error)
154{
155 g_autofree gchar *tmp = NULL;
156 g_autoptr(FuSmbios) smbios = NULL;
157 if (g_strv_length (values) < 1) {
158 g_set_error_literal (error,
159 FWUPD_ERROR,
160 FWUPD_ERROR_INVALID_ARGS,
161 "Invalid arguments");
162 return FALSE;
163 }
164 smbios = fu_smbios_new ();
165 if (!fu_smbios_setup_from_file (smbios, values[0], error))
166 return FALSE;
167 tmp = fu_smbios_to_string (smbios);
168 g_print ("%s\n", tmp);
169 return TRUE;
170}
171
Richard Hughesb5976832018-05-18 10:02:09 +0100172static gboolean
173fu_util_sigint_cb (gpointer user_data)
174{
175 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
176 g_debug ("Handling SIGINT");
177 g_cancellable_cancel (priv->cancellable);
178 return FALSE;
179}
180
181static void
182fu_util_private_free (FuUtilPrivate *priv)
183{
Mario Limonciellocc50e1a2018-08-14 17:45:24 -0500184 if (priv->current_device != NULL)
185 g_object_unref (priv->current_device);
Richard Hughes98ca9932018-05-18 10:24:07 +0100186 if (priv->engine != NULL)
187 g_object_unref (priv->engine);
Richard Hughesb5976832018-05-18 10:02:09 +0100188 if (priv->loop != NULL)
189 g_main_loop_unref (priv->loop);
190 if (priv->cancellable != NULL)
191 g_object_unref (priv->cancellable);
192 if (priv->progressbar != NULL)
193 g_object_unref (priv->progressbar);
194 if (priv->context != NULL)
195 g_option_context_free (priv->context);
Mario Limonciello32241f42019-01-24 10:12:41 -0600196 g_free (priv->current_message);
Richard Hughesb5976832018-05-18 10:02:09 +0100197 g_free (priv);
198}
199
200#pragma clang diagnostic push
201#pragma clang diagnostic ignored "-Wunused-function"
202G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
203#pragma clang diagnostic pop
204
Richard Hughes98ca9932018-05-18 10:24:07 +0100205
206static void
207fu_main_engine_device_added_cb (FuEngine *engine,
208 FuDevice *device,
209 FuUtilPrivate *priv)
210{
211 g_autofree gchar *tmp = fu_device_to_string (device);
212 g_debug ("ADDED:\n%s", tmp);
213}
214
215static void
216fu_main_engine_device_removed_cb (FuEngine *engine,
217 FuDevice *device,
218 FuUtilPrivate *priv)
219{
220 g_autofree gchar *tmp = fu_device_to_string (device);
221 g_debug ("REMOVED:\n%s", tmp);
222}
223
224static void
Richard Hughes98ca9932018-05-18 10:24:07 +0100225fu_main_engine_status_changed_cb (FuEngine *engine,
226 FwupdStatus status,
227 FuUtilPrivate *priv)
228{
229 fu_progressbar_update (priv->progressbar, status, 0);
230}
231
232static void
233fu_main_engine_percentage_changed_cb (FuEngine *engine,
234 guint percentage,
235 FuUtilPrivate *priv)
236{
237 fu_progressbar_update (priv->progressbar, FWUPD_STATUS_UNKNOWN, percentage);
238}
239
240static gboolean
241fu_util_watch (FuUtilPrivate *priv, gchar **values, GError **error)
242{
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000243 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100244 return FALSE;
245 g_main_loop_run (priv->loop);
246 return TRUE;
247}
248
Richard Hughes8c71a3f2018-05-22 19:19:52 +0100249static gint
250fu_util_plugin_name_sort_cb (FuPlugin **item1, FuPlugin **item2)
251{
252 return fu_plugin_name_compare (*item1, *item2);
253}
254
255static gboolean
256fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
257{
258 GPtrArray *plugins;
259 guint cnt = 0;
260
261 /* load engine */
262 if (!fu_engine_load_plugins (priv->engine, error))
263 return FALSE;
264
265 /* print */
266 plugins = fu_engine_get_plugins (priv->engine);
267 g_ptr_array_sort (plugins, (GCompareFunc) fu_util_plugin_name_sort_cb);
268 for (guint i = 0; i < plugins->len; i++) {
269 FuPlugin *plugin = g_ptr_array_index (plugins, i);
270 if (!fu_plugin_get_enabled (plugin))
271 continue;
272 g_print ("%s\n", fu_plugin_get_name (plugin));
273 cnt++;
274 }
275 if (cnt == 0) {
276 /* TRANSLATORS: nothing found */
277 g_print ("%s\n", _("No plugins found"));
278 return TRUE;
279 }
280
281 return TRUE;
282}
283
Richard Hughes98ca9932018-05-18 10:24:07 +0100284static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100285fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
286{
287 if (priv->filter_include != FWUPD_DEVICE_FLAG_NONE) {
288 if (!fwupd_device_has_flag (dev, priv->filter_include))
289 return FALSE;
290 }
291 if (priv->filter_exclude != FWUPD_DEVICE_FLAG_NONE) {
292 if (fwupd_device_has_flag (dev, priv->filter_exclude))
293 return FALSE;
294 }
295 return TRUE;
296}
297
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500298static gchar *
299fu_util_get_tree_title (FuUtilPrivate *priv)
300{
301 return g_strdup (fu_engine_get_host_product (priv->engine));
302}
303
Richard Hughes747f5702019-08-06 14:27:26 +0100304static gboolean
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600305fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
306{
307 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500308 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500309 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600310
311 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000312 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600313 return FALSE;
314
315 /* get devices from daemon */
316 devices = fu_engine_get_devices (priv->engine, error);
317 if (devices == NULL)
318 return FALSE;
319 for (guint i = 0; i < devices->len; i++) {
320 FwupdDevice *dev = g_ptr_array_index (devices, i);
321 g_autoptr(GPtrArray) rels = NULL;
322 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500323 GNode *child;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600324
Richard Hughes747f5702019-08-06 14:27:26 +0100325 /* not going to have results, so save a engine round-trip */
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600326 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
327 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100328 if (!fu_util_filter_device (priv, dev))
329 continue;
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600330
331 /* get the releases for this device and filter for validity */
332 rels = fu_engine_get_upgrades (priv->engine,
333 fwupd_device_get_id (dev),
334 &error_local);
335 if (rels == NULL) {
336 g_printerr ("%s\n", error_local->message);
337 continue;
338 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500339 child = g_node_append_data (root, dev);
340
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600341 for (guint j = 0; j < rels->len; j++) {
342 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500343 g_node_append_data (child, g_object_ref (rel));
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600344 }
345 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500346 if (g_node_n_nodes (root, G_TRAVERSE_ALL) > 1)
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500347 fu_util_print_tree (root, title);
Mario Limonciello3143bad2019-02-27 07:31:00 -0600348 /* save the device state for other applications to see */
349 if (!fu_util_save_current_state (priv, error))
350 return FALSE;
351
Mario Limonciello1e35e4c2019-01-28 11:13:02 -0600352 /* success */
353 return TRUE;
354}
355
356static gboolean
Mario Limonciello716ab272018-05-29 12:34:37 -0500357fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
358{
359 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500360 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500361 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciello716ab272018-05-29 12:34:37 -0500362 gint fd;
363
364 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000365 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello716ab272018-05-29 12:34:37 -0500366 return FALSE;
367
368 /* check args */
369 if (g_strv_length (values) != 1) {
370 g_set_error_literal (error,
371 FWUPD_ERROR,
372 FWUPD_ERROR_INVALID_ARGS,
373 "Invalid arguments");
374 return FALSE;
375 }
376
377 /* open file */
378 fd = open (values[0], O_RDONLY);
379 if (fd < 0) {
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500380 fu_util_maybe_prefix_sandbox_error (values[0], error);
Mario Limonciello716ab272018-05-29 12:34:37 -0500381 g_set_error (error,
382 FWUPD_ERROR,
383 FWUPD_ERROR_INVALID_FILE,
384 "failed to open %s",
385 values[0]);
386 return FALSE;
387 }
388 array = fu_engine_get_details (priv->engine, fd, error);
389 close (fd);
390
391 if (array == NULL)
392 return FALSE;
393 for (guint i = 0; i < array->len; i++) {
394 FwupdDevice *dev = g_ptr_array_index (array, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100395 if (!fu_util_filter_device (priv, dev))
396 continue;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500397 g_node_append_data (root, dev);
Mario Limonciello716ab272018-05-29 12:34:37 -0500398 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500399 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500400
Mario Limonciello716ab272018-05-29 12:34:37 -0500401 return TRUE;
402}
403
404static gboolean
Richard Hughes747f5702019-08-06 14:27:26 +0100405fu_util_get_device_flags (FuUtilPrivate *priv, gchar **values, GError **error)
406{
407 g_autoptr(GString) str = g_string_new (NULL);
408
409 for (FwupdDeviceFlags i = FWUPD_DEVICE_FLAG_INTERNAL; i < FWUPD_DEVICE_FLAG_UNKNOWN; i<<=1) {
410 const gchar *tmp = fwupd_device_flag_to_string (i);
411 if (tmp == NULL)
412 break;
413 if (i != FWUPD_DEVICE_FLAG_INTERNAL)
414 g_string_append (str, " ");
415 g_string_append (str, tmp);
416 g_string_append (str, " ~");
417 g_string_append (str, tmp);
418 }
419 g_print ("%s\n", str->str);
420
421 return TRUE;
422}
423
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500424static void
Richard Hughes0d1577e2018-05-29 13:59:06 +0100425fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FuDevice *dev)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500426{
427 for (guint i = 0; i < devs->len; i++) {
Richard Hughes0d1577e2018-05-29 13:59:06 +0100428 FuDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100429 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev_tmp)))
430 continue;
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500431 if (!priv->show_all_devices &&
432 !fu_util_is_interesting_device (FWUPD_DEVICE (dev_tmp)))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500433 continue;
Richard Hughes0d1577e2018-05-29 13:59:06 +0100434 if (fu_device_get_parent (dev_tmp) == dev) {
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500435 GNode *child = g_node_append_data (root, dev_tmp);
436 fu_util_build_device_tree (priv, child, devs, dev_tmp);
437 }
438 }
439}
440
441static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100442fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500443{
444 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500445 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500446 g_autoptr(GPtrArray) devs = NULL;
447
448 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000449 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500450 return FALSE;
451
452 /* print */
453 devs = fu_engine_get_devices (priv->engine, error);
454 if (devs == NULL)
455 return FALSE;
456
457 /* print */
458 if (devs->len == 0) {
459 /* TRANSLATORS: nothing attached that can be upgraded */
460 g_print ("%s\n", _("No hardware detected with firmware update capability"));
461 return TRUE;
462 }
463 fu_util_build_device_tree (priv, root, devs, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500464 fu_util_print_tree (root, title);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500465
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100466 /* save the device state for other applications to see */
467 return fu_util_save_current_state (priv, error);
Mario Limoncielloba9e5b92018-05-21 16:02:32 -0500468}
469
Richard Hughes98ca9932018-05-18 10:24:07 +0100470static FuDevice *
471fu_util_prompt_for_device (FuUtilPrivate *priv, GError **error)
472{
473 FuDevice *dev;
474 guint idx;
475 g_autoptr(GPtrArray) devices = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +0100476 g_autoptr(GPtrArray) devices_filtered = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100477
478 /* get devices from daemon */
479 devices = fu_engine_get_devices (priv->engine, error);
480 if (devices == NULL)
481 return NULL;
482
Richard Hughes747f5702019-08-06 14:27:26 +0100483 /* filter results */
484 devices_filtered = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
485 for (guint i = 0; i < devices->len; i++) {
486 dev = g_ptr_array_index (devices, i);
487 if (!fu_util_filter_device (priv, FWUPD_DEVICE (dev)))
488 continue;
489 if (!fwupd_device_has_flag (FWUPD_DEVICE (dev), FWUPD_DEVICE_FLAG_SUPPORTED))
490 continue;
491 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);
Richard Hughes98ca9932018-05-18 10:24:07 +0100506 return g_object_ref (dev);
507 }
508
509 /* TRANSLATORS: get interactive prompt */
510 g_print ("%s\n", _("Choose a device:"));
511 /* TRANSLATORS: this is to abort the interactive prompt */
512 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes747f5702019-08-06 14:27:26 +0100513 for (guint i = 0; i < devices_filtered->len; i++) {
514 dev = g_ptr_array_index (devices_filtered, i);
Richard Hughes98ca9932018-05-18 10:24:07 +0100515 g_print ("%u.\t%s (%s)\n",
516 i + 1,
517 fu_device_get_id (dev),
518 fu_device_get_name (dev));
519 }
Richard Hughes747f5702019-08-06 14:27:26 +0100520 idx = fu_util_prompt_for_number (devices_filtered->len);
Richard Hughes98ca9932018-05-18 10:24:07 +0100521 if (idx == 0) {
522 g_set_error_literal (error,
523 FWUPD_ERROR,
524 FWUPD_ERROR_NOTHING_TO_DO,
525 "Request canceled");
526 return NULL;
527 }
Richard Hughes747f5702019-08-06 14:27:26 +0100528 dev = g_ptr_array_index (devices_filtered, idx - 1);
Richard Hughes98ca9932018-05-18 10:24:07 +0100529 return g_object_ref (dev);
530}
531
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500532static void
Mario Limonciello3f243a92019-01-21 22:05:23 -0600533fu_util_update_device_changed_cb (FwupdClient *client,
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500534 FwupdDevice *device,
535 FuUtilPrivate *priv)
536{
537 g_autofree gchar *str = NULL;
538
Richard Hughes809abea2019-03-23 11:06:18 +0000539 /* allowed to set whenever the device has changed */
540 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
541 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
542 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
543 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
544
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500545 /* same as last time, so ignore */
546 if (priv->current_device != NULL &&
547 fwupd_device_compare (priv->current_device, device) == 0)
548 return;
549
550 /* show message in progressbar */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600551 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
552 /* TRANSLATORS: %1 is a device name */
553 str = g_strdup_printf (_("Updating %s…"),
554 fwupd_device_get_name (device));
555 fu_progressbar_set_title (priv->progressbar, str);
556 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
557 /* TRANSLATORS: %1 is a device name */
558 str = g_strdup_printf (_("Installing on %s…"),
559 fwupd_device_get_name (device));
560 fu_progressbar_set_title (priv->progressbar, str);
561 } else {
562 g_warning ("no FuUtilOperation set");
563 }
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500564 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600565
Mario Limonciello32241f42019-01-24 10:12:41 -0600566 if (priv->current_message == NULL) {
567 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
568 if (tmp != NULL)
569 priv->current_message = g_strdup (tmp);
570 }
571}
572
573static void
574fu_util_display_current_message (FuUtilPrivate *priv)
575{
576 if (priv->current_message == NULL)
577 return;
578 g_print ("%s\n", priv->current_message);
579 g_clear_pointer (&priv->current_message, g_free);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500580}
581
Richard Hughes98ca9932018-05-18 10:24:07 +0100582static gboolean
583fu_util_install_blob (FuUtilPrivate *priv, gchar **values, GError **error)
584{
585 g_autoptr(FuDevice) device = NULL;
586 g_autoptr(GBytes) blob_fw = NULL;
587
588 /* invalid args */
589 if (g_strv_length (values) == 0) {
590 g_set_error_literal (error,
591 FWUPD_ERROR,
592 FWUPD_ERROR_INVALID_ARGS,
593 "Invalid arguments");
594 return FALSE;
595 }
596
597 /* parse blob */
598 blob_fw = fu_common_get_contents_bytes (values[0], error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500599 if (blob_fw == NULL) {
600 fu_util_maybe_prefix_sandbox_error (values[0], error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100601 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500602 }
Richard Hughes98ca9932018-05-18 10:24:07 +0100603
604 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000605 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100606 return FALSE;
607
608 /* get device */
609 if (g_strv_length (values) >= 2) {
610 device = fu_engine_get_device (priv->engine, values[1], error);
611 if (device == NULL)
612 return FALSE;
613 } else {
614 device = fu_util_prompt_for_device (priv, error);
615 if (device == NULL)
616 return FALSE;
617 }
618
Mario Limonciello3f243a92019-01-21 22:05:23 -0600619 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500620 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600621 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500622
Richard Hughes98ca9932018-05-18 10:24:07 +0100623 /* write bare firmware */
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000624 if (priv->prepare_blob) {
625 g_autoptr(GPtrArray) devices = NULL;
626 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
627 g_ptr_array_add (devices, g_object_ref (device));
628 if (!fu_engine_composite_prepare (priv->engine, devices, error)) {
629 g_prefix_error (error, "failed to prepare composite action: ");
630 return FALSE;
631 }
632 }
Mario Limonciello035818b2019-03-26 11:12:16 -0500633 priv->flags = FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes84af6e72019-02-01 18:19:41 +0000634 if (!fu_engine_install_blob (priv->engine, device, blob_fw, priv->flags, error))
Mario Limonciello3f243a92019-01-21 22:05:23 -0600635 return FALSE;
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000636 if (priv->cleanup_blob) {
637 g_autoptr(FuDevice) device_new = NULL;
638 g_autoptr(GError) error_local = NULL;
639
640 /* get the possibly new device from the old ID */
641 device_new = fu_engine_get_device (priv->engine,
642 fu_device_get_id (device),
643 &error_local);
644 if (device_new == NULL) {
645 g_debug ("failed to find new device: %s",
646 error_local->message);
647 } else {
Mario Limonciellobb3fa5e2019-02-07 21:13:02 -0600648 g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Mario Limonciello53ce25d2019-02-01 16:09:03 +0000649 g_ptr_array_add (devices_new, g_steal_pointer (&device_new));
650 if (!fu_engine_composite_cleanup (priv->engine, devices_new, error)) {
651 g_prefix_error (error, "failed to cleanup composite action: ");
652 return FALSE;
653 }
654 }
655 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600656
Mario Limonciello32241f42019-01-24 10:12:41 -0600657 fu_util_display_current_message (priv);
658
Mario Limonciello3f243a92019-01-21 22:05:23 -0600659 /* success */
660 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes98ca9932018-05-18 10:24:07 +0100661}
662
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100663static gint
664fu_util_install_task_sort_cb (gconstpointer a, gconstpointer b)
665{
666 FuInstallTask *task1 = *((FuInstallTask **) a);
667 FuInstallTask *task2 = *((FuInstallTask **) b);
668 return fu_install_task_compare (task1, task2);
669}
670
671static gboolean
Richard Hughes3d178be2018-08-30 11:14:24 +0100672fu_util_download_out_of_process (const gchar *uri, const gchar *fn, GError **error)
673{
Filipe Laínse0914272019-09-20 10:04:43 +0100674 const gchar *argv[][5] = { { "wget", uri, "-O", fn, NULL },
Richard Hughes3d178be2018-08-30 11:14:24 +0100675 { "curl", uri, "--output", fn, NULL },
676 { NULL } };
677 for (guint i = 0; argv[i][0] != NULL; i++) {
678 g_autoptr(GError) error_local = NULL;
679 if (!fu_common_find_program_in_path (argv[i][0], &error_local)) {
680 g_debug ("%s", error_local->message);
681 continue;
682 }
Richard Hughesb768e4d2019-02-26 13:55:18 +0000683 return fu_common_spawn_sync (argv[i], NULL, NULL, 0, NULL, error);
Richard Hughes3d178be2018-08-30 11:14:24 +0100684 }
685 g_set_error_literal (error,
686 FWUPD_ERROR,
687 FWUPD_ERROR_NOT_FOUND,
688 "no supported out-of-process downloaders found");
689 return FALSE;
690}
691
692static gchar *
693fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
694{
695 g_autofree gchar *filename = NULL;
696 g_autoptr(SoupURI) uri = NULL;
697
698 /* a local file */
699 uri = soup_uri_new (perhapsfn);
700 if (uri == NULL)
701 return g_strdup (perhapsfn);
702
703 /* download the firmware to a cachedir */
704 filename = fu_util_get_user_cache_path (perhapsfn);
705 if (!fu_common_mkdir_parent (filename, error))
706 return NULL;
707 if (!fu_util_download_out_of_process (perhapsfn, filename, error))
708 return NULL;
709 return g_steal_pointer (&filename);
710}
711
712static gboolean
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100713fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
714{
Richard Hughes3d178be2018-08-30 11:14:24 +0100715 g_autofree gchar *filename = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100716 g_autoptr(GBytes) blob_cab = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100717 g_autoptr(GPtrArray) components = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100718 g_autoptr(GPtrArray) devices_possible = NULL;
719 g_autoptr(GPtrArray) errors = NULL;
720 g_autoptr(GPtrArray) install_tasks = NULL;
Richard Hughes481aa2a2018-09-18 20:51:46 +0100721 g_autoptr(XbSilo) silo = NULL;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100722
Mario Limonciello8949e892018-05-25 08:03:06 -0500723 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000724 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Mario Limonciello8949e892018-05-25 08:03:06 -0500725 return FALSE;
726
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100727 /* handle both forms */
728 if (g_strv_length (values) == 1) {
729 devices_possible = fu_engine_get_devices (priv->engine, error);
730 if (devices_possible == NULL)
731 return FALSE;
732 } else if (g_strv_length (values) == 2) {
733 FuDevice *device = fu_engine_get_device (priv->engine,
734 values[1],
735 error);
736 if (device == NULL)
737 return FALSE;
738 devices_possible = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
739 g_ptr_array_add (devices_possible, device);
740 } else {
741 g_set_error_literal (error,
742 FWUPD_ERROR,
743 FWUPD_ERROR_INVALID_ARGS,
744 "Invalid arguments");
745 return FALSE;
746 }
747
Richard Hughes3d178be2018-08-30 11:14:24 +0100748 /* download if required */
749 filename = fu_util_download_if_required (priv, values[0], error);
750 if (filename == NULL)
751 return FALSE;
752
Richard Hughes481aa2a2018-09-18 20:51:46 +0100753 /* parse silo */
Richard Hughes3d178be2018-08-30 11:14:24 +0100754 blob_cab = fu_common_get_contents_bytes (filename, error);
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500755 if (blob_cab == NULL) {
Richard Hughes3d178be2018-08-30 11:14:24 +0100756 fu_util_maybe_prefix_sandbox_error (filename, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100757 return FALSE;
Mario Limonciellob72aa8c2018-06-08 09:24:36 -0500758 }
Richard Hughes481aa2a2018-09-18 20:51:46 +0100759 silo = fu_engine_get_silo_from_blob (priv->engine, blob_cab, error);
760 if (silo == NULL)
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100761 return FALSE;
Mario Limonciello51ddf182019-01-26 00:31:58 -0600762 components = xb_silo_query (silo, "components/component", 0, error);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100763 if (components == NULL)
764 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100765
Richard Hughes481aa2a2018-09-18 20:51:46 +0100766 /* for each component in the silo */
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100767 errors = g_ptr_array_new_with_free_func ((GDestroyNotify) g_error_free);
768 install_tasks = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
Richard Hughes481aa2a2018-09-18 20:51:46 +0100769 for (guint i = 0; i < components->len; i++) {
770 XbNode *component = g_ptr_array_index (components, i);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100771
772 /* do any devices pass the requirements */
773 for (guint j = 0; j < devices_possible->len; j++) {
774 FuDevice *device = g_ptr_array_index (devices_possible, j);
775 g_autoptr(FuInstallTask) task = NULL;
776 g_autoptr(GError) error_local = NULL;
777
778 /* is this component valid for the device */
Richard Hughes481aa2a2018-09-18 20:51:46 +0100779 task = fu_install_task_new (device, component);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100780 if (!fu_engine_check_requirements (priv->engine,
Richard Hughes460226a2018-05-21 20:56:21 +0100781 task, priv->flags,
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100782 &error_local)) {
783 g_debug ("requirement on %s:%s failed: %s",
784 fu_device_get_id (device),
Richard Hughes481aa2a2018-09-18 20:51:46 +0100785 xb_node_query_text (component, "id", NULL),
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100786 error_local->message);
787 g_ptr_array_add (errors, g_steal_pointer (&error_local));
788 continue;
789 }
790
Mario Limonciello7a3df4b2019-01-31 10:27:22 -0600791 /* if component should have an update message from CAB */
792 fu_device_incorporate_from_component (device, component);
793
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100794 /* success */
795 g_ptr_array_add (install_tasks, g_steal_pointer (&task));
796 }
797 }
798
799 /* order the install tasks by the device priority */
800 g_ptr_array_sort (install_tasks, fu_util_install_task_sort_cb);
801
802 /* nothing suitable */
803 if (install_tasks->len == 0) {
804 GError *error_tmp = fu_common_error_array_get_best (errors);
805 g_propagate_error (error, error_tmp);
806 return FALSE;
807 }
808
Mario Limonciello3f243a92019-01-21 22:05:23 -0600809 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500810 g_signal_connect (priv->engine, "device-changed",
Mario Limonciello3f243a92019-01-21 22:05:23 -0600811 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limonciello9eb66fe2018-08-10 11:32:44 -0500812
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100813 /* install all the tasks */
Richard Hughesdbd8c762018-06-15 20:31:40 +0100814 if (!fu_engine_install_tasks (priv->engine, install_tasks, blob_cab, priv->flags, error))
815 return FALSE;
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100816
Mario Limonciello32241f42019-01-24 10:12:41 -0600817 fu_util_display_current_message (priv);
818
Mario Limonciello3f243a92019-01-21 22:05:23 -0600819 /* we don't want to ask anything */
820 if (priv->no_reboot_check) {
821 g_debug ("skipping reboot check");
822 return TRUE;
823 }
824
Mario Limonciello3143bad2019-02-27 07:31:00 -0600825 /* save the device state for other applications to see */
826 if (!fu_util_save_current_state (priv, error))
827 return FALSE;
828
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100829 /* success */
Mario Limonciello3f243a92019-01-21 22:05:23 -0600830 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesa36c9cf2018-05-20 10:41:44 +0100831}
832
Richard Hughes98ca9932018-05-18 10:24:07 +0100833static gboolean
Mario Limonciellofd734852019-08-01 16:41:42 -0500834fu_util_install_release (FuUtilPrivate *priv, FwupdRelease *rel, GError **error)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600835{
Mario Limonciellofd734852019-08-01 16:41:42 -0500836 FwupdRemote *remote;
837 const gchar *remote_id;
838 const gchar *uri_tmp;
839 g_auto(GStrv) argv = NULL;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600840
Mario Limonciellofd734852019-08-01 16:41:42 -0500841 uri_tmp = fwupd_release_get_uri (rel);
842 if (uri_tmp == NULL) {
843 g_set_error_literal (error,
844 FWUPD_ERROR,
845 FWUPD_ERROR_INVALID_FILE,
846 "release missing URI");
847 return FALSE;
848 }
849 remote_id = fwupd_release_get_remote_id (rel);
850 if (remote_id == NULL) {
851 g_set_error (error,
852 FWUPD_ERROR,
853 FWUPD_ERROR_INVALID_FILE,
854 "failed to find remote for %s",
855 uri_tmp);
Richard Hughes747f5702019-08-06 14:27:26 +0100856 return FALSE;
Mario Limonciellofd734852019-08-01 16:41:42 -0500857 }
858
859 remote = fu_engine_get_remote_by_id (priv->engine,
860 remote_id,
861 error);
862 if (remote == NULL)
Mario Limonciello46aaee82019-01-10 12:58:00 -0600863 return FALSE;
864
Mario Limonciellofd734852019-08-01 16:41:42 -0500865 argv = g_new0 (gchar *, 2);
866 /* local remotes have the firmware already */
867 if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) {
868 const gchar *fn_cache = fwupd_remote_get_filename_cache (remote);
869 g_autofree gchar *path = g_path_get_dirname (fn_cache);
870 argv[0] = g_build_filename (path, uri_tmp, NULL);
871 } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) {
872 argv[0] = g_strdup (uri_tmp + 7);
873 /* web remote, fu_util_install will download file */
874 } else {
875 argv[0] = fwupd_remote_build_firmware_uri (remote, uri_tmp, error);
876 }
877 return fu_util_install (priv, argv, error);
878}
879
880static gboolean
881fu_util_update_all (FuUtilPrivate *priv, GError **error)
882{
883 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600884
Mario Limonciello46aaee82019-01-10 12:58:00 -0600885 devices = fu_engine_get_devices (priv->engine, error);
Mario Limonciello387bda42019-02-07 14:20:22 +0000886 if (devices == NULL)
887 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600888 for (guint i = 0; i < devices->len; i++) {
889 FwupdDevice *dev = g_ptr_array_index (devices, i);
890 FwupdRelease *rel;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600891 const gchar *device_id;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600892 g_autoptr(GPtrArray) rels = NULL;
893 g_autoptr(GError) error_local = NULL;
894
895 if (!fu_util_is_interesting_device (dev))
896 continue;
897 /* only show stuff that has metadata available */
898 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
899 continue;
Richard Hughes747f5702019-08-06 14:27:26 +0100900 if (!fu_util_filter_device (priv, dev))
901 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600902
903 device_id = fu_device_get_id (dev);
904 rels = fu_engine_get_upgrades (priv->engine, device_id, &error_local);
905 if (rels == NULL) {
906 g_printerr ("%s\n", error_local->message);
907 continue;
908 }
909
910 rel = g_ptr_array_index (rels, 0);
Mario Limonciellofd734852019-08-01 16:41:42 -0500911 if (!fu_util_install_release (priv, rel, &error_local)) {
912 g_printerr ("%s\n", error_local->message);
Richard Hughes747f5702019-08-06 14:27:26 +0100913 continue;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600914 }
Mario Limonciellofd734852019-08-01 16:41:42 -0500915 fu_util_display_current_message (priv);
916 }
917 return TRUE;
918}
919
920static gboolean
921fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
922{
923 FwupdRelease *rel;
924 g_autoptr(FuDevice) dev = NULL;
925 g_autoptr(GPtrArray) rels = NULL;
926
927 /* do not allow a partial device-id */
928 dev = fu_engine_get_device (priv->engine, device_id, error);
929 if (dev == NULL)
930 return FALSE;
931
932 /* get the releases for this device and filter for validity */
933 rels = fu_engine_get_upgrades (priv->engine, device_id, error);
934 if (rels == NULL)
935 return FALSE;
936 rel = g_ptr_array_index (rels, 0);
937 if (!fu_util_install_release (priv, rel, error))
938 return FALSE;
939 fu_util_display_current_message (priv);
940
941 return TRUE;
942}
943
944static gboolean
945fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
946{
947 if (g_strv_length (values) > 1) {
948 g_set_error_literal (error,
949 FWUPD_ERROR,
950 FWUPD_ERROR_INVALID_ARGS,
951 "Invalid arguments");
952 return FALSE;
953 }
954
955 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
956 return FALSE;
957
958 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
959 g_signal_connect (priv->engine, "device-changed",
960 G_CALLBACK (fu_util_update_device_changed_cb), priv);
961
962 if (g_strv_length (values) == 1) {
963 if (!fu_util_update_by_id (priv, values[0], error))
964 return FALSE;
965 } else {
966 if (!fu_util_update_all (priv, error))
967 return FALSE;
Mario Limonciello46aaee82019-01-10 12:58:00 -0600968 }
Mario Limonciello3f243a92019-01-21 22:05:23 -0600969
970 /* we don't want to ask anything */
971 if (priv->no_reboot_check) {
972 g_debug ("skipping reboot check");
973 return TRUE;
974 }
975
Mario Limonciello3143bad2019-02-27 07:31:00 -0600976 /* save the device state for other applications to see */
977 if (!fu_util_save_current_state (priv, error))
978 return FALSE;
979
Mario Limonciello3f243a92019-01-21 22:05:23 -0600980 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Mario Limonciello46aaee82019-01-10 12:58:00 -0600981}
982
983static gboolean
Richard Hughes98ca9932018-05-18 10:24:07 +0100984fu_util_detach (FuUtilPrivate *priv, gchar **values, GError **error)
985{
986 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +0100987 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +0100988
989 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +0000990 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +0100991 return FALSE;
992
993 /* invalid args */
994 if (g_strv_length (values) == 0) {
995 g_set_error_literal (error,
996 FWUPD_ERROR,
997 FWUPD_ERROR_INVALID_ARGS,
998 "Invalid arguments");
999 return FALSE;
1000 }
1001
1002 /* get device */
1003 if (g_strv_length (values) >= 1) {
1004 device = fu_engine_get_device (priv->engine, values[0], error);
1005 if (device == NULL)
1006 return FALSE;
1007 } else {
1008 device = fu_util_prompt_for_device (priv, error);
1009 if (device == NULL)
1010 return FALSE;
1011 }
1012
1013 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001014 locker = fu_device_locker_new (device, error);
1015 if (locker == NULL)
1016 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001017 return fu_device_detach (device, error);
1018}
1019
1020static gboolean
1021fu_util_attach (FuUtilPrivate *priv, gchar **values, GError **error)
1022{
1023 g_autoptr(FuDevice) device = NULL;
Richard Hughes2a679cd2018-09-04 21:13:23 +01001024 g_autoptr(FuDeviceLocker) locker = NULL;
Richard Hughes98ca9932018-05-18 10:24:07 +01001025
1026 /* load engine */
Richard Hughesc8cc77c2019-03-07 11:57:24 +00001027 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
Richard Hughes98ca9932018-05-18 10:24:07 +01001028 return FALSE;
1029
1030 /* invalid args */
1031 if (g_strv_length (values) == 0) {
1032 g_set_error_literal (error,
1033 FWUPD_ERROR,
1034 FWUPD_ERROR_INVALID_ARGS,
1035 "Invalid arguments");
1036 return FALSE;
1037 }
1038
1039 /* get device */
1040 if (g_strv_length (values) >= 1) {
1041 device = fu_engine_get_device (priv->engine, values[0], error);
1042 if (device == NULL)
1043 return FALSE;
1044 } else {
1045 device = fu_util_prompt_for_device (priv, error);
1046 if (device == NULL)
1047 return FALSE;
1048 }
1049
1050 /* run vfunc */
Richard Hughes2a679cd2018-09-04 21:13:23 +01001051 locker = fu_device_locker_new (device, error);
1052 if (locker == NULL)
1053 return FALSE;
Richard Hughes98ca9932018-05-18 10:24:07 +01001054 return fu_device_attach (device, error);
1055}
1056
Richard Hughes1d894f12018-08-31 13:05:51 +01001057static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001058fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
1059{
1060 gboolean has_pending = FALSE;
1061 g_autoptr(FuHistory) history = fu_history_new ();
1062 g_autoptr(GPtrArray) devices = NULL;
1063
1064 /* check the history database before starting the daemon */
1065 if (g_strv_length (values) == 0) {
1066 devices = fu_history_get_devices (history, error);
1067 if (devices == NULL)
1068 return FALSE;
1069 } else if (g_strv_length (values) == 1) {
1070 FuDevice *device;
1071 device = fu_history_get_device_by_id (history, values[0], error);
1072 if (device == NULL)
1073 return FALSE;
1074 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1075 g_ptr_array_add (devices, device);
1076 } else {
1077 g_set_error_literal (error,
1078 FWUPD_ERROR,
1079 FWUPD_ERROR_INVALID_ARGS,
1080 "Invalid arguments");
1081 return FALSE;
1082 }
1083
1084 /* nothing to do */
1085 for (guint i = 0; i < devices->len; i++) {
1086 FuDevice *dev = g_ptr_array_index (devices, i);
1087 if (fu_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
1088 fu_engine_add_plugin_filter (priv->engine,
1089 fu_device_get_plugin (dev));
1090 has_pending = TRUE;
1091 }
1092 }
1093 if (!has_pending) {
1094 g_set_error_literal (error,
1095 FWUPD_ERROR,
1096 FWUPD_ERROR_NOTHING_TO_DO,
1097 "No firmware to activate");
1098 return FALSE;
1099
1100 }
1101
1102 /* load engine */
Richard Hughes88dc0f42019-03-07 11:58:11 +00001103 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_READONLY_FS, error))
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001104 return FALSE;
1105
1106 /* activate anything with _NEEDS_ACTIVATION */
1107 for (guint i = 0; i < devices->len; i++) {
1108 FuDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01001109 if (!fu_util_filter_device (priv, FWUPD_DEVICE (device)))
1110 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001111 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
1112 continue;
1113 /* TRANSLATORS: shown when shutting down to switch to the new version */
1114 g_print ("%s %s…\n", _("Activating firmware update"), fu_device_get_name (device));
1115 if (!fu_engine_activate (priv->engine, fu_device_get_id (device), error))
1116 return FALSE;
1117 }
1118
1119 return TRUE;
1120}
1121
1122static gboolean
Richard Hughes1d894f12018-08-31 13:05:51 +01001123fu_util_hwids (FuUtilPrivate *priv, gchar **values, GError **error)
1124{
1125 g_autoptr(FuSmbios) smbios = fu_smbios_new ();
1126 g_autoptr(FuHwids) hwids = fu_hwids_new ();
1127 const gchar *hwid_keys[] = {
1128 FU_HWIDS_KEY_BIOS_VENDOR,
1129 FU_HWIDS_KEY_BIOS_VERSION,
1130 FU_HWIDS_KEY_BIOS_MAJOR_RELEASE,
1131 FU_HWIDS_KEY_BIOS_MINOR_RELEASE,
1132 FU_HWIDS_KEY_MANUFACTURER,
1133 FU_HWIDS_KEY_FAMILY,
1134 FU_HWIDS_KEY_PRODUCT_NAME,
1135 FU_HWIDS_KEY_PRODUCT_SKU,
1136 FU_HWIDS_KEY_ENCLOSURE_KIND,
1137 FU_HWIDS_KEY_BASEBOARD_MANUFACTURER,
1138 FU_HWIDS_KEY_BASEBOARD_PRODUCT,
1139 NULL };
1140
1141 /* read DMI data */
1142 if (g_strv_length (values) == 0) {
1143 if (!fu_smbios_setup (smbios, error))
1144 return FALSE;
1145 } else if (g_strv_length (values) == 1) {
1146 if (!fu_smbios_setup_from_file (smbios, values[0], error))
1147 return FALSE;
1148 } else {
1149 g_set_error_literal (error,
1150 FWUPD_ERROR,
1151 FWUPD_ERROR_INVALID_ARGS,
1152 "Invalid arguments");
1153 return FALSE;
1154 }
1155 if (!fu_hwids_setup (hwids, smbios, error))
1156 return FALSE;
1157
1158 /* show debug output */
1159 g_print ("Computer Information\n");
1160 g_print ("--------------------\n");
1161 for (guint i = 0; hwid_keys[i] != NULL; i++) {
1162 const gchar *tmp = fu_hwids_get_value (hwids, hwid_keys[i]);
1163 if (tmp == NULL)
1164 continue;
1165 if (g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MAJOR_RELEASE) == 0 ||
1166 g_strcmp0 (hwid_keys[i], FU_HWIDS_KEY_BIOS_MINOR_RELEASE) == 0) {
1167 guint64 val = g_ascii_strtoull (tmp, NULL, 16);
1168 g_print ("%s: %" G_GUINT64_FORMAT "\n", hwid_keys[i], val);
1169 } else {
1170 g_print ("%s: %s\n", hwid_keys[i], tmp);
1171 }
1172 }
1173
1174 /* show GUIDs */
1175 g_print ("\nHardware IDs\n");
1176 g_print ("------------\n");
1177 for (guint i = 0; i < 15; i++) {
1178 const gchar *keys = NULL;
1179 g_autofree gchar *guid = NULL;
1180 g_autofree gchar *key = NULL;
1181 g_autofree gchar *keys_str = NULL;
1182 g_auto(GStrv) keysv = NULL;
1183 g_autoptr(GError) error_local = NULL;
1184
1185 /* get the GUID */
1186 key = g_strdup_printf ("HardwareID-%u", i);
1187 keys = fu_hwids_get_replace_keys (hwids, key);
1188 guid = fu_hwids_get_guid (hwids, key, &error_local);
1189 if (guid == NULL) {
1190 g_print ("%s\n", error_local->message);
1191 continue;
1192 }
1193
1194 /* show what makes up the GUID */
1195 keysv = g_strsplit (keys, "&", -1);
1196 keys_str = g_strjoinv (" + ", keysv);
1197 g_print ("{%s} <- %s\n", guid, keys_str);
1198 }
1199
1200 return TRUE;
1201}
1202
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001203static gboolean
1204fu_util_firmware_builder (FuUtilPrivate *priv, gchar **values, GError **error)
1205{
1206 const gchar *script_fn = "startup.sh";
1207 const gchar *output_fn = "firmware.bin";
1208 g_autoptr(GBytes) archive_blob = NULL;
1209 g_autoptr(GBytes) firmware_blob = NULL;
1210 if (g_strv_length (values) < 2) {
1211 g_set_error_literal (error,
1212 FWUPD_ERROR,
1213 FWUPD_ERROR_INVALID_ARGS,
1214 "Invalid arguments");
1215 return FALSE;
1216 }
1217 archive_blob = fu_common_get_contents_bytes (values[0], error);
1218 if (archive_blob == NULL)
1219 return FALSE;
1220 if (g_strv_length (values) > 2)
1221 script_fn = values[2];
1222 if (g_strv_length (values) > 3)
1223 output_fn = values[3];
1224 firmware_blob = fu_common_firmware_builder (archive_blob, script_fn, output_fn, error);
1225 if (firmware_blob == NULL)
1226 return FALSE;
1227 return fu_common_set_contents_bytes (values[1], firmware_blob, error);
1228}
1229
Richard Hughes3d607622019-03-07 16:59:27 +00001230static gboolean
1231fu_util_self_sign (FuUtilPrivate *priv, gchar **values, GError **error)
1232{
1233 g_autofree gchar *sig = NULL;
1234
1235 /* check args */
1236 if (g_strv_length (values) != 1) {
1237 g_set_error_literal (error,
1238 FWUPD_ERROR,
1239 FWUPD_ERROR_INVALID_ARGS,
1240 "Invalid arguments: value expected");
1241 return FALSE;
1242 }
1243
1244 /* start engine */
1245 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1246 return FALSE;
1247 sig = fu_engine_self_sign (priv->engine, values[0],
1248 FU_KEYRING_SIGN_FLAG_ADD_TIMESTAMP |
1249 FU_KEYRING_SIGN_FLAG_ADD_CERT, error);
1250 if (sig == NULL)
1251 return FALSE;
1252 g_print ("%s\n", sig);
1253 return TRUE;
1254}
1255
Mario Limonciello62f84862018-10-18 13:15:23 -05001256static void
1257fu_util_device_added_cb (FwupdClient *client,
1258 FwupdDevice *device,
1259 gpointer user_data)
1260{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001261 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001262 /* TRANSLATORS: this is when a device is hotplugged */
1263 g_print ("%s\n%s", _("Device added:"), tmp);
1264}
1265
1266static void
1267fu_util_device_removed_cb (FwupdClient *client,
1268 FwupdDevice *device,
1269 gpointer user_data)
1270{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001271 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001272 /* TRANSLATORS: this is when a device is hotplugged */
1273 g_print ("%s\n%s", _("Device removed:"), tmp);
1274}
1275
1276static void
1277fu_util_device_changed_cb (FwupdClient *client,
1278 FwupdDevice *device,
1279 gpointer user_data)
1280{
Mario Limonciellofee8f492019-08-18 12:16:07 -05001281 g_autofree gchar *tmp = fu_util_device_to_string (device, 0);
Mario Limonciello62f84862018-10-18 13:15:23 -05001282 /* TRANSLATORS: this is when a device has been updated */
1283 g_print ("%s\n%s", _("Device changed:"), tmp);
1284}
1285
1286static void
1287fu_util_changed_cb (FwupdClient *client, gpointer user_data)
1288{
1289 /* TRANSLATORS: this is when the daemon state changes */
1290 g_print ("%s\n", _("Changed"));
1291}
1292
1293static gboolean
1294fu_util_monitor (FuUtilPrivate *priv, gchar **values, GError **error)
1295{
1296 g_autoptr(FwupdClient) client = fwupd_client_new ();
1297
1298 /* get all the devices */
1299 if (!fwupd_client_connect (client, priv->cancellable, error))
1300 return FALSE;
1301
1302 /* watch for any hotplugged device */
1303 g_signal_connect (client, "changed",
1304 G_CALLBACK (fu_util_changed_cb), priv);
1305 g_signal_connect (client, "device-added",
1306 G_CALLBACK (fu_util_device_added_cb), priv);
1307 g_signal_connect (client, "device-removed",
1308 G_CALLBACK (fu_util_device_removed_cb), priv);
1309 g_signal_connect (client, "device-changed",
1310 G_CALLBACK (fu_util_device_changed_cb), priv);
1311 g_signal_connect (priv->cancellable, "cancelled",
1312 G_CALLBACK (fu_util_cancelled_cb), priv);
1313 g_main_loop_run (priv->loop);
1314 return TRUE;
1315}
1316
Richard Hughes15684492019-03-15 16:27:50 +00001317static gboolean
1318fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
1319{
1320 g_autofree gchar *str = NULL;
1321 g_autoptr(FuDevice) dev = NULL;
1322
1323 /* load engine */
1324 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1325 return FALSE;
1326
1327 /* get device */
1328 if (g_strv_length (values) == 1) {
1329 dev = fu_engine_get_device (priv->engine, values[1], error);
1330 if (dev == NULL)
1331 return FALSE;
1332 } else {
1333 dev = fu_util_prompt_for_device (priv, error);
1334 if (dev == NULL)
1335 return FALSE;
1336 }
1337
1338 /* add checksums */
1339 if (!fu_engine_verify_update (priv->engine, fu_device_get_id (dev), error))
1340 return FALSE;
1341
1342 /* show checksums */
1343 str = fu_device_to_string (dev);
1344 g_print ("%s\n", str);
1345 return TRUE;
1346}
1347
Mario Limonciellofe593942019-04-03 13:48:52 -05001348static gboolean
1349fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
1350{
1351 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001352 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001353 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limonciellofe593942019-04-03 13:48:52 -05001354
1355 /* load engine */
1356 if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
1357 return FALSE;
1358
1359 /* get all devices from the history database */
1360 devices = fu_engine_get_history (priv->engine, error);
1361 if (devices == NULL)
1362 return FALSE;
1363
1364 /* show each device */
1365 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -05001366 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciellofe593942019-04-03 13:48:52 -05001367 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -05001368 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001369 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001370 GNode *child;
Mario Limoncielloce94d162019-09-20 13:37:36 -05001371
Richard Hughes747f5702019-08-06 14:27:26 +01001372 if (!fu_util_filter_device (priv, dev))
1373 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -05001374 child = g_node_append_data (root, dev);
1375
1376 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001377 if (rel == NULL)
1378 continue;
1379 remote = fwupd_release_get_remote_id (rel);
1380
1381 /* doesn't actually map to remote */
1382 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -05001383 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -05001384 continue;
1385 }
1386
1387 /* try to lookup releases from client */
1388 rels = fu_engine_get_releases (priv->engine, fwupd_device_get_id (dev), error);
1389 if (rels == NULL)
1390 return FALSE;
1391
1392 /* map to a release in client */
1393 for (guint j = 0; j < rels->len; j++) {
1394 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
1395 if (g_strcmp0 (remote,
1396 fwupd_release_get_remote_id (rel2)) != 0)
1397 continue;
1398 if (g_strcmp0 (fwupd_release_get_version (rel),
1399 fwupd_release_get_version (rel2)) != 0)
1400 continue;
1401 g_node_append_data (child, g_object_ref (rel2));
1402 rel = NULL;
1403 break;
1404 }
1405
1406 /* didn't match anything */
1407 if (rels->len == 0 || rel != NULL) {
1408 g_node_append_data (child, rel);
1409 continue;
1410 }
Mario Limonciello3be596b2019-09-20 10:10:08 -05001411
Mario Limonciellofe593942019-04-03 13:48:52 -05001412 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001413 fu_util_print_tree (root, title);
Mario Limonciellofe593942019-04-03 13:48:52 -05001414
1415 return TRUE;
1416}
1417
Richard Hughesb5976832018-05-18 10:02:09 +01001418int
1419main (int argc, char *argv[])
1420{
Richard Hughes460226a2018-05-21 20:56:21 +01001421 gboolean allow_older = FALSE;
1422 gboolean allow_reinstall = FALSE;
Richard Hughesb5976832018-05-18 10:02:09 +01001423 gboolean force = FALSE;
1424 gboolean ret;
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001425 gboolean version = FALSE;
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001426 gboolean interactive = isatty (fileno (stdout)) != 0;
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001427 g_auto(GStrv) plugin_glob = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001428 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
1429 g_autoptr(GError) error = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00001430 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughesb5976832018-05-18 10:02:09 +01001431 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01001432 g_autofree gchar *filter = NULL;
Richard Hughesb5976832018-05-18 10:02:09 +01001433 const GOptionEntry options[] = {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001434 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
1435 /* TRANSLATORS: command line option */
1436 _("Show client and daemon versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001437 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
1438 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05001439 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes460226a2018-05-21 20:56:21 +01001440 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
1441 /* TRANSLATORS: command line option */
1442 _("Allow downgrading firmware versions"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001443 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
1444 /* TRANSLATORS: command line option */
1445 _("Override plugin warning"), NULL },
Mario Limonciello3f243a92019-01-21 22:05:23 -06001446 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
1447 /* TRANSLATORS: command line option */
1448 _("Do not check for reboot after update"), NULL },
Mario Limoncielloba9e5b92018-05-21 16:02:32 -05001449 { "show-all-devices", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all_devices,
1450 /* TRANSLATORS: command line option */
1451 _("Show devices that are not updatable"), NULL },
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001452 { "plugin-whitelist", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &plugin_glob,
1453 /* TRANSLATORS: command line option */
1454 _("Manually whitelist specific plugins"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001455 { "prepare", '\0', 0, G_OPTION_ARG_NONE, &priv->prepare_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001456 /* TRANSLATORS: command line option */
1457 _("Run the plugin composite prepare routine when using install-blob"), NULL },
Mario Limonciello8402cea2019-02-07 20:25:31 -06001458 { "cleanup", '\0', 0, G_OPTION_ARG_NONE, &priv->cleanup_blob,
Mario Limonciello53ce25d2019-02-01 16:09:03 +00001459 /* TRANSLATORS: command line option */
1460 _("Run the plugin composite cleanup routine when using install-blob"), NULL },
Mario Limonciello3143bad2019-02-27 07:31:00 -06001461 { "enable-json-state", '\0', 0, G_OPTION_ARG_NONE, &priv->enable_json_state,
1462 /* TRANSLATORS: command line option */
1463 _("Save device state into a JSON file between executions"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01001464 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
1465 /* TRANSLATORS: command line option */
1466 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01001467 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
1468 /* TRANSLATORS: command line option */
1469 _("Filter with a set of device flags using a ~ prefix to "
1470 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughesb5976832018-05-18 10:02:09 +01001471 { NULL}
1472 };
1473
1474 setlocale (LC_ALL, "");
1475
1476 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1477 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1478 textdomain (GETTEXT_PACKAGE);
1479
1480 /* ensure root user */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001481 if (interactive && (getuid () != 0 || geteuid () != 0))
Richard Hughesb5976832018-05-18 10:02:09 +01001482 /* TRANSLATORS: we're poking around as a power user */
Mario Limonciellob900c092018-05-22 14:22:21 -05001483 g_printerr ("%s\n", _("This program may only work correctly as root"));
Richard Hughesb5976832018-05-18 10:02:09 +01001484
1485 /* create helper object */
1486 priv->loop = g_main_loop_new (NULL, FALSE);
1487 priv->progressbar = fu_progressbar_new ();
1488
1489 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00001490 fu_util_cmd_array_add (cmd_array,
Mario Limonciellof6d01b12018-10-18 12:57:10 -05001491 "build-firmware",
1492 "FILE-IN FILE-OUT [SCRIPT] [OUTPUT]",
1493 /* TRANSLATORS: command description */
1494 _("Build firmware using a sandbox"),
1495 fu_util_firmware_builder);
Richard Hughesc77e1112019-03-01 10:16:26 +00001496 fu_util_cmd_array_add (cmd_array,
Richard Hughesb5976832018-05-18 10:02:09 +01001497 "smbios-dump",
1498 "FILE",
1499 /* TRANSLATORS: command description */
1500 _("Dump SMBIOS data from a file"),
1501 fu_util_smbios_dump);
Richard Hughesc77e1112019-03-01 10:16:26 +00001502 fu_util_cmd_array_add (cmd_array,
Richard Hughes8c71a3f2018-05-22 19:19:52 +01001503 "get-plugins",
1504 NULL,
1505 /* TRANSLATORS: command description */
1506 _("Get all enabled plugins registered with the system"),
1507 fu_util_get_plugins);
Richard Hughesc77e1112019-03-01 10:16:26 +00001508 fu_util_cmd_array_add (cmd_array,
Mario Limonciello716ab272018-05-29 12:34:37 -05001509 "get-details",
1510 NULL,
1511 /* TRANSLATORS: command description */
1512 _("Gets details about a firmware file"),
1513 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00001514 fu_util_cmd_array_add (cmd_array,
Mario Limonciellofe593942019-04-03 13:48:52 -05001515 "get-history",
1516 NULL,
1517 /* TRANSLATORS: command description */
1518 _("Show history of firmware updates"),
1519 fu_util_get_history);
1520 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001521 "get-updates,get-upgrades",
Mario Limonciello1e35e4c2019-01-28 11:13:02 -06001522 NULL,
1523 /* TRANSLATORS: command description */
1524 _("Gets the list of updates for connected hardware"),
1525 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00001526 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01001527 "get-devices,get-topology",
Richard Hughes98ca9932018-05-18 10:24:07 +01001528 NULL,
1529 /* TRANSLATORS: command description */
1530 _("Get all devices that support firmware updates"),
1531 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00001532 fu_util_cmd_array_add (cmd_array,
Richard Hughes747f5702019-08-06 14:27:26 +01001533 "get-device-flags",
1534 NULL,
1535 /* TRANSLATORS: command description */
1536 _("Get all device flags supported by fwupd"),
1537 fu_util_get_device_flags);
1538 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001539 "watch",
1540 NULL,
1541 /* TRANSLATORS: command description */
Piotr DrÄ…g472fa592018-06-06 14:53:48 +02001542 _("Watch for hardware changes"),
Richard Hughes98ca9932018-05-18 10:24:07 +01001543 fu_util_watch);
Richard Hughesc77e1112019-03-01 10:16:26 +00001544 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001545 "install-blob",
1546 "FILENAME DEVICE-ID",
1547 /* TRANSLATORS: command description */
1548 _("Install a firmware blob on a device"),
1549 fu_util_install_blob);
Richard Hughesc77e1112019-03-01 10:16:26 +00001550 fu_util_cmd_array_add (cmd_array,
Richard Hughesa36c9cf2018-05-20 10:41:44 +01001551 "install",
1552 "FILE [ID]",
1553 /* TRANSLATORS: command description */
1554 _("Install a firmware file on this hardware"),
1555 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00001556 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001557 "attach",
1558 "DEVICE-ID",
1559 /* TRANSLATORS: command description */
1560 _("Attach to firmware mode"),
1561 fu_util_attach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001562 fu_util_cmd_array_add (cmd_array,
Richard Hughes98ca9932018-05-18 10:24:07 +01001563 "detach",
1564 "DEVICE-ID",
1565 /* TRANSLATORS: command description */
1566 _("Detach to bootloader mode"),
1567 fu_util_detach);
Richard Hughesc77e1112019-03-01 10:16:26 +00001568 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001569 "activate",
1570 "[DEVICE-ID]",
1571 /* TRANSLATORS: command description */
1572 _("Activate pending devices"),
1573 fu_util_activate);
Richard Hughesc77e1112019-03-01 10:16:26 +00001574 fu_util_cmd_array_add (cmd_array,
Richard Hughes1d894f12018-08-31 13:05:51 +01001575 "hwids",
1576 "[FILE]",
1577 /* TRANSLATORS: command description */
1578 _("Return all the hardware IDs for the machine"),
1579 fu_util_hwids);
Richard Hughesc77e1112019-03-01 10:16:26 +00001580 fu_util_cmd_array_add (cmd_array,
Mario Limonciello62f84862018-10-18 13:15:23 -05001581 "monitor",
1582 NULL,
1583 /* TRANSLATORS: command description */
1584 _("Monitor the daemon for events"),
1585 fu_util_monitor);
Richard Hughesc77e1112019-03-01 10:16:26 +00001586 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05001587 "update,upgrade",
Mario Limonciello46aaee82019-01-10 12:58:00 -06001588 NULL,
1589 /* TRANSLATORS: command description */
1590 _("Update all devices that match local metadata"),
1591 fu_util_update);
Richard Hughes3d607622019-03-07 16:59:27 +00001592 fu_util_cmd_array_add (cmd_array,
1593 "self-sign",
1594 "TEXT",
1595 /* TRANSLATORS: command description */
Richard Hughes12a021d2019-03-27 09:23:24 +00001596 C_("command-description",
1597 "Sign data using the client certificate"),
Richard Hughes3d607622019-03-07 16:59:27 +00001598 fu_util_self_sign);
Richard Hughes15684492019-03-15 16:27:50 +00001599 fu_util_cmd_array_add (cmd_array,
1600 "verify-update",
1601 "[DEVICE_ID]",
1602 /* TRANSLATORS: command description */
1603 _("Update the stored metadata with current contents"),
1604 fu_util_verify_update);
Richard Hughesb5976832018-05-18 10:02:09 +01001605
1606 /* do stuff on ctrl+c */
1607 priv->cancellable = g_cancellable_new ();
1608 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
1609 SIGINT, fu_util_sigint_cb,
1610 priv, NULL);
1611 g_signal_connect (priv->cancellable, "cancelled",
1612 G_CALLBACK (fu_util_cancelled_cb), priv);
1613
1614 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00001615 fu_util_cmd_array_sort (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001616
Mario Limonciello3f243a92019-01-21 22:05:23 -06001617 /* non-TTY consoles cannot answer questions */
Mario Limonciello5d7aa402019-02-04 09:35:58 -06001618 if (!interactive) {
Mario Limonciello3f243a92019-01-21 22:05:23 -06001619 priv->no_reboot_check = TRUE;
Mario Limonciello9b31e6f2019-01-31 15:42:19 -06001620 fu_progressbar_set_interactive (priv->progressbar, FALSE);
1621 }
Mario Limonciello3f243a92019-01-21 22:05:23 -06001622
Richard Hughesb5976832018-05-18 10:02:09 +01001623 /* get a list of the commands */
1624 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00001625 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughesb5976832018-05-18 10:02:09 +01001626 g_option_context_set_summary (priv->context, cmd_descriptions);
1627 g_option_context_set_description (priv->context,
1628 "This tool allows an administrator to use the fwupd plugins "
1629 "without being installed on the host system.");
1630
1631 /* TRANSLATORS: program name */
1632 g_set_application_name (_("Firmware Utility"));
1633 g_option_context_add_main_entries (priv->context, options, NULL);
Mario Limonciellofde47732018-09-11 12:20:58 -05001634 g_option_context_add_group (priv->context, fu_debug_get_option_group ());
Richard Hughesb5976832018-05-18 10:02:09 +01001635 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
1636 if (!ret) {
1637 /* TRANSLATORS: the user didn't read the man page */
1638 g_print ("%s: %s\n", _("Failed to parse arguments"),
1639 error->message);
1640 return EXIT_FAILURE;
1641 }
1642
Richard Hughes0e46b222019-09-05 12:13:35 +01001643 /* allow disabling SSL strict mode for broken corporate proxies */
1644 if (priv->disable_ssl_strict) {
1645 /* TRANSLATORS: try to help */
1646 g_printerr ("%s\n", _("WARNING: Ignoring SSL strict checks, "
1647 "to do this automatically in the future "
1648 "export DISABLE_SSL_STRICT in your environment"));
1649 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
1650 }
1651
Richard Hughes747f5702019-08-06 14:27:26 +01001652 /* parse filter flags */
1653 if (filter != NULL) {
1654 if (!fu_util_parse_filter_flags (filter,
1655 &priv->filter_include,
1656 &priv->filter_exclude,
1657 &error)) {
1658 /* TRANSLATORS: the user didn't read the man page */
1659 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
1660 error->message);
1661 return EXIT_FAILURE;
1662 }
1663 }
1664
1665
Richard Hughes460226a2018-05-21 20:56:21 +01001666 /* set flags */
Richard Hughes460226a2018-05-21 20:56:21 +01001667 if (allow_reinstall)
1668 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1669 if (allow_older)
1670 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
1671 if (force)
1672 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
1673
Richard Hughes98ca9932018-05-18 10:24:07 +01001674 /* load engine */
1675 priv->engine = fu_engine_new (FU_APP_FLAGS_NO_IDLE_SOURCES);
1676 g_signal_connect (priv->engine, "device-added",
1677 G_CALLBACK (fu_main_engine_device_added_cb),
1678 priv);
1679 g_signal_connect (priv->engine, "device-removed",
1680 G_CALLBACK (fu_main_engine_device_removed_cb),
1681 priv);
Richard Hughes98ca9932018-05-18 10:24:07 +01001682 g_signal_connect (priv->engine, "status-changed",
1683 G_CALLBACK (fu_main_engine_status_changed_cb),
1684 priv);
1685 g_signal_connect (priv->engine, "percentage-changed",
1686 G_CALLBACK (fu_main_engine_percentage_changed_cb),
1687 priv);
1688
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05001689 /* just show versions and exit */
1690 if (version) {
1691 g_autofree gchar *version_str = fu_util_get_versions ();
1692 g_print ("%s\n", version_str);
1693 return EXIT_SUCCESS;
1694 }
1695
Richard Hughesc02ee4d2018-05-22 15:46:03 +01001696 /* any plugin whitelist specified */
1697 for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++)
1698 fu_engine_add_plugin_filter (priv->engine, plugin_glob[i]);
1699
Richard Hughesb5976832018-05-18 10:02:09 +01001700 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00001701 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughesb5976832018-05-18 10:02:09 +01001702 if (!ret) {
1703 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
1704 g_autofree gchar *tmp = NULL;
1705 tmp = g_option_context_get_help (priv->context, TRUE, NULL);
1706 g_print ("%s\n\n%s", error->message, tmp);
1707 return EXIT_FAILURE;
1708 }
1709 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
1710 g_print ("%s\n", error->message);
1711 return EXIT_NOTHING_TO_DO;
1712 }
1713 g_print ("%s\n", error->message);
1714 return EXIT_FAILURE;
1715 }
1716
1717 /* success */
1718 return EXIT_SUCCESS;
1719}