blob: 813cf598837e9c721bbdacf0376481c01e8ae847 [file] [log] [blame]
Richard Hughes02c90d82018-08-09 12:13:03 +01001/*
Richard Hughes5c9b1fc2021-01-07 14:20:49 +00002 * Copyright (C) 2015 Richard Hughes <richard@hughsie.com>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003 *
Mario Limonciello51308e62018-05-28 20:05:46 -05004 * SPDX-License-Identifier: LGPL-2.1+
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00005 */
6
Richard Hughesb08e7bc2018-09-11 10:51:13 +01007#define G_LOG_DOMAIN "FuMain"
8
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00009#include "config.h"
10
Richard Hughes8645ec92015-03-19 10:14:32 +000011#include <fwupd.h>
Richard Hughes481aa2a2018-09-18 20:51:46 +010012#include <xmlb.h>
Richard Hughes8bbfdf42015-02-26 22:28:09 +000013#include <fcntl.h>
Richard Hughesd8a02bf2015-04-25 17:00:59 +010014#include <glib/gstdio.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000015#include <gio/gio.h>
Richard Hughes8bbfdf42015-02-26 22:28:09 +000016#include <gio/gunixfdlist.h>
17#include <glib/gi18n.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000018#ifdef HAVE_GIO_UNIX
Richard Hughescfc44fa2016-04-28 14:36:35 +010019#include <glib-unix.h>
Richard Hughes9e5675e2019-11-22 09:35:03 +000020#endif
Richard Hughesc9cdf1d2018-02-12 10:34:53 +000021#include <json-glib/json-glib.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000022#include <locale.h>
Richard Hughes8bbfdf42015-02-26 22:28:09 +000023#include <stdlib.h>
Richard Hughes98dd6402015-08-25 14:07:45 +010024#include <unistd.h>
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000025
Richard Hughesbc3a4e12018-01-06 22:41:47 +000026#include "fu-history.h"
Richard Hughescff38bc2016-12-12 12:03:37 +000027#include "fu-plugin-private.h"
Richard Hughes0f89a0d2020-09-29 09:56:58 +010028#include "fu-polkit-agent.h"
Richard Hughes9a7db9d2017-08-21 14:03:45 +010029#include "fu-progressbar.h"
Richard Hughes196c6c62020-05-11 19:42:47 +010030#include "fu-security-attrs.h"
Richard Hughesf7616402018-05-18 09:53:18 +010031#include "fu-util-common.h"
Richard Hughes68cc00c2017-06-06 16:59:54 +010032#include "fwupd-common-private.h"
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000033
Richard Hughes3d005222019-05-17 14:02:41 +010034#ifdef HAVE_SYSTEMD
35#include "fu-systemd.h"
36#endif
37
Richard Hughes8a870d02017-06-15 21:04:48 +010038/* custom return code */
39#define EXIT_NOTHING_TO_DO 2
40
Richard Hughes171ec0d2018-08-10 10:47:57 +010041typedef enum {
42 FU_UTIL_OPERATION_UNKNOWN,
43 FU_UTIL_OPERATION_UPDATE,
44 FU_UTIL_OPERATION_DOWNGRADE,
Mario Limonciellof78f66d2018-08-13 10:00:33 +010045 FU_UTIL_OPERATION_INSTALL,
Richard Hughes171ec0d2018-08-10 10:47:57 +010046 FU_UTIL_OPERATION_LAST
47} FuUtilOperation;
48
Richard Hughesc77e1112019-03-01 10:16:26 +000049struct FuUtilPrivate {
Richard Hughescfc44fa2016-04-28 14:36:35 +010050 GCancellable *cancellable;
Richard Hughes6ed25f52021-01-10 19:27:33 +000051 GMainContext *main_ctx;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000052 GOptionContext *context;
Richard Hughes2d6e1862016-03-18 09:20:37 +000053 FwupdInstallFlags flags;
Richard Hughes6de10e12021-01-27 15:47:07 +000054 FwupdClientDownloadFlags download_flags;
Richard Hughescb07a3e2016-03-17 10:09:03 +000055 FwupdClient *client;
Richard Hughes9a7db9d2017-08-21 14:03:45 +010056 FuProgressbar *progressbar;
Mario Limoncielloa7d15302020-12-14 11:49:28 -060057 gboolean no_remote_check;
Richard Hughesf06ba472018-01-15 07:54:35 +000058 gboolean no_metadata_check;
Richard Hughes50a6f702018-02-02 16:03:31 +000059 gboolean no_reboot_check;
Richard Hughesf06ba472018-01-15 07:54:35 +000060 gboolean no_unreported_check;
Mario Limonciello98b95162019-10-30 09:20:43 -050061 gboolean no_safety_check;
Richard Hughesf06ba472018-01-15 07:54:35 +000062 gboolean assume_yes;
Richard Hughes4ffc14f2019-03-08 09:55:20 +000063 gboolean sign;
Richard Hughes5c82b942020-09-14 12:24:06 +010064 gboolean show_all;
Richard Hughes0e46b222019-09-05 12:13:35 +010065 gboolean disable_ssl_strict;
Richard Hughes171ec0d2018-08-10 10:47:57 +010066 /* only valid in update and downgrade */
67 FuUtilOperation current_operation;
68 FwupdDevice *current_device;
Mario Limonciello32241f42019-01-24 10:12:41 -060069 gchar *current_message;
Mario Limonciello3f243a92019-01-21 22:05:23 -060070 FwupdDeviceFlags completion_flags;
Richard Hughes747f5702019-08-06 14:27:26 +010071 FwupdDeviceFlags filter_include;
72 FwupdDeviceFlags filter_exclude;
Richard Hughesc77e1112019-03-01 10:16:26 +000073};
Richard Hughes8dbfb1c2015-02-26 13:07:40 +000074
Richard Hughesf06ba472018-01-15 07:54:35 +000075static gboolean fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error);
76
Richard Hughesda004cf2016-08-17 17:32:59 +010077static void
78fu_util_client_notify_cb (GObject *object,
79 GParamSpec *pspec,
80 FuUtilPrivate *priv)
81{
Richard Hughes9a7db9d2017-08-21 14:03:45 +010082 fu_progressbar_update (priv->progressbar,
83 fwupd_client_get_status (priv->client),
84 fwupd_client_get_percentage (priv->client));
Richard Hughes876c0072016-08-17 14:51:03 +010085}
86
Mario Limonciellof78f66d2018-08-13 10:00:33 +010087static void
88fu_util_update_device_changed_cb (FwupdClient *client,
89 FwupdDevice *device,
90 FuUtilPrivate *priv)
91{
92 g_autofree gchar *str = NULL;
93
Richard Hughes809abea2019-03-23 11:06:18 +000094 /* allowed to set whenever the device has changed */
95 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
96 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
97 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
98 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
99
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100100 /* same as last time, so ignore */
101 if (priv->current_device != NULL &&
102 fwupd_device_compare (priv->current_device, device) == 0)
103 return;
104
Richard Hughesee562b52020-04-07 14:32:52 +0100105 /* ignore indirect devices that might have changed */
106 if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
107 fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
108 g_debug ("ignoring %s with status %s",
109 fwupd_device_get_name (device),
110 fwupd_status_to_string (fwupd_device_get_status (device)));
111 return;
112 }
113
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100114 /* show message in progressbar */
115 if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
Mario Limonciello91353d42018-10-09 15:52:45 -0500116 /* TRANSLATORS: %1 is a device name */
117 str = g_strdup_printf (_("Updating %s…"),
118 fwupd_device_get_name (device));
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100119 fu_progressbar_set_title (priv->progressbar, str);
120 } else if (priv->current_operation == FU_UTIL_OPERATION_DOWNGRADE) {
Mario Limonciello91353d42018-10-09 15:52:45 -0500121 /* TRANSLATORS: %1 is a device name */
122 str = g_strdup_printf (_("Downgrading %s…"),
123 fwupd_device_get_name (device));
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100124 fu_progressbar_set_title (priv->progressbar, str);
125 } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
Mario Limonciellof00ca2a2018-08-16 14:56:54 -0500126 /* TRANSLATORS: %1 is a device name */
127 str = g_strdup_printf (_("Installing on %s…"),
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100128 fwupd_device_get_name (device));
129 fu_progressbar_set_title (priv->progressbar, str);
130 } else {
131 g_warning ("no FuUtilOperation set");
132 }
133 g_set_object (&priv->current_device, device);
Mario Limonciello3f243a92019-01-21 22:05:23 -0600134
Mario Limonciello32241f42019-01-24 10:12:41 -0600135 if (priv->current_message == NULL) {
136 const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
137 if (tmp != NULL)
138 priv->current_message = g_strdup (tmp);
139 }
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100140}
141
Richard Hughes747f5702019-08-06 14:27:26 +0100142static gboolean
143fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
144{
Mario Limonciello1d01d3b2019-10-14 12:09:42 -0500145 for (guint i = 0; i < 64; i++) {
Francois Berder15daf692019-11-29 11:00:08 +0000146 FwupdDeviceFlags flag = 1LLU << i;
147 if (priv->filter_include & flag) {
148 if (!fwupd_device_has_flag (dev, flag))
Mario Limonciello1d01d3b2019-10-14 12:09:42 -0500149 return FALSE;
150 }
Francois Berder15daf692019-11-29 11:00:08 +0000151 if (priv->filter_exclude & flag) {
152 if (fwupd_device_has_flag (dev, flag))
Mario Limonciello1d01d3b2019-10-14 12:09:42 -0500153 return FALSE;
154 }
Richard Hughes747f5702019-08-06 14:27:26 +0100155 }
156 return TRUE;
157}
158
Richard Hughes224002a2017-06-06 08:55:14 +0100159static FwupdDevice *
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100160fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices, GError **error)
Richard Hughes224002a2017-06-06 08:55:14 +0100161{
162 FwupdDevice *dev;
163 guint idx;
Richard Hughes224002a2017-06-06 08:55:14 +0100164 g_autoptr(GPtrArray) devices_filtered = NULL;
165
Richard Hughes224002a2017-06-06 08:55:14 +0100166 /* filter results */
167 devices_filtered = g_ptr_array_new ();
168 for (guint i = 0; i < devices->len; i++) {
Richard Hughes81c76492017-06-06 11:10:57 +0100169 dev = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100170 if (!fu_util_filter_device (priv, dev))
171 continue;
Richard Hughes224002a2017-06-06 08:55:14 +0100172 g_ptr_array_add (devices_filtered, dev);
173 }
174
175 /* nothing */
176 if (devices_filtered->len == 0) {
177 g_set_error_literal (error,
178 FWUPD_ERROR,
Richard Hughesbbde1df2017-06-15 21:28:26 +0100179 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes224002a2017-06-06 08:55:14 +0100180 "No supported devices");
181 return NULL;
182 }
183
184 /* exactly one */
185 if (devices_filtered->len == 1) {
186 dev = g_ptr_array_index (devices_filtered, 0);
Mario Limonciello1d01d3b2019-10-14 12:09:42 -0500187 /* TRANSLATORS: Device has been chosen by the daemon for the user */
188 g_print ("%s: %s\n", _("Selected device"), fwupd_device_get_name (dev));
Richard Hughes224002a2017-06-06 08:55:14 +0100189 return g_object_ref (dev);
190 }
191
192 /* TRANSLATORS: get interactive prompt */
193 g_print ("%s\n", _("Choose a device:"));
Mario Limonciello5dbdd0b2018-03-01 09:56:18 -0600194 /* TRANSLATORS: this is to abort the interactive prompt */
195 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes224002a2017-06-06 08:55:14 +0100196 for (guint i = 0; i < devices_filtered->len; i++) {
197 dev = g_ptr_array_index (devices_filtered, i);
198 g_print ("%u.\t%s (%s)\n",
199 i + 1,
200 fwupd_device_get_id (dev),
201 fwupd_device_get_name (dev));
202 }
203 idx = fu_util_prompt_for_number (devices_filtered->len);
Mario Limonciello80d1a292018-02-28 10:14:15 -0600204 if (idx == 0) {
205 g_set_error_literal (error,
206 FWUPD_ERROR,
207 FWUPD_ERROR_NOTHING_TO_DO,
208 "Request canceled");
209 return NULL;
210 }
Richard Hughes224002a2017-06-06 08:55:14 +0100211 dev = g_ptr_array_index (devices_filtered, idx - 1);
212 return g_object_ref (dev);
213}
214
Richard Hughes871e0172015-03-17 21:00:18 +0000215static gboolean
Richard Hughes0d232072018-01-11 13:15:39 +0000216fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error)
217{
Richard Hughes0d232072018-01-11 13:15:39 +0000218 g_autoptr(GError) error_local = NULL;
219 g_autoptr(GPtrArray) devices = NULL;
Richard Hughesf06ba472018-01-15 07:54:35 +0000220 g_autoptr(GPtrArray) devices_failed = g_ptr_array_new ();
221 g_autoptr(GPtrArray) devices_success = g_ptr_array_new ();
Richard Hughesf1accad2019-09-25 09:43:33 +0100222 g_autoptr(GPtrArray) remotes = NULL;
223 g_autoptr(GHashTable) remote_id_uri_map = NULL;
Mario Limonciello34c366a2019-09-24 11:24:24 -0500224 gboolean all_automatic = FALSE;
Richard Hughesf06ba472018-01-15 07:54:35 +0000225
226 /* we don't want to ask anything */
227 if (priv->no_unreported_check) {
228 g_debug ("skipping unreported check");
229 return TRUE;
230 }
Richard Hughes0d232072018-01-11 13:15:39 +0000231
232 /* get all devices from the history database */
233 devices = fwupd_client_get_history (priv->client, NULL, &error_local);
234 if (devices == NULL) {
235 if (g_error_matches (error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO))
236 return TRUE;
237 g_propagate_error (error, g_steal_pointer (&error_local));
238 return FALSE;
239 }
240
Richard Hughesf1accad2019-09-25 09:43:33 +0100241 /* create a map of RemoteID to RemoteURI */
242 remotes = fwupd_client_get_remotes (priv->client, NULL, error);
243 if (remotes == NULL)
244 return FALSE;
245 remote_id_uri_map = g_hash_table_new (g_str_hash, g_str_equal);
246 for (guint i = 0; i < remotes->len; i++) {
247 FwupdRemote *remote = g_ptr_array_index (remotes, i);
Mario Limonciello34c366a2019-09-24 11:24:24 -0500248 gboolean remote_automatic;
Richard Hughesf1accad2019-09-25 09:43:33 +0100249 if (fwupd_remote_get_id (remote) == NULL)
250 continue;
251 if (fwupd_remote_get_report_uri (remote) == NULL)
252 continue;
253 g_debug ("adding %s for %s",
254 fwupd_remote_get_report_uri (remote),
255 fwupd_remote_get_id (remote));
256 g_hash_table_insert (remote_id_uri_map,
257 (gpointer) fwupd_remote_get_id (remote),
258 (gpointer) fwupd_remote_get_report_uri (remote));
Mario Limonciello34c366a2019-09-24 11:24:24 -0500259 remote_automatic = fwupd_remote_get_automatic_reports (remote);
260 g_debug ("%s is %d", fwupd_remote_get_title (remote), remote_automatic);
261 if (remote_automatic && !all_automatic)
262 all_automatic = TRUE;
Mario Limonciello326950c2019-10-16 09:46:16 -0500263 if (!remote_automatic && all_automatic) {
Mario Limonciello34c366a2019-09-24 11:24:24 -0500264 all_automatic = FALSE;
Mario Limonciello326950c2019-10-16 09:46:16 -0500265 break;
266 }
Richard Hughesf1accad2019-09-25 09:43:33 +0100267 }
268
269 /* check that they can be reported */
Richard Hughes0d232072018-01-11 13:15:39 +0000270 for (guint i = 0; i < devices->len; i++) {
271 FwupdDevice *dev = g_ptr_array_index (devices, i);
Richard Hughesf1accad2019-09-25 09:43:33 +0100272 FwupdRelease *rel = fwupd_device_get_release_default (dev);
273 const gchar *remote_id;
274 const gchar *remote_uri;
275
Richard Hughes747f5702019-08-06 14:27:26 +0100276 if (!fu_util_filter_device (priv, dev))
277 continue;
Richard Hughes0d232072018-01-11 13:15:39 +0000278 if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED))
279 continue;
280 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
281 continue;
Richard Hughesf1accad2019-09-25 09:43:33 +0100282
283 /* find the RemoteURI to use for the device */
284 remote_id = fwupd_release_get_remote_id (rel);
285 if (remote_id == NULL) {
286 g_debug ("%s has no RemoteID", fwupd_device_get_id (dev));
287 continue;
288 }
289 remote_uri = g_hash_table_lookup (remote_id_uri_map, remote_id);
290 if (remote_uri == NULL) {
291 g_debug ("%s has no RemoteURI", remote_id);
292 continue;
293 }
294
295 /* only send success and failure */
296 if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
Richard Hughesf06ba472018-01-15 07:54:35 +0000297 g_ptr_array_add (devices_failed, dev);
Richard Hughesf1accad2019-09-25 09:43:33 +0100298 } else if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS) {
Richard Hughesf06ba472018-01-15 07:54:35 +0000299 g_ptr_array_add (devices_success, dev);
Richard Hughesf1accad2019-09-25 09:43:33 +0100300 } else {
301 g_debug ("ignoring %s with UpdateState %s",
302 fwupd_device_get_id (dev),
303 fwupd_update_state_to_string (fwupd_device_get_update_state (dev)));
Richard Hughes0d232072018-01-11 13:15:39 +0000304 }
305 }
306
307 /* nothing to do */
Richard Hughesf06ba472018-01-15 07:54:35 +0000308 if (devices_failed->len == 0 && devices_success->len == 0) {
309 g_debug ("no unreported devices");
Richard Hughes0d232072018-01-11 13:15:39 +0000310 return TRUE;
Richard Hughes0d232072018-01-11 13:15:39 +0000311 }
Richard Hughesf06ba472018-01-15 07:54:35 +0000312
Mario Limonciello34c366a2019-09-24 11:24:24 -0500313 g_debug ("All automatic: %d", all_automatic);
Richard Hughesf06ba472018-01-15 07:54:35 +0000314 /* show the success and failures */
Mario Limonciello34c366a2019-09-24 11:24:24 -0500315 if (!priv->assume_yes && !all_automatic) {
Richard Hughesf06ba472018-01-15 07:54:35 +0000316
317 /* delimit */
318 g_print ("________________________________________________\n");
319
320 /* failures */
321 if (devices_failed->len > 0) {
322 /* TRANSLATORS: a list of failed updates */
323 g_print ("\n%s\n\n", _("Devices that were not updated correctly:"));
324 for (guint i = 0; i < devices_failed->len; i++) {
325 FwupdDevice *dev = g_ptr_array_index (devices_failed, i);
326 FwupdRelease *rel = fwupd_device_get_release_default (dev);
327 g_print (" • %s (%s → %s)\n",
328 fwupd_device_get_name (dev),
329 fwupd_device_get_version (dev),
330 fwupd_release_get_version (rel));
331 }
332 }
333
334 /* success */
335 if (devices_success->len > 0) {
336 /* TRANSLATORS: a list of successful updates */
337 g_print ("\n%s\n\n", _("Devices that have been updated successfully:"));
338 for (guint i = 0; i < devices_success->len; i++) {
339 FwupdDevice *dev = g_ptr_array_index (devices_success, i);
340 FwupdRelease *rel = fwupd_device_get_release_default (dev);
341 g_print (" • %s (%s → %s)\n",
342 fwupd_device_get_name (dev),
343 fwupd_device_get_version (dev),
344 fwupd_release_get_version (rel));
345 }
346 }
347
348 /* ask for permission */
Richard Hughes5a279672021-03-29 13:56:06 +0100349 g_print ("\n%s\n%s (%s) [Y|n]:\n",
Richard Hughesf06ba472018-01-15 07:54:35 +0000350 /* TRANSLATORS: explain why we want to upload */
Mario Limonciello5a256532018-04-10 14:00:44 -0500351 _("Uploading firmware reports helps hardware vendors"
352 " to quickly identify failing and successful updates"
353 " on real devices."),
354 /* TRANSLATORS: ask the user to upload */
Richard Hughesf06ba472018-01-15 07:54:35 +0000355 _("Upload report now?"),
356 /* TRANSLATORS: metadata is downloaded from the Internet */
357 _("Requires internet connection"));
Richard Hughes5a279672021-03-29 13:56:06 +0100358 if (!fu_util_prompt_for_boolean (TRUE)) {
359 g_print ("\n%s [y|N]:\n",
360 /* TRANSLATORS: offer to disable this nag */
361 _("Do you want to disable this feature for future updates?"));
362 if (fu_util_prompt_for_boolean (FALSE)) {
363 for (guint i = 0; i < remotes->len; i++) {
364 FwupdRemote *remote = g_ptr_array_index (remotes, i);
365 const gchar *remote_id = fwupd_remote_get_id (remote);
366 if (fwupd_remote_get_report_uri (remote) == NULL)
367 continue;
368 if (!fwupd_client_modify_remote (priv->client,
369 remote_id, "ReportURI", "",
370 NULL, error))
371 return FALSE;
372 }
373 }
374 g_set_error_literal (error,
375 FWUPD_ERROR,
376 FWUPD_ERROR_NOTHING_TO_DO,
377 "Declined upload");
Mario Limonciello34c366a2019-09-24 11:24:24 -0500378 return FALSE;
Richard Hughes5a279672021-03-29 13:56:06 +0100379 }
380 }
381
382 /* upload */
383 if (!fu_util_report_history (priv, NULL, error))
384 return FALSE;
385
386 /* offer to make automatic */
387 if (!priv->assume_yes && !all_automatic) {
388 g_print ("\n%s [y|N]:\n",
389 /* TRANSLATORS: offer to stop asking the question */
390 _("Do you want to upload reports automatically for future updates?"));
391 if (fu_util_prompt_for_boolean (FALSE)) {
392 for (guint i = 0; i < remotes->len; i++) {
393 FwupdRemote *remote = g_ptr_array_index (remotes, i);
394 const gchar *remote_id = fwupd_remote_get_id (remote);
395 if (fwupd_remote_get_report_uri (remote) == NULL)
396 continue;
397 if (fwupd_remote_get_automatic_reports (remote))
398 continue;
399 if (!fwupd_client_modify_remote (priv->client,
400 remote_id, "AutomaticReports", "true",
401 NULL, error))
402 return FALSE;
403 }
404 }
Richard Hughesf06ba472018-01-15 07:54:35 +0000405 }
406
407 /* success */
Richard Hughes5a279672021-03-29 13:56:06 +0100408 return TRUE;
Richard Hughes0d232072018-01-11 13:15:39 +0000409}
410
Richard Hughes9e7d69c2018-04-11 10:33:35 +0100411static gboolean
412fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
413{
Mario Limonciello5e13aec2018-04-16 12:36:35 -0500414 const gchar *warning_markup = NULL;
Richard Hughes1fd3ecf2018-04-16 10:53:46 +0100415 g_autofree gchar *warning_plain = NULL;
Richard Hughes9e7d69c2018-04-11 10:33:35 +0100416
Richard Hughes1fd3ecf2018-04-16 10:53:46 +0100417 /* get formatted text */
418 warning_markup = fwupd_remote_get_agreement (remote);
419 if (warning_markup == NULL)
420 return TRUE;
Mario Limonciellofee8f492019-08-18 12:16:07 -0500421 warning_plain = fu_util_convert_description (warning_markup, error);
Richard Hughes1fd3ecf2018-04-16 10:53:46 +0100422 if (warning_plain == NULL)
Richard Hughes9e7d69c2018-04-11 10:33:35 +0100423 return FALSE;
424
Richard Hughese69f0f52021-04-22 11:10:23 +0100425 /* TRANSLATORS: a remote here is like a 'repo' or software source */
426 fu_util_warning_box (_("Enable new remote?"), warning_plain, 80);
Richard Hughes9e7d69c2018-04-11 10:33:35 +0100427 if (!priv->assume_yes) {
428 /* ask for permission */
429 g_print ("\n%s [Y|n]: ",
430 /* TRANSLATORS: should the remote still be enabled */
431 _("Agree and enable the remote?"));
432 if (!fu_util_prompt_for_boolean (TRUE)) {
433 g_set_error_literal (error,
434 FWUPD_ERROR,
435 FWUPD_ERROR_NOTHING_TO_DO,
436 "Declined agreement");
437 return FALSE;
438 }
439 }
440 return TRUE;
441}
442
Richard Hughes42728c42018-05-02 08:14:51 +0100443static void
Mario Limonciello78956cc2018-05-07 11:03:21 -0500444fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FwupdDevice *dev)
Richard Hughes42728c42018-05-02 08:14:51 +0100445{
446 for (guint i = 0; i < devs->len; i++) {
447 FwupdDevice *dev_tmp = g_ptr_array_index (devs, i);
Richard Hughes747f5702019-08-06 14:27:26 +0100448 if (!fu_util_filter_device (priv, dev_tmp))
449 continue;
Richard Hughes5c82b942020-09-14 12:24:06 +0100450 if (!priv->show_all &&
Mario Limonciellod1775bc2018-07-17 00:28:52 -0500451 !fu_util_is_interesting_device (dev_tmp))
Mario Limonciello78956cc2018-05-07 11:03:21 -0500452 continue;
Richard Hughes42728c42018-05-02 08:14:51 +0100453 if (fwupd_device_get_parent (dev_tmp) == dev) {
Mario Limonciello8e145442019-09-04 15:19:21 -0500454 FwupdRelease *rel = fwupd_device_get_release_default (dev_tmp);
Richard Hughes42728c42018-05-02 08:14:51 +0100455 GNode *child = g_node_append_data (root, dev_tmp);
Mario Limonciello8e145442019-09-04 15:19:21 -0500456 if (rel != NULL)
457 g_node_append_data (child, rel);
Mario Limonciello78956cc2018-05-07 11:03:21 -0500458 fu_util_build_device_tree (priv, child, devs, dev_tmp);
Richard Hughes42728c42018-05-02 08:14:51 +0100459 }
460 }
461}
462
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500463static gchar *
464fu_util_get_tree_title (FuUtilPrivate *priv)
465{
466 return g_strdup (fwupd_client_get_host_product (priv->client));
467}
468
Richard Hughes42728c42018-05-02 08:14:51 +0100469static gboolean
Mario Limonciello1a9127d2019-08-27 11:32:51 +0100470fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughes42728c42018-05-02 08:14:51 +0100471{
472 g_autoptr(GNode) root = g_node_new (NULL);
473 g_autoptr(GPtrArray) devs = NULL;
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500474 g_autofree gchar *title = fu_util_get_tree_title (priv);
Richard Hughes42728c42018-05-02 08:14:51 +0100475
476 /* get results from daemon */
477 devs = fwupd_client_get_devices (priv->client, NULL, error);
478 if (devs == NULL)
479 return FALSE;
Mario Limonciello76196652021-01-13 22:53:26 -0600480 if (devs->len > 0)
481 fu_util_build_device_tree (priv, root, devs, NULL);
Richard Hughes42728c42018-05-02 08:14:51 +0100482
483 /* print */
Mario Limonciello76196652021-01-13 22:53:26 -0600484 if (g_node_n_children (root) == 0) {
Richard Hughes42728c42018-05-02 08:14:51 +0100485 /* TRANSLATORS: nothing attached that can be upgraded */
486 g_print ("%s\n", _("No hardware detected with firmware update capability"));
487 return TRUE;
488 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500489 fu_util_print_tree (root, title);
Richard Hughes42728c42018-05-02 08:14:51 +0100490
Richard Hughesf06ba472018-01-15 07:54:35 +0000491 /* nag? */
492 if (!fu_util_perhaps_show_unreported (priv, error))
493 return FALSE;
494
Richard Hughes8dbfb1c2015-02-26 13:07:40 +0000495 return TRUE;
496}
497
Richard Hughes7bcb8d42020-10-08 15:47:47 +0100498static gboolean
499fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
500{
501 g_autoptr(GPtrArray) plugins = NULL;
502
503 /* get results from daemon */
504 plugins = fwupd_client_get_plugins (priv->client, NULL, error);
505 if (plugins == NULL)
506 return FALSE;
507
508 /* print */
509 for (guint i = 0; i < plugins->len; i++) {
510 FuPlugin *plugin = g_ptr_array_index (plugins, i);
511 g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
512 g_print ("%s\n", str);
513 }
514 if (plugins->len == 0) {
515 /* TRANSLATORS: nothing found */
516 g_print ("%s\n", _("No plugins found"));
517 }
518
519 /* success */
520 return TRUE;
521}
522
Richard Hughes3d178be2018-08-30 11:14:24 +0100523static gchar *
524fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
525{
526 g_autofree gchar *filename = NULL;
Richard Hughesf4c55d82020-07-09 10:45:31 +0100527 g_autoptr(GBytes) blob = NULL;
Richard Hughes3d178be2018-08-30 11:14:24 +0100528
529 /* a local file */
Richard Hughes45a00732019-11-22 16:57:14 +0000530 if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
531 return g_strdup (perhapsfn);
Richard Hughes3a73c342020-11-13 13:25:22 +0000532 if (!fu_util_is_url (perhapsfn))
Richard Hughes3d178be2018-08-30 11:14:24 +0100533 return g_strdup (perhapsfn);
534
535 /* download the firmware to a cachedir */
536 filename = fu_util_get_user_cache_path (perhapsfn);
537 if (!fu_common_mkdir_parent (filename, error))
538 return NULL;
Richard Hughesf4c55d82020-07-09 10:45:31 +0100539 blob = fwupd_client_download_bytes (priv->client, perhapsfn,
Richard Hughes6de10e12021-01-27 15:47:07 +0000540 priv->download_flags,
Richard Hughesf4c55d82020-07-09 10:45:31 +0100541 priv->cancellable, error);
542 if (blob == NULL)
543 return NULL;
544
545 /* save file to cache */
546 if (!fu_common_set_contents_bytes (filename, blob, error))
Richard Hughes3d178be2018-08-30 11:14:24 +0100547 return NULL;
548 return g_steal_pointer (&filename);
549}
550
Mario Limonciello32241f42019-01-24 10:12:41 -0600551static void
552fu_util_display_current_message (FuUtilPrivate *priv)
553{
Richard Hughesc56d2bd2019-09-27 09:37:05 +0100554 if (priv->current_message == NULL) {
555 /* TRANSLATORS: success message */
556 g_print ("%s\n", _("Successfully installed firmware"));
Mario Limonciello32241f42019-01-24 10:12:41 -0600557 return;
Richard Hughesc56d2bd2019-09-27 09:37:05 +0100558 }
559 /* TRANSLATORS: success message */
560 g_print ("%s: %s\n", _("Successfully installed firmware"), priv->current_message);
Mario Limonciello32241f42019-01-24 10:12:41 -0600561 g_clear_pointer (&priv->current_message, g_free);
562}
563
Mario Limoncielloa570bb52015-07-23 12:36:29 -0500564static gboolean
Richard Hughesd079b1a2015-03-06 10:09:55 +0000565fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
566{
Richard Hughes63a407a2015-07-22 08:54:14 +0100567 const gchar *id;
Richard Hughes3d178be2018-08-30 11:14:24 +0100568 g_autofree gchar *filename = NULL;
Richard Hughes63a407a2015-07-22 08:54:14 +0100569
570 /* handle both forms */
571 if (g_strv_length (values) == 1) {
572 id = FWUPD_DEVICE_ID_ANY;
573 } else if (g_strv_length (values) == 2) {
574 id = values[1];
575 } else {
Richard Hughesd079b1a2015-03-06 10:09:55 +0000576 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000577 FWUPD_ERROR,
Richard Hughes7105e422016-03-31 11:23:04 +0100578 FWUPD_ERROR_INVALID_ARGS,
Richard Hughes3b847532017-10-23 13:22:55 +0100579 "Invalid arguments");
Richard Hughesd079b1a2015-03-06 10:09:55 +0000580 return FALSE;
581 }
Richard Hughes63a407a2015-07-22 08:54:14 +0100582
Mario Limonciellof78f66d2018-08-13 10:00:33 +0100583 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
584 g_signal_connect (priv->client, "device-changed",
585 G_CALLBACK (fu_util_update_device_changed_cb), priv);
586
Richard Hughes6ab53112017-09-15 09:35:25 +0100587 /* install with flags chosen by the user */
Richard Hughes3d178be2018-08-30 11:14:24 +0100588 filename = fu_util_download_if_required (priv, values[0], error);
589 if (filename == NULL)
590 return FALSE;
Mario Limonciello3f243a92019-01-21 22:05:23 -0600591
592 if (!fwupd_client_install (priv->client, id, filename, priv->flags, NULL, error))
593 return FALSE;
594
Mario Limonciello32241f42019-01-24 10:12:41 -0600595 fu_util_display_current_message (priv);
596
Mario Limonciello3f243a92019-01-21 22:05:23 -0600597 /* we don't want to ask anything */
598 if (priv->no_reboot_check) {
599 g_debug ("skipping reboot check");
600 return TRUE;
601 }
602
603 /* show reboot if needed */
604 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughesd079b1a2015-03-06 10:09:55 +0000605}
606
Richard Hughescccc7752015-03-06 11:13:19 +0000607static gboolean
608fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
609{
Richard Hughes7289a6b2016-05-29 09:27:47 +0100610 g_autoptr(GPtrArray) array = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500611 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500612 g_autofree gchar *title = fu_util_get_tree_title (priv);
Richard Hughescccc7752015-03-06 11:13:19 +0000613
614 /* check args */
615 if (g_strv_length (values) != 1) {
616 g_set_error_literal (error,
Richard Hughes8645ec92015-03-19 10:14:32 +0000617 FWUPD_ERROR,
Richard Hughes7105e422016-03-31 11:23:04 +0100618 FWUPD_ERROR_INVALID_ARGS,
Richard Hughes3b847532017-10-23 13:22:55 +0100619 "Invalid arguments");
Richard Hughescccc7752015-03-06 11:13:19 +0000620 return FALSE;
621 }
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600622
623 /* implied, important for get-details on a device not in your system */
Richard Hughes5c82b942020-09-14 12:24:06 +0100624 priv->show_all = TRUE;
Mario Limonciellodc0608d2020-02-25 11:25:44 -0600625
Richard Hughes07f963a2017-09-15 14:28:47 +0100626 array = fwupd_client_get_details (priv->client, values[0], NULL, error);
Richard Hughes7289a6b2016-05-29 09:27:47 +0100627 if (array == NULL)
Richard Hughescccc7752015-03-06 11:13:19 +0000628 return FALSE;
Mario Limonciello8e145442019-09-04 15:19:21 -0500629 fu_util_build_device_tree (priv, root, array, NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500630 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500631
Richard Hughescccc7752015-03-06 11:13:19 +0000632 return TRUE;
633}
634
Richard Hughes50a6f702018-02-02 16:03:31 +0000635static gboolean
Richard Hughesaf54a072018-01-11 10:04:50 +0000636fu_util_clear_history (FuUtilPrivate *priv, gchar **values, GError **error)
637{
638 g_autoptr(FuHistory) history = fu_history_new ();
639 return fu_history_remove_all (history, error);
640}
641
642static gboolean
Mario Limonciello34c366a2019-09-24 11:24:24 -0500643fu_util_report_history_for_remote (FuUtilPrivate *priv,
644 const gchar *remote_id,
Richard Hughes0a7bc972018-01-11 13:14:04 +0000645 GPtrArray *devices,
646 GError **error)
647{
Richard Hughes0a7bc972018-01-11 13:14:04 +0000648 g_autofree gchar *data = NULL;
Richard Hughes4ffc14f2019-03-08 09:55:20 +0000649 g_autofree gchar *sig = NULL;
Richard Hughes6d9ae622020-06-16 15:20:23 +0100650 g_autofree gchar *uri = NULL;
Mario Limonciello34c366a2019-09-24 11:24:24 -0500651 g_autoptr(FwupdRemote) remote = NULL;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000652
653 /* convert to JSON */
654 data = fwupd_build_history_report_json (devices, error);
655 if (data == NULL)
656 return FALSE;
657
Richard Hughes4ffc14f2019-03-08 09:55:20 +0000658 /* self sign data */
659 if (priv->sign) {
660 sig = fwupd_client_self_sign (priv->client, data,
661 FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP,
662 priv->cancellable, error);
663 if (sig == NULL)
664 return FALSE;
665 }
666
Mario Limonciello34c366a2019-09-24 11:24:24 -0500667 remote = fwupd_client_get_remote_by_id (priv->client, remote_id,
668 NULL, error);
669 if (remote == NULL)
670 return FALSE;
Mario Limonciello34c366a2019-09-24 11:24:24 -0500671
Richard Hughes0a7bc972018-01-11 13:14:04 +0000672 /* ask for permission */
Mario Limonciello34c366a2019-09-24 11:24:24 -0500673 if (!priv->assume_yes && !fwupd_remote_get_automatic_reports (remote)) {
Richard Hughes6d9ae622020-06-16 15:20:23 +0100674 fu_util_print_data (_("Target"), fwupd_remote_get_report_uri (remote));
Richard Hughesf06ba472018-01-15 07:54:35 +0000675 fu_util_print_data (_("Payload"), data);
Richard Hughes4ffc14f2019-03-08 09:55:20 +0000676 if (sig != NULL)
677 fu_util_print_data (_("Signature"), sig);
Richard Hughesf06ba472018-01-15 07:54:35 +0000678 g_print ("%s [Y|n]: ", _("Proceed with upload?"));
679 if (!fu_util_prompt_for_boolean (TRUE)) {
680 g_set_error_literal (error,
681 FWUPD_ERROR,
682 FWUPD_ERROR_PERMISSION_DENIED,
683 "User declined action");
684 return FALSE;
685 }
Richard Hughes0a7bc972018-01-11 13:14:04 +0000686 }
687
Richard Hughes6d9ae622020-06-16 15:20:23 +0100688 /* POST request and parse reply */
Richard Hughes9b6d6162020-07-07 16:24:57 +0100689 if (!fu_util_send_report (priv->client,
Richard Hughes6d9ae622020-06-16 15:20:23 +0100690 fwupd_remote_get_report_uri (remote),
691 data, sig, &uri, error))
Richard Hughes271f68b2018-02-14 12:45:26 +0000692 return FALSE;
Richard Hughes6d9ae622020-06-16 15:20:23 +0100693
694 /* server wanted us to see a message */
695 if (uri != NULL) {
696 g_print ("%s %s\n",
697 /* TRANSLATORS: the server sent the user a small message */
698 _("Update failure is a known issue, visit this URL for more information:"),
699 uri);
Richard Hughes271f68b2018-02-14 12:45:26 +0000700 }
701
Richard Hughes6d9ae622020-06-16 15:20:23 +0100702 /* success */
Richard Hughes0a7bc972018-01-11 13:14:04 +0000703 return TRUE;
704}
705
706static gboolean
707fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error)
708{
Richard Hughes0a7bc972018-01-11 13:14:04 +0000709 g_autoptr(GHashTable) report_map = NULL;
Mario Limonciello34c366a2019-09-24 11:24:24 -0500710 g_autoptr(GList) ids = NULL;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000711 g_autoptr(GPtrArray) devices = NULL;
Richard Hughesc56d2bd2019-09-27 09:37:05 +0100712 g_autoptr(GString) str = g_string_new (NULL);
Richard Hughes0a7bc972018-01-11 13:14:04 +0000713
Richard Hughes0a7bc972018-01-11 13:14:04 +0000714 /* get all devices from the history database, then filter them,
715 * adding to a hash map of report-ids */
716 devices = fwupd_client_get_history (priv->client, NULL, error);
717 if (devices == NULL)
718 return FALSE;
719 report_map = g_hash_table_new_full (g_str_hash, g_str_equal,
720 g_free, (GDestroyNotify) g_ptr_array_unref);
721 for (guint i = 0; i < devices->len; i++) {
722 FwupdDevice *dev = g_ptr_array_index (devices, i);
723 FwupdRelease *rel = fwupd_device_get_release_default (dev);
724 const gchar *remote_id;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000725 GPtrArray *devices_tmp;
Richard Hughes589270a2020-06-16 11:27:49 +0100726 g_autoptr(FwupdRemote) remote = NULL;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000727
728 /* filter, if not forcing */
Richard Hughes747f5702019-08-06 14:27:26 +0100729 if (!fu_util_filter_device (priv, dev))
730 continue;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000731 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
732 if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED))
733 continue;
734 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
735 continue;
736 }
Richard Hughes3f0b3f82018-02-23 12:55:49 +0000737 /* only send success and failure */
738 if (fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED &&
739 fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_SUCCESS) {
740 g_debug ("ignoring %s with UpdateState %s",
741 fwupd_device_get_id (dev),
742 fwupd_update_state_to_string (fwupd_device_get_update_state (dev)));
743 continue;
744 }
745
Richard Hughes0a7bc972018-01-11 13:14:04 +0000746 /* find the RemoteURI to use for the device */
747 remote_id = fwupd_release_get_remote_id (rel);
748 if (remote_id == NULL) {
749 g_debug ("%s has no RemoteID", fwupd_device_get_id (dev));
750 continue;
751 }
Richard Hughes589270a2020-06-16 11:27:49 +0100752 remote = fwupd_client_get_remote_by_id (priv->client, remote_id,
753 NULL, error);
754 if (remote == NULL)
755 return FALSE;
756 if (fwupd_remote_get_report_uri (remote) == NULL) {
757 g_debug ("%s has no RemoteURI", fwupd_remote_get_report_uri (remote));
Richard Hughes0a7bc972018-01-11 13:14:04 +0000758 continue;
759 }
760
761 /* add this to the hash map */
Mario Limonciello34c366a2019-09-24 11:24:24 -0500762 devices_tmp = g_hash_table_lookup (report_map, remote_id);
Richard Hughes0a7bc972018-01-11 13:14:04 +0000763 if (devices_tmp == NULL) {
764 devices_tmp = g_ptr_array_new ();
Mario Limonciello34c366a2019-09-24 11:24:24 -0500765 g_hash_table_insert (report_map, g_strdup (remote_id), devices_tmp);
Richard Hughes0a7bc972018-01-11 13:14:04 +0000766 }
Mario Limonciello34c366a2019-09-24 11:24:24 -0500767 g_debug ("using %s for %s", remote_id, fwupd_device_get_id (dev));
Richard Hughes0a7bc972018-01-11 13:14:04 +0000768 g_ptr_array_add (devices_tmp, dev);
769 }
770
771 /* nothing to report */
772 if (g_hash_table_size (report_map) == 0) {
773 g_set_error_literal (error,
774 FWUPD_ERROR,
775 FWUPD_ERROR_NOT_SUPPORTED,
776 "No reports require uploading");
777 return FALSE;
778 }
779
780 /* process each uri */
Mario Limonciello34c366a2019-09-24 11:24:24 -0500781 ids = g_hash_table_get_keys (report_map);
782 for (GList *l = ids; l != NULL; l = l->next) {
783 const gchar *id = l->data;
784 GPtrArray *devices_tmp = g_hash_table_lookup (report_map, id);
785 if (!fu_util_report_history_for_remote (priv, id, devices_tmp, error))
Richard Hughes0a7bc972018-01-11 13:14:04 +0000786 return FALSE;
Richard Hughes0a7bc972018-01-11 13:14:04 +0000787
Richard Hughes4837ab52020-06-16 11:49:45 +0100788 /* mark each device as reported */
789 for (guint i = 0; i < devices_tmp->len; i++) {
790 FwupdDevice *dev = g_ptr_array_index (devices_tmp, i);
791 g_debug ("setting flag on %s", fwupd_device_get_id (dev));
792 if (!fwupd_client_modify_device (priv->client,
793 fwupd_device_get_id (dev),
794 "Flags", "reported",
795 NULL, error))
796 return FALSE;
797 }
Richard Hughes0a7bc972018-01-11 13:14:04 +0000798 }
799
Richard Hughesc56d2bd2019-09-27 09:37:05 +0100800 /* TRANSLATORS: success message -- where the user has uploaded
801 * success and/or failure reports to the remote server */
802 g_string_append_printf (str, ngettext ("Successfully uploaded %u report",
803 "Successfully uploaded %u reports",
804 g_hash_table_size (report_map)),
805 g_hash_table_size (report_map));
806 g_print ("%s\n", str->str);
Richard Hughes0a7bc972018-01-11 13:14:04 +0000807 return TRUE;
808}
809
810static gboolean
Richard Hughes476363a2018-01-11 10:08:58 +0000811fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
812{
813 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -0500814 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500815 g_autofree gchar *title = fu_util_get_tree_title (priv);
Richard Hughes476363a2018-01-11 10:08:58 +0000816
817 /* get all devices from the history database */
818 devices = fwupd_client_get_history (priv->client, NULL, error);
819 if (devices == NULL)
820 return FALSE;
821
822 /* show each device */
823 for (guint i = 0; i < devices->len; i++) {
Mario Limoncielloce94d162019-09-20 13:37:36 -0500824 g_autoptr(GPtrArray) rels = NULL;
Richard Hughes476363a2018-01-11 10:08:58 +0000825 FwupdDevice *dev = g_ptr_array_index (devices, i);
Mario Limonciello3be596b2019-09-20 10:10:08 -0500826 FwupdRelease *rel;
Mario Limoncielloce94d162019-09-20 13:37:36 -0500827 const gchar *remote;
Mario Limonciello3be596b2019-09-20 10:10:08 -0500828 GNode *child;
Richard Hughes9fcd29f2019-11-01 12:07:53 +0000829 g_autoptr(GError) error_local = NULL;
Mario Limoncielloce94d162019-09-20 13:37:36 -0500830
Richard Hughes747f5702019-08-06 14:27:26 +0100831 if (!fu_util_filter_device (priv, dev))
832 continue;
Mario Limonciello3be596b2019-09-20 10:10:08 -0500833 child = g_node_append_data (root, dev);
834
835 rel = fwupd_device_get_release_default (dev);
Mario Limoncielloce94d162019-09-20 13:37:36 -0500836 if (rel == NULL)
837 continue;
838 remote = fwupd_release_get_remote_id (rel);
839
840 /* doesn't actually map to remote */
841 if (remote == NULL) {
Mario Limonciello3be596b2019-09-20 10:10:08 -0500842 g_node_append_data (child, rel);
Mario Limoncielloce94d162019-09-20 13:37:36 -0500843 continue;
844 }
845
846 /* try to lookup releases from client */
Richard Hughes9fcd29f2019-11-01 12:07:53 +0000847 rels = fwupd_client_get_releases (priv->client,
848 fwupd_device_get_id (dev),
849 NULL, &error_local);
850 if (rels == NULL) {
851 g_debug ("failed to get releases for %s: %s",
852 fwupd_device_get_id (dev),
853 error_local->message);
854 g_node_append_data (child, rel);
855 continue;
856 }
Mario Limoncielloce94d162019-09-20 13:37:36 -0500857
858 /* map to a release in client */
859 for (guint j = 0; j < rels->len; j++) {
860 FwupdRelease *rel2 = g_ptr_array_index (rels, j);
861 if (g_strcmp0 (remote,
862 fwupd_release_get_remote_id (rel2)) != 0)
863 continue;
864 if (g_strcmp0 (fwupd_release_get_version (rel),
865 fwupd_release_get_version (rel2)) != 0)
866 continue;
867 g_node_append_data (child, g_object_ref (rel2));
868 rel = NULL;
869 break;
870 }
871
872 /* didn't match anything */
873 if (rels->len == 0 || rel != NULL) {
874 g_node_append_data (child, rel);
875 continue;
876 }
Richard Hughes476363a2018-01-11 10:08:58 +0000877 }
Mario Limoncielloce94d162019-09-20 13:37:36 -0500878
Mario Limonciello20cc9ee2019-09-05 07:27:26 -0500879 fu_util_print_tree (root, title);
Richard Hughes476363a2018-01-11 10:08:58 +0000880
881 return TRUE;
882}
883
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100884static FwupdDevice *
885fu_util_get_device_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
886{
887 if (fwupd_guid_is_valid (id)) {
888 g_autoptr(GPtrArray) devices = NULL;
889 devices = fwupd_client_get_devices_by_guid (priv->client, id,
890 NULL, error);
891 if (devices == NULL)
892 return NULL;
893 return fu_util_prompt_for_device (priv, devices, error);
894 }
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500895 /* did this look like a GUID? */
896 for (guint i = 0; id[i] != '\0'; i++) {
897 if (id[i] == '-') {
898 g_set_error_literal (error,
899 FWUPD_ERROR,
900 FWUPD_ERROR_INVALID_ARGS,
901 "Invalid arguments");
Richard Hughes9c09e2c2020-04-22 10:52:42 +0100902 return NULL;
Mario Limoncielloc6009f52020-04-20 15:40:11 -0500903 }
904 }
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100905 return fwupd_client_get_device_by_id (priv->client, id, NULL, error);
906}
907
908static FwupdDevice *
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600909fu_util_get_device_or_prompt (FuUtilPrivate *priv, gchar **values, GError **error)
910{
911 FwupdDevice *dev = NULL;
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100912 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600913
914 /* get device to use */
915 if (g_strv_length (values) >= 1) {
916 g_autoptr(GError) error_local = NULL;
917 if (g_strv_length (values) > 1) {
918 for (guint i = 1; i < g_strv_length (values); i++)
919 g_debug ("Ignoring extra input %s", values[i]);
920 }
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100921 dev = fu_util_get_device_by_id (priv, values[0], &error_local);
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600922 if (dev != NULL)
923 return dev;
924 g_print ("%s\n", error_local->message);
925 }
Richard Hughes3aaf53c2020-04-20 09:39:46 +0100926
927 /* get all devices from daemon */
928 devices = fwupd_client_get_devices (priv->client, NULL, error);
929 if (devices == NULL)
930 return NULL;
931 return fu_util_prompt_for_device (priv, devices, error);
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600932}
933
Richard Hughes476363a2018-01-11 10:08:58 +0000934static gboolean
Richard Hughes0e883ee2015-03-18 17:22:33 +0000935fu_util_clear_results (FuUtilPrivate *priv, gchar **values, GError **error)
936{
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600937 g_autoptr(FwupdDevice) dev = NULL;
938
939 dev = fu_util_get_device_or_prompt (priv, values, error);
940 if (dev == NULL)
Richard Hughes0e883ee2015-03-18 17:22:33 +0000941 return FALSE;
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600942
943 return fwupd_client_clear_results (priv->client, fwupd_device_get_id (dev), NULL, error);
Richard Hughes0e883ee2015-03-18 17:22:33 +0000944}
945
Richard Hughes174211b2015-06-30 15:37:34 +0100946static gboolean
Richard Hughes31bbd162017-09-15 09:30:46 +0100947fu_util_clear_offline (FuUtilPrivate *priv, gchar **values, GError **error)
948{
Richard Hughesbc3a4e12018-01-06 22:41:47 +0000949 g_autoptr(FuHistory) history = fu_history_new ();
950 return fu_history_remove_all_with_state (history, FWUPD_UPDATE_STATE_PENDING, error);
Richard Hughes31bbd162017-09-15 09:30:46 +0100951}
952
953static gboolean
Richard Hughesd51173d2015-07-30 18:59:52 +0100954fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
955{
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600956 g_autoptr(FwupdDevice) dev = NULL;
957
Mario Limonciello8fa0b382019-10-14 07:36:04 -0500958 priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY;
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600959 dev = fu_util_get_device_or_prompt (priv, values, error);
960 if (dev == NULL)
Richard Hughes29c220d2016-12-14 17:09:54 +0000961 return FALSE;
Mario Limonciello9e965ad2018-02-28 09:21:42 -0600962
Mario Limonciello49556a32019-10-12 20:43:21 -0500963 if (!fwupd_client_verify_update (priv->client, fwupd_device_get_id (dev), NULL, error)) {
964 g_prefix_error (error, "failed to verify update %s: ", fu_device_get_name (dev));
965 return FALSE;
966 }
967 /* TRANSLATORS: success message when user refreshes device checksums */
968 g_print ("%s\n", _("Successfully updated device checksums"));
969
970 return TRUE;
Richard Hughesd51173d2015-07-30 18:59:52 +0100971}
972
Richard Hughesc7cf1662015-07-20 16:04:55 +0100973static gboolean
Mario Limonciello336158a2018-04-10 19:57:53 +0100974fu_util_download_metadata_enable_lvfs (FuUtilPrivate *priv, GError **error)
975{
976 g_autoptr(FwupdRemote) remote = NULL;
977
978 /* is the LVFS available but disabled? */
979 remote = fwupd_client_get_remote_by_id (priv->client, "lvfs", NULL, error);
980 if (remote == NULL)
981 return TRUE;
982 g_print ("%s\n%s\n%s [Y|n]: ",
983 /* TRANSLATORS: explain why no metadata available */
984 _("No remotes are currently enabled so no metadata is available."),
985 /* TRANSLATORS: explain why no metadata available */
986 _("Metadata can be obtained from the Linux Vendor Firmware Service."),
987 /* TRANSLATORS: Turn on the remote */
988 _("Enable this remote?"));
989 if (!fu_util_prompt_for_boolean (TRUE))
990 return TRUE;
Richard Hughes836d9e92021-03-26 15:53:12 +0000991 if (!fwupd_client_modify_remote (priv->client,
992 fwupd_remote_get_id (remote),
993 "Enabled", "true",
994 priv->cancellable, error))
995 return FALSE;
996 if (!fu_util_modify_remote_warning (priv, remote, error))
Mario Limonciello336158a2018-04-10 19:57:53 +0100997 return FALSE;
998
999 /* refresh the newly-enabled remote */
Richard Hughesd5285712020-07-09 10:22:01 +01001000 return fwupd_client_refresh_remote (priv->client, remote,
1001 priv->cancellable, error);
Mario Limonciello336158a2018-04-10 19:57:53 +01001002}
1003
1004static gboolean
Mario Limoncielloeb442ea2020-01-10 10:56:05 -06001005fu_util_check_oldest_remote (FuUtilPrivate *priv, guint64 *age_oldest, GError **error)
1006{
1007 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciello1b8047b2020-06-17 15:23:13 -05001008 gboolean checked = FALSE;
Mario Limoncielloeb442ea2020-01-10 10:56:05 -06001009
1010 /* get the age of the oldest enabled remotes */
1011 remotes = fwupd_client_get_remotes (priv->client, NULL, error);
1012 if (remotes == NULL)
1013 return FALSE;
1014 for (guint i = 0; i < remotes->len; i++) {
1015 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1016 if (!fwupd_remote_get_enabled (remote))
1017 continue;
1018 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
1019 continue;
Mario Limonciello1b8047b2020-06-17 15:23:13 -05001020 checked = TRUE;
Mario Limoncielloeb442ea2020-01-10 10:56:05 -06001021 if (fwupd_remote_get_age (remote) > *age_oldest)
1022 *age_oldest = fwupd_remote_get_age (remote);
1023 }
Mario Limonciello1b8047b2020-06-17 15:23:13 -05001024 if (!checked) {
1025 g_set_error_literal (error,
1026 FWUPD_ERROR,
1027 FWUPD_ERROR_NOTHING_TO_DO,
1028 /* TRANSLATORS: error message for a user who ran fwupdmgr refresh recently but no remotes */
1029 "No remotes enabled.");
1030 return FALSE;
1031 }
Mario Limoncielloeb442ea2020-01-10 10:56:05 -06001032 return TRUE;
1033}
1034
1035static gboolean
Richard Hughesdfed5152017-06-02 12:13:07 +01001036fu_util_download_metadata (FuUtilPrivate *priv, GError **error)
1037{
Mario Limonciello336158a2018-04-10 19:57:53 +01001038 gboolean download_remote_enabled = FALSE;
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001039 guint devices_supported_cnt = 0;
1040 g_autoptr(GPtrArray) devs = NULL;
Richard Hughesdfed5152017-06-02 12:13:07 +01001041 g_autoptr(GPtrArray) remotes = NULL;
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001042 g_autoptr(GString) str = g_string_new (NULL);
Mario Limonciello336158a2018-04-10 19:57:53 +01001043
Mario Limoncielloeb442ea2020-01-10 10:56:05 -06001044 /* metadata refreshed recently */
1045 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
1046 guint64 age_oldest = 0;
1047 const guint64 age_limit_hours = 24;
1048
1049 if (!fu_util_check_oldest_remote (priv, &age_oldest, error))
1050 return FALSE;
1051 if (age_oldest < 60 * 60 * age_limit_hours) {
1052 g_set_error (error,
1053 FWUPD_ERROR,
1054 FWUPD_ERROR_NOTHING_TO_DO,
1055 /* TRANSLATORS: error message for a user who ran fwupdmgr refresh recently
1056 %1 is an already translated timestamp such as 6 hours or 15 seconds */
1057 "Firmware metadata last refresh: %s ago. "
1058 "Use --force to refresh again.",
1059 fu_util_time_to_str (age_oldest));
1060 return FALSE;
1061 }
1062 }
1063
Richard Hughesdfed5152017-06-02 12:13:07 +01001064 remotes = fwupd_client_get_remotes (priv->client, NULL, error);
1065 if (remotes == NULL)
1066 return FALSE;
1067 for (guint i = 0; i < remotes->len; i++) {
1068 FwupdRemote *remote = g_ptr_array_index (remotes, i);
1069 if (!fwupd_remote_get_enabled (remote))
1070 continue;
Richard Hughes2d95a712017-07-26 16:36:43 +01001071 if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
1072 continue;
Mario Limonciello336158a2018-04-10 19:57:53 +01001073 download_remote_enabled = TRUE;
Richard Hughesd5285712020-07-09 10:22:01 +01001074 g_print ("%s %s\n", _("Updating"), fwupd_remote_get_id (remote));
1075 if (!fwupd_client_refresh_remote (priv->client, remote,
1076 priv->cancellable, error))
Richard Hughesdfed5152017-06-02 12:13:07 +01001077 return FALSE;
1078 }
Mario Limonciello336158a2018-04-10 19:57:53 +01001079
1080 /* no web remote is declared; try to enable LVFS */
1081 if (!download_remote_enabled) {
Mario Limonciello561751f2019-10-18 08:51:52 -05001082 /* we don't want to ask anything */
Mario Limoncielloa7d15302020-12-14 11:49:28 -06001083 if (priv->no_remote_check) {
1084 g_debug ("skipping remote check");
Mario Limonciello561751f2019-10-18 08:51:52 -05001085 return TRUE;
1086 }
1087
Mario Limonciello336158a2018-04-10 19:57:53 +01001088 if (!fu_util_download_metadata_enable_lvfs (priv, error))
1089 return FALSE;
1090 }
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001091
1092 /* get devices from daemon */
1093 devs = fwupd_client_get_devices (priv->client, NULL, error);
1094 if (devs == NULL)
1095 return FALSE;
1096
1097 /* get results */
1098 for (guint i = 0; i < devs->len; i++) {
1099 FwupdDevice *dev = g_ptr_array_index (devs, i);
1100 if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
1101 devices_supported_cnt++;
1102 }
1103
1104 /* TRANSLATORS: success message -- where 'metadata' is information
1105 * about available firmware on the remote server */
1106 g_string_append (str, _("Successfully downloaded new metadata: "));
1107
1108 /* TRANSLATORS: how many local devices can expect updates now */
1109 g_string_append_printf (str, ngettext ("%u local device supported",
1110 "%u local devices supported",
1111 devices_supported_cnt),
1112 devices_supported_cnt);
1113 g_print ("%s\n", str->str);
Richard Hughesdfed5152017-06-02 12:13:07 +01001114 return TRUE;
Richard Hughesc7cf1662015-07-20 16:04:55 +01001115}
1116
Richard Hughesc7cf1662015-07-20 16:04:55 +01001117static gboolean
Richard Hughese97261a2015-07-22 10:29:15 +01001118fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesc7cf1662015-07-20 16:04:55 +01001119{
1120 if (g_strv_length (values) == 0)
1121 return fu_util_download_metadata (priv, error);
Richard Hughes0a87f6f2017-06-16 16:43:11 +01001122 if (g_strv_length (values) != 3) {
Richard Hughesc7cf1662015-07-20 16:04:55 +01001123 g_set_error_literal (error,
1124 FWUPD_ERROR,
Richard Hughes7105e422016-03-31 11:23:04 +01001125 FWUPD_ERROR_INVALID_ARGS,
Richard Hughes3b847532017-10-23 13:22:55 +01001126 "Invalid arguments");
Richard Hughesc7cf1662015-07-20 16:04:55 +01001127 return FALSE;
1128 }
1129
1130 /* open file */
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001131 if (!fwupd_client_update_metadata (priv->client,
1132 values[2],
1133 values[0],
1134 values[1],
1135 NULL,
1136 error))
1137 return FALSE;
1138
1139 /* TRANSLATORS: success message -- the user can do this by-hand too */
1140 g_print ("%s\n", _("Successfully refreshed metadata manually"));
1141 return TRUE;
Richard Hughesc7cf1662015-07-20 16:04:55 +01001142}
1143
Richard Hughes0e883ee2015-03-18 17:22:33 +00001144static gboolean
1145fu_util_get_results (FuUtilPrivate *priv, gchar **values, GError **error)
1146{
Richard Hughescb07a3e2016-03-17 10:09:03 +00001147 g_autofree gchar *tmp = NULL;
Richard Hughes80893e22017-09-15 16:09:22 +01001148 g_autoptr(FwupdDevice) dev = NULL;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001149 g_autoptr(FwupdDevice) rel = NULL;
Richard Hughescb07a3e2016-03-17 10:09:03 +00001150
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001151 dev = fu_util_get_device_or_prompt (priv, values, error);
Richard Hughes80893e22017-09-15 16:09:22 +01001152 if (dev == NULL)
Richard Hughes0e883ee2015-03-18 17:22:33 +00001153 return FALSE;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001154
1155 rel = fwupd_client_get_results (priv->client, fwupd_device_get_id (dev), NULL, error);
1156 if (rel == NULL)
1157 return FALSE;
Mario Limonciellofee8f492019-08-18 12:16:07 -05001158 tmp = fu_util_device_to_string (rel, 0);
Richard Hughescb07a3e2016-03-17 10:09:03 +00001159 g_print ("%s", tmp);
Richard Hughes0e883ee2015-03-18 17:22:33 +00001160 return TRUE;
1161}
Richard Hughes871e0172015-03-17 21:00:18 +00001162
Richard Hughese4a100c2017-06-04 21:23:50 +01001163static gboolean
1164fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error)
1165{
Richard Hughes224002a2017-06-06 08:55:14 +01001166 g_autoptr(FwupdDevice) dev = NULL;
Richard Hughese4a100c2017-06-04 21:23:50 +01001167 g_autoptr(GPtrArray) rels = NULL;
Richard Hughes224002a2017-06-06 08:55:14 +01001168
Mario Limonciello1d01d3b2019-10-14 12:09:42 -05001169 priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001170 dev = fu_util_get_device_or_prompt (priv, values, error);
1171 if (dev == NULL)
1172 return FALSE;
Richard Hughes224002a2017-06-06 08:55:14 +01001173
1174 /* get the releases for this device */
Richard Hughescffef262017-06-07 15:11:59 +01001175 rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev), NULL, error);
Richard Hughese4a100c2017-06-04 21:23:50 +01001176 if (rels == NULL)
1177 return FALSE;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001178
1179 if (rels->len == 0) {
1180 /* TRANSLATORS: no repositories to download from */
1181 g_print ("%s\n", _("No releases available"));
1182 return TRUE;
1183 }
Richard Hughes200990f2021-01-28 10:54:07 +00001184 if (g_getenv ("FWUPD_VERBOSE") != NULL) {
1185 for (guint i = 0; i < rels->len; i++) {
1186 FwupdRelease *rel = g_ptr_array_index (rels, i);
1187 g_autofree gchar *tmp = fwupd_release_to_string (rel);
1188 g_print ("%s\n", tmp);
1189 }
1190 } else {
1191 g_autoptr(GNode) root = g_node_new (NULL);
1192 g_autofree gchar *title = fu_util_get_tree_title (priv);
1193 for (guint i = 0; i < rels->len; i++) {
1194 FwupdRelease *rel = g_ptr_array_index (rels, i);
1195 g_node_append_data (root, rel);
1196 }
1197 fu_util_print_tree (root, title);
Richard Hughese4a100c2017-06-04 21:23:50 +01001198 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001199
Richard Hughese4a100c2017-06-04 21:23:50 +01001200 return TRUE;
1201}
1202
Richard Hughes45c15452017-06-06 09:36:03 +01001203static FwupdRelease *
1204fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error)
1205{
1206 FwupdRelease *rel;
1207 guint idx;
1208
1209 /* nothing */
1210 if (rels->len == 0) {
1211 g_set_error_literal (error,
1212 FWUPD_ERROR,
Richard Hughesbbde1df2017-06-15 21:28:26 +01001213 FWUPD_ERROR_NOTHING_TO_DO,
Richard Hughes45c15452017-06-06 09:36:03 +01001214 "No supported releases");
1215 return NULL;
1216 }
1217
1218 /* exactly one */
1219 if (rels->len == 1) {
1220 rel = g_ptr_array_index (rels, 0);
1221 return g_object_ref (rel);
1222 }
1223
1224 /* TRANSLATORS: get interactive prompt */
1225 g_print ("%s\n", _("Choose a release:"));
Richard Hughes596d5932019-07-31 10:21:11 +01001226 /* TRANSLATORS: this is to abort the interactive prompt */
1227 g_print ("0.\t%s\n", _("Cancel"));
Richard Hughes45c15452017-06-06 09:36:03 +01001228 for (guint i = 0; i < rels->len; i++) {
Richard Hughes7565baf2019-07-31 10:22:53 +01001229 const gchar *desc_tmp;
1230 g_autofree gchar *desc = NULL;
1231
Richard Hughes45c15452017-06-06 09:36:03 +01001232 rel = g_ptr_array_index (rels, i);
Richard Hughes7565baf2019-07-31 10:22:53 +01001233
1234 /* no description provided */
1235 desc_tmp = fwupd_release_get_description (rel);
1236 if (desc_tmp == NULL) {
1237 g_print ("%u.\t%s\n", i + 1, fwupd_release_get_version (rel));
1238 continue;
1239 }
1240
1241 /* remove markup, and fall back if we fail */
Mario Limonciellofee8f492019-08-18 12:16:07 -05001242 desc = fu_util_convert_description (desc_tmp, NULL);
Richard Hughes7565baf2019-07-31 10:22:53 +01001243 if (desc == NULL)
1244 desc = g_strdup (desc_tmp);
1245 g_print ("%u.\t%s (%s)\n", i + 1, fwupd_release_get_version (rel), desc);
Richard Hughes45c15452017-06-06 09:36:03 +01001246 }
1247 idx = fu_util_prompt_for_number (rels->len);
Richard Hughes596d5932019-07-31 10:21:11 +01001248 if (idx == 0) {
1249 g_set_error_literal (error,
1250 FWUPD_ERROR,
1251 FWUPD_ERROR_NOTHING_TO_DO,
1252 "Request canceled");
1253 return NULL;
1254 }
Richard Hughes45c15452017-06-06 09:36:03 +01001255 rel = g_ptr_array_index (rels, idx - 1);
1256 return g_object_ref (rel);
1257}
1258
Richard Hughesa043c2e2015-06-29 08:43:18 +01001259static gboolean
Richard Hughesa043c2e2015-06-29 08:43:18 +01001260fu_util_verify (FuUtilPrivate *priv, gchar **values, GError **error)
1261{
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001262 g_autoptr(FwupdDevice) dev = NULL;
1263
Mario Limonciello8fa0b382019-10-14 07:36:04 -05001264 priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001265 dev = fu_util_get_device_or_prompt (priv, values, error);
1266 if (dev == NULL)
Richard Hughesa043c2e2015-06-29 08:43:18 +01001267 return FALSE;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001268
Mario Limonciello49556a32019-10-12 20:43:21 -05001269 if (!fwupd_client_verify (priv->client, fwupd_device_get_id (dev), NULL, error)) {
1270 g_prefix_error (error, "failed to verify %s: ", fu_device_get_name (dev));
1271 return FALSE;
1272 }
1273 /* TRANSLATORS: success message when user verified device checksums */
1274 g_print ("%s\n", _("Successfully verified device checksums"));
1275
1276 return TRUE;
Richard Hughesa043c2e2015-06-29 08:43:18 +01001277}
1278
Richard Hughes9a410ce2016-02-28 15:58:54 +00001279static gboolean
1280fu_util_unlock (FuUtilPrivate *priv, gchar **values, GError **error)
1281{
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001282 g_autoptr(FwupdDevice) dev = NULL;
1283
Mario Limonciello1d01d3b2019-10-14 12:09:42 -05001284 priv->filter_include |= FWUPD_DEVICE_FLAG_LOCKED;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001285 dev = fu_util_get_device_or_prompt (priv, values, error);
1286 if (dev == NULL)
Richard Hughes9a410ce2016-02-28 15:58:54 +00001287 return FALSE;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001288
Mario Limonciello05c5c102019-07-14 11:09:03 -05001289 if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
1290 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
1291 if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
1292 priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
1293
1294 if (!fwupd_client_unlock (priv->client, fwupd_device_get_id (dev), NULL, error))
1295 return FALSE;
1296
1297 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes9a410ce2016-02-28 15:58:54 +00001298}
1299
Richard Hughes871e0172015-03-17 21:00:18 +00001300static gboolean
Richard Hughesf06ba472018-01-15 07:54:35 +00001301fu_util_perhaps_refresh_remotes (FuUtilPrivate *priv, GError **error)
1302{
Richard Hughesf06ba472018-01-15 07:54:35 +00001303 guint64 age_oldest = 0;
1304 const guint64 age_limit_days = 30;
1305
1306 /* we don't want to ask anything */
1307 if (priv->no_metadata_check) {
1308 g_debug ("skipping metadata check");
1309 return TRUE;
1310 }
1311
Torsten Hilbrich9b819e62020-07-22 15:55:28 +02001312 if (!fu_util_check_oldest_remote (priv, &age_oldest, NULL))
1313 return TRUE;
Richard Hughesf06ba472018-01-15 07:54:35 +00001314
1315 /* metadata is new enough */
1316 if (age_oldest < 60 * 60 * 24 * age_limit_days)
1317 return TRUE;
1318
1319 /* ask for permission */
1320 if (!priv->assume_yes) {
Piotr DrÄ…g27cd91f2018-01-25 21:05:34 +01001321 /* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */
1322 g_print (ngettext("Firmware metadata has not been updated for %u"
1323 " day and may not be up to date.",
1324 "Firmware metadata has not been updated for %u"
1325 " days and may not be up to date.",
1326 (gint) age_limit_days), (guint) age_limit_days);
Richard Hughesf06ba472018-01-15 07:54:35 +00001327 g_print ("\n\n");
1328 g_print ("%s (%s) [y|N]: ",
1329 /* TRANSLATORS: ask the user if we can update the metadata */
1330 _("Update now?"),
1331 /* TRANSLATORS: metadata is downloaded from the Internet */
1332 _("Requires internet connection"));
1333 if (!fu_util_prompt_for_boolean (FALSE))
1334 return TRUE;
1335 }
1336
1337 /* downloads new metadata */
1338 return fu_util_download_metadata (priv, error);
1339}
1340
1341static gboolean
Richard Hughes871e0172015-03-17 21:00:18 +00001342fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
1343{
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001344 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellod2afb592019-08-20 11:24:18 -05001345 gboolean supported = FALSE;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001346 g_autoptr(GNode) root = g_node_new (NULL);
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001347 g_autofree gchar *title = fu_util_get_tree_title (priv);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001348 gboolean no_updates_header = FALSE;
1349 gboolean latest_header = FALSE;
Richard Hughes871e0172015-03-17 21:00:18 +00001350
Richard Hughesf06ba472018-01-15 07:54:35 +00001351 /* are the remotes very old */
1352 if (!fu_util_perhaps_refresh_remotes (priv, error))
Richard Hughes0d232072018-01-11 13:15:39 +00001353 return FALSE;
1354
Daniel Campello9fbd7fe2020-09-28 14:53:39 -06001355 /* handle both forms */
1356 if (g_strv_length (values) == 0) {
1357 devices = fwupd_client_get_devices (priv->client, NULL, error);
1358 if (devices == NULL)
1359 return FALSE;
1360 } else if (g_strv_length (values) == 1) {
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06001361 FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error);
Daniel Campello9fbd7fe2020-09-28 14:53:39 -06001362 if (device == NULL)
1363 return FALSE;
1364 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
1365 g_ptr_array_add (devices, device);
1366 } else {
1367 g_set_error_literal (error,
1368 FWUPD_ERROR,
1369 FWUPD_ERROR_INVALID_ARGS,
1370 "Invalid arguments");
Richard Hughes871e0172015-03-17 21:00:18 +00001371 return FALSE;
Daniel Campello9fbd7fe2020-09-28 14:53:39 -06001372 }
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001373 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001374 for (guint i = 0; i < devices->len; i++) {
1375 FwupdDevice *dev = g_ptr_array_index (devices, i);
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001376 g_autoptr(GPtrArray) rels = NULL;
1377 g_autoptr(GError) error_local = NULL;
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001378 GNode *child;
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001379
1380 /* not going to have results, so save a D-Bus round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -06001381 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001382 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001383 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001384 if (!no_updates_header) {
1385 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1386 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1387 no_updates_header = TRUE;
1388 }
1389 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001390 continue;
1391 }
Richard Hughes747f5702019-08-06 14:27:26 +01001392 if (!fu_util_filter_device (priv, dev))
1393 continue;
Mario Limonciellod2afb592019-08-20 11:24:18 -05001394 supported = TRUE;
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001395
1396 /* get the releases for this device and filter for validity */
1397 rels = fwupd_client_get_upgrades (priv->client,
1398 fwupd_device_get_id (dev),
1399 NULL, &error_local);
1400 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001401 if (!latest_header) {
1402 /* TRANSLATORS: message letting the user know no device upgrade available */
1403 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1404 latest_header = TRUE;
1405 }
1406 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001407 /* discard the actual reason from user, but leave for debugging */
1408 g_debug ("%s", error_local->message);
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001409 continue;
1410 }
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001411 child = g_node_append_data (root, dev);
Richard Hughes871e0172015-03-17 21:00:18 +00001412
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001413 /* add all releases */
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001414 for (guint j = 0; j < rels->len; j++) {
1415 FwupdRelease *rel = g_ptr_array_index (rels, j);
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001416 g_node_append_data (child, g_object_ref (rel));
Richard Hughesfe8b96e2015-07-28 12:18:16 +01001417 }
Richard Hughes871e0172015-03-17 21:00:18 +00001418 }
Richard Hughesf06ba472018-01-15 07:54:35 +00001419
1420 /* nag? */
1421 if (!fu_util_perhaps_show_unreported (priv, error))
1422 return FALSE;
1423
Mario Limonciellod2afb592019-08-20 11:24:18 -05001424 /* no devices supported by LVFS or all are filtered */
1425 if (!supported) {
1426 g_set_error_literal (error,
Mario Limoncielloe8946a72020-09-30 16:31:03 -05001427 FWUPD_ERROR,
1428 FWUPD_ERROR_NOTHING_TO_DO,
1429 "No updatable devices");
Mario Limonciellod2afb592019-08-20 11:24:18 -05001430 return FALSE;
1431 }
Mario Limoncielloe8946a72020-09-30 16:31:03 -05001432 /* no updates available */
1433 if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
1434 g_set_error_literal (error,
1435 FWUPD_ERROR,
1436 FWUPD_ERROR_NOTHING_TO_DO,
1437 "No updates available for remaining devices");
1438 return FALSE;
1439 }
1440
1441 fu_util_print_tree (root, title);
Mario Limonciellod2afb592019-08-20 11:24:18 -05001442
Richard Hughesf06ba472018-01-15 07:54:35 +00001443 /* success */
Richard Hughes871e0172015-03-17 21:00:18 +00001444 return TRUE;
1445}
1446
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001447static gboolean
1448fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
1449{
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001450 g_autoptr(GNode) root = g_node_new (NULL);
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001451 g_autoptr(GPtrArray) remotes = NULL;
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001452 g_autofree gchar *title = fu_util_get_tree_title (priv);
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001453
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001454 remotes = fwupd_client_get_remotes (priv->client, NULL, error);
1455 if (remotes == NULL)
1456 return FALSE;
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001457
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001458 if (remotes->len == 0) {
1459 /* TRANSLATORS: no repositories to download from */
1460 g_print ("%s\n", _("No remotes available"));
1461 return TRUE;
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001462 }
1463
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001464 for (guint i = 0; i < remotes->len; i++) {
1465 FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
1466 g_node_append_data (root, remote_tmp);
1467 }
Mario Limonciello20cc9ee2019-09-05 07:27:26 -05001468 fu_util_print_tree (root, title);
Mario Limonciello4250d9d2019-08-29 09:53:44 -05001469
Richard Hughesf0bde3e2017-06-16 14:30:13 +01001470 return TRUE;
1471}
1472
Richard Hughes777917e2015-07-22 10:25:21 +01001473static gboolean
Richard Hughes45c15452017-06-06 09:36:03 +01001474fu_util_update_device_with_release (FuUtilPrivate *priv,
1475 FwupdDevice *dev,
1476 FwupdRelease *rel,
1477 GError **error)
1478{
Mario Limonciello98b95162019-10-30 09:20:43 -05001479 if (!priv->no_safety_check && !priv->assume_yes) {
1480 if (!fu_util_prompt_warning (dev,
1481 fu_util_get_tree_title (priv),
1482 error))
1483 return FALSE;
1484 }
Richard Hughes633ff102021-01-26 16:33:56 +00001485 return fwupd_client_install_release2 (priv->client, dev, rel, priv->flags,
Richard Hughes6de10e12021-01-27 15:47:07 +00001486 priv->download_flags,
Richard Hughes633ff102021-01-26 16:33:56 +00001487 priv->cancellable, error);
Richard Hughes45c15452017-06-06 09:36:03 +01001488}
1489
1490static gboolean
Mario Limonciello34c366a2019-09-24 11:24:24 -05001491fu_util_maybe_send_reports (FuUtilPrivate *priv, const gchar *remote_id,
1492 GError **error)
1493{
1494 g_autoptr(FwupdRemote) remote = NULL;
1495 g_autoptr(GError) error_local = NULL;
1496 if (remote_id == NULL) {
1497 g_debug ("not sending reports, no remote");
1498 return TRUE;
1499 }
1500 remote = fwupd_client_get_remote_by_id (priv->client,
1501 remote_id,
1502 NULL,
1503 error);
1504 if (remote == NULL)
1505 return FALSE;
1506 if (fwupd_remote_get_automatic_reports (remote)) {
1507 if (!fu_util_report_history (priv, NULL, &error_local))
Mario Limonciello9213c5c2019-10-06 22:19:47 -05001508 if (!g_error_matches (error_local,
1509 FWUPD_ERROR,
1510 FWUPD_ERROR_NOT_SUPPORTED))
1511 g_warning ("%s", error_local->message);
Mario Limonciello34c366a2019-09-24 11:24:24 -05001512 }
1513
1514 return TRUE;
1515}
1516
1517static gboolean
Richard Hughes898fc802018-08-21 09:59:12 +01001518fu_util_update_all (FuUtilPrivate *priv, GError **error)
Richard Hughes777917e2015-07-22 10:25:21 +01001519{
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001520 g_autoptr(GPtrArray) devices = NULL;
Mario Limonciellod122fac2019-10-02 20:21:33 -05001521 gboolean supported = FALSE;
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001522 gboolean no_updates_header = FALSE;
1523 gboolean latest_header = FALSE;
Richard Hughes777917e2015-07-22 10:25:21 +01001524
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001525 /* get devices from daemon */
Richard Hugheseb94c142017-09-15 14:24:15 +01001526 devices = fwupd_client_get_devices (priv->client, NULL, error);
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001527 if (devices == NULL)
Richard Hughes777917e2015-07-22 10:25:21 +01001528 return FALSE;
Richard Hughes171ec0d2018-08-10 10:47:57 +01001529 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1530 g_signal_connect (priv->client, "device-changed",
1531 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001532 g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001533 for (guint i = 0; i < devices->len; i++) {
1534 FwupdDevice *dev = g_ptr_array_index (devices, i);
1535 FwupdRelease *rel;
Mario Limonciello34c366a2019-09-24 11:24:24 -05001536 const gchar *remote_id;
Mario Limonciello98b95162019-10-30 09:20:43 -05001537 g_autofree gchar *upgrade_str = NULL;
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001538 g_autoptr(GPtrArray) rels = NULL;
1539 g_autoptr(GError) error_local = NULL;
1540
1541 /* not going to have results, so save a D-Bus round-trip */
Mario Limonciello27164b72020-02-17 23:19:36 -06001542 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
Richard Hughes777917e2015-07-22 10:25:21 +01001543 continue;
Mario Limonciello27164b72020-02-17 23:19:36 -06001544 if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001545 if (!no_updates_header) {
1546 /* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
1547 g_printerr ("%s\n", _("Devices with no available firmware updates: "));
1548 no_updates_header = TRUE;
1549 }
1550 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001551 continue;
1552 }
Richard Hughes747f5702019-08-06 14:27:26 +01001553 if (!fu_util_filter_device (priv, dev))
1554 continue;
Mario Limonciellod122fac2019-10-02 20:21:33 -05001555 supported = TRUE;
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001556
1557 /* get the releases for this device and filter for validity */
1558 rels = fwupd_client_get_upgrades (priv->client,
1559 fwupd_device_get_id (dev),
1560 NULL, &error_local);
1561 if (rels == NULL) {
Mario Limoncielloeb7be162020-07-01 15:38:47 -05001562 if (!latest_header) {
1563 /* TRANSLATORS: message letting the user know no device upgrade available */
1564 g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
1565 latest_header = TRUE;
1566 }
1567 g_printerr (" • %s\n", fwupd_device_get_name (dev));
Mario Limonciello27164b72020-02-17 23:19:36 -06001568 /* discard the actual reason from user, but leave for debugging */
1569 g_debug ("%s", error_local->message);
Richard Hughes777917e2015-07-22 10:25:21 +01001570 continue;
Richard Hughesf11fa3c2017-09-13 18:27:15 +01001571 }
1572 rel = g_ptr_array_index (rels, 0);
Mario Limonciello98b95162019-10-30 09:20:43 -05001573 /* TRANSLATORS: message letting the user know an upgrade is available
1574 * %1 is the device name and %2 and %3 are version strings */
1575 upgrade_str = g_strdup_printf (_("Upgrade available for %s from %s to %s"),
1576 fwupd_device_get_name (dev),
1577 fwupd_device_get_version (dev),
1578 fwupd_release_get_version (rel));
1579 g_print ("%s\n", upgrade_str);
Richard Hughes45c15452017-06-06 09:36:03 +01001580 if (!fu_util_update_device_with_release (priv, dev, rel, error))
Richard Hughes777917e2015-07-22 10:25:21 +01001581 return FALSE;
Mario Limonciello32241f42019-01-24 10:12:41 -06001582
1583 fu_util_display_current_message (priv);
Mario Limonciello34c366a2019-09-24 11:24:24 -05001584
1585 /* send report if we're supposed to */
1586 remote_id = fwupd_release_get_remote_id (rel);
1587 if (!fu_util_maybe_send_reports (priv, remote_id, error))
1588 return FALSE;
Richard Hughes50a6f702018-02-02 16:03:31 +00001589 }
1590
Mario Limonciellod122fac2019-10-02 20:21:33 -05001591 /* no devices supported by LVFS or all are filtered */
1592 if (!supported) {
1593 g_set_error_literal (error,
1594 FWUPD_ERROR,
1595 FWUPD_ERROR_NOTHING_TO_DO,
1596 "No updatable devices");
1597 return FALSE;
1598 }
1599
Richard Hughes50a6f702018-02-02 16:03:31 +00001600 /* we don't want to ask anything */
1601 if (priv->no_reboot_check) {
1602 g_debug ("skipping reboot check");
1603 return TRUE;
1604 }
1605
Mario Limonciello3f243a92019-01-21 22:05:23 -06001606 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes777917e2015-07-22 10:25:21 +01001607}
1608
Richard Hughesb8f8db22017-04-25 15:56:00 +01001609static gboolean
Richard Hughes898fc802018-08-21 09:59:12 +01001610fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
1611{
1612 FwupdRelease *rel;
Mario Limonciello34c366a2019-09-24 11:24:24 -05001613 const gchar *remote_id;
Richard Hughes898fc802018-08-21 09:59:12 +01001614 g_autoptr(FwupdDevice) dev = NULL;
1615 g_autoptr(GPtrArray) rels = NULL;
1616
1617 /* do not allow a partial device-id */
Mario Limonciellobc3c2412020-04-20 15:39:31 -05001618 dev = fu_util_get_device_by_id (priv, device_id, error);
Richard Hughes898fc802018-08-21 09:59:12 +01001619 if (dev == NULL)
1620 return FALSE;
1621
1622 /* get devices from daemon */
1623 priv->current_operation = FU_UTIL_OPERATION_UPDATE;
1624 g_signal_connect (priv->client, "device-changed",
1625 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1626
1627 /* get the releases for this device and filter for validity */
1628 rels = fwupd_client_get_upgrades (priv->client,
1629 fwupd_device_get_id (dev),
1630 NULL, error);
1631 if (rels == NULL)
1632 return FALSE;
1633 rel = g_ptr_array_index (rels, 0);
1634 if (!fu_util_update_device_with_release (priv, dev, rel, error))
1635 return FALSE;
Mario Limonciello32241f42019-01-24 10:12:41 -06001636 fu_util_display_current_message (priv);
1637
Mario Limonciello34c366a2019-09-24 11:24:24 -05001638 /* send report if we're supposed to */
1639 remote_id = fwupd_release_get_remote_id (rel);
1640 if (!fu_util_maybe_send_reports (priv, remote_id, error))
1641 return FALSE;
1642
Richard Hughes898fc802018-08-21 09:59:12 +01001643 /* we don't want to ask anything */
1644 if (priv->no_reboot_check) {
1645 g_debug ("skipping reboot check");
1646 return TRUE;
1647 }
1648
1649 /* the update needs the user to restart the computer */
Richard Hughes54e636e2019-05-17 16:22:27 +01001650 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes898fc802018-08-21 09:59:12 +01001651}
1652
1653static gboolean
1654fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
1655{
Mario Limonciello0f109b02019-11-14 10:26:44 -06001656 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
1657 g_set_error_literal (error,
1658 FWUPD_ERROR,
1659 FWUPD_ERROR_INVALID_ARGS,
1660 "--allow-older is not supported for this command");
1661 return FALSE;
1662 }
1663
1664 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1665 g_set_error_literal (error,
1666 FWUPD_ERROR,
1667 FWUPD_ERROR_INVALID_ARGS,
1668 "--allow-reinstall is not supported for this command");
1669 return FALSE;
1670 }
1671
Richard Hughes898fc802018-08-21 09:59:12 +01001672 if (g_strv_length (values) == 0)
1673 return fu_util_update_all (priv, error);
1674 if (g_strv_length (values) == 1)
1675 return fu_util_update_by_id (priv, values[0], error);
1676 g_set_error_literal (error,
1677 FWUPD_ERROR,
1678 FWUPD_ERROR_INVALID_ARGS,
1679 "Invalid arguments");
1680 return FALSE;
1681}
1682
1683static gboolean
Richard Hughesdfd228a2018-04-11 10:26:24 +01001684fu_util_remote_modify (FuUtilPrivate *priv, gchar **values, GError **error)
Richard Hughesa6bd5582017-09-07 14:32:22 +01001685{
Richard Hughes836d9e92021-03-26 15:53:12 +00001686 g_autoptr(FwupdRemote) remote = NULL;
Richard Hughesa6bd5582017-09-07 14:32:22 +01001687 if (g_strv_length (values) < 3) {
1688 g_set_error_literal (error,
1689 FWUPD_ERROR,
1690 FWUPD_ERROR_INVALID_ARGS,
Richard Hughes3b847532017-10-23 13:22:55 +01001691 "Invalid arguments");
Richard Hughesa6bd5582017-09-07 14:32:22 +01001692 return FALSE;
1693 }
Richard Hughes836d9e92021-03-26 15:53:12 +00001694
1695 /* ensure the remote exists */
1696 remote = fwupd_client_get_remote_by_id (priv->client, values[0],
1697 priv->cancellable, error);
1698 if (remote == NULL)
1699 return FALSE;
1700 if (!fwupd_client_modify_remote (priv->client,
1701 fwupd_remote_get_id (remote),
1702 values[1], values[2],
1703 priv->cancellable, error))
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001704 return FALSE;
1705
1706 /* TRANSLATORS: success message for a per-remote setting change */
Mario Limonciello0a956792019-10-25 09:16:11 -05001707 g_print ("%s\n", _("Successfully modified remote"));
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001708 return TRUE;
Richard Hughesdfd228a2018-04-11 10:26:24 +01001709}
1710
1711static gboolean
1712fu_util_remote_enable (FuUtilPrivate *priv, gchar **values, GError **error)
1713{
Richard Hughes836d9e92021-03-26 15:53:12 +00001714 g_autoptr(FwupdRemote) remote = NULL;
Richard Hughesdfd228a2018-04-11 10:26:24 +01001715 if (g_strv_length (values) != 1) {
1716 g_set_error_literal (error,
1717 FWUPD_ERROR,
1718 FWUPD_ERROR_INVALID_ARGS,
1719 "Invalid arguments");
1720 return FALSE;
1721 }
Richard Hughes836d9e92021-03-26 15:53:12 +00001722 remote = fwupd_client_get_remote_by_id (priv->client, values[0],
1723 priv->cancellable, error);
1724 if (remote == NULL)
1725 return FALSE;
1726 if (!fu_util_modify_remote_warning (priv, remote, error))
1727 return FALSE;
1728 if (!fwupd_client_modify_remote (priv->client,
1729 fwupd_remote_get_id (remote),
1730 "Enabled", "true",
1731 priv->cancellable, error))
1732 return FALSE;
1733
1734 /* ask for permission to refresh */
1735 if (priv->no_remote_check ||
1736 fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD) {
1737 /* TRANSLATORS: success message */
1738 g_print ("%s\n", _("Successfully enabled remote"));
1739 return TRUE;
1740 }
1741 if (!priv->assume_yes) {
1742 g_print ("%s (%s) [Y|n]: ",
1743 /* TRANSLATORS: ask the user if we can update the metadata */
1744 _("Do you want to refresh this remote now?"),
1745 /* TRANSLATORS: metadata is downloaded from the Internet */
1746 _("Requires internet connection"));
1747 if (!fu_util_prompt_for_boolean (TRUE)) {
1748 /* TRANSLATORS: success message */
1749 g_print ("%s\n", _("Successfully enabled remote"));
1750 return TRUE;
1751 }
1752 }
1753 if (!fwupd_client_refresh_remote (priv->client, remote,
1754 priv->cancellable, error))
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001755 return FALSE;
1756
1757 /* TRANSLATORS: success message */
Richard Hughes836d9e92021-03-26 15:53:12 +00001758 g_print ("\n%s\n", _("Successfully enabled and refreshed remote"));
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001759 return TRUE;
Richard Hughesdfd228a2018-04-11 10:26:24 +01001760}
1761
1762static gboolean
1763fu_util_remote_disable (FuUtilPrivate *priv, gchar **values, GError **error)
1764{
Richard Hughes836d9e92021-03-26 15:53:12 +00001765 g_autoptr(FwupdRemote) remote = NULL;
1766
Richard Hughesdfd228a2018-04-11 10:26:24 +01001767 if (g_strv_length (values) != 1) {
1768 g_set_error_literal (error,
1769 FWUPD_ERROR,
1770 FWUPD_ERROR_INVALID_ARGS,
1771 "Invalid arguments");
1772 return FALSE;
1773 }
Richard Hughes836d9e92021-03-26 15:53:12 +00001774
1775 /* ensure the remote exists */
1776 remote = fwupd_client_get_remote_by_id (priv->client, values[0],
1777 priv->cancellable, error);
1778 if (remote == NULL)
1779 return FALSE;
1780 if (!fwupd_client_modify_remote (priv->client,
1781 values[0], "Enabled", "false",
1782 priv->cancellable, error))
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001783 return FALSE;
1784
1785 /* TRANSLATORS: success message */
1786 g_print ("%s\n", _("Successfully disabled remote"));
1787 return TRUE;
Richard Hughesa6bd5582017-09-07 14:32:22 +01001788}
1789
1790static gboolean
Richard Hughes45c15452017-06-06 09:36:03 +01001791fu_util_downgrade (FuUtilPrivate *priv, gchar **values, GError **error)
1792{
Mario Limonciello34c366a2019-09-24 11:24:24 -05001793 const gchar *remote_id;
Richard Hughes45c15452017-06-06 09:36:03 +01001794 g_autoptr(FwupdDevice) dev = NULL;
1795 g_autoptr(FwupdRelease) rel = NULL;
1796 g_autoptr(GPtrArray) rels = NULL;
Richard Hughes45c15452017-06-06 09:36:03 +01001797
Mario Limonciello0f109b02019-11-14 10:26:44 -06001798 if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
1799 g_set_error_literal (error,
1800 FWUPD_ERROR,
1801 FWUPD_ERROR_INVALID_ARGS,
1802 "--allow-reinstall is not supported for this command");
1803 return FALSE;
1804 }
1805
Mario Limonciello1d01d3b2019-10-14 12:09:42 -05001806 priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
Mario Limonciello9e965ad2018-02-28 09:21:42 -06001807 dev = fu_util_get_device_or_prompt (priv, values, error);
1808 if (dev == NULL)
1809 return FALSE;
Richard Hughes45c15452017-06-06 09:36:03 +01001810
1811 /* get the releases for this device and filter for validity */
Richard Hughes97284b12017-09-13 17:07:58 +01001812 rels = fwupd_client_get_downgrades (priv->client,
1813 fwupd_device_get_id (dev),
1814 NULL, error);
Mario Limonciello7e4949c2019-12-09 10:21:20 -06001815 if (rels == NULL) {
1816 /* TRANSLATORS: message letting the user know no device downgrade available
1817 * %1 is the device name */
1818 g_autofree gchar *downgrade_str = g_strdup_printf (_("No downgrades for %s"),
1819 fwupd_device_get_name (dev));
1820 g_prefix_error (error, "%s: ", downgrade_str);
Richard Hughes45c15452017-06-06 09:36:03 +01001821 return FALSE;
Mario Limonciello7e4949c2019-12-09 10:21:20 -06001822 }
Richard Hughes45c15452017-06-06 09:36:03 +01001823
1824 /* get the chosen release */
Richard Hughes97284b12017-09-13 17:07:58 +01001825 rel = fu_util_prompt_for_release (priv, rels, error);
Richard Hughes45c15452017-06-06 09:36:03 +01001826 if (rel == NULL)
1827 return FALSE;
Richard Hughes171ec0d2018-08-10 10:47:57 +01001828
1829 /* update the console if composite devices are also updated */
1830 priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE;
1831 g_signal_connect (priv->client, "device-changed",
1832 G_CALLBACK (fu_util_update_device_changed_cb), priv);
Richard Hughes45c15452017-06-06 09:36:03 +01001833 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughesc56d2bd2019-09-27 09:37:05 +01001834 if (!fu_util_update_device_with_release (priv, dev, rel, error))
1835 return FALSE;
1836
Mario Limonciello75b39042020-04-09 10:47:27 -05001837 fu_util_display_current_message (priv);
Mario Limonciello34c366a2019-09-24 11:24:24 -05001838
1839 /* send report if we're supposed to */
1840 remote_id = fwupd_release_get_remote_id (rel);
1841 if (!fu_util_maybe_send_reports (priv, remote_id, error))
1842 return FALSE;
1843
Richard Hughesefc31382021-01-13 09:58:16 +00001844 /* we don't want to ask anything */
1845 if (priv->no_reboot_check) {
1846 g_debug ("skipping reboot check");
1847 return TRUE;
1848 }
1849
1850 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
Richard Hughes45c15452017-06-06 09:36:03 +01001851}
1852
Mario Limonciello96a0dd52019-02-25 13:50:03 -06001853static gboolean
Mario Limonciellod837ca82019-10-02 19:04:09 -05001854fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
1855{
1856 const gchar *remote_id;
1857 g_autoptr(FwupdRelease) rel = NULL;
1858 g_autoptr(GPtrArray) rels = NULL;
Mario Limonciello1d01d3b2019-10-14 12:09:42 -05001859 g_autoptr(FwupdDevice) dev = NULL;
1860
1861 priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
1862 dev = fu_util_get_device_or_prompt (priv, values, error);
Mario Limonciellod837ca82019-10-02 19:04:09 -05001863 if (dev == NULL)
1864 return FALSE;
1865
1866 /* try to lookup/match release from client */
1867 rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev),
1868 NULL, error);
1869 if (rels == NULL)
1870 return FALSE;
1871 for (guint j = 0; j < rels->len; j++) {
1872 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
Richard Hughes9a680842020-02-20 11:11:13 +00001873 if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
1874 fu_device_get_version (dev),
1875 fwupd_device_get_version_format (dev)) == 0) {
Mario Limonciellod837ca82019-10-02 19:04:09 -05001876 rel = g_object_ref (rel_tmp);
1877 break;
1878 }
1879 }
1880 if (rel == NULL) {
1881 g_set_error (error,
1882 FWUPD_ERROR,
1883 FWUPD_ERROR_NOT_SUPPORTED,
1884 "Unable to locate release for %s version %s",
1885 fu_device_get_name (dev),
1886 fu_device_get_version (dev));
1887 return FALSE;
1888 }
1889
1890 /* update the console if composite devices are also updated */
1891 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
1892 g_signal_connect (priv->client, "device-changed",
1893 G_CALLBACK (fu_util_update_device_changed_cb), priv);
1894 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
1895 if (!fu_util_update_device_with_release (priv, dev, rel, error))
1896 return FALSE;
1897 fu_util_display_current_message (priv);
1898
1899 /* send report if we're supposed to */
1900 remote_id = fwupd_release_get_remote_id (rel);
1901 if (!fu_util_maybe_send_reports (priv, remote_id, error))
1902 return FALSE;
1903
1904 /* we don't want to ask anything */
1905 if (priv->no_reboot_check) {
1906 g_debug ("skipping reboot check");
1907 return TRUE;
1908 }
1909
1910 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
1911}
1912
1913static gboolean
Richard Hughes89ee9ed2021-01-20 16:15:29 +00001914_g_str_equal0 (gconstpointer str1, gconstpointer str2)
1915{
1916 return g_strcmp0 (str1, str2) == 0;
1917}
1918
1919static gboolean
Richard Hughes460c4b72020-09-25 20:59:28 +01001920fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
1921{
1922 const gchar *remote_id;
1923 const gchar *branch;
1924 g_autoptr(FwupdRelease) rel = NULL;
1925 g_autoptr(GPtrArray) rels = NULL;
1926 g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
1927 g_autoptr(FwupdDevice) dev = NULL;
1928
1929 /* find the device and check it has multiple branches */
Richard Hughescb0e6142020-10-22 16:31:56 +01001930 priv->filter_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES;
Richard Hughes814a5ee2021-01-20 16:25:43 +00001931 priv->filter_include |= FWUPD_DEVICE_FLAG_UPDATABLE;
Richard Hughes460c4b72020-09-25 20:59:28 +01001932 dev = fu_util_get_device_or_prompt (priv, values, error);
1933 if (dev == NULL)
1934 return FALSE;
Richard Hughes460c4b72020-09-25 20:59:28 +01001935
1936 /* get all releases, including the alternate branch versions */
1937 rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev),
1938 NULL, error);
1939 if (rels == NULL)
1940 return FALSE;
1941
Richard Hughes1c62ade2020-10-15 14:58:19 +01001942 /* get all the unique branches */
1943 for (guint i = 0; i < rels->len; i++) {
1944 FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00001945 const gchar *branch_tmp = fwupd_release_get_branch (rel_tmp);
Richard Hughesdb8533f2020-12-14 12:04:30 +00001946#if GLIB_CHECK_VERSION(2,54,3)
Richard Hughes1c62ade2020-10-15 14:58:19 +01001947 if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
Richard Hughes89ee9ed2021-01-20 16:15:29 +00001948 _g_str_equal0, NULL))
Richard Hughes1c62ade2020-10-15 14:58:19 +01001949 continue;
Richard Hughesdb8533f2020-12-14 12:04:30 +00001950#endif
Richard Hughes1c62ade2020-10-15 14:58:19 +01001951 g_ptr_array_add (branches, g_strdup (branch_tmp));
1952 }
1953
Richard Hughes460c4b72020-09-25 20:59:28 +01001954 /* branch name is optional */
1955 if (g_strv_length (values) > 1) {
1956 branch = values[1];
Richard Hughes1c62ade2020-10-15 14:58:19 +01001957 } else if (branches->len == 1) {
1958 branch = g_ptr_array_index (branches, 0);
Richard Hughes460c4b72020-09-25 20:59:28 +01001959 } else {
1960 guint idx;
1961
1962 /* TRANSLATORS: get interactive prompt, where branch is the
1963 * supplier of the firmware, e.g. "non-free" or "free" */
1964 g_print ("%s\n", _("Choose a branch:"));
1965 /* TRANSLATORS: this is to abort the interactive prompt */
1966 g_print ("0.\t%s\n", _("Cancel"));
1967 for (guint i = 0; i < branches->len; i++) {
1968 const gchar *branch_tmp = g_ptr_array_index (branches, i);
Richard Hughes89ee9ed2021-01-20 16:15:29 +00001969 g_print ("%u.\t%s\n", i + 1, fu_util_branch_for_display (branch_tmp));
Richard Hughes460c4b72020-09-25 20:59:28 +01001970 }
1971 idx = fu_util_prompt_for_number (branches->len);
1972 if (idx == 0) {
1973 g_set_error_literal (error,
1974 FWUPD_ERROR,
1975 FWUPD_ERROR_NOTHING_TO_DO,
1976 "Request canceled");
1977 return FALSE;
1978 }
1979 branch = g_ptr_array_index (branches, idx - 1);
1980 }
1981
Richard Hughes1c62ade2020-10-15 14:58:19 +01001982 /* sanity check */
1983 if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
1984 g_set_error (error,
1985 FWUPD_ERROR,
1986 FWUPD_ERROR_NOT_SUPPORTED,
1987 "Device %s is already on branch %s",
1988 fu_device_get_name (dev),
Richard Hughes89ee9ed2021-01-20 16:15:29 +00001989 fu_util_branch_for_display (branch));
Richard Hughes1c62ade2020-10-15 14:58:19 +01001990 return FALSE;
1991 }
1992
Richard Hughes460c4b72020-09-25 20:59:28 +01001993 /* the releases are ordered by version */
1994 for (guint j = 0; j < rels->len; j++) {
1995 FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
1996 if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
1997 rel = g_object_ref (rel_tmp);
1998 break;
1999 }
2000 }
2001 if (rel == NULL) {
2002 g_set_error (error,
2003 FWUPD_ERROR,
2004 FWUPD_ERROR_NOT_SUPPORTED,
2005 "No releases for branch %s",
Richard Hughes89ee9ed2021-01-20 16:15:29 +00002006 fu_util_branch_for_display (branch));
Richard Hughes460c4b72020-09-25 20:59:28 +01002007 return FALSE;
2008 }
2009
2010 /* we're switching branch */
Mario Limonciello02f2cc32020-10-20 15:39:47 -05002011 if (!fu_util_switch_branch_warning (dev, rel, priv->assume_yes, error))
Richard Hughes460c4b72020-09-25 20:59:28 +01002012 return FALSE;
2013
2014 /* update the console if composite devices are also updated */
2015 priv->current_operation = FU_UTIL_OPERATION_INSTALL;
2016 g_signal_connect (priv->client, "device-changed",
2017 G_CALLBACK (fu_util_update_device_changed_cb), priv);
2018 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Richard Hughes5bbf0132020-10-05 13:44:39 +01002019 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
Richard Hughes460c4b72020-09-25 20:59:28 +01002020 if (!fu_util_update_device_with_release (priv, dev, rel, error))
2021 return FALSE;
2022 fu_util_display_current_message (priv);
2023
2024 /* send report if we're supposed to */
2025 remote_id = fwupd_release_get_remote_id (rel);
2026 if (!fu_util_maybe_send_reports (priv, remote_id, error))
2027 return FALSE;
2028
2029 /* we don't want to ask anything */
2030 if (priv->no_reboot_check) {
2031 g_debug ("skipping reboot check");
2032 return TRUE;
2033 }
2034
2035 return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
2036}
2037
2038static gboolean
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002039fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
2040{
2041 g_autoptr(GPtrArray) devices = NULL;
2042 gboolean has_pending = FALSE;
2043
2044 /* handle both forms */
2045 if (g_strv_length (values) == 0) {
2046 /* activate anything with _NEEDS_ACTIVATION */
2047 devices = fwupd_client_get_devices (priv->client, NULL, error);
2048 if (devices == NULL)
2049 return FALSE;
2050 for (guint i = 0; i < devices->len; i++) {
2051 FuDevice *device = g_ptr_array_index (devices, i);
2052 if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
2053 has_pending = TRUE;
2054 break;
2055 }
2056 }
2057 } else if (g_strv_length (values) == 1) {
Daniel Campellof8fb2dc2020-09-29 13:48:55 -06002058 FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002059 if (device == NULL)
2060 return FALSE;
2061 devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
2062 g_ptr_array_add (devices, device);
2063 if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
2064 has_pending = TRUE;
2065 } else {
2066 g_set_error_literal (error,
2067 FWUPD_ERROR,
2068 FWUPD_ERROR_INVALID_ARGS,
2069 "Invalid arguments");
2070 return FALSE;
2071 }
2072
2073 /* nothing to do */
2074 if (!has_pending) {
2075 g_set_error_literal (error,
2076 FWUPD_ERROR,
2077 FWUPD_ERROR_NOTHING_TO_DO,
2078 "No firmware to activate");
2079 return FALSE;
2080 }
2081
2082 /* activate anything with _NEEDS_ACTIVATION */
Mario Limonciello02085a02020-09-11 14:59:35 -05002083 /* order by device priority */
2084 g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002085 for (guint i = 0; i < devices->len; i++) {
2086 FwupdDevice *device = g_ptr_array_index (devices, i);
Richard Hughes747f5702019-08-06 14:27:26 +01002087 if (!fu_util_filter_device (priv, device))
2088 continue;
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002089 if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
2090 continue;
2091 /* TRANSLATORS: shown when shutting down to switch to the new version */
2092 g_print ("%s %s…\n", _("Activating firmware update for"),
2093 fwupd_device_get_name (device));
2094 if (!fwupd_client_activate (priv->client, NULL,
2095 fwupd_device_get_id (device), error))
2096 return FALSE;
2097 }
2098
Richard Hughesc56d2bd2019-09-27 09:37:05 +01002099 /* TRANSLATORS: success message -- where activation is making the new
2100 * firmware take effect, usually after updating offline */
2101 g_print ("%s\n", _("Successfully activated all devices"));
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002102 return TRUE;
2103}
2104
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002105static gboolean
2106fu_util_set_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
2107{
2108 g_auto(GStrv) checksums = NULL;
2109
2110 /* check args */
2111 if (g_strv_length (values) != 1) {
2112 g_set_error_literal (error,
2113 FWUPD_ERROR,
2114 FWUPD_ERROR_INVALID_ARGS,
2115 "Invalid arguments: list of checksums expected");
2116 return FALSE;
2117 }
2118
2119 /* call into daemon */
2120 checksums = g_strsplit (values[0], ",", -1);
2121 return fwupd_client_set_approved_firmware (priv->client,
2122 checksums,
2123 priv->cancellable,
2124 error);
2125}
2126
2127static gboolean
2128fu_util_get_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
2129{
2130 g_auto(GStrv) checksums = NULL;
2131
2132 /* check args */
2133 if (g_strv_length (values) != 0) {
2134 g_set_error_literal (error,
2135 FWUPD_ERROR,
2136 FWUPD_ERROR_INVALID_ARGS,
2137 "Invalid arguments: none expected");
2138 return FALSE;
2139 }
2140
2141 /* call into daemon */
2142 checksums = fwupd_client_get_approved_firmware (priv->client,
2143 priv->cancellable,
2144 error);
2145 if (checksums == NULL)
2146 return FALSE;
2147 if (g_strv_length (checksums) == 0) {
2148 /* TRANSLATORS: approved firmware has been checked by
2149 * the domain administrator */
2150 g_print ("%s\n", _("There is no approved firmware."));
2151 } else {
2152 /* TRANSLATORS: approved firmware has been checked by
2153 * the domain administrator */
Richard Hughes7eb1e922019-03-27 09:30:54 +00002154 g_print ("%s\n", ngettext ("Approved firmware:",
2155 "Approved firmware:",
2156 g_strv_length (checksums)));
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002157 for (guint i = 0; checksums[i] != NULL; i++)
2158 g_print (" * %s\n", checksums[i]);
2159 }
2160 return TRUE;
2161}
2162
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002163static gboolean
2164fu_util_modify_config (FuUtilPrivate *priv, gchar **values, GError **error)
2165{
2166 /* check args */
2167 if (g_strv_length (values) != 2) {
2168 g_set_error_literal (error,
2169 FWUPD_ERROR,
2170 FWUPD_ERROR_INVALID_ARGS,
2171 "Invalid arguments: KEY VALUE expected");
2172 return FALSE;
2173 }
2174 if (!fwupd_client_modify_config (priv->client,
2175 values[0], values[1],
2176 priv->cancellable,
2177 error))
2178 return FALSE;
2179 if (!priv->assume_yes) {
Richard Hughesbcee63a2019-09-27 13:56:27 +01002180 g_print ("%s [Y|n]: ",
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002181 /* TRANSLATORS: configuration changes only take effect on restart */
2182 _("Restart the daemon to make the change effective?"));
2183 if (!fu_util_prompt_for_boolean (FALSE))
2184 return TRUE;
2185 }
Richard Hughesd92ccca2019-05-20 11:28:31 +01002186#ifdef HAVE_SYSTEMD
2187 if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), error))
2188 return FALSE;
2189#endif
Richard Hughesc56d2bd2019-09-27 09:37:05 +01002190 /* TRANSLATORS: success message -- a per-system setting value */
2191 g_print ("%s\n", _("Successfully modified configuration value"));
Richard Hughesd92ccca2019-05-20 11:28:31 +01002192 return TRUE;
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002193}
2194
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002195static FwupdRemote *
2196fu_util_get_remote_with_security_report_uri (FuUtilPrivate *priv, GError **error)
2197{
2198 g_autoptr(GPtrArray) remotes = NULL;
2199
2200 /* get all remotes */
2201 remotes = fwupd_client_get_remotes (priv->client, NULL, error);
2202 if (remotes == NULL)
2203 return NULL;
2204
2205 for (guint i = 0; i < remotes->len; i++) {
2206 FwupdRemote *remote = g_ptr_array_index (remotes, i);
2207 if (!fwupd_remote_get_enabled (remote))
2208 continue;
2209 if (fwupd_remote_get_security_report_uri (remote) != NULL)
2210 return g_object_ref (remote);
2211 }
2212
2213 /* failed */
2214 g_set_error_literal (error,
2215 FWUPD_ERROR,
2216 FWUPD_ERROR_NOT_SUPPORTED,
2217 "No remotes specified SecurityReportURI");
Richard Hughesadabe532020-08-17 14:48:30 +01002218 return NULL;
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002219}
2220
2221static gboolean
2222fu_util_upload_security (FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
2223{
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002224 GHashTableIter iter;
2225 const gchar *key;
2226 const gchar *value;
2227 g_autofree gchar *data = NULL;
2228 g_autofree gchar *sig = NULL;
2229 g_autoptr(FwupdRemote) remote = NULL;
Richard Hughes9b6d6162020-07-07 16:24:57 +01002230 g_autoptr(GBytes) upload_response = NULL;
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002231 g_autoptr(GError) error_local = NULL;
2232 g_autoptr(GHashTable) metadata = NULL;
2233 g_autoptr(JsonBuilder) builder = NULL;
2234 g_autoptr(JsonGenerator) json_generator = NULL;
2235 g_autoptr(JsonNode) json_root = NULL;
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002236
2237 /* can we find a remote with a security attr */
2238 remote = fu_util_get_remote_with_security_report_uri (priv, &error_local);
2239 if (remote == NULL) {
2240 g_debug ("failed to find suitable remote: %s", error_local->message);
2241 return TRUE;
2242 }
2243 if (!priv->assume_yes &&
2244 !fwupd_remote_get_automatic_security_reports (remote)) {
2245 g_autofree gchar *tmp = NULL;
2246 /* TRANSLATORS: ask the user to share, %s is something like:
2247 * "Linux Vendor Firmware Service" */
2248 tmp = g_strdup_printf ("Upload these anonymous results to the %s to help other users?",
2249 fwupd_remote_get_title (remote));
2250
2251 g_print ("\n%s [y|N]: ", tmp);
2252 if (!fu_util_prompt_for_boolean (FALSE)) {
2253 g_print ("%s [Y|n]: ",
2254 /* TRANSLATORS: stop nagging the user */
2255 _("Ask again next time?"));
2256 if (!fu_util_prompt_for_boolean (TRUE)) {
2257 if (!fwupd_client_modify_remote (priv->client,
2258 fwupd_remote_get_id (remote),
2259 "SecurityReportURI", "",
2260 NULL, error))
2261 return FALSE;
2262 }
2263 return TRUE;
2264 }
2265 }
2266
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002267 /* get metadata */
2268 metadata = fwupd_client_get_report_metadata (priv->client,
2269 priv->cancellable,
2270 error);
2271 if (metadata == NULL)
2272 return FALSE;
2273
2274 /* create header */
2275 builder = json_builder_new ();
2276 json_builder_begin_object (builder);
2277 json_builder_set_member_name (builder, "ReportVersion");
2278 json_builder_add_int_value (builder, 2);
2279 json_builder_set_member_name (builder, "MachineId");
2280 json_builder_add_string_value (builder, fwupd_client_get_host_machine_id (priv->client));
2281
2282 /* this is system metadata not stored in the database */
2283 json_builder_set_member_name (builder, "Metadata");
2284 json_builder_begin_object (builder);
2285
2286 g_hash_table_iter_init (&iter, metadata);
2287 while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
2288 json_builder_set_member_name (builder, key);
2289 json_builder_add_string_value (builder, value);
2290 }
2291 json_builder_set_member_name (builder, "HostSecurityId");
2292 json_builder_add_string_value (builder, fwupd_client_get_host_security_id (priv->client));
2293 json_builder_end_object (builder);
2294
2295 /* attrs */
2296 json_builder_set_member_name (builder, "SecurityAttributes");
2297 json_builder_begin_array (builder);
2298 for (guint i = 0; i < attrs->len; i++) {
2299 FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
2300 json_builder_begin_object (builder);
2301 fwupd_security_attr_to_json (attr, builder);
2302 json_builder_end_object (builder);
2303 }
2304 json_builder_end_array (builder);
2305 json_builder_end_object (builder);
2306
2307 /* export as a string */
2308 json_root = json_builder_get_root (builder);
2309 json_generator = json_generator_new ();
2310 json_generator_set_pretty (json_generator, TRUE);
2311 json_generator_set_root (json_generator, json_root);
2312 data = json_generator_to_data (json_generator, NULL);
2313 if (data == NULL) {
2314 g_set_error_literal (error,
2315 FWUPD_ERROR,
2316 FWUPD_ERROR_INTERNAL,
2317 "Failed to convert to JSON string");
2318 return FALSE;
2319 }
2320
2321 /* self sign data */
2322 if (priv->sign) {
2323 sig = fwupd_client_self_sign (priv->client, data,
2324 FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP,
2325 priv->cancellable, error);
2326 if (sig == NULL)
2327 return FALSE;
2328 }
2329
2330 /* ask for permission */
2331 if (!priv->assume_yes &&
2332 !fwupd_remote_get_automatic_security_reports (remote)) {
2333 fu_util_print_data (_("Target"), fwupd_remote_get_security_report_uri (remote));
2334 fu_util_print_data (_("Payload"), data);
2335 if (sig != NULL)
2336 fu_util_print_data (_("Signature"), sig);
2337 g_print ("%s [Y|n]: ", _("Proceed with upload?"));
2338 if (!fu_util_prompt_for_boolean (TRUE)) {
2339 g_set_error_literal (error,
2340 FWUPD_ERROR,
2341 FWUPD_ERROR_PERMISSION_DENIED,
2342 "User declined action");
2343 return FALSE;
2344 }
2345 }
2346
2347 /* POST request */
Richard Hughes9b6d6162020-07-07 16:24:57 +01002348 upload_response = fwupd_client_upload_bytes (priv->client,
2349 fwupd_remote_get_security_report_uri (remote),
2350 data, sig,
2351 FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART,
2352 priv->cancellable, error);
2353 if (upload_response == NULL)
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002354 return FALSE;
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002355
2356 /* TRANSLATORS: success, so say thank you to the user */
2357 g_print ("%s\n", "Host Security ID attributes uploaded successfully, thanks!");
2358
2359 /* as this worked, ask if the user want to do this every time */
2360 if (!fwupd_remote_get_automatic_security_reports (remote)) {
2361 g_print ("%s [y|N]: ",
2362 /* TRANSLATORS: can we JFDI? */
2363 _("Automatically upload every time?"));
2364 if (fu_util_prompt_for_boolean (FALSE)) {
2365 if (!fwupd_client_modify_remote (priv->client,
2366 fwupd_remote_get_id (remote),
2367 "AutomaticSecurityReports", "true",
2368 NULL, error))
2369 return FALSE;
2370 }
2371 }
2372
2373 return TRUE;
2374}
2375
Richard Hughes196c6c62020-05-11 19:42:47 +01002376static gboolean
2377fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
2378{
Richard Hughes5c82b942020-09-14 12:24:06 +01002379 FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
Richard Hughes196c6c62020-05-11 19:42:47 +01002380 g_autoptr(GPtrArray) attrs = NULL;
2381 g_autofree gchar *str = NULL;
2382
2383 /* not ready yet */
2384 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
2385 g_set_error_literal (error,
2386 FWUPD_ERROR,
2387 FWUPD_ERROR_NOT_SUPPORTED,
2388 "The HSI specification is not yet complete. "
2389 "To ignore this warning, use --force");
2390 return FALSE;
2391 }
2392
2393 /* TRANSLATORS: this is a string like 'HSI:2-U' */
2394 g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
2395 fwupd_client_get_host_security_id (priv->client));
2396
2397 /* print the "why" */
2398 attrs = fwupd_client_get_host_security_attrs (priv->client,
2399 priv->cancellable,
2400 error);
2401 if (attrs == NULL)
2402 return FALSE;
Richard Hughes5c82b942020-09-14 12:24:06 +01002403
2404 /* show or hide different elements */
2405 if (priv->show_all) {
2406 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
2407 flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
2408 }
2409 str = fu_util_security_attrs_to_string (attrs, flags);
Richard Hughes196c6c62020-05-11 19:42:47 +01002410 g_print ("%s\n", str);
Richard Hughes9bc9deb2020-05-19 19:49:51 +01002411
2412 /* opted-out */
2413 if (priv->no_unreported_check)
2414 return TRUE;
2415
2416 /* upload, with confirmation */
2417 return fu_util_upload_security (priv, attrs, error);
Richard Hughes196c6c62020-05-11 19:42:47 +01002418}
2419
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002420static void
2421fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
2422 const gchar *message, gpointer user_data)
2423{
2424}
2425
Richard Hughes9e5675e2019-11-22 09:35:03 +00002426#ifdef HAVE_GIO_UNIX
Richard Hughescfc44fa2016-04-28 14:36:35 +01002427static gboolean
2428fu_util_sigint_cb (gpointer user_data)
2429{
2430 FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
2431 g_debug ("Handling SIGINT");
2432 g_cancellable_cancel (priv->cancellable);
2433 return FALSE;
2434}
Richard Hughes9e5675e2019-11-22 09:35:03 +00002435#endif
Richard Hughescfc44fa2016-04-28 14:36:35 +01002436
Richard Hughes741dc502017-05-08 20:38:17 +01002437static void
2438fu_util_private_free (FuUtilPrivate *priv)
2439{
Richard Hughes741dc502017-05-08 20:38:17 +01002440 if (priv->client != NULL)
2441 g_object_unref (priv->client);
Richard Hughes171ec0d2018-08-10 10:47:57 +01002442 if (priv->current_device != NULL)
2443 g_object_unref (priv->current_device);
Mario Limonciello32241f42019-01-24 10:12:41 -06002444 g_free (priv->current_message);
Richard Hughes6ed25f52021-01-10 19:27:33 +00002445 g_main_context_unref (priv->main_ctx);
Richard Hughes741dc502017-05-08 20:38:17 +01002446 g_object_unref (priv->cancellable);
Richard Hughes9a7db9d2017-08-21 14:03:45 +01002447 g_object_unref (priv->progressbar);
Richard Hughes741dc502017-05-08 20:38:17 +01002448 g_option_context_free (priv->context);
2449 g_free (priv);
2450}
2451
Mario Limonciello81d83c42019-05-07 17:07:51 -05002452static gboolean
2453fu_util_check_daemon_version (FuUtilPrivate *priv, GError **error)
2454{
Mario Limonciello81d83c42019-05-07 17:07:51 -05002455 const gchar *daemon = fwupd_client_get_daemon_version (priv->client);
2456
Mario Limoncielloa11eab62020-06-23 19:13:29 -05002457 if (daemon == NULL) {
2458 g_set_error_literal (error,
2459 FWUPD_ERROR,
2460 FWUPD_ERROR_NOT_SUPPORTED,
2461 /* TRANSLATORS: error message */
2462 _("Unable to connect to service"));
2463 return FALSE;
2464 }
2465
Richard Hughesfe4b3ea2020-03-30 10:53:20 +01002466 if (g_strcmp0 (daemon, SOURCE_VERSION) != 0) {
Mario Limonciello81d83c42019-05-07 17:07:51 -05002467 g_set_error (error,
2468 FWUPD_ERROR,
2469 FWUPD_ERROR_NOT_SUPPORTED,
2470 /* TRANSLATORS: error message */
2471 _("Unsupported daemon version %s, client version is %s"),
Richard Hughesfe4b3ea2020-03-30 10:53:20 +01002472 daemon, SOURCE_VERSION);
Mario Limonciello81d83c42019-05-07 17:07:51 -05002473 return FALSE;
2474 }
2475
2476 return TRUE;
2477}
2478
Mario Limonciello057c67a2019-05-23 10:44:19 -05002479static gboolean
2480fu_util_check_polkit_actions (GError **error)
2481{
Mario Limonciello11b71f42020-10-13 13:39:14 -05002482#ifdef HAVE_POLKIT
Mario Limonciello057c67a2019-05-23 10:44:19 -05002483 g_autofree gchar *directory = fu_common_get_path (FU_PATH_KIND_POLKIT_ACTIONS);
2484 g_autofree gchar *filename = g_build_filename (directory,
2485 "org.freedesktop.fwupd.policy",
2486 NULL);
2487 if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
2488 g_set_error_literal (error,
2489 FWUPD_ERROR,
2490 FWUPD_ERROR_AUTH_FAILED,
Mario Limonciello71d2f622019-08-22 09:31:04 -05002491 "PolicyKit files are missing, see https://github.com/fwupd/fwupd/wiki/PolicyKit-files-are-missing");
Mario Limonciello057c67a2019-05-23 10:44:19 -05002492 return FALSE;
2493 }
Mario Limonciello11b71f42020-10-13 13:39:14 -05002494#endif
Mario Limonciello057c67a2019-05-23 10:44:19 -05002495
2496 return TRUE;
2497}
2498
Mario Limoncielloa98df552018-04-16 12:15:51 -05002499#pragma clang diagnostic push
2500#pragma clang diagnostic ignored "-Wunused-function"
Richard Hughes741dc502017-05-08 20:38:17 +01002501G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
Mario Limoncielloa98df552018-04-16 12:15:51 -05002502#pragma clang diagnostic pop
Richard Hughes741dc502017-05-08 20:38:17 +01002503
Richard Hughes31206832020-07-27 15:31:11 +01002504static gchar *
2505fu_util_get_history_checksum (FuUtilPrivate *priv, GError **error)
2506{
2507 const gchar *csum;
2508 g_autoptr(FwupdDevice) device = NULL;
2509 g_autoptr(FwupdRelease) release = NULL;
2510 g_autoptr(GPtrArray) devices = NULL;
2511
2512 devices = fwupd_client_get_history (priv->client, NULL, error);
2513 if (devices == NULL)
2514 return NULL;
2515 device = fu_util_prompt_for_device (priv, devices, error);
2516 if (device == NULL)
2517 return NULL;
2518 release = fu_util_prompt_for_release (priv, fwupd_device_get_releases (device), error);
2519 if (release == NULL)
2520 return NULL;
2521 csum = fwupd_checksum_get_best (fwupd_release_get_checksums (release));
2522 if (csum == NULL) {
2523 g_set_error_literal (error,
2524 FWUPD_ERROR,
2525 FWUPD_ERROR_NOTHING_TO_DO,
2526 "No suitable checksums");
2527 return NULL;
2528 }
2529 return g_strdup (csum);
2530}
2531
2532static gboolean
2533fu_util_block_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
2534{
2535 guint idx = 0;
2536 g_autofree gchar *csum = NULL;
2537 g_auto(GStrv) csums_new = NULL;
2538 g_auto(GStrv) csums = NULL;
2539
2540 /* get existing checksums */
2541 csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
2542 if (csums == NULL)
2543 return FALSE;
2544
2545 /* get new value */
2546 if (g_strv_length (values) == 0) {
2547 csum = fu_util_get_history_checksum (priv, error);
2548 if (csum == NULL)
2549 return FALSE;
2550 } else {
2551 csum = g_strdup (values[0]);
2552 }
2553
2554 /* ensure it's not already there */
2555 if (g_strv_contains ((const gchar * const *) csums, csum)) {
2556 g_set_error_literal (error,
2557 FWUPD_ERROR,
2558 FWUPD_ERROR_NOTHING_TO_DO,
2559 /* TRANSLATORS: user selected something not possible */
2560 _("Firmware is already blocked"));
2561 return FALSE;
2562 }
2563
2564 /* TRANSLATORS: we will not offer this firmware to the user */
2565 g_print ("%s %s\n", _("Blocking firmware:"), csum);
2566
2567 /* remove it from the new list */
2568 csums_new = g_new0 (gchar *, g_strv_length (csums) + 2);
2569 for (guint i = 0; csums[i] != NULL; i++) {
2570 if (g_strcmp0 (csums[i], csum) != 0)
2571 csums_new[idx++] = g_strdup (csums[i]);
2572 }
2573 csums_new[idx] = g_strdup (csum);
2574 return fwupd_client_set_blocked_firmware (priv->client, csums_new,
2575 priv->cancellable, error);
2576}
2577
2578static gboolean
2579fu_util_unblock_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
2580{
2581 guint idx = 0;
2582 g_auto(GStrv) csums = NULL;
2583 g_auto(GStrv) csums_new = NULL;
2584 g_autofree gchar *csum = NULL;
2585
2586 /* get existing checksums */
2587 csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
2588 if (csums == NULL)
2589 return FALSE;
2590
2591 /* empty list */
2592 if (g_strv_length (csums) == 0) {
2593 g_set_error_literal (error,
2594 FWUPD_ERROR,
2595 FWUPD_ERROR_NOTHING_TO_DO,
2596 /* TRANSLATORS: nothing to show */
2597 _("There are no blocked firmware files"));
2598 return FALSE;
2599 }
2600
2601 /* get new value */
2602 if (g_strv_length (values) == 0) {
2603 csum = fu_util_get_history_checksum (priv, error);
2604 if (csum == NULL)
2605 return FALSE;
2606 } else {
2607 csum = g_strdup (values[0]);
2608 }
2609
2610 /* ensure it's there */
2611 if (!g_strv_contains ((const gchar * const *) csums, csum)) {
2612 g_set_error_literal (error,
2613 FWUPD_ERROR,
2614 FWUPD_ERROR_NOTHING_TO_DO,
2615 /* TRANSLATORS: user selected something not possible */
2616 _("Firmware is not already blocked"));
2617 return FALSE;
2618 }
2619
Richard Hughes59761252020-08-20 07:51:36 +01002620 /* TRANSLATORS: we will now offer this firmware to the user */
Richard Hughes31206832020-07-27 15:31:11 +01002621 g_print ("%s %s\n", _("Unblocking firmware:"), csum);
2622
2623 /* remove it from the new list */
2624 csums_new = g_new0 (gchar *, g_strv_length (csums));
2625 for (guint i = 0; csums[i] != NULL; i++) {
2626 if (g_strcmp0 (csums[i], csum) != 0)
2627 csums_new[idx++] = g_strdup (csums[i]);
2628 }
2629 return fwupd_client_set_blocked_firmware (priv->client, csums_new,
2630 priv->cancellable, error);
2631}
2632
2633static gboolean
2634fu_util_get_blocked_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
2635{
2636 g_auto(GStrv) csums = NULL;
2637
2638 /* get checksums */
2639 csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
2640 if (csums == NULL)
2641 return FALSE;
2642
2643 /* empty list */
2644 if (g_strv_length (csums) == 0) {
2645 /* TRANSLATORS: nothing to show */
2646 g_print ("%s\n", _("There are no blocked firmware files"));
2647 return TRUE;
2648 }
2649
2650 /* TRANSLATORS: there follows a list of hashes */
2651 g_print ("%s\n", _("Blocked firmware files:"));
2652 for (guint i = 0; csums[i] != NULL; i++) {
2653 g_print ("%u.\t%s\n", i + 1, csums[i]);
2654 }
2655
2656 /* success */
2657 return TRUE;
2658}
2659
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002660static void
2661fu_util_show_plugin_warnings (FuUtilPrivate *priv)
2662{
2663 FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE;
2664 g_autoptr(GPtrArray) plugins = NULL;
2665
2666 /* get plugins from daemon, ignoring if the daemon is too old */
2667 plugins = fwupd_client_get_plugins (priv->client, NULL, NULL);
2668 if (plugins == NULL)
2669 return;
2670
2671 /* get a superset so we do not show the same message more than once */
2672 for (guint i = 0; i < plugins->len; i++) {
2673 FwupdPlugin *plugin = g_ptr_array_index (plugins, i);
2674 if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING))
2675 continue;
2676 flags |= fwupd_plugin_get_flags (plugin);
2677 }
2678
2679 /* never show these, they're way too generic */
2680 flags &= ~FWUPD_PLUGIN_FLAG_DISABLED;
2681 flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE;
Richard Hughesd94286b2021-03-01 21:12:18 +00002682 flags &= ~FWUPD_PLUGIN_FLAG_REQUIRE_HWID;
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002683
2684 /* print */
2685 for (guint i = 0; i < 64; i++) {
Richard Hughes1df9b822020-10-30 15:18:45 +00002686 FwupdPluginFlags flag = (guint64) 1 << i;
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002687 const gchar *tmp;
2688 g_autofree gchar *fmt = NULL;
Richard Hughes1df9b822020-10-30 15:18:45 +00002689 g_autofree gchar *url= NULL;
2690 g_autoptr(GString) str = g_string_new (NULL);
2691 if ((flags & flag) == 0)
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002692 continue;
Richard Hughes1df9b822020-10-30 15:18:45 +00002693 tmp = fu_util_plugin_flag_to_string (flag);
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002694 if (tmp == NULL)
2695 continue;
2696 /* TRANSLATORS: this is a prefix on the console */
2697 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes1df9b822020-10-30 15:18:45 +00002698 g_string_append_printf (str, "%s %s\n", fmt, tmp);
2699
2700 url = g_strdup_printf ("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s",
2701 fwupd_plugin_flag_to_string (flag));
2702 g_string_append (str, " ");
2703 /* TRANSLATORS: %s is a link to a website */
2704 g_string_append_printf (str, _("See %s for more information."), url);
2705 g_string_append (str, "\n");
2706 g_printerr ("%s", str->str);
Richard Hughes7bcb8d42020-10-08 15:47:47 +01002707 }
2708}
2709
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002710int
2711main (int argc, char *argv[])
2712{
Mario Limonciello71a5b982016-05-10 15:38:53 -05002713 gboolean force = FALSE;
Richard Hughes5bbf0132020-10-05 13:44:39 +01002714 gboolean allow_branch_switch = FALSE;
Richard Hughes63a407a2015-07-22 08:54:14 +01002715 gboolean allow_older = FALSE;
2716 gboolean allow_reinstall = FALSE;
Richard Hughes6de10e12021-01-27 15:47:07 +00002717 gboolean enable_ipfs = FALSE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01002718 gboolean ignore_power = FALSE;
Richard Hughesdf89cd52020-06-26 20:25:18 +01002719 gboolean is_interactive = TRUE;
Richard Hughes76e0f942018-05-14 16:24:00 +01002720 gboolean no_history = FALSE;
Richard Hughes63a407a2015-07-22 08:54:14 +01002721 gboolean offline = FALSE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002722 gboolean ret;
2723 gboolean verbose = FALSE;
Richard Hughes89483f12017-07-04 20:47:00 +01002724 gboolean version = FALSE;
Richard Hughesf06ba472018-01-15 07:54:35 +00002725 g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
Richard Hughes66d0ead2021-01-10 19:55:24 +00002726 g_autoptr(GDateTime) dt_now = g_date_time_new_now_utc ();
Richard Hughes46832432015-09-11 13:43:15 +01002727 g_autoptr(GError) error = NULL;
Richard Hughes0f89a0d2020-09-29 09:56:58 +01002728 g_autoptr(GError) error_polkit = NULL;
Richard Hughesc77e1112019-03-01 10:16:26 +00002729 g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
Richard Hughes46832432015-09-11 13:43:15 +01002730 g_autofree gchar *cmd_descriptions = NULL;
Richard Hughes747f5702019-08-06 14:27:26 +01002731 g_autofree gchar *filter = NULL;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002732 const GOptionEntry options[] = {
2733 { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
2734 /* TRANSLATORS: command line option */
2735 _("Show extra debugging information"), NULL },
Richard Hughes89483f12017-07-04 20:47:00 +01002736 { "version", '\0', 0, G_OPTION_ARG_NONE, &version,
2737 /* TRANSLATORS: command line option */
2738 _("Show client and daemon versions"), NULL },
Richard Hughes63a407a2015-07-22 08:54:14 +01002739 { "offline", '\0', 0, G_OPTION_ARG_NONE, &offline,
Richard Hughese7c12642015-03-04 20:28:59 +00002740 /* TRANSLATORS: command line option */
Mario Limonciello11dee102016-10-19 13:55:19 -05002741 _("Schedule installation for next reboot when possible"), NULL },
Richard Hughes63a407a2015-07-22 08:54:14 +01002742 { "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
2743 /* TRANSLATORS: command line option */
Mario Limonciello350fc4c2019-10-03 07:08:51 -05002744 _("Allow reinstalling existing firmware versions"), NULL },
Richard Hughes63a407a2015-07-22 08:54:14 +01002745 { "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
2746 /* TRANSLATORS: command line option */
2747 _("Allow downgrading firmware versions"), NULL },
Richard Hughes5bbf0132020-10-05 13:44:39 +01002748 { "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
2749 /* TRANSLATORS: command line option */
2750 _("Allow switching firmware branch"), NULL },
Mario Limonciello71a5b982016-05-10 15:38:53 -05002751 { "force", '\0', 0, G_OPTION_ARG_NONE, &force,
2752 /* TRANSLATORS: command line option */
Richard Hughes6450d0d2020-10-06 16:05:24 +01002753 _("Force the action by relaxing some runtime checks"), NULL },
Richard Hughesf06ba472018-01-15 07:54:35 +00002754 { "assume-yes", 'y', 0, G_OPTION_ARG_NONE, &priv->assume_yes,
2755 /* TRANSLATORS: command line option */
2756 _("Answer yes to all questions"), NULL },
Richard Hughes4ffc14f2019-03-08 09:55:20 +00002757 { "sign", '\0', 0, G_OPTION_ARG_NONE, &priv->sign,
2758 /* TRANSLATORS: command line option */
2759 _("Sign the uploaded data with the client certificate"), NULL },
Richard Hughesf06ba472018-01-15 07:54:35 +00002760 { "no-unreported-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_unreported_check,
2761 /* TRANSLATORS: command line option */
2762 _("Do not check for unreported history"), NULL },
2763 { "no-metadata-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_metadata_check,
2764 /* TRANSLATORS: command line option */
2765 _("Do not check for old metadata"), NULL },
Mario Limoncielloa7d15302020-12-14 11:49:28 -06002766 { "no-remote-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_remote_check,
2767 /* TRANSLATORS: command line option */
2768 _("Do not check if download remotes should be enabled"), NULL },
Richard Hughes50a6f702018-02-02 16:03:31 +00002769 { "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
2770 /* TRANSLATORS: command line option */
John Karahalisae690c52021-04-23 07:18:26 -04002771 _("Do not check or prompt for reboot after update"), NULL },
Mario Limonciello98b95162019-10-30 09:20:43 -05002772 { "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
2773 /* TRANSLATORS: command line option */
2774 _("Do not perform device safety checks"), NULL },
Richard Hughes76e0f942018-05-14 16:24:00 +01002775 { "no-history", '\0', 0, G_OPTION_ARG_NONE, &no_history,
2776 /* TRANSLATORS: command line option */
2777 _("Do not write to the history database"), NULL },
Richard Hughes5c82b942020-09-14 12:24:06 +01002778 { "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
2779 /* TRANSLATORS: command line option */
2780 _("Show all results"), NULL },
2781 { "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
Mario Limonciello78956cc2018-05-07 11:03:21 -05002782 /* TRANSLATORS: command line option */
2783 _("Show devices that are not updatable"), NULL },
Richard Hughes0e46b222019-09-05 12:13:35 +01002784 { "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
2785 /* TRANSLATORS: command line option */
2786 _("Ignore SSL strict checks when downloading files"), NULL },
Richard Hughes6de10e12021-01-27 15:47:07 +00002787 { "ipfs", '\0', 0, G_OPTION_ARG_NONE, &enable_ipfs,
2788 /* TRANSLATORS: command line option */
2789 _("Only use IPFS when downloading files"), NULL },
Richard Hughes747f5702019-08-06 14:27:26 +01002790 { "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
2791 /* TRANSLATORS: command line option */
2792 _("Filter with a set of device flags using a ~ prefix to "
2793 "exclude, e.g. 'internal,~needs-reboot'"), NULL },
Richard Hughes6450d0d2020-10-06 16:05:24 +01002794 { "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
2795 /* TRANSLATORS: command line option */
2796 _("Ignore requirement of external power source"), NULL },
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002797 { NULL}
2798 };
2799
2800 setlocale (LC_ALL, "");
2801
Richard Hughes668ee212019-11-22 09:17:46 +00002802 bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002803 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
2804 textdomain (GETTEXT_PACKAGE);
2805
Richard Hughes697eb602015-09-23 10:04:17 +01002806 /* ensure D-Bus errors are registered */
2807 fwupd_error_quark ();
2808
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002809 /* create helper object */
Richard Hughes6ed25f52021-01-10 19:27:33 +00002810 priv->main_ctx = g_main_context_new ();
Richard Hughes9a7db9d2017-08-21 14:03:45 +01002811 priv->progressbar = fu_progressbar_new ();
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002812
2813 /* add commands */
Richard Hughesc77e1112019-03-01 10:16:26 +00002814 fu_util_cmd_array_add (cmd_array,
Mario Limonciello1a9127d2019-08-27 11:32:51 +01002815 "get-devices,get-topology",
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00002816 NULL,
2817 /* TRANSLATORS: command description */
2818 _("Get all devices that support firmware updates"),
2819 fu_util_get_devices);
Richard Hughesc77e1112019-03-01 10:16:26 +00002820 fu_util_cmd_array_add (cmd_array,
Richard Hughes476363a2018-01-11 10:08:58 +00002821 "get-history",
2822 NULL,
2823 /* TRANSLATORS: command description */
2824 _("Show history of firmware updates"),
2825 fu_util_get_history);
Richard Hughesc77e1112019-03-01 10:16:26 +00002826 fu_util_cmd_array_add (cmd_array,
Richard Hughesaf54a072018-01-11 10:04:50 +00002827 "clear-history",
2828 NULL,
2829 /* TRANSLATORS: command description */
2830 _("Erase all firmware update history"),
2831 fu_util_clear_history);
Richard Hughesc77e1112019-03-01 10:16:26 +00002832 fu_util_cmd_array_add (cmd_array,
Richard Hughes0a7bc972018-01-11 13:14:04 +00002833 "report-history",
2834 NULL,
2835 /* TRANSLATORS: command description */
2836 _("Share firmware history with the developers"),
2837 fu_util_report_history);
Richard Hughesc77e1112019-03-01 10:16:26 +00002838 fu_util_cmd_array_add (cmd_array,
Richard Hughesd079b1a2015-03-06 10:09:55 +00002839 "install",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002840 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2841 _("FILE [DEVICE-ID|GUID]"),
Richard Hughesd079b1a2015-03-06 10:09:55 +00002842 /* TRANSLATORS: command description */
2843 _("Install a firmware file on this hardware"),
2844 fu_util_install);
Richard Hughesc77e1112019-03-01 10:16:26 +00002845 fu_util_cmd_array_add (cmd_array,
Richard Hughescccc7752015-03-06 11:13:19 +00002846 "get-details",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002847 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2848 _("FILE"),
Richard Hughescccc7752015-03-06 11:13:19 +00002849 /* TRANSLATORS: command description */
2850 _("Gets details about a firmware file"),
2851 fu_util_get_details);
Richard Hughesc77e1112019-03-01 10:16:26 +00002852 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002853 "get-updates,get-upgrades",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002854 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2855 _("[DEVICE-ID|GUID]"),
Richard Hughes871e0172015-03-17 21:00:18 +00002856 /* TRANSLATORS: command description */
2857 _("Gets the list of updates for connected hardware"),
2858 fu_util_get_updates);
Richard Hughesc77e1112019-03-01 10:16:26 +00002859 fu_util_cmd_array_add (cmd_array,
Mario Limonciellodfff18e2019-08-29 11:51:41 -05002860 "update,upgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002861 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2862 _("[DEVICE-ID|GUID]"),
Richard Hughes777917e2015-07-22 10:25:21 +01002863 /* TRANSLATORS: command description */
2864 _("Updates all firmware to latest versions available"),
2865 fu_util_update);
Richard Hughesc77e1112019-03-01 10:16:26 +00002866 fu_util_cmd_array_add (cmd_array,
Richard Hughesa043c2e2015-06-29 08:43:18 +01002867 "verify",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002868 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2869 _("[DEVICE-ID|GUID]"),
Richard Hughesa043c2e2015-06-29 08:43:18 +01002870 /* TRANSLATORS: command description */
Mario Limonciello8fa0b382019-10-14 07:36:04 -05002871 _("Checks cryptographic hash matches firmware"),
Richard Hughesa043c2e2015-06-29 08:43:18 +01002872 fu_util_verify);
Richard Hughesc77e1112019-03-01 10:16:26 +00002873 fu_util_cmd_array_add (cmd_array,
Richard Hughes9a410ce2016-02-28 15:58:54 +00002874 "unlock",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002875 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2876 _("DEVICE-ID|GUID"),
Richard Hughes9a410ce2016-02-28 15:58:54 +00002877 /* TRANSLATORS: command description */
2878 _("Unlocks the device for firmware access"),
2879 fu_util_unlock);
Richard Hughesc77e1112019-03-01 10:16:26 +00002880 fu_util_cmd_array_add (cmd_array,
Richard Hughes0e883ee2015-03-18 17:22:33 +00002881 "clear-results",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002882 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2883 _("DEVICE-ID|GUID"),
Richard Hughes0e883ee2015-03-18 17:22:33 +00002884 /* TRANSLATORS: command description */
2885 _("Clears the results from the last update"),
2886 fu_util_clear_results);
Richard Hughesc77e1112019-03-01 10:16:26 +00002887 fu_util_cmd_array_add (cmd_array,
Richard Hughes31bbd162017-09-15 09:30:46 +01002888 "clear-offline",
2889 NULL,
2890 /* TRANSLATORS: command description */
2891 _("Clears any updates scheduled to be updated offline"),
2892 fu_util_clear_offline);
Richard Hughesc77e1112019-03-01 10:16:26 +00002893 fu_util_cmd_array_add (cmd_array,
Richard Hughes0e883ee2015-03-18 17:22:33 +00002894 "get-results",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002895 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2896 _("DEVICE-ID|GUID"),
Richard Hughes0e883ee2015-03-18 17:22:33 +00002897 /* TRANSLATORS: command description */
2898 _("Gets the results from the last update"),
2899 fu_util_get_results);
Richard Hughesc77e1112019-03-01 10:16:26 +00002900 fu_util_cmd_array_add (cmd_array,
Richard Hughese4a100c2017-06-04 21:23:50 +01002901 "get-releases",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002902 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2903 _("[DEVICE-ID|GUID]"),
Richard Hughese4a100c2017-06-04 21:23:50 +01002904 /* TRANSLATORS: command description */
2905 _("Gets the releases for a device"),
2906 fu_util_get_releases);
Richard Hughesc77e1112019-03-01 10:16:26 +00002907 fu_util_cmd_array_add (cmd_array,
Richard Hughesf0bde3e2017-06-16 14:30:13 +01002908 "get-remotes",
2909 NULL,
2910 /* TRANSLATORS: command description */
2911 _("Gets the configured remotes"),
2912 fu_util_get_remotes);
Richard Hughesc77e1112019-03-01 10:16:26 +00002913 fu_util_cmd_array_add (cmd_array,
Richard Hughes45c15452017-06-06 09:36:03 +01002914 "downgrade",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002915 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2916 _("[DEVICE-ID|GUID]"),
Richard Hughes45c15452017-06-06 09:36:03 +01002917 /* TRANSLATORS: command description */
2918 _("Downgrades the firmware on a device"),
2919 fu_util_downgrade);
Richard Hughesc77e1112019-03-01 10:16:26 +00002920 fu_util_cmd_array_add (cmd_array,
Richard Hughese97261a2015-07-22 10:29:15 +01002921 "refresh",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002922 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2923 _("[FILE FILE_SIG REMOTE-ID]"),
Richard Hughesae0efdc2015-06-24 16:18:29 +01002924 /* TRANSLATORS: command description */
Richard Hughese97261a2015-07-22 10:29:15 +01002925 _("Refresh metadata from remote server"),
2926 fu_util_refresh);
Richard Hughesc77e1112019-03-01 10:16:26 +00002927 fu_util_cmd_array_add (cmd_array,
Richard Hughes5dc6f5c2015-07-01 12:16:06 +01002928 "verify-update",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002929 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2930 _("[DEVICE-ID|GUID]"),
Richard Hughes5dc6f5c2015-07-01 12:16:06 +01002931 /* TRANSLATORS: command description */
Mario Limonciello8fa0b382019-10-14 07:36:04 -05002932 _("Update the stored cryptographic hash with current ROM contents"),
Richard Hughes5dc6f5c2015-07-01 12:16:06 +01002933 fu_util_verify_update);
Richard Hughesc77e1112019-03-01 10:16:26 +00002934 fu_util_cmd_array_add (cmd_array,
Richard Hughesa6bd5582017-09-07 14:32:22 +01002935 "modify-remote",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002936 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2937 _("REMOTE-ID KEY VALUE"),
Richard Hughesa6bd5582017-09-07 14:32:22 +01002938 /* TRANSLATORS: command description */
2939 _("Modifies a given remote"),
Richard Hughesdfd228a2018-04-11 10:26:24 +01002940 fu_util_remote_modify);
Richard Hughesc77e1112019-03-01 10:16:26 +00002941 fu_util_cmd_array_add (cmd_array,
Richard Hughesdfd228a2018-04-11 10:26:24 +01002942 "enable-remote",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002943 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2944 _("REMOTE-ID"),
Richard Hughesdfd228a2018-04-11 10:26:24 +01002945 /* TRANSLATORS: command description */
2946 _("Enables a given remote"),
2947 fu_util_remote_enable);
Richard Hughesc77e1112019-03-01 10:16:26 +00002948 fu_util_cmd_array_add (cmd_array,
Richard Hughesdfd228a2018-04-11 10:26:24 +01002949 "disable-remote",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002950 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2951 _("REMOTE-ID"),
Richard Hughesdfd228a2018-04-11 10:26:24 +01002952 /* TRANSLATORS: command description */
2953 _("Disables a given remote"),
2954 fu_util_remote_disable);
Richard Hughesc77e1112019-03-01 10:16:26 +00002955 fu_util_cmd_array_add (cmd_array,
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002956 "activate",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002957 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2958 _("[DEVICE-ID|GUID]"),
Mario Limonciello96a0dd52019-02-25 13:50:03 -06002959 /* TRANSLATORS: command description */
2960 _("Activate devices"),
2961 fu_util_activate);
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002962 fu_util_cmd_array_add (cmd_array,
2963 "get-approved-firmware",
2964 NULL,
2965 /* TRANSLATORS: firmware approved by the admin */
Richard Hughes43053d22020-10-18 19:49:25 +01002966 _("Gets the list of approved firmware"),
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002967 fu_util_get_approved_firmware);
2968 fu_util_cmd_array_add (cmd_array,
2969 "set-approved-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002970 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2971 _("CHECKSUM1[,CHECKSUM2][,CHECKSUM3]"),
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002972 /* TRANSLATORS: firmware approved by the admin */
Richard Hughes43053d22020-10-18 19:49:25 +01002973 _("Sets the list of approved firmware"),
Richard Hughes8dd4c1c2019-03-03 18:27:57 +00002974 fu_util_set_approved_firmware);
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002975 fu_util_cmd_array_add (cmd_array,
2976 "modify-config",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002977 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2978 _("KEY,VALUE"),
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002979 /* TRANSLATORS: sets something in daemon.conf */
Richard Hughes43053d22020-10-18 19:49:25 +01002980 _("Modifies a daemon configuration value"),
Mario Limonciellobfcf75b2019-04-17 15:05:39 +01002981 fu_util_modify_config);
Mario Limonciellod837ca82019-10-02 19:04:09 -05002982 fu_util_cmd_array_add (cmd_array,
2983 "reinstall",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002984 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2985 _("[DEVICE-ID|GUID]"),
Mario Limonciellod837ca82019-10-02 19:04:09 -05002986 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002987 _("Reinstall current firmware on the device"),
Mario Limonciellod837ca82019-10-02 19:04:09 -05002988 fu_util_reinstall);
Richard Hughes196c6c62020-05-11 19:42:47 +01002989 fu_util_cmd_array_add (cmd_array,
Richard Hughes460c4b72020-09-25 20:59:28 +01002990 "switch-branch",
Richard Hughesc1e5f942020-11-25 14:33:46 +00002991 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
2992 _("[DEVICE-ID|GUID] [BRANCH]"),
Richard Hughes460c4b72020-09-25 20:59:28 +01002993 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01002994 _("Switch the firmware branch on the device"),
Richard Hughes460c4b72020-09-25 20:59:28 +01002995 fu_util_switch_branch);
2996 fu_util_cmd_array_add (cmd_array,
Richard Hughes196c6c62020-05-11 19:42:47 +01002997 "security",
2998 NULL,
2999 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003000 _("Gets the host security attributes"),
Richard Hughes196c6c62020-05-11 19:42:47 +01003001 fu_util_security);
Richard Hughes31206832020-07-27 15:31:11 +01003002 fu_util_cmd_array_add (cmd_array,
3003 "block-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003004 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3005 _("[CHECKSUM]"),
Richard Hughes31206832020-07-27 15:31:11 +01003006 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003007 _("Blocks a specific firmware from being installed"),
Richard Hughes31206832020-07-27 15:31:11 +01003008 fu_util_block_firmware);
3009 fu_util_cmd_array_add (cmd_array,
3010 "unblock-firmware",
Richard Hughesc1e5f942020-11-25 14:33:46 +00003011 /* TRANSLATORS: command argument: uppercase, spaces->dashes */
3012 _("[CHECKSUM]"),
Richard Hughes31206832020-07-27 15:31:11 +01003013 /* TRANSLATORS: command description */
exploide0c105072020-11-01 19:30:58 +01003014 _("Unblocks a specific firmware from being installed"),
Richard Hughes31206832020-07-27 15:31:11 +01003015 fu_util_unblock_firmware);
3016 fu_util_cmd_array_add (cmd_array,
3017 "get-blocked-firmware",
3018 NULL,
3019 /* TRANSLATORS: command description */
Richard Hughes43053d22020-10-18 19:49:25 +01003020 _("Gets the list of blocked firmware"),
Richard Hughes31206832020-07-27 15:31:11 +01003021 fu_util_get_blocked_firmware);
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003022 fu_util_cmd_array_add (cmd_array,
3023 "get-plugins",
3024 NULL,
3025 /* TRANSLATORS: command description */
3026 _("Get all enabled plugins registered with the system"),
3027 fu_util_get_plugins);
Mario Limonciello96a0dd52019-02-25 13:50:03 -06003028
Richard Hughescfc44fa2016-04-28 14:36:35 +01003029 /* do stuff on ctrl+c */
3030 priv->cancellable = g_cancellable_new ();
Richard Hughes9e5675e2019-11-22 09:35:03 +00003031#ifdef HAVE_GIO_UNIX
Richard Hughescfc44fa2016-04-28 14:36:35 +01003032 g_unix_signal_add_full (G_PRIORITY_DEFAULT,
3033 SIGINT, fu_util_sigint_cb,
3034 priv, NULL);
Richard Hughes9e5675e2019-11-22 09:35:03 +00003035#endif
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003036
3037 /* sort by command name */
Richard Hughesc77e1112019-03-01 10:16:26 +00003038 fu_util_cmd_array_sort (cmd_array);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003039
3040 /* get a list of the commands */
3041 priv->context = g_option_context_new (NULL);
Richard Hughesc77e1112019-03-01 10:16:26 +00003042 cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003043 g_option_context_set_summary (priv->context, cmd_descriptions);
Richard Hughes3b847532017-10-23 13:22:55 +01003044 g_option_context_set_description (priv->context,
Richard Hughesc1e5f942020-11-25 14:33:46 +00003045 /* TRANSLATORS: CLI description */
3046 _("This tool allows an administrator to query and control the "
3047 "fwupd daemon, allowing them to perform actions such as "
3048 "installing or downgrading firmware."));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003049
3050 /* TRANSLATORS: program name */
Richard Hughes63a407a2015-07-22 08:54:14 +01003051 g_set_application_name (_("Firmware Utility"));
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003052 g_option_context_add_main_entries (priv->context, options, NULL);
3053 ret = g_option_context_parse (priv->context, &argc, &argv, &error);
3054 if (!ret) {
3055 /* TRANSLATORS: the user didn't read the man page */
3056 g_print ("%s: %s\n", _("Failed to parse arguments"),
3057 error->message);
Richard Hughes741dc502017-05-08 20:38:17 +01003058 return EXIT_FAILURE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003059 }
3060
Richard Hughes0e46b222019-09-05 12:13:35 +01003061 /* allow disabling SSL strict mode for broken corporate proxies */
3062 if (priv->disable_ssl_strict) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003063 g_autofree gchar *fmt = NULL;
3064 /* TRANSLATORS: this is a prefix on the console */
3065 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
Richard Hughes0e46b222019-09-05 12:13:35 +01003066 /* TRANSLATORS: try to help */
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003067 g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
3068 "to do this automatically in the future "
3069 "export DISABLE_SSL_STRICT in your environment"));
Richard Hughes0e46b222019-09-05 12:13:35 +01003070 g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
3071 }
3072
Richard Hughes66d0ead2021-01-10 19:55:24 +00003073 /* this doesn't have to be precise (e.g. using the build-year) as we just
3074 * want to check the clock is not set to the default of 1970-01-01... */
3075 if (g_date_time_get_year (dt_now) < 2021) {
3076 g_autofree gchar *fmt = NULL;
3077 /* TRANSLATORS: this is a prefix on the console */
3078 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
3079 /* TRANSLATORS: try to help */
3080 g_printerr ("%s %s\n", fmt, _("The system clock has not been set "
3081 "correctly and downloading files may fail."));
3082 }
3083
Mario Limonciellob390b142019-07-15 21:05:44 -05003084 /* non-TTY consoles cannot answer questions */
Mario Limonciellod81ea2e2020-01-13 14:11:43 -06003085 if (isatty (fileno (stdout)) == 0) {
Richard Hughesdf89cd52020-06-26 20:25:18 +01003086 is_interactive = FALSE;
Mario Limonciellob390b142019-07-15 21:05:44 -05003087 priv->no_unreported_check = TRUE;
3088 priv->no_metadata_check = TRUE;
3089 priv->no_reboot_check = TRUE;
Mario Limonciello98b95162019-10-30 09:20:43 -05003090 priv->no_safety_check = TRUE;
Mario Limoncielloa7d15302020-12-14 11:49:28 -06003091 priv->no_remote_check = TRUE;
Mario Limonciellob390b142019-07-15 21:05:44 -05003092 fu_progressbar_set_interactive (priv->progressbar, FALSE);
3093 }
3094
Richard Hughes747f5702019-08-06 14:27:26 +01003095 /* parse filter flags */
3096 if (filter != NULL) {
3097 if (!fu_util_parse_filter_flags (filter,
3098 &priv->filter_include,
3099 &priv->filter_exclude,
3100 &error)) {
3101 /* TRANSLATORS: the user didn't read the man page */
3102 g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
3103 error->message);
3104 return EXIT_FAILURE;
3105 }
3106 }
3107
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003108 /* set verbose? */
3109 if (verbose) {
Richard Hughes954de522017-06-15 21:26:53 +01003110 g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
Mario Limonciellob76960e2019-08-26 23:43:40 -05003111 g_setenv ("FWUPD_VERBOSE", "1", FALSE);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003112 } else {
3113 g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
3114 fu_util_ignore_cb, NULL);
3115 }
3116
Richard Hughes63a407a2015-07-22 08:54:14 +01003117 /* set flags */
3118 if (offline)
Richard Hughes2d6e1862016-03-18 09:20:37 +00003119 priv->flags |= FWUPD_INSTALL_FLAG_OFFLINE;
Richard Hughes63a407a2015-07-22 08:54:14 +01003120 if (allow_reinstall)
Richard Hughes2d6e1862016-03-18 09:20:37 +00003121 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
Richard Hughes63a407a2015-07-22 08:54:14 +01003122 if (allow_older)
Richard Hughes2d6e1862016-03-18 09:20:37 +00003123 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
Richard Hughes5bbf0132020-10-05 13:44:39 +01003124 if (allow_branch_switch)
3125 priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003126 if (force) {
Mario Limonciello71a5b982016-05-10 15:38:53 -05003127 priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003128 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
3129 }
Richard Hughes76e0f942018-05-14 16:24:00 +01003130 if (no_history)
3131 priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
Richard Hughes6450d0d2020-10-06 16:05:24 +01003132 if (ignore_power)
3133 priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
Richard Hughese7c12642015-03-04 20:28:59 +00003134
Richard Hughes6de10e12021-01-27 15:47:07 +00003135 /* use IPFS for metadata and firmware *only* if specified */
3136 if (enable_ipfs)
3137 priv->download_flags |= FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_IPFS;
3138
Mario Limonciello11b71f42020-10-13 13:39:14 -05003139#ifdef HAVE_POLKIT
Richard Hughes0f89a0d2020-09-29 09:56:58 +01003140 /* start polkit tty agent to listen for password requests */
3141 if (is_interactive) {
3142 if (!fu_polkit_agent_open (&error_polkit)) {
3143 g_printerr ("Failed to open polkit agent: %s\n",
3144 error_polkit->message);
3145 }
3146 }
Mario Limonciello11b71f42020-10-13 13:39:14 -05003147#endif
Richard Hughes0f89a0d2020-09-29 09:56:58 +01003148
Richard Hughes0e883ee2015-03-18 17:22:33 +00003149 /* connect to the daemon */
Richard Hughescb07a3e2016-03-17 10:09:03 +00003150 priv->client = fwupd_client_new ();
Richard Hughes6ed25f52021-01-10 19:27:33 +00003151 fwupd_client_set_main_context (priv->client, priv->main_ctx);
Richard Hughes876c0072016-08-17 14:51:03 +01003152 g_signal_connect (priv->client, "notify::percentage",
Richard Hughesda004cf2016-08-17 17:32:59 +01003153 G_CALLBACK (fu_util_client_notify_cb), priv);
3154 g_signal_connect (priv->client, "notify::status",
3155 G_CALLBACK (fu_util_client_notify_cb), priv);
Richard Hughes0e883ee2015-03-18 17:22:33 +00003156
Richard Hughes89483f12017-07-04 20:47:00 +01003157 /* just show versions and exit */
3158 if (version) {
Mario Limonciello2d4b7a52018-09-12 22:08:04 -05003159 g_autofree gchar *version_str = fu_util_get_versions();
3160 g_print ("%s\n", version_str);
Richard Hughes89483f12017-07-04 20:47:00 +01003161 if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) {
3162 g_printerr ("Failed to connect to daemon: %s\n",
3163 error->message);
3164 return EXIT_FAILURE;
3165 }
3166 g_print ("daemon version:\t%s\n",
3167 fwupd_client_get_daemon_version (priv->client));
3168 return EXIT_SUCCESS;
3169 }
3170
Richard Hughesf425d292019-01-18 17:57:39 +00003171 /* show a warning if the daemon is tainted */
3172 if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) {
3173 g_printerr ("Failed to connect to daemon: %s\n",
3174 error->message);
3175 return EXIT_FAILURE;
3176 }
3177 if (fwupd_client_get_tainted (priv->client)) {
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003178 g_autofree gchar *fmt = NULL;
3179 /* TRANSLATORS: this is a prefix on the console */
3180 fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
3181 g_printerr ("%s %s\n",
3182 fmt,
3183 /* TRANSLATORS: the user is SOL for support... */
3184 _("The daemon has loaded 3rd party code and "
3185 "is no longer supported by the upstream developers!"));
Richard Hughesf425d292019-01-18 17:57:39 +00003186 }
3187
Richard Hughes7bcb8d42020-10-08 15:47:47 +01003188 /* show user-visible warnings from the plugins */
3189 fu_util_show_plugin_warnings (priv);
3190
Mario Limonciellobd60de12020-11-11 13:09:43 -06003191 /* show any unsupported warnings */
3192 fu_util_show_unsupported_warn ();
3193
Richard Hughes9b6d6162020-07-07 16:24:57 +01003194 /* we know the runtime daemon version now */
3195 fwupd_client_set_user_agent_for_package (priv->client, "fwupdmgr", PACKAGE_VERSION);
3196
Mario Limonciello81d83c42019-05-07 17:07:51 -05003197 /* check that we have at least this version daemon running */
Richard Hughesc4ee8832019-09-05 17:42:48 +01003198 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
3199 !fu_util_check_daemon_version (priv, &error)) {
Mario Limonciello81d83c42019-05-07 17:07:51 -05003200 g_printerr ("%s\n", error->message);
3201 return EXIT_FAILURE;
3202 }
3203
Mario Limonciello88f8b7f2019-05-07 15:52:39 -05003204#ifdef HAVE_SYSTEMD
3205 /* make sure the correct daemon is in use */
Richard Hughes1877e862019-05-16 12:55:43 +01003206 if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
Mario Limoncielloeb4c7642019-11-11 10:31:29 -06003207 !fwupd_client_get_daemon_interactive (priv->client) &&
Richard Hughes1877e862019-05-16 12:55:43 +01003208 !fu_util_using_correct_daemon (&error)) {
Mario Limonciello88f8b7f2019-05-07 15:52:39 -05003209 g_printerr ("%s\n", error->message);
3210 return EXIT_FAILURE;
3211 }
3212#endif
3213
Mario Limonciello057c67a2019-05-23 10:44:19 -05003214 /* make sure polkit actions were installed */
3215 if (!fu_util_check_polkit_actions (&error)) {
3216 g_printerr ("%s\n", error->message);
3217 return EXIT_FAILURE;
3218 }
3219
Richard Hughesdf89cd52020-06-26 20:25:18 +01003220 /* send our implemented feature set */
3221 if (is_interactive) {
3222 if (!fwupd_client_set_feature_flags (priv->client,
3223 FWUPD_FEATURE_FLAG_CAN_REPORT |
Richard Hughes460c4b72020-09-25 20:59:28 +01003224 FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
Richard Hughesdf89cd52020-06-26 20:25:18 +01003225 FWUPD_FEATURE_FLAG_UPDATE_ACTION |
3226 FWUPD_FEATURE_FLAG_DETACH_ACTION,
3227 priv->cancellable, &error)) {
3228 g_printerr ("Failed to set front-end features: %s\n",
3229 error->message);
3230 return EXIT_FAILURE;
3231 }
3232 }
3233
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003234 /* run the specified command */
Richard Hughesc77e1112019-03-01 10:16:26 +00003235 ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003236 if (!ret) {
Mario Limonciellob390b142019-07-15 21:05:44 -05003237 g_printerr ("%s\n", error->message);
Mario Limonciello89096312020-04-30 09:51:14 -05003238 if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
3239 /* TRANSLATORS: error message explaining command to run to how to get help */
3240 g_printerr ("\n%s\n", _("Use fwupdmgr --help for help"));
3241 } else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
3242 g_debug ("%s\n", error->message);
3243 return EXIT_NOTHING_TO_DO;
3244 }
3245 return EXIT_FAILURE;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003246 }
3247
Richard Hughes0f89a0d2020-09-29 09:56:58 +01003248
Mario Limonciello11b71f42020-10-13 13:39:14 -05003249#ifdef HAVE_POLKIT
Richard Hughes0f89a0d2020-09-29 09:56:58 +01003250 /* stop listening for polkit questions */
3251 fu_polkit_agent_close ();
Mario Limonciello11b71f42020-10-13 13:39:14 -05003252#endif
Richard Hughes0f89a0d2020-09-29 09:56:58 +01003253
Mario Limonciello89096312020-04-30 09:51:14 -05003254 /* success */
3255 return EXIT_SUCCESS;
Richard Hughes8dbfb1c2015-02-26 13:07:40 +00003256}