| /* |
| * Copyright (C) 2017 Richard Hughes <richard@hughsie.com> |
| * |
| * SPDX-License-Identifier: LGPL-2.1+ |
| */ |
| |
| #define G_LOG_DOMAIN "FuMain" |
| |
| #include <config.h> |
| |
| #include <stdio.h> |
| #include <glib/gi18n.h> |
| #ifdef HAVE_GUSB |
| #include <gusb.h> |
| #endif |
| #include <xmlb.h> |
| #include <fwupd.h> |
| #ifdef HAVE_LIBCURL |
| #include <curl/curl.h> |
| #endif |
| |
| #include "fu-common.h" |
| #include "fu-device-private.h" |
| #include "fu-util-common.h" |
| #include "fu-device.h" |
| #include "fu-security-attr.h" |
| #include "fu-security-attrs.h" |
| |
| #ifdef HAVE_SYSTEMD |
| #include "fu-systemd.h" |
| #endif |
| |
| #define SYSTEMD_FWUPD_UNIT "fwupd.service" |
| #define SYSTEMD_SNAP_FWUPD_UNIT "snap.fwupd.fwupd.service" |
| |
| const gchar * |
| fu_util_get_systemd_unit (void) |
| { |
| if (g_getenv ("SNAP") != NULL) |
| return SYSTEMD_SNAP_FWUPD_UNIT; |
| return SYSTEMD_FWUPD_UNIT; |
| } |
| |
| gchar * |
| fu_util_term_format (const gchar *text, FuUtilTermColor fg_color) |
| { |
| return g_strdup_printf ("\033[%um\033[1m%s\033[0m", fg_color, text); |
| } |
| |
| #ifdef HAVE_SYSTEMD |
| static const gchar * |
| fu_util_get_expected_command (const gchar *target) |
| { |
| if (g_strcmp0 (target, SYSTEMD_SNAP_FWUPD_UNIT) == 0) |
| return "fwupd.fwupdmgr"; |
| return "fwupdmgr"; |
| } |
| #endif |
| |
| gboolean |
| fu_util_using_correct_daemon (GError **error) |
| { |
| #ifdef HAVE_SYSTEMD |
| g_autofree gchar *default_target = NULL; |
| g_autoptr(GError) error_local = NULL; |
| const gchar *target = fu_util_get_systemd_unit (); |
| |
| default_target = fu_systemd_get_default_target (&error_local); |
| if (default_target == NULL) { |
| g_debug ("Systemd isn't accessible: %s\n", error_local->message); |
| return TRUE; |
| } |
| if (!fu_systemd_unit_check_exists (target, &error_local)) { |
| g_debug ("wrong target: %s\n", error_local->message); |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_INVALID_ARGS, |
| /* TRANSLATORS: error message */ |
| _("Mismatched daemon and client, use %s instead"), |
| fu_util_get_expected_command (target)); |
| return FALSE; |
| } |
| #endif |
| return TRUE; |
| } |
| |
| void |
| fu_util_print_data (const gchar *title, const gchar *msg) |
| { |
| gsize title_len; |
| g_auto(GStrv) lines = NULL; |
| |
| if (msg == NULL) |
| return; |
| g_print ("%s:", title); |
| |
| /* pad */ |
| title_len = fu_common_strwidth (title) + 1; |
| lines = g_strsplit (msg, "\n", -1); |
| for (guint j = 0; lines[j] != NULL; j++) { |
| for (gsize i = title_len; i < 25; i++) |
| g_print (" "); |
| g_print ("%s\n", lines[j]); |
| title_len = 0; |
| } |
| } |
| |
| guint |
| fu_util_prompt_for_number (guint maxnum) |
| { |
| gint retval; |
| guint answer = 0; |
| |
| do { |
| char buffer[64]; |
| |
| /* swallow the \n at end of line too */ |
| if (!fgets (buffer, sizeof (buffer), stdin)) |
| break; |
| if (strlen (buffer) == sizeof (buffer) - 1) |
| continue; |
| |
| /* get a number */ |
| retval = sscanf (buffer, "%u", &answer); |
| |
| /* positive */ |
| if (retval == 1 && answer <= maxnum) |
| break; |
| |
| /* TRANSLATORS: the user isn't reading the question */ |
| g_print (_("Please enter a number from 0 to %u: "), maxnum); |
| } while (TRUE); |
| return answer; |
| } |
| |
| gboolean |
| fu_util_prompt_for_boolean (gboolean def) |
| { |
| do { |
| char buffer[4]; |
| if (!fgets (buffer, sizeof (buffer), stdin)) |
| continue; |
| if (strlen (buffer) == sizeof (buffer) - 1) |
| continue; |
| if (g_strcmp0 (buffer, "\n") == 0) |
| return def; |
| buffer[0] = g_ascii_toupper (buffer[0]); |
| if (g_strcmp0 (buffer, "Y\n") == 0) |
| return TRUE; |
| if (g_strcmp0 (buffer, "N\n") == 0) |
| return FALSE; |
| } while (TRUE); |
| return FALSE; |
| } |
| |
| static gboolean |
| fu_util_traverse_tree (GNode *n, gpointer data) |
| { |
| guint idx = g_node_depth (n) - 1; |
| g_autofree gchar *tmp = NULL; |
| g_auto(GStrv) split = NULL; |
| |
| /* get split lines */ |
| if (FWUPD_IS_DEVICE (n->data)) { |
| FwupdDevice *dev = FWUPD_DEVICE (n->data); |
| tmp = fu_util_device_to_string (dev, idx); |
| } else if (FWUPD_IS_REMOTE (n->data)) { |
| FwupdRemote *remote = FWUPD_REMOTE (n->data); |
| tmp = fu_util_remote_to_string (remote, idx); |
| } else if (FWUPD_IS_RELEASE (n->data)) { |
| FwupdRelease *release = FWUPD_RELEASE (n->data); |
| tmp = fu_util_release_to_string (release, idx); |
| g_debug ("%s", tmp); |
| } |
| |
| /* root node */ |
| if (n->data == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { |
| const gchar *str = data; |
| g_print ("%s\n│\n", str != NULL ? str : "○"); |
| return FALSE; |
| } |
| |
| if (n->parent == NULL) |
| return FALSE; |
| |
| if (tmp == NULL) |
| return FALSE; |
| split = g_strsplit (tmp, "\n", -1); |
| for (guint i = 0; split[i] != NULL; i++) { |
| g_autoptr(GString) str = g_string_new (NULL); |
| |
| /* header */ |
| if (i == 0) { |
| if (g_node_next_sibling (n) == NULL) |
| g_string_prepend (str, "└─"); |
| else |
| g_string_prepend (str, "├─"); |
| |
| /* properties */ |
| } else { |
| g_string_prepend (str, n->children == NULL ? " " : " │"); |
| g_string_prepend (str, g_node_next_sibling (n) == NULL ? " " : "│"); |
| g_string_append (str, " "); |
| } |
| |
| /* ancestors */ |
| for (GNode *c = n->parent; c->parent != NULL; c = c->parent) { |
| if (g_node_next_sibling (c) != NULL || idx == 0) { |
| g_string_prepend (str, "│ "); |
| continue; |
| } |
| g_string_prepend (str, " "); |
| } |
| |
| /* empty line */ |
| if (split[i][0] == '\0') { |
| g_print ("%s\n", str->str); |
| continue; |
| } |
| |
| /* dump to the console */ |
| g_string_append (str, split[i] + (idx * 2)); |
| g_print ("%s\n", str->str); |
| } |
| |
| return FALSE; |
| } |
| |
| void |
| fu_util_print_tree (GNode *n, gpointer data) |
| { |
| g_node_traverse (n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, |
| fu_util_traverse_tree, data); |
| } |
| |
| static gboolean |
| fu_util_is_interesting_child (FwupdDevice *dev) |
| { |
| GPtrArray *children = fwupd_device_get_children (dev); |
| for (guint i = 0; i < children->len; i++) { |
| FwupdDevice *child = g_ptr_array_index (children, i); |
| if (fu_util_is_interesting_device (child)) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| gboolean |
| fu_util_is_interesting_device (FwupdDevice *dev) |
| { |
| if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) |
| return TRUE; |
| if (fwupd_device_get_update_error (dev) != NULL) |
| return TRUE; |
| /* device not plugged in, get-details */ |
| if (fwupd_device_get_flags (dev) == 0) |
| return TRUE; |
| if (fu_util_is_interesting_child (dev)) |
| return TRUE; |
| return FALSE; |
| } |
| |
| gchar * |
| fu_util_get_user_cache_path (const gchar *fn) |
| { |
| const gchar *root = g_get_user_cache_dir (); |
| g_autofree gchar *basename = g_path_get_basename (fn); |
| g_autofree gchar *cachedir_legacy = NULL; |
| |
| /* if run from a systemd unit, use the cache directory set there */ |
| if (g_getenv ("CACHE_DIRECTORY") != NULL) |
| root = g_getenv ("CACHE_DIRECTORY"); |
| |
| /* return the legacy path if it exists rather than renaming it to |
| * prevent problems when using old and new versions of fwupd */ |
| cachedir_legacy = g_build_filename (root, "fwupdmgr", NULL); |
| if (g_file_test (cachedir_legacy, G_FILE_TEST_IS_DIR)) |
| return g_build_filename (cachedir_legacy, basename, NULL); |
| |
| return g_build_filename (root, "fwupd", basename, NULL); |
| } |
| |
| gchar * |
| fu_util_get_versions (void) |
| { |
| GString *string = g_string_new (""); |
| |
| g_string_append_printf (string, "client version:\t%s\n", SOURCE_VERSION); |
| g_string_append_printf (string, |
| "compile-time dependency versions\n"); |
| #ifdef HAVE_GUSB |
| g_string_append_printf (string, |
| "\tgusb:\t%d.%d.%d\n", |
| G_USB_MAJOR_VERSION, |
| G_USB_MINOR_VERSION, |
| G_USB_MICRO_VERSION); |
| #endif |
| #ifdef EFIVAR_LIBRARY_VERSION |
| g_string_append_printf (string, |
| "\tefivar:\t%s", |
| EFIVAR_LIBRARY_VERSION); |
| #endif |
| return g_string_free (string, FALSE); |
| } |
| |
| static gboolean |
| fu_util_update_shutdown (GError **error) |
| { |
| g_autoptr(GDBusConnection) connection = NULL; |
| g_autoptr(GVariant) val = NULL; |
| |
| connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); |
| if (connection == NULL) |
| return FALSE; |
| |
| #ifdef HAVE_LOGIND |
| /* shutdown using logind */ |
| val = g_dbus_connection_call_sync (connection, |
| "org.freedesktop.login1", |
| "/org/freedesktop/login1", |
| "org.freedesktop.login1.Manager", |
| "PowerOff", |
| g_variant_new ("(b)", TRUE), |
| NULL, |
| G_DBUS_CALL_FLAGS_NONE, |
| -1, |
| NULL, |
| error); |
| #elif defined(HAVE_CONSOLEKIT) |
| /* shutdown using ConsoleKit */ |
| val = g_dbus_connection_call_sync (connection, |
| "org.freedesktop.ConsoleKit", |
| "/org/freedesktop/ConsoleKit/Manager", |
| "org.freedesktop.ConsoleKit.Manager", |
| "Stop", |
| NULL, |
| NULL, |
| G_DBUS_CALL_FLAGS_NONE, |
| -1, |
| NULL, |
| error); |
| #else |
| g_set_error_literal (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_INVALID_ARGS, |
| "No supported backend compiled in to perform the operation."); |
| #endif |
| return val != NULL; |
| } |
| |
| gboolean |
| fu_util_update_reboot (GError **error) |
| { |
| g_autoptr(GDBusConnection) connection = NULL; |
| g_autoptr(GVariant) val = NULL; |
| |
| connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); |
| if (connection == NULL) |
| return FALSE; |
| |
| #ifdef HAVE_LOGIND |
| /* reboot using logind */ |
| val = g_dbus_connection_call_sync (connection, |
| "org.freedesktop.login1", |
| "/org/freedesktop/login1", |
| "org.freedesktop.login1.Manager", |
| "Reboot", |
| g_variant_new ("(b)", TRUE), |
| NULL, |
| G_DBUS_CALL_FLAGS_NONE, |
| -1, |
| NULL, |
| error); |
| #elif defined(HAVE_CONSOLEKIT) |
| /* reboot using ConsoleKit */ |
| val = g_dbus_connection_call_sync (connection, |
| "org.freedesktop.ConsoleKit", |
| "/org/freedesktop/ConsoleKit/Manager", |
| "org.freedesktop.ConsoleKit.Manager", |
| "Restart", |
| NULL, |
| NULL, |
| G_DBUS_CALL_FLAGS_NONE, |
| -1, |
| NULL, |
| error); |
| #else |
| g_set_error_literal (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_INVALID_ARGS, |
| "No supported backend compiled in to perform the operation."); |
| #endif |
| return val != NULL; |
| } |
| |
| gboolean |
| fu_util_prompt_warning (FwupdDevice *device, const gchar *machine, GError **error) |
| { |
| FwupdDeviceFlags flags; |
| g_autofree gchar *str = NULL; |
| |
| /* device is already in bootloader mode */ |
| flags = fwupd_device_get_flags (device); |
| if (flags & FWUPD_DEVICE_FLAG_IS_BOOTLOADER) |
| return TRUE; |
| |
| /* device may reboot */ |
| if ((flags & FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) == 0) { |
| /* TRANSLATORS: warn the user before updating, %1 is a device name */ |
| str = g_strdup_printf (_("%s and all connected devices may not be usable while updating."), |
| fwupd_device_get_name (device)); |
| /* device can get bricked */ |
| } else if ((flags & FWUPD_DEVICE_FLAG_SELF_RECOVERY) == 0) { |
| /* external device */ |
| if ((flags & FWUPD_DEVICE_FLAG_INTERNAL) == 0) { |
| /* TRANSLATORS: warn the user before updating, %1 is a device name */ |
| str = g_strdup_printf (_("%s must remain connected for the duration of the update to avoid damage."), |
| fwupd_device_get_name (device)); |
| } else if (flags & FWUPD_DEVICE_FLAG_REQUIRE_AC) { |
| /* TRANSLATORS: warn the user before updating, %1 is a machine name */ |
| str = g_strdup_printf (_("%s must remain plugged into a power source for the duration of the update to avoid damage."), |
| machine); |
| } |
| } |
| if (str != NULL) { |
| g_print ("%s %s [Y|n]: ", |
| str, |
| /* TRANSLATORS: prompt to apply the update */ |
| _("Continue with update?")); |
| if (!fu_util_prompt_for_boolean (TRUE)) { |
| g_set_error_literal (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOTHING_TO_DO, |
| "Request canceled"); |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| gboolean |
| fu_util_prompt_complete (FwupdDeviceFlags flags, gboolean prompt, GError **error) |
| { |
| if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { |
| if (prompt) { |
| g_print ("\n%s %s [y|N]: ", |
| /* TRANSLATORS: explain why we want to shutdown */ |
| _("An update requires the system to shutdown to complete."), |
| /* TRANSLATORS: shutdown to apply the update */ |
| _("Shutdown now?")); |
| if (!fu_util_prompt_for_boolean (FALSE)) |
| return TRUE; |
| } |
| return fu_util_update_shutdown (error); |
| } |
| if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { |
| if (prompt) { |
| g_print ("\n%s %s [y|N]: ", |
| /* TRANSLATORS: explain why we want to reboot */ |
| _("An update requires a reboot to complete."), |
| /* TRANSLATORS: reboot to apply the update */ |
| _("Restart now?")); |
| if (!fu_util_prompt_for_boolean (FALSE)) |
| return TRUE; |
| } |
| return fu_util_update_reboot (error); |
| } |
| |
| return TRUE; |
| } |
| |
| static void |
| fu_util_cmd_free (FuUtilCmd *item) |
| { |
| g_free (item->name); |
| g_free (item->arguments); |
| g_free (item->description); |
| g_free (item); |
| } |
| |
| GPtrArray * |
| fu_util_cmd_array_new (void) |
| { |
| return g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_cmd_free); |
| } |
| |
| static gint |
| fu_util_cmd_sort_cb (FuUtilCmd **item1, FuUtilCmd **item2) |
| { |
| return g_strcmp0 ((*item1)->name, (*item2)->name); |
| } |
| |
| void |
| fu_util_cmd_array_sort (GPtrArray *array) |
| { |
| g_ptr_array_sort (array, (GCompareFunc) fu_util_cmd_sort_cb); |
| } |
| |
| void |
| fu_util_cmd_array_add (GPtrArray *array, |
| const gchar *name, |
| const gchar *arguments, |
| const gchar *description, |
| FuUtilCmdFunc callback) |
| { |
| g_auto(GStrv) names = NULL; |
| |
| g_return_if_fail (name != NULL); |
| g_return_if_fail (description != NULL); |
| g_return_if_fail (callback != NULL); |
| |
| /* add each one */ |
| names = g_strsplit (name, ",", -1); |
| for (guint i = 0; names[i] != NULL; i++) { |
| FuUtilCmd *item = g_new0 (FuUtilCmd, 1); |
| item->name = g_strdup (names[i]); |
| if (i == 0) { |
| item->description = g_strdup (description); |
| } else { |
| /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ |
| item->description = g_strdup_printf (_("Alias to %s"), |
| names[0]); |
| } |
| item->arguments = g_strdup (arguments); |
| item->callback = callback; |
| g_ptr_array_add (array, item); |
| } |
| } |
| |
| gboolean |
| fu_util_cmd_array_run (GPtrArray *array, |
| FuUtilPrivate *priv, |
| const gchar *command, |
| gchar **values, |
| GError **error) |
| { |
| g_auto(GStrv) values_copy = g_new0 (gchar *, g_strv_length (values) + 1); |
| |
| /* clear out bash completion sentinel */ |
| for (guint i = 0; values[i] != NULL; i++) { |
| if (g_strcmp0 (values[i], "{") == 0) |
| break; |
| values_copy[i] = g_strdup (values[i]); |
| } |
| |
| /* find command */ |
| for (guint i = 0; i < array->len; i++) { |
| FuUtilCmd *item = g_ptr_array_index (array, i); |
| if (g_strcmp0 (item->name, command) == 0) |
| return item->callback (priv, values_copy, error); |
| } |
| |
| /* not found */ |
| g_set_error_literal (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_INVALID_ARGS, |
| /* TRANSLATORS: error message */ |
| _("Command not found")); |
| return FALSE; |
| } |
| |
| gchar * |
| fu_util_cmd_array_to_string (GPtrArray *array) |
| { |
| gsize len; |
| const gsize max_len = 35; |
| GString *string; |
| |
| /* print each command */ |
| string = g_string_new (""); |
| for (guint i = 0; i < array->len; i++) { |
| FuUtilCmd *item = g_ptr_array_index (array, i); |
| g_string_append (string, " "); |
| g_string_append (string, item->name); |
| len = fu_common_strwidth (item->name) + 2; |
| if (item->arguments != NULL) { |
| g_string_append (string, " "); |
| g_string_append (string, item->arguments); |
| len += fu_common_strwidth (item->arguments) + 1; |
| } |
| if (len < max_len) { |
| for (gsize j = len; j < max_len + 1; j++) |
| g_string_append_c (string, ' '); |
| g_string_append (string, item->description); |
| g_string_append_c (string, '\n'); |
| } else { |
| g_string_append_c (string, '\n'); |
| for (gsize j = 0; j < max_len + 1; j++) |
| g_string_append_c (string, ' '); |
| g_string_append (string, item->description); |
| g_string_append_c (string, '\n'); |
| } |
| } |
| |
| /* remove trailing newline */ |
| if (string->len > 0) |
| g_string_set_size (string, string->len - 1); |
| |
| return g_string_free (string, FALSE); |
| } |
| |
| const gchar * |
| fu_util_branch_for_display (const gchar *branch) |
| { |
| if (branch == NULL) { |
| /* TRANSLATORS: this is the default branch name when unset */ |
| return _("default"); |
| } |
| return branch; |
| } |
| |
| gchar * |
| fu_util_release_get_name (FwupdRelease *release) |
| { |
| const gchar *name = fwupd_release_get_name (release); |
| GPtrArray *cats = fwupd_release_get_categories (release); |
| |
| for (guint i = 0; i < cats->len; i++) { |
| const gchar *cat = g_ptr_array_index (cats, i); |
| if (g_strcmp0 (cat, "X-Device") == 0) { |
| /* TRANSLATORS: a specific part of hardware, |
| * the first %s is the device name, e.g. 'Unifying Receiver` */ |
| return g_strdup_printf (_("%s Device Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Configuration") == 0) { |
| /* TRANSLATORS: a specific part of hardware, |
| * the first %s is the device name, e.g. 'Secure Boot` */ |
| return g_strdup_printf (_("%s Configuration Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-System") == 0) { |
| /* TRANSLATORS: the entire system, e.g. all internal devices, |
| * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s System Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-EmbeddedController") == 0) { |
| /* TRANSLATORS: the EC is typically the keyboard controller chip, |
| * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s Embedded Controller Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-ManagementEngine") == 0) { |
| /* TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, |
| * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s ME Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0) { |
| /* TRANSLATORS: ME stands for Management Engine (with Intel AMT), |
| * where the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s Corporate ME Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0) { |
| /* TRANSLATORS: ME stands for Management Engine, where |
| * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s Consumer ME Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Controller") == 0) { |
| /* TRANSLATORS: the controller is a device that has other devices |
| * plugged into it, for example ThunderBolt, FireWire or USB, |
| * the first %s is the device name, e.g. 'Intel ThunderBolt` */ |
| return g_strdup_printf (_("%s Controller Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-ThunderboltController") == 0) { |
| /* TRANSLATORS: the Thunderbolt controller is a device that |
| * has other high speed Thunderbolt devices plugged into it; |
| * the first %s is the system name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s Thunderbolt Controller Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-CpuMicrocode") == 0) { |
| /* TRANSLATORS: the CPU microcode is firmware loaded onto the CPU |
| * at system bootup */ |
| return g_strdup_printf (_("%s CPU Microcode Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Battery") == 0) { |
| /* TRANSLATORS: battery refers to the system power source */ |
| return g_strdup_printf (_("%s Battery Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Camera") == 0) { |
| /* TRANSLATORS: camera can refer to the laptop internal |
| * camera in the bezel or external USB webcam */ |
| return g_strdup_printf (_("%s Camera Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-TPM") == 0) { |
| /* TRANSLATORS: TPM refers to a Trusted Platform Module */ |
| return g_strdup_printf (_("%s TPM Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Touchpad") == 0) { |
| /* TRANSLATORS: TouchPad refers to a flat input device */ |
| return g_strdup_printf (_("%s Touchpad Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Mouse") == 0) { |
| /* TRANSLATORS: Mouse refers to a handheld input device */ |
| return g_strdup_printf (_("%s Mouse Update"), name); |
| } |
| if (g_strcmp0 (cat, "X-Keyboard") == 0) { |
| /* TRANSLATORS: Keyboard refers to an input device for typing */ |
| return g_strdup_printf (_("%s Keyboard Update"), name); |
| } |
| } |
| |
| /* TRANSLATORS: this is the fallback where we don't know if the release |
| * is updating the system, the device, or a device class, or something else -- |
| * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| return g_strdup_printf (_("%s Update"), name); |
| } |
| |
| static GPtrArray * |
| fu_util_strsplit_words (const gchar *text, guint line_len) |
| { |
| g_auto(GStrv) tokens = NULL; |
| g_autoptr(GPtrArray) lines = g_ptr_array_new (); |
| g_autoptr(GString) curline = g_string_new (NULL); |
| |
| /* sanity check */ |
| if (text == NULL || text[0] == '\0') |
| return NULL; |
| if (line_len == 0) |
| return NULL; |
| |
| /* tokenize the string */ |
| tokens = g_strsplit (text, " ", -1); |
| for (guint i = 0; tokens[i] != NULL; i++) { |
| |
| /* current line plus new token is okay */ |
| if (curline->len + fu_common_strwidth (tokens[i]) < line_len) { |
| g_string_append_printf (curline, "%s ", tokens[i]); |
| continue; |
| } |
| |
| /* too long, so remove space, add newline and dump */ |
| if (curline->len > 0) |
| g_string_truncate (curline, curline->len - 1); |
| g_ptr_array_add (lines, g_strdup (curline->str)); |
| g_string_truncate (curline, 0); |
| g_string_append_printf (curline, "%s ", tokens[i]); |
| } |
| |
| /* any incomplete line? */ |
| if (curline->len > 0) { |
| g_string_truncate (curline, curline->len - 1); |
| g_ptr_array_add (lines, g_strdup (curline->str)); |
| } |
| return g_steal_pointer (&lines); |
| } |
| |
| static void |
| fu_util_warning_box_line (const gchar *start, |
| const gchar *text, |
| const gchar *end, |
| const gchar *padding, |
| guint width) |
| { |
| guint offset = 0; |
| if (start != NULL) { |
| offset += fu_common_strwidth (start); |
| g_print ("%s", start); |
| } |
| if (text != NULL) { |
| offset += fu_common_strwidth (text); |
| g_print ("%s", text); |
| } |
| if (end != NULL) |
| offset += fu_common_strwidth (end); |
| for (guint i = offset; i < width; i++) |
| g_print ("%s", padding); |
| if (end != NULL) |
| g_print ("%s\n", end); |
| } |
| |
| void |
| fu_util_warning_box (const gchar *title, const gchar *body, guint width) |
| { |
| /* nothing to do */ |
| if (title == NULL && body == NULL) |
| return; |
| |
| /* header */ |
| fu_util_warning_box_line ("╔", NULL, "╗", "═", width); |
| |
| /* optional title */ |
| if (title != NULL) { |
| g_autoptr(GPtrArray) lines = fu_util_strsplit_words (title, width - 4); |
| for (guint j = 0; j < lines->len; j++) { |
| const gchar *line = g_ptr_array_index (lines, j); |
| fu_util_warning_box_line ("║ ", line, " ║", " ", width); |
| } |
| } |
| |
| /* join */ |
| if (title != NULL && body != NULL) |
| fu_util_warning_box_line ("╠", NULL, "╣", "═", width); |
| |
| /* optional body */ |
| if (body != NULL) { |
| g_auto(GStrv) split = g_strsplit (body, "\n", -1); |
| for (guint i = 0; split[i] != NULL; i++) { |
| g_autoptr(GPtrArray) lines = fu_util_strsplit_words (split[i], width - 4); |
| if (lines == NULL) |
| continue; |
| for (guint j = 0; j < lines->len; j++) { |
| const gchar *line = g_ptr_array_index (lines, j); |
| fu_util_warning_box_line ("║ ", line, " ║", " ", width); |
| } |
| } |
| } |
| |
| /* footer */ |
| fu_util_warning_box_line ("╚", NULL, "╝", "═", width); |
| } |
| |
| gboolean |
| fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include, |
| FwupdDeviceFlags *exclude, GError **error) |
| { |
| FwupdDeviceFlags tmp; |
| g_auto(GStrv) strv = g_strsplit (filter, ",", -1); |
| |
| g_return_val_if_fail (include != NULL, FALSE); |
| g_return_val_if_fail (exclude != NULL, FALSE); |
| |
| for (guint i = 0; strv[i] != NULL; i++) { |
| if (g_str_has_prefix (strv[i], "~")) { |
| tmp = fwupd_device_flag_from_string (strv[i] + 1); |
| if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Unknown device flag %s", |
| strv[i] + 1); |
| return FALSE; |
| } |
| if ((tmp & *include) > 0) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Filter %s already included", |
| fwupd_device_flag_to_string (tmp)); |
| return FALSE; |
| } |
| if ((tmp & *exclude) > 0) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Filter %s already excluded", |
| fwupd_device_flag_to_string (tmp)); |
| return FALSE; |
| } |
| *exclude |= tmp; |
| } else { |
| tmp = fwupd_device_flag_from_string (strv[i]); |
| if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Unknown device flag %s", |
| strv[i]); |
| return FALSE; |
| } |
| if ((tmp & *exclude) > 0) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Filter %s already excluded", |
| fwupd_device_flag_to_string (tmp)); |
| return FALSE; |
| } |
| if ((tmp & *include) > 0) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_NOT_SUPPORTED, |
| "Filter %s already included", |
| fwupd_device_flag_to_string (tmp)); |
| return FALSE; |
| } |
| *include |= tmp; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| typedef struct { |
| guint cnt; |
| GString *str; |
| } FuUtilConvertHelper; |
| |
| static gboolean |
| fu_util_convert_description_head_cb (XbNode *n, gpointer user_data) |
| { |
| FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data; |
| helper->cnt++; |
| |
| /* start */ |
| if (g_strcmp0 (xb_node_get_element (n), "em") == 0) { |
| g_string_append (helper->str, "\033[3m"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "strong") == 0) { |
| g_string_append (helper->str, "\033[1m"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) { |
| g_string_append (helper->str, "`"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) { |
| g_string_append (helper->str, "• "); |
| } else if (g_strcmp0 (xb_node_get_element (n), "p") == 0 || |
| g_strcmp0 (xb_node_get_element (n), "ul") == 0 || |
| g_strcmp0 (xb_node_get_element (n), "ol") == 0) { |
| g_string_append (helper->str, "\n"); |
| } |
| |
| /* text */ |
| if (xb_node_get_text (n) != NULL) |
| g_string_append (helper->str, xb_node_get_text (n)); |
| |
| return FALSE; |
| } |
| |
| static gboolean |
| fu_util_convert_description_tail_cb (XbNode *n, gpointer user_data) |
| { |
| FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data; |
| helper->cnt++; |
| |
| /* end */ |
| if (g_strcmp0 (xb_node_get_element (n), "em") == 0 || |
| g_strcmp0 (xb_node_get_element (n), "strong") == 0) { |
| g_string_append (helper->str, "\033[0m"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) { |
| g_string_append (helper->str, "`"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) { |
| g_string_append (helper->str, "\n"); |
| } else if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { |
| g_string_append (helper->str, "\n"); |
| } |
| |
| /* tail */ |
| if (xb_node_get_tail (n) != NULL) |
| g_string_append (helper->str, xb_node_get_tail (n)); |
| |
| return FALSE; |
| } |
| |
| gchar * |
| fu_util_convert_description (const gchar *xml, GError **error) |
| { |
| g_autoptr(GString) str = g_string_new (NULL); |
| g_autoptr(XbNode) n = NULL; |
| g_autoptr(XbSilo) silo = NULL; |
| FuUtilConvertHelper helper = { |
| .cnt = 0, |
| .str = str, |
| }; |
| |
| /* parse XML */ |
| silo = xb_silo_new_from_xml (xml, error); |
| if (silo == NULL) |
| return NULL; |
| |
| /* convert to something we can show on the console */ |
| n = xb_silo_get_root (silo); |
| xb_node_transmogrify (n, |
| fu_util_convert_description_head_cb, |
| fu_util_convert_description_tail_cb, |
| &helper); |
| |
| /* success */ |
| return fu_common_strstrip (str->str); |
| } |
| |
| /** |
| * fu_util_time_to_str: |
| * @tmp: the time in seconds |
| * |
| * Converts a timestamp to a 'pretty' translated string |
| * |
| * Return value: (transfer full): A string |
| * |
| * Since: 1.3.7 |
| **/ |
| gchar * |
| fu_util_time_to_str (guint64 tmp) |
| { |
| g_return_val_if_fail (tmp != 0, NULL); |
| |
| /* seconds */ |
| if (tmp < 60) { |
| /* TRANSLATORS: duration in seconds */ |
| return g_strdup_printf (ngettext ("%u second", "%u seconds", |
| (gint) tmp), |
| (guint) tmp); |
| } |
| |
| /* minutes */ |
| tmp /= 60; |
| if (tmp < 60) { |
| /* TRANSLATORS: duration in minutes */ |
| return g_strdup_printf (ngettext ("%u minute", "%u minutes", |
| (gint) tmp), |
| (guint) tmp); |
| } |
| |
| /* hours */ |
| tmp /= 60; |
| if (tmp < 60) { |
| /* TRANSLATORS: duration in minutes */ |
| return g_strdup_printf (ngettext ("%u hour", "%u hours", |
| (gint) tmp), |
| (guint) tmp); |
| } |
| |
| /* days */ |
| tmp /= 24; |
| /* TRANSLATORS: duration in days! */ |
| return g_strdup_printf (ngettext ("%u day", "%u days", |
| (gint) tmp), |
| (guint) tmp); |
| } |
| |
| static gchar * |
| fu_util_device_flag_to_string (guint64 device_flag) |
| { |
| if (device_flag == FWUPD_DEVICE_FLAG_NONE) { |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) { |
| /* TRANSLATORS: Device cannot be removed easily*/ |
| return _("Internal device"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE || |
| device_flag == FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN) { |
| /* TRANSLATORS: Device is updatable in this or any other mode */ |
| return _("Updatable"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) { |
| /* TRANSLATORS: Update can only be done from offline mode */ |
| return _("Update requires a reboot"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) { |
| /* TRANSLATORS: Must be plugged in to an outlet */ |
| return _("System requires external power source"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) { |
| /* TRANSLATORS: Is locked and can be unlocked */ |
| return _("Device is locked"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) { |
| /* TRANSLATORS: Is found in current metadata */ |
| return _("Supported on remote server"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) { |
| /* TRANSLATORS: Requires a bootloader mode to be manually enabled by the user */ |
| return _("Requires a bootloader"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { |
| /* TRANSLATORS: Requires a reboot to apply firmware or to reload hardware */ |
| return _("Needs a reboot after installation"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { |
| /* TRANSLATORS: Requires system shutdown to apply firmware */ |
| return _("Needs shutdown after installation"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) { |
| /* TRANSLATORS: Has been reported to a metadata server */ |
| return _("Reported to remote server"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) { |
| /* TRANSLATORS: User has been notified */ |
| return _("User has been notified"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) { |
| /* skip */ |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) { |
| /* TRANSLATORS: Install composite firmware on the parent before the child */ |
| return _("Install to parent device first"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) { |
| /* TRANSLATORS: Is currently in bootloader mode */ |
| return _("Is in bootloader mode"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) { |
| /* TRANSLATORS: The hardware is waiting to be replugged */ |
| return _("Hardware is waiting to be replugged"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) { |
| /* TRANSLATORS: Ignore validation safety checks when flashing this device */ |
| return _("Ignore validation safety checks"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) { |
| /* skip */ |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) { |
| /* TRANSLATORS: Device update needs to be separately activated */ |
| return _("Device update needs activation"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_HISTORICAL) { |
| /* skip */ |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_WILL_DISAPPEAR) { |
| /* TRANSLATORS: Device will not return after update completes */ |
| return _("Device will not re-appear after update completes"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY) { |
| /* TRANSLATORS: Device supports some form of checksum verification */ |
| return _("Cryptographic hash verification is available"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) { |
| /* skip */ |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_DUAL_IMAGE) { |
| /* TRANSLATORS: Device supports a safety mechanism for flashing */ |
| return _("Device stages updates"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_SELF_RECOVERY) { |
| /* TRANSLATORS: Device supports a safety mechanism for flashing */ |
| return _("Device can recover flash failures"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) { |
| /* TRANSLATORS: Device remains usable during update */ |
| return _("Device is usable for the duration of the update"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) { |
| /* TRANSLATORS: a version check is required for all firmware */ |
| return _("Device firmware is required to have a version check"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) { |
| /* TRANSLATORS: a version check is required for all firmware */ |
| return _("Device is required to install all provided releases"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) { |
| /* TRANSLATORS: there is more than one supplier of the firmware */ |
| return _("Device supports switching to a different branch of firmware"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) { |
| /* TRANSLATORS: save the old firmware to disk before installing the new one */ |
| return _("Device will backup firmware before installing"); |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) { |
| /* skip */ |
| return NULL; |
| } |
| if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| return NULL; |
| } |
| return NULL; |
| } |
| |
| static const gchar * |
| fu_util_update_state_to_string (FwupdUpdateState update_state) |
| { |
| if (update_state == FWUPD_UPDATE_STATE_PENDING) { |
| /* TRANSLATORS: The update state of the specific device */ |
| return _("Pending"); |
| } |
| if (update_state == FWUPD_UPDATE_STATE_SUCCESS) { |
| /* TRANSLATORS: The update state of the specific device */ |
| return _("Success"); |
| } |
| if (update_state == FWUPD_UPDATE_STATE_FAILED) { |
| /* TRANSLATORS: The update state of the specific device */ |
| return _("Failed"); |
| } |
| if (update_state == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { |
| /* TRANSLATORS: The update state of the specific device */ |
| return _("Transient failure"); |
| } |
| if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { |
| /* TRANSLATORS: The update state of the specific device */ |
| return _("Needs reboot"); |
| } |
| return NULL; |
| } |
| |
| gchar * |
| fu_util_device_to_string (FwupdDevice *dev, guint idt) |
| { |
| FwupdUpdateState state; |
| GPtrArray *guids = fwupd_device_get_guids (dev); |
| GPtrArray *vendor_ids = fwupd_device_get_vendor_ids (dev); |
| GPtrArray *instance_ids = fwupd_device_get_instance_ids (dev); |
| const gchar *tmp; |
| const gchar *tmp2; |
| guint64 flags = fwupd_device_get_flags (dev); |
| guint64 modified = fwupd_device_get_modified (dev); |
| g_autoptr(GHashTable) ids = NULL; |
| g_autoptr(GString) str = g_string_new (NULL); |
| |
| /* some fields are intentionally not included and are only shown in --verbose */ |
| if (g_getenv ("FWUPD_VERBOSE") != NULL) { |
| g_autofree gchar *debug_str = NULL; |
| debug_str = fwupd_device_to_string (dev); |
| g_debug ("%s", debug_str); |
| return NULL; |
| } |
| |
| tmp = fwupd_device_get_name (dev); |
| if (tmp == NULL) { |
| /* TRANSLATORS: Name of hardware */ |
| tmp = _("Unknown Device"); |
| } |
| fu_common_string_append_kv (str, idt, tmp, NULL); |
| |
| tmp = fwupd_device_get_id (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ |
| fu_common_string_append_kv (str, idt + 1, _("Device ID"), tmp); |
| } |
| |
| /* summary */ |
| tmp = fwupd_device_get_summary (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: one line summary of device */ |
| fu_common_string_append_kv (str, idt + 1, _("Summary"), tmp); |
| } |
| |
| /* description */ |
| tmp = fwupd_device_get_description (dev); |
| if (tmp != NULL) { |
| g_autofree gchar *desc = NULL; |
| desc = fu_util_convert_description (tmp, NULL); |
| /* TRANSLATORS: multiline description of device */ |
| fu_common_string_append_kv (str, idt + 1, _("Description"), desc); |
| } |
| |
| /* versions */ |
| tmp = fwupd_device_get_version (dev); |
| if (tmp != NULL) { |
| if (flags & FWUPD_DEVICE_FLAG_HISTORICAL) { |
| /* TRANSLATORS: version number of previous firmware */ |
| fu_common_string_append_kv (str, idt + 1, _("Previous version"), tmp); |
| } else { |
| /* TRANSLATORS: version number of current firmware */ |
| fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp); |
| } |
| } |
| tmp = fwupd_device_get_version_lowest (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: smallest version number installable on device */ |
| fu_common_string_append_kv (str, idt + 1, _("Minimum Version"), tmp); |
| } |
| tmp = fwupd_device_get_version_bootloader (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: firmware version of bootloader */ |
| fu_common_string_append_kv (str, idt + 1, _("Bootloader Version"), tmp); |
| } |
| |
| /* vendor */ |
| tmp = fwupd_device_get_vendor (dev); |
| if (tmp != NULL && vendor_ids->len > 0) { |
| g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids); |
| g_autofree gchar *both = g_strdup_printf ("%s (%s)", tmp, strv); |
| /* TRANSLATORS: manufacturer of hardware */ |
| fu_common_string_append_kv (str, idt + 1, _("Vendor"), both); |
| } else if (tmp != NULL) { |
| /* TRANSLATORS: manufacturer of hardware */ |
| fu_common_string_append_kv (str, idt + 1, _("Vendor"), tmp); |
| } else if (vendor_ids->len > 0) { |
| g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids); |
| /* TRANSLATORS: manufacturer of hardware */ |
| fu_common_string_append_kv (str, idt + 1, _("Vendor"), strv); |
| } |
| |
| /* branch */ |
| if (fwupd_device_get_branch (dev) != NULL) { |
| /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ |
| fu_common_string_append_kv (str, idt + 1, _("Release Branch"), |
| fwupd_device_get_branch (dev)); |
| } |
| |
| /* install duration */ |
| if (fwupd_device_get_install_duration (dev) > 0) { |
| g_autofree gchar *time = fu_util_time_to_str (fwupd_device_get_install_duration (dev)); |
| /* TRANSLATORS: length of time the update takes to apply */ |
| fu_common_string_append_kv (str, idt + 1, _("Install Duration"), time); |
| } |
| |
| /* serial # */ |
| tmp = fwupd_device_get_serial (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: serial number of hardware */ |
| fu_common_string_append_kv (str, idt + 1, _("Serial Number"), tmp); |
| } |
| |
| /* update state */ |
| state = fwupd_device_get_update_state (dev); |
| if (state != FWUPD_UPDATE_STATE_UNKNOWN) { |
| /* TRANSLATORS: hardware state, e.g. "pending" */ |
| fu_common_string_append_kv (str, idt + 1, _("Update State"), |
| fu_util_update_state_to_string (state)); |
| |
| if (state == FWUPD_UPDATE_STATE_SUCCESS) { |
| tmp = fwupd_device_get_update_message (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: helpful messages from last update */ |
| fu_common_string_append_kv (str, idt + 1, _("Update Message"), tmp); |
| } |
| } |
| } |
| tmp = fwupd_device_get_update_error (dev); |
| if (tmp != NULL) { |
| /* TRANSLATORS: error message from last update attempt */ |
| fu_common_string_append_kv (str, idt + 1, _("Update Error"), tmp); |
| } |
| |
| /* modified date: for history devices */ |
| if (modified > 0) { |
| g_autoptr(GDateTime) date = NULL; |
| g_autofree gchar *time_str = NULL; |
| date = g_date_time_new_from_unix_utc (modified); |
| time_str = g_date_time_format (date, "%F %R"); |
| /* TRANSLATORS: the original time/date the device was modified */ |
| fu_common_string_append_kv (str, idt +1, _("Last modified"), time_str); |
| } |
| |
| /* all GUIDs for this hardware, with IDs if available */ |
| ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); |
| for (guint i = 0; i < instance_ids->len; i++) { |
| const gchar *instance_id = g_ptr_array_index (instance_ids, i); |
| g_hash_table_insert (ids, |
| fwupd_guid_hash_string (instance_id), |
| g_strdup (instance_id)); |
| } |
| for (guint i = 0; i < guids->len; i++) { |
| const gchar *guid = g_ptr_array_index (guids, i); |
| const gchar *instance_id = g_hash_table_lookup (ids, guid); |
| g_autofree gchar *guid_src = NULL; |
| |
| /* instance IDs are only available as root */ |
| if (instance_id == NULL) { |
| guid_src = g_strdup (guid); |
| } else { |
| guid_src = g_strdup_printf ("%s ← %s", guid, instance_id); |
| } |
| if (i == 0) { |
| /* TRANSLATORS: global ID common to all similar hardware */ |
| fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), guid_src); |
| } else { |
| fu_common_string_append_kv (str, idt + 1, "", guid_src); |
| } |
| } |
| |
| /* TRANSLATORS: description of device ability */ |
| tmp = _("Device Flags"); |
| for (guint i = 0; i < 64; i++) { |
| if ((flags & ((guint64) 1 << i)) == 0) |
| continue; |
| tmp2 = fu_util_device_flag_to_string ((guint64) 1 << i); |
| if (tmp2 == NULL) |
| continue; |
| /* header */ |
| if (tmp != NULL) { |
| g_autofree gchar *bullet = NULL; |
| bullet = g_strdup_printf ("• %s", tmp2); |
| fu_common_string_append_kv (str, idt + 1, tmp, bullet); |
| tmp = NULL; |
| } else { |
| g_autofree gchar *bullet = NULL; |
| bullet = g_strdup_printf ("• %s", tmp2); |
| fu_common_string_append_kv (str, idt + 1, "", bullet); |
| } |
| } |
| |
| return g_string_free (g_steal_pointer (&str), FALSE); |
| } |
| |
| const gchar * |
| fu_util_plugin_flag_to_string (FwupdPluginFlags plugin_flag) |
| { |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_UNKNOWN) |
| return NULL; |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) |
| return NULL; |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) |
| return NULL; |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_REQUIRE_HWID) |
| return NULL; |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_NONE) { |
| /* TRANSLATORS: Plugin is active and in use */ |
| return _("Enabled"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) { |
| /* TRANSLATORS: Plugin is inactive and not used */ |
| return _("Disabled"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) { |
| /* TRANSLATORS: not required for this system */ |
| return _("Required hardware was not found"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) { |
| /* TRANSLATORS: system is not booted in UEFI mode */ |
| return _("UEFI firmware can not be updated in legacy BIOS mode"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) { |
| /* TRANSLATORS: capsule updates are an optional BIOS feature */ |
| return _("UEFI capsule updates not available or enabled in firmware setup"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) { |
| /* TRANSLATORS: user needs to run a command */ |
| return _("Firmware updates disabled; run 'fwupdmgr unlock' to enable"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) { |
| /* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */ |
| return _("Required efivarfs filesystem was not found"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) { |
| /* TRANSLATORS: partition refers to something on disk, again, hey Arch users */ |
| return _("UEFI ESP partition not detected or configured"); |
| } |
| if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) { |
| /* TRANSLATORS: Failed to open plugin, hey Arch users */ |
| return _("Plugin dependencies missing"); |
| } |
| |
| /* fall back for unknown types */ |
| return fwupd_plugin_flag_to_string (plugin_flag); |
| } |
| |
| static gchar * |
| fu_util_plugin_flag_to_cli_text (FwupdPluginFlags plugin_flag) |
| { |
| switch (plugin_flag) { |
| case FWUPD_PLUGIN_FLAG_UNKNOWN: |
| case FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: |
| case FWUPD_PLUGIN_FLAG_USER_WARNING: |
| case FWUPD_PLUGIN_FLAG_REQUIRE_HWID: |
| return NULL; |
| case FWUPD_PLUGIN_FLAG_NONE: |
| return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| FU_UTIL_CLI_COLOR_GREEN); |
| case FWUPD_PLUGIN_FLAG_DISABLED: |
| case FWUPD_PLUGIN_FLAG_NO_HARDWARE: |
| return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| FU_UTIL_CLI_COLOR_BLACK); |
| case FWUPD_PLUGIN_FLAG_LEGACY_BIOS: |
| case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: |
| case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: |
| case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: |
| case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: |
| return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| FU_UTIL_TERM_COLOR_RED); |
| default: |
| break; |
| } |
| |
| /* fall back for unknown types */ |
| return g_strdup (fwupd_plugin_flag_to_string (plugin_flag)); |
| } |
| |
| gchar * |
| fu_util_plugin_to_string (FwupdPlugin *plugin, guint idt) |
| { |
| GString *str = g_string_new (NULL); |
| const gchar *hdr; |
| guint64 flags = fwupd_plugin_get_flags (plugin); |
| |
| fu_common_string_append_kv (str, idt, fwupd_plugin_get_name (plugin), NULL); |
| |
| /* TRANSLATORS: description of plugin state, e.g. disabled */ |
| hdr = _("Flags"); |
| if (flags == 0x0) { |
| const gchar *tmp = fu_util_plugin_flag_to_cli_text (flags); |
| g_autofree gchar *li = g_strdup_printf ("• %s", tmp); |
| fu_common_string_append_kv (str, idt + 1, hdr, li); |
| } else { |
| for (guint i = 0; i < 64; i++) { |
| g_autofree gchar *li = NULL; |
| g_autofree gchar *tmp = NULL; |
| if ((flags & ((guint64) 1 << i)) == 0) |
| continue; |
| tmp = fu_util_plugin_flag_to_cli_text ((guint64) 1 << i); |
| if (tmp == NULL) |
| continue; |
| li = g_strdup_printf ("• %s", tmp); |
| fu_common_string_append_kv (str, idt + 1, hdr, li); |
| |
| /* clear header */ |
| hdr = ""; |
| } |
| } |
| |
| return g_string_free (str, FALSE); |
| } |
| |
| static const gchar * |
| fu_util_license_to_string (const gchar *license) |
| { |
| if (license == NULL) { |
| /* TRANSLATORS: we don't know the license of the update */ |
| return _("Unknown"); |
| } |
| if (g_strcmp0 (license, "LicenseRef-proprietary") == 0 || |
| g_strcmp0 (license, "proprietary") == 0) { |
| /* TRANSLATORS: a non-free software license */ |
| return _("Proprietary"); |
| } |
| return license; |
| } |
| |
| static const gchar * |
| fu_util_release_urgency_to_string (FwupdReleaseUrgency release_urgency) |
| { |
| if (release_urgency == FWUPD_RELEASE_URGENCY_LOW) { |
| /* TRANSLATORS: the release urgency */ |
| return _("Low"); |
| } |
| if (release_urgency == FWUPD_RELEASE_URGENCY_MEDIUM) { |
| /* TRANSLATORS: the release urgency */ |
| return _("Medium"); |
| } |
| if (release_urgency == FWUPD_RELEASE_URGENCY_HIGH) { |
| /* TRANSLATORS: the release urgency */ |
| return _("High"); |
| } |
| if (release_urgency == FWUPD_RELEASE_URGENCY_CRITICAL) { |
| /* TRANSLATORS: the release urgency */ |
| return _("Critical"); |
| } |
| /* TRANSLATORS: unknown release urgency */ |
| return _("Unknown"); |
| } |
| |
| gchar * |
| fu_util_release_to_string (FwupdRelease *rel, guint idt) |
| { |
| GPtrArray *issues = fwupd_release_get_issues (rel); |
| GString *str = g_string_new (NULL); |
| guint64 flags = fwupd_release_get_flags (rel); |
| g_autoptr(GString) flags_str = g_string_new (NULL); |
| |
| g_return_val_if_fail (FWUPD_IS_RELEASE (rel), NULL); |
| |
| fu_common_string_append_kv (str, idt, fwupd_release_get_name (rel), NULL); |
| |
| /* TRANSLATORS: version number of new firmware */ |
| fu_common_string_append_kv (str, idt + 1 , _("New version"), |
| fwupd_release_get_version (rel)); |
| |
| if (fwupd_release_get_remote_id (rel) != NULL) { |
| /* TRANSLATORS: the server the file is coming from */ |
| fu_common_string_append_kv (str, idt + 1, _("Remote ID"), |
| fwupd_release_get_remote_id (rel)); |
| } |
| if (fwupd_release_get_branch (rel) != NULL) { |
| /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ |
| fu_common_string_append_kv (str, idt + 1, _("Branch"), |
| fwupd_release_get_branch (rel)); |
| } |
| if (fwupd_release_get_summary (rel) != NULL) { |
| /* TRANSLATORS: one line summary of device */ |
| fu_common_string_append_kv (str, idt + 1, _("Summary"), |
| fwupd_release_get_summary (rel)); |
| } |
| if (fwupd_release_get_name_variant_suffix (rel) != NULL) { |
| /* TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') */ |
| fu_common_string_append_kv (str, idt + 1, _("Variant"), |
| fwupd_release_get_name_variant_suffix (rel)); |
| } |
| /* TRANSLATORS: e.g. GPLv2+, Proprietary etc */ |
| fu_common_string_append_kv (str, idt + 1, _("License"), |
| fu_util_license_to_string (fwupd_release_get_license (rel))); |
| if (fwupd_release_get_size (rel) != 0) { |
| g_autofree gchar *tmp = NULL; |
| tmp = g_format_size (fwupd_release_get_size (rel)); |
| /* TRANSLATORS: file size of the download */ |
| fu_common_string_append_kv (str, idt + 1, _("Size"), tmp); |
| } |
| if (fwupd_release_get_created (rel) != 0) { |
| gint64 value = (gint64) fwupd_release_get_created (rel); |
| g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc (value); |
| g_autofree gchar *tmp = g_date_time_format (date, "%F"); |
| /* TRANSLATORS: when the update was built */ |
| fu_common_string_append_kv (str, idt + 1, _("Created"), tmp); |
| } |
| if (fwupd_release_get_urgency (rel) != FWUPD_RELEASE_URGENCY_UNKNOWN) { |
| FwupdReleaseUrgency tmp = fwupd_release_get_urgency (rel); |
| /* TRANSLATORS: how important the release is */ |
| fu_common_string_append_kv (str, idt + 1, _("Urgency"), |
| fu_util_release_urgency_to_string (tmp)); |
| } |
| if (fwupd_release_get_details_url (rel) != NULL) { |
| /* TRANSLATORS: more details about the update link */ |
| fu_common_string_append_kv (str, idt + 1, _("Details"), |
| fwupd_release_get_details_url (rel)); |
| } |
| if (fwupd_release_get_source_url (rel) != NULL) { |
| /* TRANSLATORS: source (as in code) link */ |
| fu_common_string_append_kv (str, idt + 1, _("Source"), |
| fwupd_release_get_source_url (rel)); |
| } |
| if (fwupd_release_get_vendor (rel) != NULL) { |
| /* TRANSLATORS: manufacturer of hardware */ |
| fu_common_string_append_kv (str, idt + 1, _("Vendor"), |
| fwupd_release_get_vendor (rel)); |
| } |
| if (fwupd_release_get_install_duration (rel) != 0) { |
| g_autofree gchar *tmp = fu_util_time_to_str (fwupd_release_get_install_duration (rel)); |
| /* TRANSLATORS: length of time the update takes to apply */ |
| fu_common_string_append_kv (str, idt + 1, _("Duration"), tmp); |
| } |
| if (fwupd_release_get_update_message (rel) != NULL) { |
| /* TRANSLATORS: helpful messages for the update */ |
| fu_common_string_append_kv (str, idt + 1, _("Update Message"), |
| fwupd_release_get_update_message (rel)); |
| } |
| |
| for (guint i = 0; i < 64; i++) { |
| if ((flags & ((guint64) 1 << i)) == 0) |
| continue; |
| g_string_append_printf (flags_str, "%s|", |
| fwupd_release_flag_to_string ((guint64) 1 << i)); |
| } |
| if (flags_str->len > 0) { |
| g_string_truncate (flags_str, flags_str->len - 1); |
| /* TRANSLATORS: release properties */ |
| fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); |
| } |
| if (fwupd_release_get_description (rel) != NULL) { |
| g_autofree gchar *desc = NULL; |
| desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL); |
| /* TRANSLATORS: multiline description of device */ |
| fu_common_string_append_kv (str, idt + 1, _("Description"), desc); |
| } |
| for (guint i = 0; i < issues->len; i++) { |
| const gchar *issue = g_ptr_array_index (issues, i); |
| if (i == 0) { |
| /* TRANSLATORS: issue fixed with the release, e.g. CVE */ |
| fu_common_string_append_kv (str, idt + 1, ngettext ("Issue", "Issues", issues->len), issue); |
| } else { |
| fu_common_string_append_kv (str, idt + 1, "", issue); |
| } |
| } |
| |
| return g_string_free (str, FALSE); |
| } |
| |
| gchar * |
| fu_util_remote_to_string (FwupdRemote *remote, guint idt) |
| { |
| GString *str = g_string_new (NULL); |
| FwupdRemoteKind kind = fwupd_remote_get_kind (remote); |
| FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind (remote); |
| const gchar *tmp; |
| gint priority; |
| |
| g_return_val_if_fail (FWUPD_IS_REMOTE (remote), NULL); |
| |
| fu_common_string_append_kv (str, idt, |
| fwupd_remote_get_title (remote), NULL); |
| |
| /* TRANSLATORS: remote identifier, e.g. lvfs-testing */ |
| fu_common_string_append_kv (str, idt + 1, _("Remote ID"), |
| fwupd_remote_get_id (remote)); |
| |
| /* TRANSLATORS: remote type, e.g. remote or local */ |
| fu_common_string_append_kv (str, idt + 1, _("Type"), |
| fwupd_remote_kind_to_string (kind)); |
| |
| /* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */ |
| if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { |
| fu_common_string_append_kv (str, idt + 1, _("Keyring"), |
| fwupd_keyring_kind_to_string (keyring_kind)); |
| } |
| |
| /* TRANSLATORS: if the remote is enabled */ |
| fu_common_string_append_kv (str, idt + 1, _("Enabled"), |
| fwupd_remote_get_enabled (remote) ? "true" : "false"); |
| |
| tmp = fwupd_remote_get_checksum (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: remote checksum */ |
| fu_common_string_append_kv (str, idt + 1, _("Checksum"), tmp); |
| } |
| |
| /* optional parameters */ |
| if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && |
| fwupd_remote_get_age (remote) > 0 && |
| fwupd_remote_get_age (remote) != G_MAXUINT64) { |
| const gchar *unit = "s"; |
| gdouble age = fwupd_remote_get_age (remote); |
| g_autofree gchar *age_str = NULL; |
| if (age > 60) { |
| age /= 60.f; |
| unit = "m"; |
| } |
| if (age > 60) { |
| age /= 60.f; |
| unit = "h"; |
| } |
| if (age > 24) { |
| age /= 24.f; |
| unit = "d"; |
| } |
| if (age > 7) { |
| age /= 7.f; |
| unit = "w"; |
| } |
| age_str = g_strdup_printf ("%.2f%s", age, unit); |
| /* TRANSLATORS: the age of the metadata */ |
| fu_common_string_append_kv (str, idt + 1, _("Age"), age_str); |
| } |
| priority = fwupd_remote_get_priority (remote); |
| if (priority != 0) { |
| g_autofree gchar *priority_str = NULL; |
| priority_str = g_strdup_printf ("%i", priority); |
| /* TRANSLATORS: the numeric priority */ |
| fu_common_string_append_kv (str, idt + 1, _("Priority"), priority_str); |
| } |
| tmp = fwupd_remote_get_username (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: remote filename base */ |
| fu_common_string_append_kv (str, idt + 1, _("Username"), tmp); |
| } |
| tmp = fwupd_remote_get_password (remote); |
| if (tmp != NULL) { |
| g_autofree gchar *hidden = g_strnfill (fu_common_strwidth (tmp), '*'); |
| /* TRANSLATORS: remote filename base */ |
| fu_common_string_append_kv (str, idt + 1, _("Password"), hidden); |
| } |
| tmp = fwupd_remote_get_filename_cache (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: filename of the local file */ |
| fu_common_string_append_kv (str, idt + 1, _("Filename"), tmp); |
| } |
| tmp = fwupd_remote_get_filename_cache_sig (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: filename of the local file */ |
| fu_common_string_append_kv (str, idt + 1, _("Filename Signature"), tmp); |
| } |
| tmp = fwupd_remote_get_metadata_uri (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: remote URI */ |
| fu_common_string_append_kv (str, idt + 1, _("Metadata URI"), tmp); |
| } |
| tmp = fwupd_remote_get_metadata_uri_sig (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: remote URI */ |
| fu_common_string_append_kv (str, idt + 1, _("Metadata Signature"), tmp); |
| } |
| tmp = fwupd_remote_get_firmware_base_uri (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: remote URI */ |
| fu_common_string_append_kv (str, idt + 1, _("Firmware Base URI"), tmp); |
| } |
| tmp = fwupd_remote_get_report_uri (remote); |
| if (tmp != NULL) { |
| /* TRANSLATORS: URI to send success/failure reports */ |
| fu_common_string_append_kv (str, idt + 1, _("Report URI"), tmp); |
| /* TRANSLATORS: Boolean value to automatically send reports */ |
| fu_common_string_append_kv (str, idt + 1, _("Automatic Reporting"), |
| fwupd_remote_get_automatic_reports (remote) ? "true" : "false"); |
| } |
| |
| return g_string_free (str, FALSE); |
| } |
| |
| static void |
| fu_security_attr_append_str (FwupdSecurityAttr *attr, GString *str, FuSecurityAttrToStringFlags flags) |
| { |
| g_autofree gchar *name = NULL; |
| |
| /* hide obsoletes by default */ |
| if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) && |
| (flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES) == 0) |
| return; |
| |
| name = fu_security_attr_get_name (attr); |
| if (name == NULL) |
| name = g_strdup (fwupd_security_attr_get_appstream_id (attr)); |
| if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
| g_string_append (str, "✦ "); |
| } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { |
| g_string_append (str, "✔ "); |
| } else { |
| g_string_append (str, "✘ "); |
| } |
| g_string_append_printf (str, "%s:", name); |
| for (guint i = fu_common_strwidth (name); i < 30; i++) |
| g_string_append (str, " "); |
| if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
| g_string_append_printf (str, "\033[37m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
| } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { |
| g_string_append_printf (str, "\033[32m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
| } else { |
| g_string_append_printf (str, "\033[31m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
| } |
| if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 && |
| fwupd_security_attr_get_url (attr) != NULL) { |
| g_string_append_printf (str, ": %s", |
| fwupd_security_attr_get_url (attr)); |
| } |
| if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
| /* TRANSLATORS: this is shown as a suffix for obsoleted tests */ |
| g_string_append_printf (str, " %s", _("(obsoleted)")); |
| } |
| g_string_append_printf (str, "\n"); |
| } |
| |
| gchar * |
| fu_util_security_attrs_to_string (GPtrArray *attrs, FuSecurityAttrToStringFlags strflags) |
| { |
| FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE; |
| const FwupdSecurityAttrFlags hpi_suffixes[] = { |
| FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, |
| FWUPD_SECURITY_ATTR_FLAG_NONE, |
| }; |
| GString *str = g_string_new (NULL); |
| gboolean low_help = FALSE; |
| gboolean runtime_help = FALSE; |
| gboolean pcr0_help = FALSE; |
| |
| for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { |
| gboolean has_header = FALSE; |
| for (guint i = 0; i < attrs->len; i++) { |
| FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| if (fwupd_security_attr_get_level (attr) != j) |
| continue; |
| if (!has_header) { |
| g_string_append_printf (str, "\n\033[1mHSI-%u\033[0m\n", j); |
| has_header = TRUE; |
| } |
| fu_security_attr_append_str (attr, str, strflags); |
| /* make sure they have at least HSI-1 */ |
| if (j < FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT && |
| !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) |
| low_help = TRUE; |
| |
| /* check for PCR0 not matching */ |
| if (g_strcmp0 (fwupd_security_attr_get_appstream_id (attr), |
| FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0 && |
| fwupd_security_attr_get_result (attr) == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) |
| pcr0_help = TRUE; |
| } |
| } |
| for (guint i = 0; i < attrs->len; i++) { |
| FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| flags |= fwupd_security_attr_get_flags (attr); |
| } |
| for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { |
| if (flags & hpi_suffixes[j]) { |
| g_string_append_printf (str, "\n\033[1m%s -%s\033[0m\n", |
| /* TRANSLATORS: this is the HSI suffix */ |
| _("Runtime Suffix"), |
| fwupd_security_attr_flag_to_suffix (hpi_suffixes[j])); |
| for (guint i = 0; i < attrs->len; i++) { |
| FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| if (!fwupd_security_attr_has_flag (attr, hpi_suffixes[j])) |
| continue; |
| if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) && |
| !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) |
| runtime_help = TRUE; |
| fu_security_attr_append_str (attr, str, strflags); |
| } |
| } |
| } |
| |
| if (low_help) { |
| g_string_append_printf (str, "\n%s\n » %s\n", |
| /* TRANSLATORS: this is instructions on how to improve the HSI security level */ |
| _("This system has a low HSI security level."), |
| "https://github.com/fwupd/fwupd/wiki/Low-host-security-level"); |
| } |
| if (runtime_help) { |
| g_string_append_printf (str, "\n%s\n » %s\n", |
| /* TRANSLATORS: this is instructions on how to improve the HSI suffix */ |
| _("This system has HSI runtime issues."), |
| "https://github.com/fwupd/fwupd/wiki/Host-security-ID-runtime-issues"); |
| } |
| |
| if (pcr0_help) { |
| g_string_append_printf (str, "\n%s\n » %s\n", |
| /* TRANSLATORS: this is more background on a security measurement problem */ |
| _("The TPM PCR0 differs from reconstruction."), |
| "https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction"); |
| |
| } |
| |
| return g_string_free (str, FALSE); |
| } |
| |
| gboolean |
| fu_util_send_report (FwupdClient *client, |
| const gchar *report_uri, |
| const gchar *data, |
| const gchar *sig, |
| gchar **uri, /* (nullable) (out) */ |
| GError **error) |
| { |
| const gchar *server_msg = NULL; |
| JsonNode *json_root; |
| JsonObject *json_object; |
| g_autofree gchar *str = NULL; |
| g_autoptr(GBytes) upload_response = NULL; |
| g_autoptr(JsonParser) json_parser = NULL; |
| |
| /* POST request */ |
| upload_response = fwupd_client_upload_bytes (client, report_uri, data, sig, |
| FWUPD_CLIENT_UPLOAD_FLAG_NONE, |
| NULL, error); |
| if (upload_response == NULL) |
| return FALSE; |
| |
| /* server returned nothing, and probably exploded in a ball of flames */ |
| if (g_bytes_get_size (upload_response) == 0) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_INVALID_FILE, |
| "Failed to upload to %s", |
| report_uri); |
| return FALSE; |
| } |
| |
| /* parse JSON reply */ |
| json_parser = json_parser_new (); |
| str = g_strndup (g_bytes_get_data (upload_response, NULL), |
| g_bytes_get_size (upload_response)); |
| if (!json_parser_load_from_data (json_parser, str, -1, error)) { |
| g_prefix_error (error, "Failed to parse JSON response from '%s': ", str); |
| return FALSE; |
| } |
| json_root = json_parser_get_root (json_parser); |
| if (json_root == NULL) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_PERMISSION_DENIED, |
| "JSON response was malformed: '%s'", str); |
| return FALSE; |
| } |
| json_object = json_node_get_object (json_root); |
| if (json_object == NULL) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_PERMISSION_DENIED, |
| "JSON response object was malformed: '%s'", str); |
| return FALSE; |
| } |
| |
| /* get any optional server message */ |
| if (json_object_has_member (json_object, "msg")) |
| server_msg = json_object_get_string_member (json_object, "msg"); |
| |
| /* server reported failed */ |
| if (!json_object_get_boolean_member (json_object, "success")) { |
| g_set_error (error, |
| FWUPD_ERROR, |
| FWUPD_ERROR_PERMISSION_DENIED, |
| "Server rejected report: %s", |
| server_msg != NULL ? server_msg : "unspecified"); |
| return FALSE; |
| } |
| |
| /* server wanted us to see the message */ |
| if (server_msg != NULL) { |
| g_debug ("server message: %s", server_msg); |
| if (g_strstr_len (server_msg, -1, "known issue") != NULL && |
| json_object_has_member (json_object, "uri")) { |
| if (uri != NULL) |
| *uri = g_strdup (json_object_get_string_member (json_object, "uri")); |
| } |
| } |
| |
| /* success */ |
| return TRUE; |
| } |
| |
| gint |
| fu_util_sort_devices_by_flags_cb (gconstpointer a, gconstpointer b) |
| { |
| FuDevice *dev_a = *((FuDevice **) a); |
| FuDevice *dev_b = *((FuDevice **) b); |
| |
| if ((!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && |
| fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || |
| (!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && |
| fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) |
| return -1; |
| if ((fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && |
| !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || |
| (fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && |
| !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) |
| return 1; |
| |
| return 0; |
| } |
| |
| static gint |
| fu_util_device_order_compare (FuDevice *device1, FuDevice *device2) |
| { |
| if (fu_device_get_order (device1) < fu_device_get_order (device2)) |
| return -1; |
| if (fu_device_get_order (device1) > fu_device_get_order (device2)) |
| return 1; |
| return 0; |
| } |
| |
| gint |
| fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b) |
| { |
| FuDevice *device_a = *((FuDevice **) a); |
| FuDevice *device_b = *((FuDevice **) b); |
| return fu_util_device_order_compare (device_a, device_b); |
| } |
| |
| |
| gboolean |
| fu_util_switch_branch_warning (FwupdDevice *dev, |
| FwupdRelease *rel, |
| gboolean assume_yes, |
| GError **error) |
| { |
| const gchar *desc_markup = NULL; |
| g_autofree gchar *desc_plain = NULL; |
| g_autofree gchar *title = 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); |
| |
| /* TRANSLATORS: show and ask user to confirm -- |
| * %1 is the old branch name, %2 is the new branch name */ |
| title = g_strdup_printf (_("Switch branch from %s to %s?"), |
| fu_util_branch_for_display (fwupd_device_get_branch (dev)), |
| fu_util_branch_for_display (fwupd_release_get_branch (rel))); |
| fu_util_warning_box (title, desc_full->str, 80); |
| if (!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; |
| } |
| |
| void |
| fu_util_show_unsupported_warn (void) |
| { |
| #ifndef SUPPORTED_BUILD |
| g_autofree gchar *fmt = NULL; |
| /* TRANSLATORS: this is a prefix on the console */ |
| fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_CLI_COLOR_YELLOW); |
| /* TRANSLATORS: unsupported build of the package */ |
| g_printerr ("%s %s\n", fmt, _("This package has not been validated, it may not work properly.")); |
| #endif |
| } |
| |
| #ifdef HAVE_LIBCURL_7_62_0 |
| G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup) |
| #endif |
| |
| gboolean |
| fu_util_is_url (const gchar *perhaps_url) |
| { |
| #ifdef HAVE_LIBCURL_7_62_0 |
| g_autoptr(CURLU) h = curl_url (); |
| return curl_url_set (h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK; |
| #else |
| return g_str_has_prefix (perhaps_url, "http://") || |
| g_str_has_prefix (perhaps_url, "https://"); |
| #endif |
| } |