fwupd/src/fu-util.c

3235 lines
98 KiB
C

/*
* Copyright (C) 2015-2018 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuMain"
#include "config.h"
#include <fwupd.h>
#include <xmlb.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <gio/gunixfdlist.h>
#include <glib/gi18n.h>
#ifdef HAVE_GIO_UNIX
#include <glib-unix.h>
#endif
#include <json-glib/json-glib.h>
#include <locale.h>
#include <stdlib.h>
#include <unistd.h>
#include "fu-history.h"
#include "fu-plugin-private.h"
#include "fu-polkit-agent.h"
#include "fu-progressbar.h"
#include "fu-security-attrs.h"
#include "fu-util-common.h"
#include "fwupd-common-private.h"
#ifdef HAVE_SYSTEMD
#include "fu-systemd.h"
#endif
/* custom return code */
#define EXIT_NOTHING_TO_DO 2
typedef enum {
FU_UTIL_HISTORY_DO_NOTHING,
FU_UTIL_HISTORY_NEVER,
FU_UTIL_HISTORY_PROMPT,
FU_UTIL_HISTORY_AUTOMATIC,
FU_UTIL_HISTORY_LAST,
} FuUtilHistoryAction;
typedef enum {
FU_UTIL_OPERATION_UNKNOWN,
FU_UTIL_OPERATION_UPDATE,
FU_UTIL_OPERATION_DOWNGRADE,
FU_UTIL_OPERATION_INSTALL,
FU_UTIL_OPERATION_LAST
} FuUtilOperation;
struct FuUtilPrivate {
GCancellable *cancellable;
GMainLoop *loop;
GOptionContext *context;
FwupdInstallFlags flags;
FwupdClient *client;
FuProgressbar *progressbar;
gboolean no_metadata_check;
gboolean no_reboot_check;
gboolean no_unreported_check;
gboolean no_safety_check;
gboolean assume_yes;
gboolean sign;
gboolean show_all;
gboolean disable_ssl_strict;
/* only valid in update and downgrade */
FuUtilOperation current_operation;
FwupdDevice *current_device;
gchar *current_message;
FwupdDeviceFlags completion_flags;
FwupdDeviceFlags filter_include;
FwupdDeviceFlags filter_exclude;
};
static gboolean fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error);
static void
fu_util_client_notify_cb (GObject *object,
GParamSpec *pspec,
FuUtilPrivate *priv)
{
fu_progressbar_update (priv->progressbar,
fwupd_client_get_status (priv->client),
fwupd_client_get_percentage (priv->client));
}
static void
fu_util_update_device_changed_cb (FwupdClient *client,
FwupdDevice *device,
FuUtilPrivate *priv)
{
g_autofree gchar *str = NULL;
/* allowed to set whenever the device has changed */
if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
/* same as last time, so ignore */
if (priv->current_device != NULL &&
fwupd_device_compare (priv->current_device, device) == 0)
return;
/* ignore indirect devices that might have changed */
if (fwupd_device_get_status (device) == FWUPD_STATUS_IDLE ||
fwupd_device_get_status (device) == FWUPD_STATUS_UNKNOWN) {
g_debug ("ignoring %s with status %s",
fwupd_device_get_name (device),
fwupd_status_to_string (fwupd_device_get_status (device)));
return;
}
/* show message in progressbar */
if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) {
/* TRANSLATORS: %1 is a device name */
str = g_strdup_printf (_("Updating %s…"),
fwupd_device_get_name (device));
fu_progressbar_set_title (priv->progressbar, str);
} else if (priv->current_operation == FU_UTIL_OPERATION_DOWNGRADE) {
/* TRANSLATORS: %1 is a device name */
str = g_strdup_printf (_("Downgrading %s…"),
fwupd_device_get_name (device));
fu_progressbar_set_title (priv->progressbar, str);
} else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) {
/* TRANSLATORS: %1 is a device name */
str = g_strdup_printf (_("Installing on %s…"),
fwupd_device_get_name (device));
fu_progressbar_set_title (priv->progressbar, str);
} else {
g_warning ("no FuUtilOperation set");
}
g_set_object (&priv->current_device, device);
if (priv->current_message == NULL) {
const gchar *tmp = fwupd_device_get_update_message (priv->current_device);
if (tmp != NULL)
priv->current_message = g_strdup (tmp);
}
}
static gboolean
fu_util_filter_device (FuUtilPrivate *priv, FwupdDevice *dev)
{
for (guint i = 0; i < 64; i++) {
FwupdDeviceFlags flag = 1LLU << i;
if (priv->filter_include & flag) {
if (!fwupd_device_has_flag (dev, flag))
return FALSE;
}
if (priv->filter_exclude & flag) {
if (fwupd_device_has_flag (dev, flag))
return FALSE;
}
}
return TRUE;
}
static FwupdDevice *
fu_util_prompt_for_device (FuUtilPrivate *priv, GPtrArray *devices, GError **error)
{
FwupdDevice *dev;
guint idx;
g_autoptr(GPtrArray) devices_filtered = NULL;
/* filter results */
devices_filtered = g_ptr_array_new ();
for (guint i = 0; i < devices->len; i++) {
dev = g_ptr_array_index (devices, i);
if (!fu_util_filter_device (priv, dev))
continue;
g_ptr_array_add (devices_filtered, dev);
}
/* nothing */
if (devices_filtered->len == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No supported devices");
return NULL;
}
/* exactly one */
if (devices_filtered->len == 1) {
dev = g_ptr_array_index (devices_filtered, 0);
/* TRANSLATORS: Device has been chosen by the daemon for the user */
g_print ("%s: %s\n", _("Selected device"), fwupd_device_get_name (dev));
return g_object_ref (dev);
}
/* TRANSLATORS: get interactive prompt */
g_print ("%s\n", _("Choose a device:"));
/* TRANSLATORS: this is to abort the interactive prompt */
g_print ("0.\t%s\n", _("Cancel"));
for (guint i = 0; i < devices_filtered->len; i++) {
dev = g_ptr_array_index (devices_filtered, i);
g_print ("%u.\t%s (%s)\n",
i + 1,
fwupd_device_get_id (dev),
fwupd_device_get_name (dev));
}
idx = fu_util_prompt_for_number (devices_filtered->len);
if (idx == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Request canceled");
return NULL;
}
dev = g_ptr_array_index (devices_filtered, idx - 1);
return g_object_ref (dev);
}
static gboolean
fu_util_maybe_enable_automatic (FuUtilPrivate *priv, GPtrArray *remotes, GError **error)
{
guint idx;
/* TRANSLATORS: Display a message asking every time an update is performed */
g_print ("%d.\t%s\n",
FU_UTIL_HISTORY_DO_NOTHING,
ngettext ("Do not upload report at this time, but prompt again for future updates",
"Do not upload reports at this time, but prompt again for future updates",
remotes->len));
/* TRANSLATORS: Display a message asking every time an update is performed */
g_print ("%d.\t%s\n",
FU_UTIL_HISTORY_NEVER,
ngettext ("Do not upload report, and never ask to upload reports for future updates",
"Do not upload reports, and never ask to upload reports for future updates",
remotes->len));
/* TRANSLATORS: Display a message asking every time an update is performed */
g_print ("%d.\t%s\n",
FU_UTIL_HISTORY_PROMPT,
ngettext ("Upload report just this one time, but prompt again for future updates",
"Upload reports just this one time, but prompt again for future updates",
remotes->len));
/* TRANSLATORS: Display a message asking every time an update is performed */
g_print ("%d.\t%s\n",
FU_UTIL_HISTORY_AUTOMATIC,
ngettext ("Upload report this time and automatically upload reports after completing future updates",
"Upload reports this time and automatically upload reports after completing future updates",
remotes->len));
idx = fu_util_prompt_for_number (FU_UTIL_HISTORY_LAST - 1);
switch (idx) {
case FU_UTIL_HISTORY_NEVER:
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
const gchar *remote_id = fwupd_remote_get_id (remote);
if (fwupd_remote_get_report_uri (remote) == NULL)
continue;
if (!fwupd_client_modify_remote (priv->client,
remote_id, "ReportURI", "",
NULL, error))
return FALSE;
}
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Reporting disabled");
return FALSE;
case FU_UTIL_HISTORY_DO_NOTHING:
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Request canceled");
return FALSE;
case FU_UTIL_HISTORY_AUTOMATIC:
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
const gchar *remote_id = fwupd_remote_get_id (remote);
if (fwupd_remote_get_report_uri (remote) == NULL)
continue;
if (fwupd_remote_get_automatic_reports (remote))
continue;
if (!fwupd_client_modify_remote (priv->client,
remote_id, "AutomaticReports", "true",
NULL, error))
return FALSE;
}
break;
default:
break;
}
return TRUE;
}
static gboolean
fu_util_perhaps_show_unreported (FuUtilPrivate *priv, GError **error)
{
g_autoptr(GError) error_local = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) devices_failed = g_ptr_array_new ();
g_autoptr(GPtrArray) devices_success = g_ptr_array_new ();
g_autoptr(GPtrArray) remotes = NULL;
g_autoptr(GHashTable) remote_id_uri_map = NULL;
gboolean all_automatic = FALSE;
/* we don't want to ask anything */
if (priv->no_unreported_check) {
g_debug ("skipping unreported check");
return TRUE;
}
/* get all devices from the history database */
devices = fwupd_client_get_history (priv->client, NULL, &error_local);
if (devices == NULL) {
if (g_error_matches (error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO))
return TRUE;
g_propagate_error (error, g_steal_pointer (&error_local));
return FALSE;
}
/* create a map of RemoteID to RemoteURI */
remotes = fwupd_client_get_remotes (priv->client, NULL, error);
if (remotes == NULL)
return FALSE;
remote_id_uri_map = g_hash_table_new (g_str_hash, g_str_equal);
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
gboolean remote_automatic;
if (fwupd_remote_get_id (remote) == NULL)
continue;
if (fwupd_remote_get_report_uri (remote) == NULL)
continue;
g_debug ("adding %s for %s",
fwupd_remote_get_report_uri (remote),
fwupd_remote_get_id (remote));
g_hash_table_insert (remote_id_uri_map,
(gpointer) fwupd_remote_get_id (remote),
(gpointer) fwupd_remote_get_report_uri (remote));
remote_automatic = fwupd_remote_get_automatic_reports (remote);
g_debug ("%s is %d", fwupd_remote_get_title (remote), remote_automatic);
if (remote_automatic && !all_automatic)
all_automatic = TRUE;
if (!remote_automatic && all_automatic) {
all_automatic = FALSE;
break;
}
}
/* check that they can be reported */
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
FwupdRelease *rel = fwupd_device_get_release_default (dev);
const gchar *remote_id;
const gchar *remote_uri;
if (!fu_util_filter_device (priv, dev))
continue;
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED))
continue;
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
continue;
/* find the RemoteURI to use for the device */
remote_id = fwupd_release_get_remote_id (rel);
if (remote_id == NULL) {
g_debug ("%s has no RemoteID", fwupd_device_get_id (dev));
continue;
}
remote_uri = g_hash_table_lookup (remote_id_uri_map, remote_id);
if (remote_uri == NULL) {
g_debug ("%s has no RemoteURI", remote_id);
continue;
}
/* only send success and failure */
if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_FAILED) {
g_ptr_array_add (devices_failed, dev);
} else if (fwupd_device_get_update_state (dev) == FWUPD_UPDATE_STATE_SUCCESS) {
g_ptr_array_add (devices_success, dev);
} else {
g_debug ("ignoring %s with UpdateState %s",
fwupd_device_get_id (dev),
fwupd_update_state_to_string (fwupd_device_get_update_state (dev)));
}
}
/* nothing to do */
if (devices_failed->len == 0 && devices_success->len == 0) {
g_debug ("no unreported devices");
return TRUE;
}
g_debug ("All automatic: %d", all_automatic);
/* show the success and failures */
if (!priv->assume_yes && !all_automatic) {
/* delimit */
g_print ("________________________________________________\n");
/* failures */
if (devices_failed->len > 0) {
/* TRANSLATORS: a list of failed updates */
g_print ("\n%s\n\n", _("Devices that were not updated correctly:"));
for (guint i = 0; i < devices_failed->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices_failed, i);
FwupdRelease *rel = fwupd_device_get_release_default (dev);
g_print (" • %s (%s → %s)\n",
fwupd_device_get_name (dev),
fwupd_device_get_version (dev),
fwupd_release_get_version (rel));
}
}
/* success */
if (devices_success->len > 0) {
/* TRANSLATORS: a list of successful updates */
g_print ("\n%s\n\n", _("Devices that have been updated successfully:"));
for (guint i = 0; i < devices_success->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices_success, i);
FwupdRelease *rel = fwupd_device_get_release_default (dev);
g_print (" • %s (%s → %s)\n",
fwupd_device_get_name (dev),
fwupd_device_get_version (dev),
fwupd_release_get_version (rel));
}
}
/* ask for permission */
g_print ("\n%s\n%s (%s):\n",
/* TRANSLATORS: explain why we want to upload */
_("Uploading firmware reports helps hardware vendors"
" to quickly identify failing and successful updates"
" on real devices."),
/* TRANSLATORS: ask the user to upload */
_("Upload report now?"),
/* TRANSLATORS: metadata is downloaded from the Internet */
_("Requires internet connection"));
if (!fu_util_maybe_enable_automatic (priv, remotes, error))
return FALSE;
}
/* success */
return fu_util_report_history (priv, NULL, error);
}
static gboolean
fu_util_modify_remote_warning (FuUtilPrivate *priv, FwupdRemote *remote, GError **error)
{
const gchar *warning_markup = NULL;
g_autofree gchar *warning_plain = NULL;
/* get formatted text */
warning_markup = fwupd_remote_get_agreement (remote);
if (warning_markup == NULL)
return TRUE;
warning_plain = fu_util_convert_description (warning_markup, error);
if (warning_plain == NULL)
return FALSE;
/* show and ask user to confirm */
fu_util_warning_box (warning_plain, 80);
if (!priv->assume_yes) {
/* ask for permission */
g_print ("\n%s [Y|n]: ",
/* TRANSLATORS: should the remote still be enabled */
_("Agree and enable the remote?"));
if (!fu_util_prompt_for_boolean (TRUE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Declined agreement");
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_util_modify_remote (FuUtilPrivate *priv,
const gchar *remote_id,
const gchar *key,
const gchar *value,
GError **error)
{
g_autoptr(FwupdRemote) remote = NULL;
/* ensure the remote exists */
remote = fwupd_client_get_remote_by_id (priv->client, remote_id, NULL, error);
if (remote == NULL)
return FALSE;
/* show some kind of warning when enabling download-type remotes */
if (g_strcmp0 (key, "Enabled") == 0 && g_strcmp0 (value, "true") == 0) {
if (!fu_util_modify_remote_warning (priv, remote, error))
return FALSE;
}
return fwupd_client_modify_remote (priv->client,
remote_id, key, value,
NULL, error);
}
static void
fu_util_build_device_tree (FuUtilPrivate *priv, GNode *root, GPtrArray *devs, FwupdDevice *dev)
{
for (guint i = 0; i < devs->len; i++) {
FwupdDevice *dev_tmp = g_ptr_array_index (devs, i);
if (!fu_util_filter_device (priv, dev_tmp))
continue;
if (!priv->show_all &&
!fu_util_is_interesting_device (dev_tmp))
continue;
if (fwupd_device_get_parent (dev_tmp) == dev) {
FwupdRelease *rel = fwupd_device_get_release_default (dev_tmp);
GNode *child = g_node_append_data (root, dev_tmp);
if (rel != NULL)
g_node_append_data (child, rel);
fu_util_build_device_tree (priv, child, devs, dev_tmp);
}
}
}
static gchar *
fu_util_get_tree_title (FuUtilPrivate *priv)
{
return g_strdup (fwupd_client_get_host_product (priv->client));
}
static gboolean
fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GNode) root = g_node_new (NULL);
g_autoptr(GPtrArray) devs = NULL;
g_autofree gchar *title = fu_util_get_tree_title (priv);
/* get results from daemon */
devs = fwupd_client_get_devices (priv->client, NULL, error);
if (devs == NULL)
return FALSE;
/* print */
if (devs->len == 0) {
/* TRANSLATORS: nothing attached that can be upgraded */
g_print ("%s\n", _("No hardware detected with firmware update capability"));
return TRUE;
}
fu_util_build_device_tree (priv, root, devs, NULL);
fu_util_print_tree (root, title);
/* nag? */
if (!fu_util_perhaps_show_unreported (priv, error))
return FALSE;
return TRUE;
}
static gboolean
fu_util_get_plugins (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) plugins = NULL;
/* get results from daemon */
plugins = fwupd_client_get_plugins (priv->client, NULL, error);
if (plugins == NULL)
return FALSE;
/* print */
for (guint i = 0; i < plugins->len; i++) {
FuPlugin *plugin = g_ptr_array_index (plugins, i);
g_autofree gchar *str = fu_util_plugin_to_string (FWUPD_PLUGIN (plugin), 0);
g_print ("%s\n", str);
}
if (plugins->len == 0) {
/* TRANSLATORS: nothing found */
g_print ("%s\n", _("No plugins found"));
}
/* success */
return TRUE;
}
static gchar *
fu_util_download_if_required (FuUtilPrivate *priv, const gchar *perhapsfn, GError **error)
{
g_autofree gchar *filename = NULL;
g_autoptr(GBytes) blob = NULL;
g_autoptr(SoupURI) uri = NULL;
/* a local file */
if (g_file_test (perhapsfn, G_FILE_TEST_EXISTS))
return g_strdup (perhapsfn);
uri = soup_uri_new (perhapsfn);
if (uri == NULL)
return g_strdup (perhapsfn);
/* download the firmware to a cachedir */
filename = fu_util_get_user_cache_path (perhapsfn);
if (!fu_common_mkdir_parent (filename, error))
return NULL;
blob = fwupd_client_download_bytes (priv->client, perhapsfn,
FWUPD_CLIENT_DOWNLOAD_FLAG_NONE,
priv->cancellable, error);
if (blob == NULL)
return NULL;
/* save file to cache */
if (!fu_common_set_contents_bytes (filename, blob, error))
return NULL;
return g_steal_pointer (&filename);
}
static void
fu_util_display_current_message (FuUtilPrivate *priv)
{
if (priv->current_message == NULL) {
/* TRANSLATORS: success message */
g_print ("%s\n", _("Successfully installed firmware"));
return;
}
/* TRANSLATORS: success message */
g_print ("%s: %s\n", _("Successfully installed firmware"), priv->current_message);
g_clear_pointer (&priv->current_message, g_free);
}
static gboolean
fu_util_install (FuUtilPrivate *priv, gchar **values, GError **error)
{
const gchar *id;
g_autofree gchar *filename = NULL;
/* handle both forms */
if (g_strv_length (values) == 1) {
id = FWUPD_DEVICE_ID_ANY;
} else if (g_strv_length (values) == 2) {
id = values[1];
} else {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
/* install with flags chosen by the user */
filename = fu_util_download_if_required (priv, values[0], error);
if (filename == NULL)
return FALSE;
if (!fwupd_client_install (priv->client, id, filename, priv->flags, NULL, error))
return FALSE;
fu_util_display_current_message (priv);
/* we don't want to ask anything */
if (priv->no_reboot_check) {
g_debug ("skipping reboot check");
return TRUE;
}
/* show reboot if needed */
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_get_details (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) array = NULL;
g_autoptr(GNode) root = g_node_new (NULL);
g_autofree gchar *title = fu_util_get_tree_title (priv);
/* check args */
if (g_strv_length (values) != 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* implied, important for get-details on a device not in your system */
priv->show_all = TRUE;
array = fwupd_client_get_details (priv->client, values[0], NULL, error);
if (array == NULL)
return FALSE;
fu_util_build_device_tree (priv, root, array, NULL);
fu_util_print_tree (root, title);
return TRUE;
}
static gboolean
fu_util_clear_history (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FuHistory) history = fu_history_new ();
return fu_history_remove_all (history, error);
}
static gboolean
fu_util_report_history_for_remote (FuUtilPrivate *priv,
const gchar *remote_id,
GPtrArray *devices,
GError **error)
{
g_autofree gchar *data = NULL;
g_autofree gchar *sig = NULL;
g_autofree gchar *uri = NULL;
g_autoptr(FwupdRemote) remote = NULL;
/* convert to JSON */
data = fwupd_build_history_report_json (devices, error);
if (data == NULL)
return FALSE;
/* self sign data */
if (priv->sign) {
sig = fwupd_client_self_sign (priv->client, data,
FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP,
priv->cancellable, error);
if (sig == NULL)
return FALSE;
}
remote = fwupd_client_get_remote_by_id (priv->client, remote_id,
NULL, error);
if (remote == NULL)
return FALSE;
/* ask for permission */
if (!priv->assume_yes && !fwupd_remote_get_automatic_reports (remote)) {
fu_util_print_data (_("Target"), fwupd_remote_get_report_uri (remote));
fu_util_print_data (_("Payload"), data);
if (sig != NULL)
fu_util_print_data (_("Signature"), sig);
g_print ("%s [Y|n]: ", _("Proceed with upload?"));
if (!fu_util_prompt_for_boolean (TRUE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"User declined action");
return FALSE;
}
}
/* POST request and parse reply */
if (!fu_util_send_report (priv->client,
fwupd_remote_get_report_uri (remote),
data, sig, &uri, error))
return FALSE;
/* server wanted us to see a message */
if (uri != NULL) {
g_print ("%s %s\n",
/* TRANSLATORS: the server sent the user a small message */
_("Update failure is a known issue, visit this URL for more information:"),
uri);
}
/* success */
return TRUE;
}
static gboolean
fu_util_report_history (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GHashTable) report_map = NULL;
g_autoptr(GList) ids = NULL;
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GPtrArray) remotes = NULL;
g_autoptr(GString) str = g_string_new (NULL);
/* get all devices from the history database, then filter them,
* adding to a hash map of report-ids */
devices = fwupd_client_get_history (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
report_map = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) g_ptr_array_unref);
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
FwupdRelease *rel = fwupd_device_get_release_default (dev);
const gchar *remote_id;
GPtrArray *devices_tmp;
g_autoptr(FwupdRemote) remote = NULL;
/* filter, if not forcing */
if (!fu_util_filter_device (priv, dev))
continue;
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_REPORTED))
continue;
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
continue;
}
/* only send success and failure */
if (fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_FAILED &&
fwupd_device_get_update_state (dev) != FWUPD_UPDATE_STATE_SUCCESS) {
g_debug ("ignoring %s with UpdateState %s",
fwupd_device_get_id (dev),
fwupd_update_state_to_string (fwupd_device_get_update_state (dev)));
continue;
}
/* find the RemoteURI to use for the device */
remote_id = fwupd_release_get_remote_id (rel);
if (remote_id == NULL) {
g_debug ("%s has no RemoteID", fwupd_device_get_id (dev));
continue;
}
remote = fwupd_client_get_remote_by_id (priv->client, remote_id,
NULL, error);
if (remote == NULL)
return FALSE;
if (fwupd_remote_get_report_uri (remote) == NULL) {
g_debug ("%s has no RemoteURI", fwupd_remote_get_report_uri (remote));
continue;
}
/* add this to the hash map */
devices_tmp = g_hash_table_lookup (report_map, remote_id);
if (devices_tmp == NULL) {
devices_tmp = g_ptr_array_new ();
g_hash_table_insert (report_map, g_strdup (remote_id), devices_tmp);
}
g_debug ("using %s for %s", remote_id, fwupd_device_get_id (dev));
g_ptr_array_add (devices_tmp, dev);
}
/* nothing to report */
if (g_hash_table_size (report_map) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"No reports require uploading");
return FALSE;
}
/* process each uri */
ids = g_hash_table_get_keys (report_map);
for (GList *l = ids; l != NULL; l = l->next) {
const gchar *id = l->data;
GPtrArray *devices_tmp = g_hash_table_lookup (report_map, id);
if (!fu_util_report_history_for_remote (priv, id, devices_tmp, error))
return FALSE;
/* mark each device as reported */
for (guint i = 0; i < devices_tmp->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices_tmp, i);
g_debug ("setting flag on %s", fwupd_device_get_id (dev));
if (!fwupd_client_modify_device (priv->client,
fwupd_device_get_id (dev),
"Flags", "reported",
NULL, error))
return FALSE;
}
}
/* TRANSLATORS: success message -- where the user has uploaded
* success and/or failure reports to the remote server */
g_string_append_printf (str, ngettext ("Successfully uploaded %u report",
"Successfully uploaded %u reports",
g_hash_table_size (report_map)),
g_hash_table_size (report_map));
g_print ("%s\n", str->str);
return TRUE;
}
static gboolean
fu_util_get_history (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
g_autoptr(GNode) root = g_node_new (NULL);
g_autofree gchar *title = fu_util_get_tree_title (priv);
/* get all devices from the history database */
devices = fwupd_client_get_history (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
/* show each device */
for (guint i = 0; i < devices->len; i++) {
g_autoptr(GPtrArray) rels = NULL;
FwupdDevice *dev = g_ptr_array_index (devices, i);
FwupdRelease *rel;
const gchar *remote;
GNode *child;
g_autoptr(GError) error_local = NULL;
if (!fu_util_filter_device (priv, dev))
continue;
child = g_node_append_data (root, dev);
rel = fwupd_device_get_release_default (dev);
if (rel == NULL)
continue;
remote = fwupd_release_get_remote_id (rel);
/* doesn't actually map to remote */
if (remote == NULL) {
g_node_append_data (child, rel);
continue;
}
/* try to lookup releases from client */
rels = fwupd_client_get_releases (priv->client,
fwupd_device_get_id (dev),
NULL, &error_local);
if (rels == NULL) {
g_debug ("failed to get releases for %s: %s",
fwupd_device_get_id (dev),
error_local->message);
g_node_append_data (child, rel);
continue;
}
/* map to a release in client */
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel2 = g_ptr_array_index (rels, j);
if (g_strcmp0 (remote,
fwupd_release_get_remote_id (rel2)) != 0)
continue;
if (g_strcmp0 (fwupd_release_get_version (rel),
fwupd_release_get_version (rel2)) != 0)
continue;
g_node_append_data (child, g_object_ref (rel2));
rel = NULL;
break;
}
/* didn't match anything */
if (rels->len == 0 || rel != NULL) {
g_node_append_data (child, rel);
continue;
}
}
fu_util_print_tree (root, title);
return TRUE;
}
static FwupdDevice *
fu_util_get_device_by_id (FuUtilPrivate *priv, const gchar *id, GError **error)
{
if (fwupd_guid_is_valid (id)) {
g_autoptr(GPtrArray) devices = NULL;
devices = fwupd_client_get_devices_by_guid (priv->client, id,
NULL, error);
if (devices == NULL)
return NULL;
return fu_util_prompt_for_device (priv, devices, error);
}
/* did this look like a GUID? */
for (guint i = 0; id[i] != '\0'; i++) {
if (id[i] == '-') {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return NULL;
}
}
return fwupd_client_get_device_by_id (priv->client, id, NULL, error);
}
static FwupdDevice *
fu_util_get_device_or_prompt (FuUtilPrivate *priv, gchar **values, GError **error)
{
FwupdDevice *dev = NULL;
g_autoptr(GPtrArray) devices = NULL;
/* get device to use */
if (g_strv_length (values) >= 1) {
g_autoptr(GError) error_local = NULL;
if (g_strv_length (values) > 1) {
for (guint i = 1; i < g_strv_length (values); i++)
g_debug ("Ignoring extra input %s", values[i]);
}
dev = fu_util_get_device_by_id (priv, values[0], &error_local);
if (dev != NULL)
return dev;
g_print ("%s\n", error_local->message);
}
/* get all devices from daemon */
devices = fwupd_client_get_devices (priv->client, NULL, error);
if (devices == NULL)
return NULL;
return fu_util_prompt_for_device (priv, devices, error);
}
static gboolean
fu_util_clear_results (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FwupdDevice) dev = NULL;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
return fwupd_client_clear_results (priv->client, fwupd_device_get_id (dev), NULL, error);
}
static gboolean
fu_util_clear_offline (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FuHistory) history = fu_history_new ();
return fu_history_remove_all_with_state (history, FWUPD_UPDATE_STATE_PENDING, error);
}
static gboolean
fu_util_verify_update (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FwupdDevice) dev = NULL;
priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
if (!fwupd_client_verify_update (priv->client, fwupd_device_get_id (dev), NULL, error)) {
g_prefix_error (error, "failed to verify update %s: ", fu_device_get_name (dev));
return FALSE;
}
/* TRANSLATORS: success message when user refreshes device checksums */
g_print ("%s\n", _("Successfully updated device checksums"));
return TRUE;
}
static gboolean
fu_util_download_metadata_enable_lvfs (FuUtilPrivate *priv, GError **error)
{
g_autoptr(FwupdRemote) remote = NULL;
/* is the LVFS available but disabled? */
remote = fwupd_client_get_remote_by_id (priv->client, "lvfs", NULL, error);
if (remote == NULL)
return TRUE;
g_print ("%s\n%s\n%s [Y|n]: ",
/* TRANSLATORS: explain why no metadata available */
_("No remotes are currently enabled so no metadata is available."),
/* TRANSLATORS: explain why no metadata available */
_("Metadata can be obtained from the Linux Vendor Firmware Service."),
/* TRANSLATORS: Turn on the remote */
_("Enable this remote?"));
if (!fu_util_prompt_for_boolean (TRUE))
return TRUE;
if (!fu_util_modify_remote (priv, "lvfs", "Enabled", "true", error))
return FALSE;
/* refresh the newly-enabled remote */
return fwupd_client_refresh_remote (priv->client, remote,
priv->cancellable, error);
}
static gboolean
fu_util_check_oldest_remote (FuUtilPrivate *priv, guint64 *age_oldest, GError **error)
{
g_autoptr(GPtrArray) remotes = NULL;
gboolean checked = FALSE;
/* get the age of the oldest enabled remotes */
remotes = fwupd_client_get_remotes (priv->client, NULL, error);
if (remotes == NULL)
return FALSE;
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
if (!fwupd_remote_get_enabled (remote))
continue;
if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
continue;
checked = TRUE;
if (fwupd_remote_get_age (remote) > *age_oldest)
*age_oldest = fwupd_remote_get_age (remote);
}
if (!checked) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: error message for a user who ran fwupdmgr refresh recently but no remotes */
"No remotes enabled.");
return FALSE;
}
return TRUE;
}
static gboolean
fu_util_download_metadata (FuUtilPrivate *priv, GError **error)
{
gboolean download_remote_enabled = FALSE;
guint devices_supported_cnt = 0;
g_autoptr(GPtrArray) devs = NULL;
g_autoptr(GPtrArray) remotes = NULL;
g_autoptr(GString) str = g_string_new (NULL);
/* metadata refreshed recently */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
guint64 age_oldest = 0;
const guint64 age_limit_hours = 24;
if (!fu_util_check_oldest_remote (priv, &age_oldest, error))
return FALSE;
if (age_oldest < 60 * 60 * age_limit_hours) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: error message for a user who ran fwupdmgr refresh recently
%1 is an already translated timestamp such as 6 hours or 15 seconds */
"Firmware metadata last refresh: %s ago. "
"Use --force to refresh again.",
fu_util_time_to_str (age_oldest));
return FALSE;
}
}
remotes = fwupd_client_get_remotes (priv->client, NULL, error);
if (remotes == NULL)
return FALSE;
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
if (!fwupd_remote_get_enabled (remote))
continue;
if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD)
continue;
download_remote_enabled = TRUE;
g_print ("%s %s\n", _("Updating"), fwupd_remote_get_id (remote));
if (!fwupd_client_refresh_remote (priv->client, remote,
priv->cancellable, error))
return FALSE;
}
/* no web remote is declared; try to enable LVFS */
if (!download_remote_enabled) {
/* we don't want to ask anything */
if (priv->no_metadata_check) {
g_debug ("skipping metadata check");
return TRUE;
}
if (!fu_util_download_metadata_enable_lvfs (priv, error))
return FALSE;
}
/* get devices from daemon */
devs = fwupd_client_get_devices (priv->client, NULL, error);
if (devs == NULL)
return FALSE;
/* get results */
for (guint i = 0; i < devs->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devs, i);
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED))
devices_supported_cnt++;
}
/* TRANSLATORS: success message -- where 'metadata' is information
* about available firmware on the remote server */
g_string_append (str, _("Successfully downloaded new metadata: "));
/* TRANSLATORS: how many local devices can expect updates now */
g_string_append_printf (str, ngettext ("%u local device supported",
"%u local devices supported",
devices_supported_cnt),
devices_supported_cnt);
g_print ("%s\n", str->str);
return TRUE;
}
static gboolean
fu_util_refresh (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) == 0)
return fu_util_download_metadata (priv, error);
if (g_strv_length (values) != 3) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* open file */
if (!fwupd_client_update_metadata (priv->client,
values[2],
values[0],
values[1],
NULL,
error))
return FALSE;
/* TRANSLATORS: success message -- the user can do this by-hand too */
g_print ("%s\n", _("Successfully refreshed metadata manually"));
return TRUE;
}
static gboolean
fu_util_get_results (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autofree gchar *tmp = NULL;
g_autoptr(FwupdDevice) dev = NULL;
g_autoptr(FwupdDevice) rel = NULL;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
rel = fwupd_client_get_results (priv->client, fwupd_device_get_id (dev), NULL, error);
if (rel == NULL)
return FALSE;
tmp = fu_util_device_to_string (rel, 0);
g_print ("%s", tmp);
return TRUE;
}
static gboolean
fu_util_get_releases (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FwupdDevice) dev = NULL;
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GNode) root = g_node_new (NULL);
g_autofree gchar *title = fu_util_get_tree_title (priv);
priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
/* get the releases for this device */
rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev), NULL, error);
if (rels == NULL)
return FALSE;
if (rels->len == 0) {
/* TRANSLATORS: no repositories to download from */
g_print ("%s\n", _("No releases available"));
return TRUE;
}
for (guint i = 0; i < rels->len; i++) {
FwupdRelease *rel = g_ptr_array_index (rels, i);
g_node_append_data (root, rel);
}
fu_util_print_tree (root, title);
return TRUE;
}
static FwupdRelease *
fu_util_prompt_for_release (FuUtilPrivate *priv, GPtrArray *rels, GError **error)
{
FwupdRelease *rel;
guint idx;
/* nothing */
if (rels->len == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No supported releases");
return NULL;
}
/* exactly one */
if (rels->len == 1) {
rel = g_ptr_array_index (rels, 0);
return g_object_ref (rel);
}
/* TRANSLATORS: get interactive prompt */
g_print ("%s\n", _("Choose a release:"));
/* TRANSLATORS: this is to abort the interactive prompt */
g_print ("0.\t%s\n", _("Cancel"));
for (guint i = 0; i < rels->len; i++) {
const gchar *desc_tmp;
g_autofree gchar *desc = NULL;
rel = g_ptr_array_index (rels, i);
/* no description provided */
desc_tmp = fwupd_release_get_description (rel);
if (desc_tmp == NULL) {
g_print ("%u.\t%s\n", i + 1, fwupd_release_get_version (rel));
continue;
}
/* remove markup, and fall back if we fail */
desc = fu_util_convert_description (desc_tmp, NULL);
if (desc == NULL)
desc = g_strdup (desc_tmp);
g_print ("%u.\t%s (%s)\n", i + 1, fwupd_release_get_version (rel), desc);
}
idx = fu_util_prompt_for_number (rels->len);
if (idx == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Request canceled");
return NULL;
}
rel = g_ptr_array_index (rels, idx - 1);
return g_object_ref (rel);
}
static gboolean
fu_util_verify (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FwupdDevice) dev = NULL;
priv->filter_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
if (!fwupd_client_verify (priv->client, fwupd_device_get_id (dev), NULL, error)) {
g_prefix_error (error, "failed to verify %s: ", fu_device_get_name (dev));
return FALSE;
}
/* TRANSLATORS: success message when user verified device checksums */
g_print ("%s\n", _("Successfully verified device checksums"));
return TRUE;
}
static gboolean
fu_util_unlock (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(FwupdDevice) dev = NULL;
priv->filter_include |= FWUPD_DEVICE_FLAG_LOCKED;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN))
priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN;
if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT))
priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT;
if (!fwupd_client_unlock (priv->client, fwupd_device_get_id (dev), NULL, error))
return FALSE;
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_perhaps_refresh_remotes (FuUtilPrivate *priv, GError **error)
{
guint64 age_oldest = 0;
const guint64 age_limit_days = 30;
/* we don't want to ask anything */
if (priv->no_metadata_check) {
g_debug ("skipping metadata check");
return TRUE;
}
if (!fu_util_check_oldest_remote (priv, &age_oldest, NULL))
return TRUE;
/* metadata is new enough */
if (age_oldest < 60 * 60 * 24 * age_limit_days)
return TRUE;
/* ask for permission */
if (!priv->assume_yes) {
/* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */
g_print (ngettext("Firmware metadata has not been updated for %u"
" day and may not be up to date.",
"Firmware metadata has not been updated for %u"
" days and may not be up to date.",
(gint) age_limit_days), (guint) age_limit_days);
g_print ("\n\n");
g_print ("%s (%s) [y|N]: ",
/* TRANSLATORS: ask the user if we can update the metadata */
_("Update now?"),
/* TRANSLATORS: metadata is downloaded from the Internet */
_("Requires internet connection"));
if (!fu_util_prompt_for_boolean (FALSE))
return TRUE;
}
/* downloads new metadata */
return fu_util_download_metadata (priv, error);
}
static gboolean
fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
gboolean supported = FALSE;
g_autoptr(GNode) root = g_node_new (NULL);
g_autofree gchar *title = fu_util_get_tree_title (priv);
gboolean no_updates_header = FALSE;
gboolean latest_header = FALSE;
/* are the remotes very old */
if (!fu_util_perhaps_refresh_remotes (priv, error))
return FALSE;
/* handle both forms */
if (g_strv_length (values) == 0) {
devices = fwupd_client_get_devices (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
} else if (g_strv_length (values) == 1) {
FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error);
if (device == NULL)
return FALSE;
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
g_ptr_array_add (devices, device);
} else {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GError) error_local = NULL;
GNode *child;
/* not going to have results, so save a D-Bus round-trip */
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
continue;
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
if (!no_updates_header) {
/* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
g_printerr ("%s\n", _("Devices with no available firmware updates: "));
no_updates_header = TRUE;
}
g_printerr (" • %s\n", fwupd_device_get_name (dev));
continue;
}
if (!fu_util_filter_device (priv, dev))
continue;
supported = TRUE;
/* get the releases for this device and filter for validity */
rels = fwupd_client_get_upgrades (priv->client,
fwupd_device_get_id (dev),
NULL, &error_local);
if (rels == NULL) {
if (!latest_header) {
/* TRANSLATORS: message letting the user know no device upgrade available */
g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
latest_header = TRUE;
}
g_printerr (" • %s\n", fwupd_device_get_name (dev));
/* discard the actual reason from user, but leave for debugging */
g_debug ("%s", error_local->message);
continue;
}
child = g_node_append_data (root, dev);
/* add all releases */
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel = g_ptr_array_index (rels, j);
g_node_append_data (child, g_object_ref (rel));
}
}
/* nag? */
if (!fu_util_perhaps_show_unreported (priv, error))
return FALSE;
/* no devices supported by LVFS or all are filtered */
if (!supported) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No updatable devices");
return FALSE;
}
/* no updates available */
if (g_node_n_nodes (root, G_TRAVERSE_ALL) <= 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No updates available for remaining devices");
return FALSE;
}
fu_util_print_tree (root, title);
/* success */
return TRUE;
}
static gboolean
fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GNode) root = g_node_new (NULL);
g_autoptr(GPtrArray) remotes = NULL;
g_autofree gchar *title = fu_util_get_tree_title (priv);
remotes = fwupd_client_get_remotes (priv->client, NULL, error);
if (remotes == NULL)
return FALSE;
if (remotes->len == 0) {
/* TRANSLATORS: no repositories to download from */
g_print ("%s\n", _("No remotes available"));
return TRUE;
}
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote_tmp = g_ptr_array_index (remotes, i);
g_node_append_data (root, remote_tmp);
}
fu_util_print_tree (root, title);
return TRUE;
}
static gboolean
fu_util_update_device_with_release (FuUtilPrivate *priv,
FwupdDevice *dev,
FwupdRelease *rel,
GError **error)
{
if (!priv->no_safety_check && !priv->assume_yes) {
if (!fu_util_prompt_warning (dev,
fu_util_get_tree_title (priv),
error))
return FALSE;
}
return fwupd_client_install_release (priv->client, dev, rel, priv->flags,
priv->cancellable, error);
}
static gboolean
fu_util_maybe_send_reports (FuUtilPrivate *priv, const gchar *remote_id,
GError **error)
{
g_autoptr(FwupdRemote) remote = NULL;
g_autoptr(GError) error_local = NULL;
if (remote_id == NULL) {
g_debug ("not sending reports, no remote");
return TRUE;
}
remote = fwupd_client_get_remote_by_id (priv->client,
remote_id,
NULL,
error);
if (remote == NULL)
return FALSE;
if (fwupd_remote_get_automatic_reports (remote)) {
if (!fu_util_report_history (priv, NULL, &error_local))
if (!g_error_matches (error_local,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED))
g_warning ("%s", error_local->message);
}
return TRUE;
}
static gboolean
fu_util_update_all (FuUtilPrivate *priv, GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
gboolean supported = FALSE;
gboolean no_updates_header = FALSE;
gboolean latest_header = FALSE;
/* get devices from daemon */
devices = fwupd_client_get_devices (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
priv->current_operation = FU_UTIL_OPERATION_UPDATE;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
g_ptr_array_sort (devices, fu_util_sort_devices_by_flags_cb);
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *dev = g_ptr_array_index (devices, i);
FwupdRelease *rel;
const gchar *remote_id;
g_autofree gchar *upgrade_str = NULL;
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GError) error_local = NULL;
/* not going to have results, so save a D-Bus round-trip */
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE))
continue;
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_SUPPORTED)) {
if (!no_updates_header) {
/* TRANSLATORS: message letting the user know no device upgrade available due to missing on LVFS */
g_printerr ("%s\n", _("Devices with no available firmware updates: "));
no_updates_header = TRUE;
}
g_printerr (" • %s\n", fwupd_device_get_name (dev));
continue;
}
if (!fu_util_filter_device (priv, dev))
continue;
supported = TRUE;
/* get the releases for this device and filter for validity */
rels = fwupd_client_get_upgrades (priv->client,
fwupd_device_get_id (dev),
NULL, &error_local);
if (rels == NULL) {
if (!latest_header) {
/* TRANSLATORS: message letting the user know no device upgrade available */
g_printerr ("%s\n", _("Devices with the latest available firmware version:"));
latest_header = TRUE;
}
g_printerr (" • %s\n", fwupd_device_get_name (dev));
/* discard the actual reason from user, but leave for debugging */
g_debug ("%s", error_local->message);
continue;
}
rel = g_ptr_array_index (rels, 0);
/* TRANSLATORS: message letting the user know an upgrade is available
* %1 is the device name and %2 and %3 are version strings */
upgrade_str = g_strdup_printf (_("Upgrade available for %s from %s to %s"),
fwupd_device_get_name (dev),
fwupd_device_get_version (dev),
fwupd_release_get_version (rel));
g_print ("%s\n", upgrade_str);
if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE;
fu_util_display_current_message (priv);
/* send report if we're supposed to */
remote_id = fwupd_release_get_remote_id (rel);
if (!fu_util_maybe_send_reports (priv, remote_id, error))
return FALSE;
}
/* no devices supported by LVFS or all are filtered */
if (!supported) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No updatable devices");
return FALSE;
}
/* we don't want to ask anything */
if (priv->no_reboot_check) {
g_debug ("skipping reboot check");
return TRUE;
}
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_update_by_id (FuUtilPrivate *priv, const gchar *device_id, GError **error)
{
FwupdRelease *rel;
const gchar *remote_id;
g_autoptr(FwupdDevice) dev = NULL;
g_autoptr(GPtrArray) rels = NULL;
/* do not allow a partial device-id */
dev = fu_util_get_device_by_id (priv, device_id, error);
if (dev == NULL)
return FALSE;
/* get devices from daemon */
priv->current_operation = FU_UTIL_OPERATION_UPDATE;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
/* get the releases for this device and filter for validity */
rels = fwupd_client_get_upgrades (priv->client,
fwupd_device_get_id (dev),
NULL, error);
if (rels == NULL)
return FALSE;
rel = g_ptr_array_index (rels, 0);
if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE;
fu_util_display_current_message (priv);
/* send report if we're supposed to */
remote_id = fwupd_release_get_remote_id (rel);
if (!fu_util_maybe_send_reports (priv, remote_id, error))
return FALSE;
/* we don't want to ask anything */
if (priv->no_reboot_check) {
g_debug ("skipping reboot check");
return TRUE;
}
/* the update needs the user to restart the computer */
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_update (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"--allow-older is not supported for this command");
return FALSE;
}
if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"--allow-reinstall is not supported for this command");
return FALSE;
}
if (g_strv_length (values) == 0)
return fu_util_update_all (priv, error);
if (g_strv_length (values) == 1)
return fu_util_update_by_id (priv, values[0], error);
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
static gboolean
fu_util_remote_modify (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) < 3) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
if (!fu_util_modify_remote (priv, values[0], values[1], values[2], error))
return FALSE;
/* TRANSLATORS: success message for a per-remote setting change */
g_print ("%s\n", _("Successfully modified remote"));
return TRUE;
}
static gboolean
fu_util_remote_enable (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) != 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
if (!fu_util_modify_remote (priv, values[0], "Enabled", "true", error))
return FALSE;
/* TRANSLATORS: success message */
g_print ("%s\n", _("Successfully enabled remote"));
return TRUE;
}
static gboolean
fu_util_remote_disable (FuUtilPrivate *priv, gchar **values, GError **error)
{
if (g_strv_length (values) != 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
if (!fu_util_modify_remote (priv, values[0], "Enabled", "false", error))
return FALSE;
/* TRANSLATORS: success message */
g_print ("%s\n", _("Successfully disabled remote"));
return TRUE;
}
static gboolean
fu_util_downgrade (FuUtilPrivate *priv, gchar **values, GError **error)
{
const gchar *remote_id;
g_autoptr(FwupdDevice) dev = NULL;
g_autoptr(FwupdRelease) rel = NULL;
g_autoptr(GPtrArray) rels = NULL;
if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"--allow-reinstall is not supported for this command");
return FALSE;
}
priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
/* get the releases for this device and filter for validity */
rels = fwupd_client_get_downgrades (priv->client,
fwupd_device_get_id (dev),
NULL, error);
if (rels == NULL) {
/* TRANSLATORS: message letting the user know no device downgrade available
* %1 is the device name */
g_autofree gchar *downgrade_str = g_strdup_printf (_("No downgrades for %s"),
fwupd_device_get_name (dev));
g_prefix_error (error, "%s: ", downgrade_str);
return FALSE;
}
/* get the chosen release */
rel = fu_util_prompt_for_release (priv, rels, error);
if (rel == NULL)
return FALSE;
/* update the console if composite devices are also updated */
priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE;
fu_util_display_current_message (priv);
/* send report if we're supposed to */
remote_id = fwupd_release_get_remote_id (rel);
if (!fu_util_maybe_send_reports (priv, remote_id, error))
return FALSE;
return TRUE;
}
static gboolean
fu_util_reinstall (FuUtilPrivate *priv, gchar **values, GError **error)
{
const gchar *remote_id;
g_autoptr(FwupdRelease) rel = NULL;
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(FwupdDevice) dev = NULL;
priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
/* try to lookup/match release from client */
rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev),
NULL, error);
if (rels == NULL)
return FALSE;
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
if (fu_common_vercmp_full (fwupd_release_get_version (rel_tmp),
fu_device_get_version (dev),
fwupd_device_get_version_format (dev)) == 0) {
rel = g_object_ref (rel_tmp);
break;
}
}
if (rel == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Unable to locate release for %s version %s",
fu_device_get_name (dev),
fu_device_get_version (dev));
return FALSE;
}
/* update the console if composite devices are also updated */
priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE;
fu_util_display_current_message (priv);
/* send report if we're supposed to */
remote_id = fwupd_release_get_remote_id (rel);
if (!fu_util_maybe_send_reports (priv, remote_id, error))
return FALSE;
/* we don't want to ask anything */
if (priv->no_reboot_check) {
g_debug ("skipping reboot check");
return TRUE;
}
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_switch_branch_warning (FuUtilPrivate *priv,
FwupdDevice *dev,
FwupdRelease *rel,
GError **error)
{
const gchar *desc_markup = NULL;
g_autofree gchar *desc_plain = NULL;
g_autoptr(GString) desc_full = g_string_new (NULL);
/* warn the user if the vendor is different */
if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) {
/* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */
g_string_append_printf (desc_full, _("The firmware from %s is not "
"supplied by %s, the hardware vendor."),
fwupd_release_get_vendor (rel),
fwupd_device_get_vendor (dev));
g_string_append (desc_full, "\n\n");
/* TRANSLATORS: %1 is the device vendor name */
g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, "
"and installing this release may void any warranty "
"with %s."),
fwupd_device_get_vendor (dev));
g_string_append (desc_full, "\n\n");
}
/* from the <description> in the AppStream data */
desc_markup = fwupd_release_get_description (rel);
if (desc_markup == NULL)
return TRUE;
desc_plain = fu_util_convert_description (desc_markup, error);
if (desc_plain == NULL)
return FALSE;
g_string_append (desc_full, desc_plain);
/* show and ask user to confirm */
fu_util_warning_box (desc_full->str, 80);
if (!priv->assume_yes) {
/* ask for permission */
g_print ("\n%s [y|N]: ",
/* TRANSLATORS: should the branch be changed */
_("Do you understand the consequences of changing the firmware branch?"));
if (!fu_util_prompt_for_boolean (FALSE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Declined branch switch");
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_util_switch_branch (FuUtilPrivate *priv, gchar **values, GError **error)
{
const gchar *remote_id;
const gchar *branch;
g_autoptr(FwupdRelease) rel = NULL;
g_autoptr(GPtrArray) rels = NULL;
g_autoptr(GPtrArray) branches = g_ptr_array_new_with_free_func (g_free);
g_autoptr(FwupdDevice) dev = NULL;
/* find the device and check it has multiple branches */
priv->filter_include |= FWUPD_DEVICE_FLAG_SUPPORTED;
dev = fu_util_get_device_or_prompt (priv, values, error);
if (dev == NULL)
return FALSE;
if (!fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Multiple branches not available");
return FALSE;
}
/* get all releases, including the alternate branch versions */
rels = fwupd_client_get_releases (priv->client, fwupd_device_get_id (dev),
NULL, error);
if (rels == NULL)
return FALSE;
/* get all the unique branches */
for (guint i = 0; i < rels->len; i++) {
FwupdRelease *rel_tmp = g_ptr_array_index (rels, i);
const gchar *branch_tmp = fu_util_release_get_branch (rel_tmp);
if (g_ptr_array_find_with_equal_func (branches, branch_tmp,
g_str_equal, NULL))
continue;
g_ptr_array_add (branches, g_strdup (branch_tmp));
}
/* branch name is optional */
if (g_strv_length (values) > 1) {
branch = values[1];
} else if (branches->len == 1) {
branch = g_ptr_array_index (branches, 0);
} else {
guint idx;
/* TRANSLATORS: get interactive prompt, where branch is the
* supplier of the firmware, e.g. "non-free" or "free" */
g_print ("%s\n", _("Choose a branch:"));
/* TRANSLATORS: this is to abort the interactive prompt */
g_print ("0.\t%s\n", _("Cancel"));
for (guint i = 0; i < branches->len; i++) {
const gchar *branch_tmp = g_ptr_array_index (branches, i);
g_print ("%u.\t%s\n", i + 1, branch_tmp);
}
idx = fu_util_prompt_for_number (branches->len);
if (idx == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"Request canceled");
return FALSE;
}
branch = g_ptr_array_index (branches, idx - 1);
}
/* sanity check */
if (g_strcmp0 (branch, fu_device_get_branch (dev)) == 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"Device %s is already on branch %s",
fu_device_get_name (dev),
branch);
return FALSE;
}
/* the releases are ordered by version */
for (guint j = 0; j < rels->len; j++) {
FwupdRelease *rel_tmp = g_ptr_array_index (rels, j);
if (g_strcmp0 (fwupd_release_get_branch (rel_tmp), branch) == 0) {
rel = g_object_ref (rel_tmp);
break;
}
}
if (rel == NULL) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"No releases for branch %s",
branch);
return FALSE;
}
/* we're switching branch */
if (!fu_util_switch_branch_warning (priv, dev, rel, error))
return FALSE;
/* update the console if composite devices are also updated */
priv->current_operation = FU_UTIL_OPERATION_INSTALL;
g_signal_connect (priv->client, "device-changed",
G_CALLBACK (fu_util_update_device_changed_cb), priv);
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
if (!fu_util_update_device_with_release (priv, dev, rel, error))
return FALSE;
fu_util_display_current_message (priv);
/* send report if we're supposed to */
remote_id = fwupd_release_get_remote_id (rel);
if (!fu_util_maybe_send_reports (priv, remote_id, error))
return FALSE;
/* we don't want to ask anything */
if (priv->no_reboot_check) {
g_debug ("skipping reboot check");
return TRUE;
}
return fu_util_prompt_complete (priv->completion_flags, TRUE, error);
}
static gboolean
fu_util_activate (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) devices = NULL;
gboolean has_pending = FALSE;
/* handle both forms */
if (g_strv_length (values) == 0) {
/* activate anything with _NEEDS_ACTIVATION */
devices = fwupd_client_get_devices (priv->client, NULL, error);
if (devices == NULL)
return FALSE;
for (guint i = 0; i < devices->len; i++) {
FuDevice *device = g_ptr_array_index (devices, i);
if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) {
has_pending = TRUE;
break;
}
}
} else if (g_strv_length (values) == 1) {
FwupdDevice *device = fu_util_get_device_by_id (priv, values[0], error);
if (device == NULL)
return FALSE;
devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
g_ptr_array_add (devices, device);
if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
has_pending = TRUE;
} else {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* nothing to do */
if (!has_pending) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No firmware to activate");
return FALSE;
}
/* activate anything with _NEEDS_ACTIVATION */
/* order by device priority */
g_ptr_array_sort (devices, fu_util_device_order_sort_cb);
for (guint i = 0; i < devices->len; i++) {
FwupdDevice *device = g_ptr_array_index (devices, i);
if (!fu_util_filter_device (priv, device))
continue;
if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION))
continue;
/* TRANSLATORS: shown when shutting down to switch to the new version */
g_print ("%s %s…\n", _("Activating firmware update for"),
fwupd_device_get_name (device));
if (!fwupd_client_activate (priv->client, NULL,
fwupd_device_get_id (device), error))
return FALSE;
}
/* TRANSLATORS: success message -- where activation is making the new
* firmware take effect, usually after updating offline */
g_print ("%s\n", _("Successfully activated all devices"));
return TRUE;
}
static gboolean
fu_util_set_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_auto(GStrv) checksums = NULL;
/* check args */
if (g_strv_length (values) != 1) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments: list of checksums expected");
return FALSE;
}
/* call into daemon */
checksums = g_strsplit (values[0], ",", -1);
return fwupd_client_set_approved_firmware (priv->client,
checksums,
priv->cancellable,
error);
}
static gboolean
fu_util_get_approved_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_auto(GStrv) checksums = NULL;
/* check args */
if (g_strv_length (values) != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments: none expected");
return FALSE;
}
/* call into daemon */
checksums = fwupd_client_get_approved_firmware (priv->client,
priv->cancellable,
error);
if (checksums == NULL)
return FALSE;
if (g_strv_length (checksums) == 0) {
/* TRANSLATORS: approved firmware has been checked by
* the domain administrator */
g_print ("%s\n", _("There is no approved firmware."));
} else {
/* TRANSLATORS: approved firmware has been checked by
* the domain administrator */
g_print ("%s\n", ngettext ("Approved firmware:",
"Approved firmware:",
g_strv_length (checksums)));
for (guint i = 0; checksums[i] != NULL; i++)
g_print (" * %s\n", checksums[i]);
}
return TRUE;
}
static gboolean
fu_util_modify_config (FuUtilPrivate *priv, gchar **values, GError **error)
{
/* check args */
if (g_strv_length (values) != 2) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments: KEY VALUE expected");
return FALSE;
}
if (!fwupd_client_modify_config (priv->client,
values[0], values[1],
priv->cancellable,
error))
return FALSE;
if (!priv->assume_yes) {
g_print ("%s [Y|n]: ",
/* TRANSLATORS: configuration changes only take effect on restart */
_("Restart the daemon to make the change effective?"));
if (!fu_util_prompt_for_boolean (FALSE))
return TRUE;
}
#ifdef HAVE_SYSTEMD
if (!fu_systemd_unit_stop (fu_util_get_systemd_unit (), error))
return FALSE;
#endif
/* TRANSLATORS: success message -- a per-system setting value */
g_print ("%s\n", _("Successfully modified configuration value"));
return TRUE;
}
static FwupdRemote *
fu_util_get_remote_with_security_report_uri (FuUtilPrivate *priv, GError **error)
{
g_autoptr(GPtrArray) remotes = NULL;
/* get all remotes */
remotes = fwupd_client_get_remotes (priv->client, NULL, error);
if (remotes == NULL)
return NULL;
for (guint i = 0; i < remotes->len; i++) {
FwupdRemote *remote = g_ptr_array_index (remotes, i);
if (!fwupd_remote_get_enabled (remote))
continue;
if (fwupd_remote_get_security_report_uri (remote) != NULL)
return g_object_ref (remote);
}
/* failed */
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"No remotes specified SecurityReportURI");
return NULL;
}
static gboolean
fu_util_upload_security (FuUtilPrivate *priv, GPtrArray *attrs, GError **error)
{
GHashTableIter iter;
const gchar *key;
const gchar *value;
g_autofree gchar *data = NULL;
g_autofree gchar *sig = NULL;
g_autoptr(FwupdRemote) remote = NULL;
g_autoptr(GBytes) upload_response = NULL;
g_autoptr(GError) error_local = NULL;
g_autoptr(GHashTable) metadata = NULL;
g_autoptr(JsonBuilder) builder = NULL;
g_autoptr(JsonGenerator) json_generator = NULL;
g_autoptr(JsonNode) json_root = NULL;
/* can we find a remote with a security attr */
remote = fu_util_get_remote_with_security_report_uri (priv, &error_local);
if (remote == NULL) {
g_debug ("failed to find suitable remote: %s", error_local->message);
return TRUE;
}
if (!priv->assume_yes &&
!fwupd_remote_get_automatic_security_reports (remote)) {
g_autofree gchar *tmp = NULL;
/* TRANSLATORS: ask the user to share, %s is something like:
* "Linux Vendor Firmware Service" */
tmp = g_strdup_printf ("Upload these anonymous results to the %s to help other users?",
fwupd_remote_get_title (remote));
g_print ("\n%s [y|N]: ", tmp);
if (!fu_util_prompt_for_boolean (FALSE)) {
g_print ("%s [Y|n]: ",
/* TRANSLATORS: stop nagging the user */
_("Ask again next time?"));
if (!fu_util_prompt_for_boolean (TRUE)) {
if (!fwupd_client_modify_remote (priv->client,
fwupd_remote_get_id (remote),
"SecurityReportURI", "",
NULL, error))
return FALSE;
}
return TRUE;
}
}
/* get metadata */
metadata = fwupd_client_get_report_metadata (priv->client,
priv->cancellable,
error);
if (metadata == NULL)
return FALSE;
/* create header */
builder = json_builder_new ();
json_builder_begin_object (builder);
json_builder_set_member_name (builder, "ReportVersion");
json_builder_add_int_value (builder, 2);
json_builder_set_member_name (builder, "MachineId");
json_builder_add_string_value (builder, fwupd_client_get_host_machine_id (priv->client));
/* this is system metadata not stored in the database */
json_builder_set_member_name (builder, "Metadata");
json_builder_begin_object (builder);
g_hash_table_iter_init (&iter, metadata);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
json_builder_set_member_name (builder, key);
json_builder_add_string_value (builder, value);
}
json_builder_set_member_name (builder, "HostSecurityId");
json_builder_add_string_value (builder, fwupd_client_get_host_security_id (priv->client));
json_builder_end_object (builder);
/* attrs */
json_builder_set_member_name (builder, "SecurityAttributes");
json_builder_begin_array (builder);
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
json_builder_begin_object (builder);
fwupd_security_attr_to_json (attr, builder);
json_builder_end_object (builder);
}
json_builder_end_array (builder);
json_builder_end_object (builder);
/* export as a string */
json_root = json_builder_get_root (builder);
json_generator = json_generator_new ();
json_generator_set_pretty (json_generator, TRUE);
json_generator_set_root (json_generator, json_root);
data = json_generator_to_data (json_generator, NULL);
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"Failed to convert to JSON string");
return FALSE;
}
/* self sign data */
if (priv->sign) {
sig = fwupd_client_self_sign (priv->client, data,
FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP,
priv->cancellable, error);
if (sig == NULL)
return FALSE;
}
/* ask for permission */
if (!priv->assume_yes &&
!fwupd_remote_get_automatic_security_reports (remote)) {
fu_util_print_data (_("Target"), fwupd_remote_get_security_report_uri (remote));
fu_util_print_data (_("Payload"), data);
if (sig != NULL)
fu_util_print_data (_("Signature"), sig);
g_print ("%s [Y|n]: ", _("Proceed with upload?"));
if (!fu_util_prompt_for_boolean (TRUE)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"User declined action");
return FALSE;
}
}
/* POST request */
upload_response = fwupd_client_upload_bytes (priv->client,
fwupd_remote_get_security_report_uri (remote),
data, sig,
FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART,
priv->cancellable, error);
if (upload_response == NULL)
return FALSE;
/* TRANSLATORS: success, so say thank you to the user */
g_print ("%s\n", "Host Security ID attributes uploaded successfully, thanks!");
/* as this worked, ask if the user want to do this every time */
if (!fwupd_remote_get_automatic_security_reports (remote)) {
g_print ("%s [y|N]: ",
/* TRANSLATORS: can we JFDI? */
_("Automatically upload every time?"));
if (fu_util_prompt_for_boolean (FALSE)) {
if (!fwupd_client_modify_remote (priv->client,
fwupd_remote_get_id (remote),
"AutomaticSecurityReports", "true",
NULL, error))
return FALSE;
}
}
return TRUE;
}
static gboolean
fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
{
FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE;
g_autoptr(GPtrArray) attrs = NULL;
g_autofree gchar *str = NULL;
/* not ready yet */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"The HSI specification is not yet complete. "
"To ignore this warning, use --force");
return FALSE;
}
/* TRANSLATORS: this is a string like 'HSI:2-U' */
g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
fwupd_client_get_host_security_id (priv->client));
/* print the "why" */
attrs = fwupd_client_get_host_security_attrs (priv->client,
priv->cancellable,
error);
if (attrs == NULL)
return FALSE;
/* show or hide different elements */
if (priv->show_all) {
flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES;
flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS;
}
str = fu_util_security_attrs_to_string (attrs, flags);
g_print ("%s\n", str);
/* opted-out */
if (priv->no_unreported_check)
return TRUE;
/* upload, with confirmation */
return fu_util_upload_security (priv, attrs, error);
}
static void
fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
}
#ifdef HAVE_GIO_UNIX
static gboolean
fu_util_sigint_cb (gpointer user_data)
{
FuUtilPrivate *priv = (FuUtilPrivate *) user_data;
g_debug ("Handling SIGINT");
g_cancellable_cancel (priv->cancellable);
return FALSE;
}
#endif
static void
fu_util_private_free (FuUtilPrivate *priv)
{
if (priv->client != NULL)
g_object_unref (priv->client);
if (priv->current_device != NULL)
g_object_unref (priv->current_device);
g_free (priv->current_message);
g_main_loop_unref (priv->loop);
g_object_unref (priv->cancellable);
g_object_unref (priv->progressbar);
g_option_context_free (priv->context);
g_free (priv);
}
static gboolean
fu_util_check_daemon_version (FuUtilPrivate *priv, GError **error)
{
const gchar *daemon = fwupd_client_get_daemon_version (priv->client);
if (daemon == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
/* TRANSLATORS: error message */
_("Unable to connect to service"));
return FALSE;
}
if (g_strcmp0 (daemon, SOURCE_VERSION) != 0) {
g_set_error (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
/* TRANSLATORS: error message */
_("Unsupported daemon version %s, client version is %s"),
daemon, SOURCE_VERSION);
return FALSE;
}
return TRUE;
}
static gboolean
fu_util_check_polkit_actions (GError **error)
{
#ifdef HAVE_POLKIT
g_autofree gchar *directory = fu_common_get_path (FU_PATH_KIND_POLKIT_ACTIONS);
g_autofree gchar *filename = g_build_filename (directory,
"org.freedesktop.fwupd.policy",
NULL);
if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_AUTH_FAILED,
"PolicyKit files are missing, see https://github.com/fwupd/fwupd/wiki/PolicyKit-files-are-missing");
return FALSE;
}
#endif
return TRUE;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
#pragma clang diagnostic pop
static gchar *
fu_util_get_history_checksum (FuUtilPrivate *priv, GError **error)
{
const gchar *csum;
g_autoptr(FwupdDevice) device = NULL;
g_autoptr(FwupdRelease) release = NULL;
g_autoptr(GPtrArray) devices = NULL;
devices = fwupd_client_get_history (priv->client, NULL, error);
if (devices == NULL)
return NULL;
device = fu_util_prompt_for_device (priv, devices, error);
if (device == NULL)
return NULL;
release = fu_util_prompt_for_release (priv, fwupd_device_get_releases (device), error);
if (release == NULL)
return NULL;
csum = fwupd_checksum_get_best (fwupd_release_get_checksums (release));
if (csum == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No suitable checksums");
return NULL;
}
return g_strdup (csum);
}
static gboolean
fu_util_block_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
guint idx = 0;
g_autofree gchar *csum = NULL;
g_auto(GStrv) csums_new = NULL;
g_auto(GStrv) csums = NULL;
/* get existing checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* get new value */
if (g_strv_length (values) == 0) {
csum = fu_util_get_history_checksum (priv, error);
if (csum == NULL)
return FALSE;
} else {
csum = g_strdup (values[0]);
}
/* ensure it's not already there */
if (g_strv_contains ((const gchar * const *) csums, csum)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: user selected something not possible */
_("Firmware is already blocked"));
return FALSE;
}
/* TRANSLATORS: we will not offer this firmware to the user */
g_print ("%s %s\n", _("Blocking firmware:"), csum);
/* remove it from the new list */
csums_new = g_new0 (gchar *, g_strv_length (csums) + 2);
for (guint i = 0; csums[i] != NULL; i++) {
if (g_strcmp0 (csums[i], csum) != 0)
csums_new[idx++] = g_strdup (csums[i]);
}
csums_new[idx] = g_strdup (csum);
return fwupd_client_set_blocked_firmware (priv->client, csums_new,
priv->cancellable, error);
}
static gboolean
fu_util_unblock_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
guint idx = 0;
g_auto(GStrv) csums = NULL;
g_auto(GStrv) csums_new = NULL;
g_autofree gchar *csum = NULL;
/* get existing checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* empty list */
if (g_strv_length (csums) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: nothing to show */
_("There are no blocked firmware files"));
return FALSE;
}
/* get new value */
if (g_strv_length (values) == 0) {
csum = fu_util_get_history_checksum (priv, error);
if (csum == NULL)
return FALSE;
} else {
csum = g_strdup (values[0]);
}
/* ensure it's there */
if (!g_strv_contains ((const gchar * const *) csums, csum)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: user selected something not possible */
_("Firmware is not already blocked"));
return FALSE;
}
/* TRANSLATORS: we will now offer this firmware to the user */
g_print ("%s %s\n", _("Unblocking firmware:"), csum);
/* remove it from the new list */
csums_new = g_new0 (gchar *, g_strv_length (csums));
for (guint i = 0; csums[i] != NULL; i++) {
if (g_strcmp0 (csums[i], csum) != 0)
csums_new[idx++] = g_strdup (csums[i]);
}
return fwupd_client_set_blocked_firmware (priv->client, csums_new,
priv->cancellable, error);
}
static gboolean
fu_util_get_blocked_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_auto(GStrv) csums = NULL;
/* get checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* empty list */
if (g_strv_length (csums) == 0) {
/* TRANSLATORS: nothing to show */
g_print ("%s\n", _("There are no blocked firmware files"));
return TRUE;
}
/* TRANSLATORS: there follows a list of hashes */
g_print ("%s\n", _("Blocked firmware files:"));
for (guint i = 0; csums[i] != NULL; i++) {
g_print ("%u.\t%s\n", i + 1, csums[i]);
}
/* success */
return TRUE;
}
static void
fu_util_show_plugin_warnings (FuUtilPrivate *priv)
{
FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE;
g_autoptr(GPtrArray) plugins = NULL;
/* get plugins from daemon, ignoring if the daemon is too old */
plugins = fwupd_client_get_plugins (priv->client, NULL, NULL);
if (plugins == NULL)
return;
/* get a superset so we do not show the same message more than once */
for (guint i = 0; i < plugins->len; i++) {
FwupdPlugin *plugin = g_ptr_array_index (plugins, i);
if (!fwupd_plugin_has_flag (plugin, FWUPD_PLUGIN_FLAG_USER_WARNING))
continue;
flags |= fwupd_plugin_get_flags (plugin);
}
/* never show these, they're way too generic */
flags &= ~FWUPD_PLUGIN_FLAG_DISABLED;
flags &= ~FWUPD_PLUGIN_FLAG_NO_HARDWARE;
/* print */
for (guint i = 0; i < 64; i++) {
const gchar *tmp;
g_autofree gchar *fmt = NULL;
if ((flags & ((guint64) 1 << i)) == 0)
continue;
tmp = fu_util_plugin_flag_to_string ((guint64) 1 << i);
if (tmp == NULL)
continue;
/* TRANSLATORS: this is a prefix on the console */
fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
g_printerr ("%s %s\n", fmt, tmp);
}
}
int
main (int argc, char *argv[])
{
gboolean force = FALSE;
gboolean allow_branch_switch = FALSE;
gboolean allow_older = FALSE;
gboolean allow_reinstall = FALSE;
gboolean ignore_power = FALSE;
gboolean is_interactive = TRUE;
gboolean no_history = FALSE;
gboolean offline = FALSE;
gboolean ret;
gboolean verbose = FALSE;
gboolean version = FALSE;
g_autoptr(FuUtilPrivate) priv = g_new0 (FuUtilPrivate, 1);
g_autoptr(GError) error = NULL;
g_autoptr(GError) error_polkit = NULL;
g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new ();
g_autofree gchar *cmd_descriptions = NULL;
g_autofree gchar *filter = NULL;
const GOptionEntry options[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
/* TRANSLATORS: command line option */
_("Show extra debugging information"), NULL },
{ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
/* TRANSLATORS: command line option */
_("Show client and daemon versions"), NULL },
{ "offline", '\0', 0, G_OPTION_ARG_NONE, &offline,
/* TRANSLATORS: command line option */
_("Schedule installation for next reboot when possible"), NULL },
{ "allow-reinstall", '\0', 0, G_OPTION_ARG_NONE, &allow_reinstall,
/* TRANSLATORS: command line option */
_("Allow reinstalling existing firmware versions"), NULL },
{ "allow-older", '\0', 0, G_OPTION_ARG_NONE, &allow_older,
/* TRANSLATORS: command line option */
_("Allow downgrading firmware versions"), NULL },
{ "allow-branch-switch", '\0', 0, G_OPTION_ARG_NONE, &allow_branch_switch,
/* TRANSLATORS: command line option */
_("Allow switching firmware branch"), NULL },
{ "force", '\0', 0, G_OPTION_ARG_NONE, &force,
/* TRANSLATORS: command line option */
_("Force the action by relaxing some runtime checks"), NULL },
{ "assume-yes", 'y', 0, G_OPTION_ARG_NONE, &priv->assume_yes,
/* TRANSLATORS: command line option */
_("Answer yes to all questions"), NULL },
{ "sign", '\0', 0, G_OPTION_ARG_NONE, &priv->sign,
/* TRANSLATORS: command line option */
_("Sign the uploaded data with the client certificate"), NULL },
{ "no-unreported-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_unreported_check,
/* TRANSLATORS: command line option */
_("Do not check for unreported history"), NULL },
{ "no-metadata-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_metadata_check,
/* TRANSLATORS: command line option */
_("Do not check for old metadata"), NULL },
{ "no-reboot-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_reboot_check,
/* TRANSLATORS: command line option */
_("Do not check for reboot after update"), NULL },
{ "no-safety-check", '\0', 0, G_OPTION_ARG_NONE, &priv->no_safety_check,
/* TRANSLATORS: command line option */
_("Do not perform device safety checks"), NULL },
{ "no-history", '\0', 0, G_OPTION_ARG_NONE, &no_history,
/* TRANSLATORS: command line option */
_("Do not write to the history database"), NULL },
{ "show-all", '\0', 0, G_OPTION_ARG_NONE, &priv->show_all,
/* TRANSLATORS: command line option */
_("Show all results"), NULL },
{ "show-all-devices", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &priv->show_all,
/* TRANSLATORS: command line option */
_("Show devices that are not updatable"), NULL },
{ "disable-ssl-strict", '\0', 0, G_OPTION_ARG_NONE, &priv->disable_ssl_strict,
/* TRANSLATORS: command line option */
_("Ignore SSL strict checks when downloading files"), NULL },
{ "filter", '\0', 0, G_OPTION_ARG_STRING, &filter,
/* TRANSLATORS: command line option */
_("Filter with a set of device flags using a ~ prefix to "
"exclude, e.g. 'internal,~needs-reboot'"), NULL },
{ "ignore-power", '\0', 0, G_OPTION_ARG_NONE, &ignore_power,
/* TRANSLATORS: command line option */
_("Ignore requirement of external power source"), NULL },
{ NULL}
};
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, FWUPD_LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
/* ensure D-Bus errors are registered */
fwupd_error_quark ();
/* create helper object */
priv->loop = g_main_loop_new (NULL, FALSE);
priv->progressbar = fu_progressbar_new ();
/* add commands */
fu_util_cmd_array_add (cmd_array,
"get-devices,get-topology",
NULL,
/* TRANSLATORS: command description */
_("Get all devices that support firmware updates"),
fu_util_get_devices);
fu_util_cmd_array_add (cmd_array,
"get-history",
NULL,
/* TRANSLATORS: command description */
_("Show history of firmware updates"),
fu_util_get_history);
fu_util_cmd_array_add (cmd_array,
"clear-history",
NULL,
/* TRANSLATORS: command description */
_("Erase all firmware update history"),
fu_util_clear_history);
fu_util_cmd_array_add (cmd_array,
"report-history",
NULL,
/* TRANSLATORS: command description */
_("Share firmware history with the developers"),
fu_util_report_history);
fu_util_cmd_array_add (cmd_array,
"install",
"FILE [DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Install a firmware file on this hardware"),
fu_util_install);
fu_util_cmd_array_add (cmd_array,
"get-details",
"FILE",
/* TRANSLATORS: command description */
_("Gets details about a firmware file"),
fu_util_get_details);
fu_util_cmd_array_add (cmd_array,
"get-updates,get-upgrades",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Gets the list of updates for connected hardware"),
fu_util_get_updates);
fu_util_cmd_array_add (cmd_array,
"update,upgrade",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Updates all firmware to latest versions available"),
fu_util_update);
fu_util_cmd_array_add (cmd_array,
"verify",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Checks cryptographic hash matches firmware"),
fu_util_verify);
fu_util_cmd_array_add (cmd_array,
"unlock",
"DEVICE-ID|GUID",
/* TRANSLATORS: command description */
_("Unlocks the device for firmware access"),
fu_util_unlock);
fu_util_cmd_array_add (cmd_array,
"clear-results",
"DEVICE-ID|GUID",
/* TRANSLATORS: command description */
_("Clears the results from the last update"),
fu_util_clear_results);
fu_util_cmd_array_add (cmd_array,
"clear-offline",
NULL,
/* TRANSLATORS: command description */
_("Clears any updates scheduled to be updated offline"),
fu_util_clear_offline);
fu_util_cmd_array_add (cmd_array,
"get-results",
"DEVICE-ID|GUID",
/* TRANSLATORS: command description */
_("Gets the results from the last update"),
fu_util_get_results);
fu_util_cmd_array_add (cmd_array,
"get-releases",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Gets the releases for a device"),
fu_util_get_releases);
fu_util_cmd_array_add (cmd_array,
"get-remotes",
NULL,
/* TRANSLATORS: command description */
_("Gets the configured remotes"),
fu_util_get_remotes);
fu_util_cmd_array_add (cmd_array,
"downgrade",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Downgrades the firmware on a device"),
fu_util_downgrade);
fu_util_cmd_array_add (cmd_array,
"refresh",
"[FILE FILE_SIG REMOTE-ID]",
/* TRANSLATORS: command description */
_("Refresh metadata from remote server"),
fu_util_refresh);
fu_util_cmd_array_add (cmd_array,
"verify-update",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Update the stored cryptographic hash with current ROM contents"),
fu_util_verify_update);
fu_util_cmd_array_add (cmd_array,
"modify-remote",
"REMOTE-ID KEY VALUE",
/* TRANSLATORS: command description */
_("Modifies a given remote"),
fu_util_remote_modify);
fu_util_cmd_array_add (cmd_array,
"enable-remote",
"REMOTE-ID",
/* TRANSLATORS: command description */
_("Enables a given remote"),
fu_util_remote_enable);
fu_util_cmd_array_add (cmd_array,
"disable-remote",
"REMOTE-ID",
/* TRANSLATORS: command description */
_("Disables a given remote"),
fu_util_remote_disable);
fu_util_cmd_array_add (cmd_array,
"activate",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Activate devices"),
fu_util_activate);
fu_util_cmd_array_add (cmd_array,
"get-approved-firmware",
NULL,
/* TRANSLATORS: firmware approved by the admin */
_("Gets the list of approved firmware"),
fu_util_get_approved_firmware);
fu_util_cmd_array_add (cmd_array,
"set-approved-firmware",
"CHECKSUM1[,CHECKSUM2][,CHECKSUM3]",
/* TRANSLATORS: firmware approved by the admin */
_("Sets the list of approved firmware"),
fu_util_set_approved_firmware);
fu_util_cmd_array_add (cmd_array,
"modify-config",
"KEY,VALUE",
/* TRANSLATORS: sets something in daemon.conf */
_("Modifies a daemon configuration value"),
fu_util_modify_config);
fu_util_cmd_array_add (cmd_array,
"reinstall",
"[DEVICE-ID|GUID]",
/* TRANSLATORS: command description */
_("Reinstall current firmware on the device"),
fu_util_reinstall);
fu_util_cmd_array_add (cmd_array,
"switch-branch",
"[DEVICE-ID|GUID] [BRANCH]",
/* TRANSLATORS: command description */
_("Switch the firmware branch on the device"),
fu_util_switch_branch);
fu_util_cmd_array_add (cmd_array,
"security",
NULL,
/* TRANSLATORS: command description */
_("Gets the host security attributes"),
fu_util_security);
fu_util_cmd_array_add (cmd_array,
"block-firmware",
"[CHECKSUM]",
/* TRANSLATORS: command description */
_("Blocks a specific firmware from being installed"),
fu_util_block_firmware);
fu_util_cmd_array_add (cmd_array,
"unblock-firmware",
"[CHECKSUM]",
/* TRANSLATORS: command description */
_("Blocks a specific firmware from being installed"),
fu_util_unblock_firmware);
fu_util_cmd_array_add (cmd_array,
"get-blocked-firmware",
NULL,
/* TRANSLATORS: command description */
_("Gets the list of blocked firmware"),
fu_util_get_blocked_firmware);
fu_util_cmd_array_add (cmd_array,
"get-plugins",
NULL,
/* TRANSLATORS: command description */
_("Get all enabled plugins registered with the system"),
fu_util_get_plugins);
/* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new ();
#ifdef HAVE_GIO_UNIX
g_unix_signal_add_full (G_PRIORITY_DEFAULT,
SIGINT, fu_util_sigint_cb,
priv, NULL);
#endif
/* sort by command name */
fu_util_cmd_array_sort (cmd_array);
/* get a list of the commands */
priv->context = g_option_context_new (NULL);
cmd_descriptions = fu_util_cmd_array_to_string (cmd_array);
g_option_context_set_summary (priv->context, cmd_descriptions);
g_option_context_set_description (priv->context,
"This tool allows an administrator to query and control the "
"fwupd daemon, allowing them to perform actions such as "
"installing or downgrading firmware.");
/* TRANSLATORS: program name */
g_set_application_name (_("Firmware Utility"));
g_option_context_add_main_entries (priv->context, options, NULL);
ret = g_option_context_parse (priv->context, &argc, &argv, &error);
if (!ret) {
/* TRANSLATORS: the user didn't read the man page */
g_print ("%s: %s\n", _("Failed to parse arguments"),
error->message);
return EXIT_FAILURE;
}
/* allow disabling SSL strict mode for broken corporate proxies */
if (priv->disable_ssl_strict) {
g_autofree gchar *fmt = NULL;
/* TRANSLATORS: this is a prefix on the console */
fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
/* TRANSLATORS: try to help */
g_printerr ("%s %s\n", fmt, _("Ignoring SSL strict checks, "
"to do this automatically in the future "
"export DISABLE_SSL_STRICT in your environment"));
g_setenv ("DISABLE_SSL_STRICT", "1", TRUE);
}
/* non-TTY consoles cannot answer questions */
if (isatty (fileno (stdout)) == 0) {
is_interactive = FALSE;
priv->no_unreported_check = TRUE;
priv->no_metadata_check = TRUE;
priv->no_reboot_check = TRUE;
priv->no_safety_check = TRUE;
fu_progressbar_set_interactive (priv->progressbar, FALSE);
}
/* parse filter flags */
if (filter != NULL) {
if (!fu_util_parse_filter_flags (filter,
&priv->filter_include,
&priv->filter_exclude,
&error)) {
/* TRANSLATORS: the user didn't read the man page */
g_print ("%s: %s\n", _("Failed to parse flags for --filter"),
error->message);
return EXIT_FAILURE;
}
}
/* set verbose? */
if (verbose) {
g_setenv ("G_MESSAGES_DEBUG", "all", FALSE);
g_setenv ("FWUPD_VERBOSE", "1", FALSE);
} else {
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
fu_util_ignore_cb, NULL);
}
/* set flags */
if (offline)
priv->flags |= FWUPD_INSTALL_FLAG_OFFLINE;
if (allow_reinstall)
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL;
if (allow_older)
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER;
if (allow_branch_switch)
priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH;
if (force) {
priv->flags |= FWUPD_INSTALL_FLAG_FORCE;
priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
}
if (no_history)
priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY;
if (ignore_power)
priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_POWER;
#ifdef HAVE_POLKIT
/* start polkit tty agent to listen for password requests */
if (is_interactive) {
if (!fu_polkit_agent_open (&error_polkit)) {
g_printerr ("Failed to open polkit agent: %s\n",
error_polkit->message);
}
}
#endif
/* connect to the daemon */
priv->client = fwupd_client_new ();
g_signal_connect (priv->client, "notify::percentage",
G_CALLBACK (fu_util_client_notify_cb), priv);
g_signal_connect (priv->client, "notify::status",
G_CALLBACK (fu_util_client_notify_cb), priv);
/* just show versions and exit */
if (version) {
g_autofree gchar *version_str = fu_util_get_versions();
g_print ("%s\n", version_str);
if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) {
g_printerr ("Failed to connect to daemon: %s\n",
error->message);
return EXIT_FAILURE;
}
g_print ("daemon version:\t%s\n",
fwupd_client_get_daemon_version (priv->client));
return EXIT_SUCCESS;
}
/* show a warning if the daemon is tainted */
if (!fwupd_client_connect (priv->client, priv->cancellable, &error)) {
g_printerr ("Failed to connect to daemon: %s\n",
error->message);
return EXIT_FAILURE;
}
if (fwupd_client_get_tainted (priv->client)) {
g_autofree gchar *fmt = NULL;
/* TRANSLATORS: this is a prefix on the console */
fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_TERM_COLOR_RED);
g_printerr ("%s %s\n",
fmt,
/* TRANSLATORS: the user is SOL for support... */
_("The daemon has loaded 3rd party code and "
"is no longer supported by the upstream developers!"));
}
/* show user-visible warnings from the plugins */
fu_util_show_plugin_warnings (priv);
/* we know the runtime daemon version now */
fwupd_client_set_user_agent_for_package (priv->client, "fwupdmgr", PACKAGE_VERSION);
/* check that we have at least this version daemon running */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
!fu_util_check_daemon_version (priv, &error)) {
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
#ifdef HAVE_SYSTEMD
/* make sure the correct daemon is in use */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 &&
!fwupd_client_get_daemon_interactive (priv->client) &&
!fu_util_using_correct_daemon (&error)) {
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
#endif
/* make sure polkit actions were installed */
if (!fu_util_check_polkit_actions (&error)) {
g_printerr ("%s\n", error->message);
return EXIT_FAILURE;
}
/* send our implemented feature set */
if (is_interactive) {
if (!fwupd_client_set_feature_flags (priv->client,
FWUPD_FEATURE_FLAG_CAN_REPORT |
FWUPD_FEATURE_FLAG_SWITCH_BRANCH |
FWUPD_FEATURE_FLAG_UPDATE_ACTION |
FWUPD_FEATURE_FLAG_DETACH_ACTION,
priv->cancellable, &error)) {
g_printerr ("Failed to set front-end features: %s\n",
error->message);
return EXIT_FAILURE;
}
}
/* run the specified command */
ret = fu_util_cmd_array_run (cmd_array, priv, argv[1], (gchar**) &argv[2], &error);
if (!ret) {
g_printerr ("%s\n", error->message);
if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) {
/* TRANSLATORS: error message explaining command to run to how to get help */
g_printerr ("\n%s\n", _("Use fwupdmgr --help for help"));
} else if (g_error_matches (error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) {
g_debug ("%s\n", error->message);
return EXIT_NOTHING_TO_DO;
}
return EXIT_FAILURE;
}
#ifdef HAVE_POLKIT
/* stop listening for polkit questions */
fu_polkit_agent_close ();
#endif
/* success */
return EXIT_SUCCESS;
}