/* * Copyright (C) 2015-2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #define G_LOG_DOMAIN "FuMain" #include "config.h" #include #include #include #include #include #include #include #ifdef HAVE_GIO_UNIX #include #endif #include #include #include #include #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 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; }