Richard Hughes | 02c90d8 | 2018-08-09 12:13:03 +0100 | [diff] [blame] | 1 | /* |
Richard Hughes | 5c9b1fc | 2021-01-07 14:20:49 +0000 | [diff] [blame] | 2 | * Copyright (C) 2017 Richard Hughes <richard@hughsie.com> |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 3 | * |
Mario Limonciello | 51308e6 | 2018-05-28 20:05:46 -0500 | [diff] [blame] | 4 | * SPDX-License-Identifier: LGPL-2.1+ |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 5 | */ |
| 6 | |
Mario Limonciello | 0887b67 | 2019-08-27 10:16:18 -0500 | [diff] [blame] | 7 | #define G_LOG_DOMAIN "FuMain" |
| 8 | |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 9 | #include <config.h> |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 10 | |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 11 | #include <stdio.h> |
| 12 | #include <glib/gi18n.h> |
Richard Hughes | 1a3d3b3 | 2021-01-13 18:43:44 +0000 | [diff] [blame] | 13 | #ifdef HAVE_GUSB |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 14 | #include <gusb.h> |
Richard Hughes | 1a3d3b3 | 2021-01-13 18:43:44 +0000 | [diff] [blame] | 15 | #endif |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 16 | #include <xmlb.h> |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 17 | #include <fwupd.h> |
Richard Hughes | 67473f1 | 2021-01-07 17:02:23 +0000 | [diff] [blame] | 18 | #ifdef HAVE_LIBCURL |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 19 | #include <curl/curl.h> |
Richard Hughes | 67473f1 | 2021-01-07 17:02:23 +0000 | [diff] [blame] | 20 | #endif |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 21 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 22 | #include "fu-common.h" |
Mario Limonciello | 02085a0 | 2020-09-11 14:59:35 -0500 | [diff] [blame] | 23 | #include "fu-device-private.h" |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 24 | #include "fu-util-common.h" |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 25 | #include "fu-device.h" |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 26 | #include "fu-security-attr.h" |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 27 | #include "fu-security-attrs.h" |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 28 | |
Richard Hughes | 3d00522 | 2019-05-17 14:02:41 +0100 | [diff] [blame] | 29 | #ifdef HAVE_SYSTEMD |
| 30 | #include "fu-systemd.h" |
| 31 | #endif |
| 32 | |
Richard Hughes | fb9cfff | 2019-04-17 15:01:03 +0100 | [diff] [blame] | 33 | #define SYSTEMD_FWUPD_UNIT "fwupd.service" |
Mario Limonciello | ea527ca | 2019-05-07 15:50:46 -0500 | [diff] [blame] | 34 | #define SYSTEMD_SNAP_FWUPD_UNIT "snap.fwupd.fwupd.service" |
| 35 | |
Richard Hughes | 3d00522 | 2019-05-17 14:02:41 +0100 | [diff] [blame] | 36 | const gchar * |
Mario Limonciello | ea527ca | 2019-05-07 15:50:46 -0500 | [diff] [blame] | 37 | fu_util_get_systemd_unit (void) |
| 38 | { |
| 39 | if (g_getenv ("SNAP") != NULL) |
| 40 | return SYSTEMD_SNAP_FWUPD_UNIT; |
| 41 | return SYSTEMD_FWUPD_UNIT; |
| 42 | } |
Richard Hughes | fb9cfff | 2019-04-17 15:01:03 +0100 | [diff] [blame] | 43 | |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 44 | gchar * |
| 45 | fu_util_term_format (const gchar *text, FuUtilTermColor fg_color) |
| 46 | { |
| 47 | return g_strdup_printf ("\033[%um\033[1m%s\033[0m", fg_color, text); |
| 48 | } |
| 49 | |
Richard Hughes | d92ccca | 2019-05-20 11:28:31 +0100 | [diff] [blame] | 50 | #ifdef HAVE_SYSTEMD |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 51 | static const gchar * |
| 52 | fu_util_get_expected_command (const gchar *target) |
| 53 | { |
Richard Hughes | ad755e5 | 2019-05-16 12:53:15 +0100 | [diff] [blame] | 54 | if (g_strcmp0 (target, SYSTEMD_SNAP_FWUPD_UNIT) == 0) |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 55 | return "fwupd.fwupdmgr"; |
| 56 | return "fwupdmgr"; |
| 57 | } |
Richard Hughes | d92ccca | 2019-05-20 11:28:31 +0100 | [diff] [blame] | 58 | #endif |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 59 | |
| 60 | gboolean |
| 61 | fu_util_using_correct_daemon (GError **error) |
| 62 | { |
Richard Hughes | d92ccca | 2019-05-20 11:28:31 +0100 | [diff] [blame] | 63 | #ifdef HAVE_SYSTEMD |
Richard Hughes | 3d00522 | 2019-05-17 14:02:41 +0100 | [diff] [blame] | 64 | g_autofree gchar *default_target = NULL; |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 65 | g_autoptr(GError) error_local = NULL; |
| 66 | const gchar *target = fu_util_get_systemd_unit (); |
| 67 | |
Richard Hughes | 3d00522 | 2019-05-17 14:02:41 +0100 | [diff] [blame] | 68 | default_target = fu_systemd_get_default_target (&error_local); |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 69 | if (default_target == NULL) { |
| 70 | g_debug ("Systemd isn't accessible: %s\n", error_local->message); |
| 71 | return TRUE; |
| 72 | } |
Richard Hughes | 3d00522 | 2019-05-17 14:02:41 +0100 | [diff] [blame] | 73 | if (!fu_systemd_unit_check_exists (target, &error_local)) { |
| 74 | g_debug ("wrong target: %s\n", error_local->message); |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 75 | g_set_error (error, |
| 76 | FWUPD_ERROR, |
| 77 | FWUPD_ERROR_INVALID_ARGS, |
| 78 | /* TRANSLATORS: error message */ |
| 79 | _("Mismatched daemon and client, use %s instead"), |
| 80 | fu_util_get_expected_command (target)); |
| 81 | return FALSE; |
| 82 | } |
Richard Hughes | d92ccca | 2019-05-20 11:28:31 +0100 | [diff] [blame] | 83 | #endif |
Mario Limonciello | 88f8b7f | 2019-05-07 15:52:39 -0500 | [diff] [blame] | 84 | return TRUE; |
| 85 | } |
| 86 | |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 87 | void |
| 88 | fu_util_print_data (const gchar *title, const gchar *msg) |
| 89 | { |
| 90 | gsize title_len; |
| 91 | g_auto(GStrv) lines = NULL; |
| 92 | |
| 93 | if (msg == NULL) |
| 94 | return; |
| 95 | g_print ("%s:", title); |
| 96 | |
| 97 | /* pad */ |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 98 | title_len = fu_common_strwidth (title) + 1; |
Richard Hughes | f761640 | 2018-05-18 09:53:18 +0100 | [diff] [blame] | 99 | lines = g_strsplit (msg, "\n", -1); |
| 100 | for (guint j = 0; lines[j] != NULL; j++) { |
| 101 | for (gsize i = title_len; i < 25; i++) |
| 102 | g_print (" "); |
| 103 | g_print ("%s\n", lines[j]); |
| 104 | title_len = 0; |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | guint |
| 109 | fu_util_prompt_for_number (guint maxnum) |
| 110 | { |
| 111 | gint retval; |
| 112 | guint answer = 0; |
| 113 | |
| 114 | do { |
| 115 | char buffer[64]; |
| 116 | |
| 117 | /* swallow the \n at end of line too */ |
| 118 | if (!fgets (buffer, sizeof (buffer), stdin)) |
| 119 | break; |
| 120 | if (strlen (buffer) == sizeof (buffer) - 1) |
| 121 | continue; |
| 122 | |
| 123 | /* get a number */ |
| 124 | retval = sscanf (buffer, "%u", &answer); |
| 125 | |
| 126 | /* positive */ |
| 127 | if (retval == 1 && answer <= maxnum) |
| 128 | break; |
| 129 | |
| 130 | /* TRANSLATORS: the user isn't reading the question */ |
| 131 | g_print (_("Please enter a number from 0 to %u: "), maxnum); |
| 132 | } while (TRUE); |
| 133 | return answer; |
| 134 | } |
| 135 | |
| 136 | gboolean |
| 137 | fu_util_prompt_for_boolean (gboolean def) |
| 138 | { |
| 139 | do { |
| 140 | char buffer[4]; |
| 141 | if (!fgets (buffer, sizeof (buffer), stdin)) |
| 142 | continue; |
| 143 | if (strlen (buffer) == sizeof (buffer) - 1) |
| 144 | continue; |
| 145 | if (g_strcmp0 (buffer, "\n") == 0) |
| 146 | return def; |
| 147 | buffer[0] = g_ascii_toupper (buffer[0]); |
| 148 | if (g_strcmp0 (buffer, "Y\n") == 0) |
| 149 | return TRUE; |
| 150 | if (g_strcmp0 (buffer, "N\n") == 0) |
| 151 | return FALSE; |
| 152 | } while (TRUE); |
| 153 | return FALSE; |
| 154 | } |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 155 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 156 | static gboolean |
| 157 | fu_util_traverse_tree (GNode *n, gpointer data) |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 158 | { |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 159 | guint idx = g_node_depth (n) - 1; |
| 160 | g_autofree gchar *tmp = NULL; |
| 161 | g_auto(GStrv) split = NULL; |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 162 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 163 | /* get split lines */ |
| 164 | if (FWUPD_IS_DEVICE (n->data)) { |
| 165 | FwupdDevice *dev = FWUPD_DEVICE (n->data); |
| 166 | tmp = fu_util_device_to_string (dev, idx); |
| 167 | } else if (FWUPD_IS_REMOTE (n->data)) { |
| 168 | FwupdRemote *remote = FWUPD_REMOTE (n->data); |
| 169 | tmp = fu_util_remote_to_string (remote, idx); |
| 170 | } else if (FWUPD_IS_RELEASE (n->data)) { |
| 171 | FwupdRelease *release = FWUPD_RELEASE (n->data); |
| 172 | tmp = fu_util_release_to_string (release, idx); |
| 173 | g_debug ("%s", tmp); |
| 174 | } |
| 175 | |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 176 | /* root node */ |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 177 | if (n->data == NULL && g_getenv ("FWUPD_VERBOSE") == NULL) { |
Mario Limonciello | 20cc9ee | 2019-09-05 07:27:26 -0500 | [diff] [blame] | 178 | const gchar *str = data; |
| 179 | g_print ("%s\n│\n", str != NULL ? str : "○"); |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 180 | return FALSE; |
| 181 | } |
Mario Limonciello | 20cc9ee | 2019-09-05 07:27:26 -0500 | [diff] [blame] | 182 | |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 183 | if (n->parent == NULL) |
| 184 | return FALSE; |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 185 | |
Mario Limonciello | 17e9bf5 | 2019-08-27 09:03:19 -0500 | [diff] [blame] | 186 | if (tmp == NULL) |
| 187 | return FALSE; |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 188 | split = g_strsplit (tmp, "\n", -1); |
| 189 | for (guint i = 0; split[i] != NULL; i++) { |
| 190 | g_autoptr(GString) str = g_string_new (NULL); |
| 191 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 192 | /* header */ |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 193 | if (i == 0) { |
| 194 | if (g_node_next_sibling (n) == NULL) |
| 195 | g_string_prepend (str, "└─"); |
| 196 | else |
| 197 | g_string_prepend (str, "├─"); |
| 198 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 199 | /* properties */ |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 200 | } else { |
| 201 | g_string_prepend (str, n->children == NULL ? " " : " │"); |
| 202 | g_string_prepend (str, g_node_next_sibling (n) == NULL ? " " : "│"); |
| 203 | g_string_append (str, " "); |
| 204 | } |
| 205 | |
| 206 | /* ancestors */ |
| 207 | for (GNode *c = n->parent; c->parent != NULL; c = c->parent) { |
| 208 | if (g_node_next_sibling (c) != NULL || idx == 0) { |
| 209 | g_string_prepend (str, "│ "); |
| 210 | continue; |
| 211 | } |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 212 | g_string_prepend (str, " "); |
Richard Hughes | c67b4e7 | 2019-08-27 09:48:42 +0100 | [diff] [blame] | 213 | } |
| 214 | |
| 215 | /* empty line */ |
| 216 | if (split[i][0] == '\0') { |
| 217 | g_print ("%s\n", str->str); |
| 218 | continue; |
| 219 | } |
| 220 | |
| 221 | /* dump to the console */ |
| 222 | g_string_append (str, split[i] + (idx * 2)); |
| 223 | g_print ("%s\n", str->str); |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 224 | } |
| 225 | |
Mario Limonciello | ba9e5b9 | 2018-05-21 16:02:32 -0500 | [diff] [blame] | 226 | return FALSE; |
| 227 | } |
Mario Limonciello | d1775bc | 2018-07-17 00:28:52 -0500 | [diff] [blame] | 228 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 229 | void |
| 230 | fu_util_print_tree (GNode *n, gpointer data) |
| 231 | { |
| 232 | g_node_traverse (n, G_PRE_ORDER, G_TRAVERSE_ALL, -1, |
| 233 | fu_util_traverse_tree, data); |
| 234 | } |
| 235 | |
Richard Hughes | 95c7876 | 2020-01-06 14:02:38 +0000 | [diff] [blame] | 236 | static gboolean |
| 237 | fu_util_is_interesting_child (FwupdDevice *dev) |
| 238 | { |
| 239 | GPtrArray *children = fwupd_device_get_children (dev); |
| 240 | for (guint i = 0; i < children->len; i++) { |
| 241 | FwupdDevice *child = g_ptr_array_index (children, i); |
| 242 | if (fu_util_is_interesting_device (child)) |
| 243 | return TRUE; |
| 244 | } |
| 245 | return FALSE; |
| 246 | } |
| 247 | |
Mario Limonciello | d1775bc | 2018-07-17 00:28:52 -0500 | [diff] [blame] | 248 | gboolean |
| 249 | fu_util_is_interesting_device (FwupdDevice *dev) |
| 250 | { |
| 251 | if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE)) |
| 252 | return TRUE; |
| 253 | if (fwupd_device_get_update_error (dev) != NULL) |
| 254 | return TRUE; |
Mario Limonciello | 3996af3 | 2019-09-05 10:28:52 -0500 | [diff] [blame] | 255 | /* device not plugged in, get-details */ |
| 256 | if (fwupd_device_get_flags (dev) == 0) |
| 257 | return TRUE; |
Richard Hughes | 95c7876 | 2020-01-06 14:02:38 +0000 | [diff] [blame] | 258 | if (fu_util_is_interesting_child (dev)) |
| 259 | return TRUE; |
Mario Limonciello | d1775bc | 2018-07-17 00:28:52 -0500 | [diff] [blame] | 260 | return FALSE; |
| 261 | } |
Richard Hughes | 798cb06 | 2018-08-30 14:17:42 +0100 | [diff] [blame] | 262 | |
| 263 | gchar * |
| 264 | fu_util_get_user_cache_path (const gchar *fn) |
| 265 | { |
Mario Limonciello | b390b14 | 2019-07-15 21:05:44 -0500 | [diff] [blame] | 266 | const gchar *root = g_get_user_cache_dir (); |
Richard Hughes | 798cb06 | 2018-08-30 14:17:42 +0100 | [diff] [blame] | 267 | g_autofree gchar *basename = g_path_get_basename (fn); |
| 268 | g_autofree gchar *cachedir_legacy = NULL; |
| 269 | |
Mario Limonciello | b390b14 | 2019-07-15 21:05:44 -0500 | [diff] [blame] | 270 | /* if run from a systemd unit, use the cache directory set there */ |
| 271 | if (g_getenv ("CACHE_DIRECTORY") != NULL) |
| 272 | root = g_getenv ("CACHE_DIRECTORY"); |
| 273 | |
Richard Hughes | 798cb06 | 2018-08-30 14:17:42 +0100 | [diff] [blame] | 274 | /* return the legacy path if it exists rather than renaming it to |
| 275 | * prevent problems when using old and new versions of fwupd */ |
Mario Limonciello | b390b14 | 2019-07-15 21:05:44 -0500 | [diff] [blame] | 276 | cachedir_legacy = g_build_filename (root, "fwupdmgr", NULL); |
Richard Hughes | 798cb06 | 2018-08-30 14:17:42 +0100 | [diff] [blame] | 277 | if (g_file_test (cachedir_legacy, G_FILE_TEST_IS_DIR)) |
| 278 | return g_build_filename (cachedir_legacy, basename, NULL); |
| 279 | |
Mario Limonciello | b390b14 | 2019-07-15 21:05:44 -0500 | [diff] [blame] | 280 | return g_build_filename (root, "fwupd", basename, NULL); |
Richard Hughes | 798cb06 | 2018-08-30 14:17:42 +0100 | [diff] [blame] | 281 | } |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 282 | |
| 283 | gchar * |
Mario Limonciello | 429a512 | 2019-10-31 10:45:31 -0500 | [diff] [blame] | 284 | fu_util_get_versions (void) |
| 285 | { |
| 286 | GString *string = g_string_new (""); |
Mario Limonciello | 429a512 | 2019-10-31 10:45:31 -0500 | [diff] [blame] | 287 | |
Richard Hughes | fe4b3ea | 2020-03-30 10:53:20 +0100 | [diff] [blame] | 288 | g_string_append_printf (string, "client version:\t%s\n", SOURCE_VERSION); |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 289 | g_string_append_printf (string, |
| 290 | "compile-time dependency versions\n"); |
Richard Hughes | 1a3d3b3 | 2021-01-13 18:43:44 +0000 | [diff] [blame] | 291 | #ifdef HAVE_GUSB |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 292 | g_string_append_printf (string, |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 293 | "\tgusb:\t%d.%d.%d\n", |
| 294 | G_USB_MAJOR_VERSION, |
| 295 | G_USB_MINOR_VERSION, |
| 296 | G_USB_MICRO_VERSION); |
Richard Hughes | 1a3d3b3 | 2021-01-13 18:43:44 +0000 | [diff] [blame] | 297 | #endif |
Mario Limonciello | 2d4b7a5 | 2018-09-12 22:08:04 -0500 | [diff] [blame] | 298 | #ifdef EFIVAR_LIBRARY_VERSION |
| 299 | g_string_append_printf (string, |
| 300 | "\tefivar:\t%s", |
| 301 | EFIVAR_LIBRARY_VERSION); |
| 302 | #endif |
| 303 | return g_string_free (string, FALSE); |
| 304 | } |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 305 | |
| 306 | static gboolean |
| 307 | fu_util_update_shutdown (GError **error) |
| 308 | { |
| 309 | g_autoptr(GDBusConnection) connection = NULL; |
| 310 | g_autoptr(GVariant) val = NULL; |
| 311 | |
| 312 | connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); |
| 313 | if (connection == NULL) |
| 314 | return FALSE; |
| 315 | |
Marcin Sucharski | de2b70b | 2019-02-24 00:28:10 +0100 | [diff] [blame] | 316 | #ifdef HAVE_LOGIND |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 317 | /* shutdown using logind */ |
| 318 | val = g_dbus_connection_call_sync (connection, |
| 319 | "org.freedesktop.login1", |
| 320 | "/org/freedesktop/login1", |
| 321 | "org.freedesktop.login1.Manager", |
| 322 | "PowerOff", |
| 323 | g_variant_new ("(b)", TRUE), |
| 324 | NULL, |
| 325 | G_DBUS_CALL_FLAGS_NONE, |
| 326 | -1, |
| 327 | NULL, |
| 328 | error); |
| 329 | #elif defined(HAVE_CONSOLEKIT) |
| 330 | /* shutdown using ConsoleKit */ |
| 331 | val = g_dbus_connection_call_sync (connection, |
| 332 | "org.freedesktop.ConsoleKit", |
| 333 | "/org/freedesktop/ConsoleKit/Manager", |
| 334 | "org.freedesktop.ConsoleKit.Manager", |
| 335 | "Stop", |
| 336 | NULL, |
| 337 | NULL, |
| 338 | G_DBUS_CALL_FLAGS_NONE, |
| 339 | -1, |
| 340 | NULL, |
| 341 | error); |
| 342 | #else |
| 343 | g_set_error_literal (error, |
| 344 | FWUPD_ERROR, |
| 345 | FWUPD_ERROR_INVALID_ARGS, |
| 346 | "No supported backend compiled in to perform the operation."); |
| 347 | #endif |
| 348 | return val != NULL; |
| 349 | } |
| 350 | |
Richard Hughes | 4499d19 | 2019-03-08 11:44:44 +0000 | [diff] [blame] | 351 | gboolean |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 352 | fu_util_update_reboot (GError **error) |
| 353 | { |
| 354 | g_autoptr(GDBusConnection) connection = NULL; |
| 355 | g_autoptr(GVariant) val = NULL; |
| 356 | |
| 357 | connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); |
| 358 | if (connection == NULL) |
| 359 | return FALSE; |
| 360 | |
Marcin Sucharski | de2b70b | 2019-02-24 00:28:10 +0100 | [diff] [blame] | 361 | #ifdef HAVE_LOGIND |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 362 | /* reboot using logind */ |
| 363 | val = g_dbus_connection_call_sync (connection, |
| 364 | "org.freedesktop.login1", |
| 365 | "/org/freedesktop/login1", |
| 366 | "org.freedesktop.login1.Manager", |
| 367 | "Reboot", |
| 368 | g_variant_new ("(b)", TRUE), |
| 369 | NULL, |
| 370 | G_DBUS_CALL_FLAGS_NONE, |
| 371 | -1, |
| 372 | NULL, |
| 373 | error); |
| 374 | #elif defined(HAVE_CONSOLEKIT) |
| 375 | /* reboot using ConsoleKit */ |
| 376 | val = g_dbus_connection_call_sync (connection, |
| 377 | "org.freedesktop.ConsoleKit", |
| 378 | "/org/freedesktop/ConsoleKit/Manager", |
| 379 | "org.freedesktop.ConsoleKit.Manager", |
| 380 | "Restart", |
| 381 | NULL, |
| 382 | NULL, |
| 383 | G_DBUS_CALL_FLAGS_NONE, |
| 384 | -1, |
| 385 | NULL, |
| 386 | error); |
| 387 | #else |
| 388 | g_set_error_literal (error, |
| 389 | FWUPD_ERROR, |
| 390 | FWUPD_ERROR_INVALID_ARGS, |
| 391 | "No supported backend compiled in to perform the operation."); |
| 392 | #endif |
| 393 | return val != NULL; |
| 394 | } |
| 395 | |
| 396 | gboolean |
Mario Limonciello | 98b9516 | 2019-10-30 09:20:43 -0500 | [diff] [blame] | 397 | fu_util_prompt_warning (FwupdDevice *device, const gchar *machine, GError **error) |
| 398 | { |
| 399 | FwupdDeviceFlags flags; |
| 400 | g_autofree gchar *str = NULL; |
| 401 | |
| 402 | /* device is already in bootloader mode */ |
| 403 | flags = fwupd_device_get_flags (device); |
| 404 | if (flags & FWUPD_DEVICE_FLAG_IS_BOOTLOADER) |
| 405 | return TRUE; |
| 406 | |
| 407 | /* device may reboot */ |
| 408 | if ((flags & FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) == 0) { |
| 409 | /* TRANSLATORS: warn the user before updating, %1 is a device name */ |
| 410 | str = g_strdup_printf (_("%s and all connected devices may not be usable while updating."), |
| 411 | fwupd_device_get_name (device)); |
| 412 | /* device can get bricked */ |
| 413 | } else if ((flags & FWUPD_DEVICE_FLAG_SELF_RECOVERY) == 0) { |
| 414 | /* external device */ |
| 415 | if ((flags & FWUPD_DEVICE_FLAG_INTERNAL) == 0) { |
| 416 | /* TRANSLATORS: warn the user before updating, %1 is a device name */ |
| 417 | str = g_strdup_printf (_("%s must remain connected for the duration of the update to avoid damage."), |
| 418 | fwupd_device_get_name (device)); |
| 419 | } else if (flags & FWUPD_DEVICE_FLAG_REQUIRE_AC) { |
| 420 | /* TRANSLATORS: warn the user before updating, %1 is a machine name */ |
| 421 | str = g_strdup_printf (_("%s must remain plugged into a power source for the duration of the update to avoid damage."), |
| 422 | machine); |
| 423 | } |
| 424 | } |
| 425 | if (str != NULL) { |
| 426 | g_print ("%s %s [Y|n]: ", |
| 427 | str, |
| 428 | /* TRANSLATORS: prompt to apply the update */ |
| 429 | _("Continue with update?")); |
| 430 | if (!fu_util_prompt_for_boolean (TRUE)) { |
| 431 | g_set_error_literal (error, |
| 432 | FWUPD_ERROR, |
| 433 | FWUPD_ERROR_NOTHING_TO_DO, |
| 434 | "Request canceled"); |
| 435 | return FALSE; |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | return TRUE; |
| 440 | } |
| 441 | |
| 442 | gboolean |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 443 | fu_util_prompt_complete (FwupdDeviceFlags flags, gboolean prompt, GError **error) |
| 444 | { |
| 445 | if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { |
| 446 | if (prompt) { |
Mario Limonciello | 5d94afa | 2019-12-11 06:45:49 -0600 | [diff] [blame] | 447 | g_print ("\n%s %s [y|N]: ", |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 448 | /* TRANSLATORS: explain why we want to shutdown */ |
| 449 | _("An update requires the system to shutdown to complete."), |
| 450 | /* TRANSLATORS: shutdown to apply the update */ |
| 451 | _("Shutdown now?")); |
Mario Limonciello | c5efb21 | 2019-11-25 07:45:18 -0600 | [diff] [blame] | 452 | if (!fu_util_prompt_for_boolean (FALSE)) |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 453 | return TRUE; |
| 454 | } |
| 455 | return fu_util_update_shutdown (error); |
| 456 | } |
| 457 | if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { |
| 458 | if (prompt) { |
Mario Limonciello | 5d94afa | 2019-12-11 06:45:49 -0600 | [diff] [blame] | 459 | g_print ("\n%s %s [y|N]: ", |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 460 | /* TRANSLATORS: explain why we want to reboot */ |
| 461 | _("An update requires a reboot to complete."), |
| 462 | /* TRANSLATORS: reboot to apply the update */ |
| 463 | _("Restart now?")); |
Mario Limonciello | c5efb21 | 2019-11-25 07:45:18 -0600 | [diff] [blame] | 464 | if (!fu_util_prompt_for_boolean (FALSE)) |
Mario Limonciello | 3f243a9 | 2019-01-21 22:05:23 -0600 | [diff] [blame] | 465 | return TRUE; |
| 466 | } |
| 467 | return fu_util_update_reboot (error); |
| 468 | } |
| 469 | |
| 470 | return TRUE; |
| 471 | } |
Richard Hughes | c77e111 | 2019-03-01 10:16:26 +0000 | [diff] [blame] | 472 | |
| 473 | static void |
| 474 | fu_util_cmd_free (FuUtilCmd *item) |
| 475 | { |
| 476 | g_free (item->name); |
| 477 | g_free (item->arguments); |
| 478 | g_free (item->description); |
| 479 | g_free (item); |
| 480 | } |
| 481 | |
| 482 | GPtrArray * |
| 483 | fu_util_cmd_array_new (void) |
| 484 | { |
| 485 | return g_ptr_array_new_with_free_func ((GDestroyNotify) fu_util_cmd_free); |
| 486 | } |
| 487 | |
| 488 | static gint |
| 489 | fu_util_cmd_sort_cb (FuUtilCmd **item1, FuUtilCmd **item2) |
| 490 | { |
| 491 | return g_strcmp0 ((*item1)->name, (*item2)->name); |
| 492 | } |
| 493 | |
| 494 | void |
| 495 | fu_util_cmd_array_sort (GPtrArray *array) |
| 496 | { |
| 497 | g_ptr_array_sort (array, (GCompareFunc) fu_util_cmd_sort_cb); |
| 498 | } |
| 499 | |
| 500 | void |
| 501 | fu_util_cmd_array_add (GPtrArray *array, |
| 502 | const gchar *name, |
| 503 | const gchar *arguments, |
| 504 | const gchar *description, |
| 505 | FuUtilCmdFunc callback) |
| 506 | { |
| 507 | g_auto(GStrv) names = NULL; |
| 508 | |
| 509 | g_return_if_fail (name != NULL); |
| 510 | g_return_if_fail (description != NULL); |
| 511 | g_return_if_fail (callback != NULL); |
| 512 | |
| 513 | /* add each one */ |
| 514 | names = g_strsplit (name, ",", -1); |
| 515 | for (guint i = 0; names[i] != NULL; i++) { |
| 516 | FuUtilCmd *item = g_new0 (FuUtilCmd, 1); |
| 517 | item->name = g_strdup (names[i]); |
| 518 | if (i == 0) { |
| 519 | item->description = g_strdup (description); |
| 520 | } else { |
| 521 | /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ |
| 522 | item->description = g_strdup_printf (_("Alias to %s"), |
| 523 | names[0]); |
| 524 | } |
| 525 | item->arguments = g_strdup (arguments); |
| 526 | item->callback = callback; |
| 527 | g_ptr_array_add (array, item); |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | gboolean |
| 532 | fu_util_cmd_array_run (GPtrArray *array, |
| 533 | FuUtilPrivate *priv, |
| 534 | const gchar *command, |
| 535 | gchar **values, |
| 536 | GError **error) |
| 537 | { |
Mario Limonciello | 428ed8a | 2020-03-03 13:57:46 -0600 | [diff] [blame] | 538 | g_auto(GStrv) values_copy = g_new0 (gchar *, g_strv_length (values) + 1); |
| 539 | |
| 540 | /* clear out bash completion sentinel */ |
| 541 | for (guint i = 0; values[i] != NULL; i++) { |
| 542 | if (g_strcmp0 (values[i], "{") == 0) |
| 543 | break; |
| 544 | values_copy[i] = g_strdup (values[i]); |
| 545 | } |
| 546 | |
Richard Hughes | c77e111 | 2019-03-01 10:16:26 +0000 | [diff] [blame] | 547 | /* find command */ |
| 548 | for (guint i = 0; i < array->len; i++) { |
| 549 | FuUtilCmd *item = g_ptr_array_index (array, i); |
| 550 | if (g_strcmp0 (item->name, command) == 0) |
Mario Limonciello | 428ed8a | 2020-03-03 13:57:46 -0600 | [diff] [blame] | 551 | return item->callback (priv, values_copy, error); |
Richard Hughes | c77e111 | 2019-03-01 10:16:26 +0000 | [diff] [blame] | 552 | } |
| 553 | |
| 554 | /* not found */ |
| 555 | g_set_error_literal (error, |
| 556 | FWUPD_ERROR, |
| 557 | FWUPD_ERROR_INVALID_ARGS, |
| 558 | /* TRANSLATORS: error message */ |
| 559 | _("Command not found")); |
| 560 | return FALSE; |
| 561 | } |
| 562 | |
| 563 | gchar * |
| 564 | fu_util_cmd_array_to_string (GPtrArray *array) |
| 565 | { |
| 566 | gsize len; |
| 567 | const gsize max_len = 35; |
| 568 | GString *string; |
| 569 | |
| 570 | /* print each command */ |
| 571 | string = g_string_new (""); |
| 572 | for (guint i = 0; i < array->len; i++) { |
| 573 | FuUtilCmd *item = g_ptr_array_index (array, i); |
| 574 | g_string_append (string, " "); |
| 575 | g_string_append (string, item->name); |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 576 | len = fu_common_strwidth (item->name) + 2; |
Richard Hughes | c77e111 | 2019-03-01 10:16:26 +0000 | [diff] [blame] | 577 | if (item->arguments != NULL) { |
| 578 | g_string_append (string, " "); |
| 579 | g_string_append (string, item->arguments); |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 580 | len += fu_common_strwidth (item->arguments) + 1; |
Richard Hughes | c77e111 | 2019-03-01 10:16:26 +0000 | [diff] [blame] | 581 | } |
| 582 | if (len < max_len) { |
| 583 | for (gsize j = len; j < max_len + 1; j++) |
| 584 | g_string_append_c (string, ' '); |
| 585 | g_string_append (string, item->description); |
| 586 | g_string_append_c (string, '\n'); |
| 587 | } else { |
| 588 | g_string_append_c (string, '\n'); |
| 589 | for (gsize j = 0; j < max_len + 1; j++) |
| 590 | g_string_append_c (string, ' '); |
| 591 | g_string_append (string, item->description); |
| 592 | g_string_append_c (string, '\n'); |
| 593 | } |
| 594 | } |
| 595 | |
| 596 | /* remove trailing newline */ |
| 597 | if (string->len > 0) |
| 598 | g_string_set_size (string, string->len - 1); |
| 599 | |
| 600 | return g_string_free (string, FALSE); |
| 601 | } |
Richard Hughes | 7120667 | 2019-03-01 10:24:57 +0000 | [diff] [blame] | 602 | |
Richard Hughes | 1c62ade | 2020-10-15 14:58:19 +0100 | [diff] [blame] | 603 | const gchar * |
Richard Hughes | 89ee9ed | 2021-01-20 16:15:29 +0000 | [diff] [blame] | 604 | fu_util_branch_for_display (const gchar *branch) |
Richard Hughes | 1c62ade | 2020-10-15 14:58:19 +0100 | [diff] [blame] | 605 | { |
Richard Hughes | 89ee9ed | 2021-01-20 16:15:29 +0000 | [diff] [blame] | 606 | if (branch == NULL) { |
Richard Hughes | 1c62ade | 2020-10-15 14:58:19 +0100 | [diff] [blame] | 607 | /* TRANSLATORS: this is the default branch name when unset */ |
| 608 | return _("default"); |
| 609 | } |
Richard Hughes | 89ee9ed | 2021-01-20 16:15:29 +0000 | [diff] [blame] | 610 | return branch; |
Richard Hughes | 1c62ade | 2020-10-15 14:58:19 +0100 | [diff] [blame] | 611 | } |
| 612 | |
Richard Hughes | 02ac92c | 2019-03-29 17:56:46 +0000 | [diff] [blame] | 613 | gchar * |
| 614 | fu_util_release_get_name (FwupdRelease *release) |
| 615 | { |
| 616 | const gchar *name = fwupd_release_get_name (release); |
| 617 | GPtrArray *cats = fwupd_release_get_categories (release); |
| 618 | |
| 619 | for (guint i = 0; i < cats->len; i++) { |
| 620 | const gchar *cat = g_ptr_array_index (cats, i); |
| 621 | if (g_strcmp0 (cat, "X-Device") == 0) { |
| 622 | /* TRANSLATORS: a specific part of hardware, |
| 623 | * the first %s is the device name, e.g. 'Unifying Receiver` */ |
| 624 | return g_strdup_printf (_("%s Device Update"), name); |
| 625 | } |
Richard Hughes | 04afb39 | 2020-08-17 16:20:24 +0100 | [diff] [blame] | 626 | if (g_strcmp0 (cat, "X-Configuration") == 0) { |
| 627 | /* TRANSLATORS: a specific part of hardware, |
| 628 | * the first %s is the device name, e.g. 'Secure Boot` */ |
| 629 | return g_strdup_printf (_("%s Configuration Update"), name); |
| 630 | } |
Richard Hughes | 02ac92c | 2019-03-29 17:56:46 +0000 | [diff] [blame] | 631 | if (g_strcmp0 (cat, "X-System") == 0) { |
| 632 | /* TRANSLATORS: the entire system, e.g. all internal devices, |
| 633 | * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 634 | return g_strdup_printf (_("%s System Update"), name); |
| 635 | } |
| 636 | if (g_strcmp0 (cat, "X-EmbeddedController") == 0) { |
| 637 | /* TRANSLATORS: the EC is typically the keyboard controller chip, |
| 638 | * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 639 | return g_strdup_printf (_("%s Embedded Controller Update"), name); |
| 640 | } |
| 641 | if (g_strcmp0 (cat, "X-ManagementEngine") == 0) { |
| 642 | /* TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, |
| 643 | * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 644 | return g_strdup_printf (_("%s ME Update"), name); |
| 645 | } |
Richard Hughes | b30f82f | 2019-05-20 09:29:24 +0100 | [diff] [blame] | 646 | if (g_strcmp0 (cat, "X-CorporateManagementEngine") == 0) { |
| 647 | /* TRANSLATORS: ME stands for Management Engine (with Intel AMT), |
| 648 | * where the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 649 | return g_strdup_printf (_("%s Corporate ME Update"), name); |
| 650 | } |
| 651 | if (g_strcmp0 (cat, "X-ConsumerManagementEngine") == 0) { |
| 652 | /* TRANSLATORS: ME stands for Management Engine, where |
| 653 | * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 654 | return g_strdup_printf (_("%s Consumer ME Update"), name); |
| 655 | } |
Richard Hughes | 02ac92c | 2019-03-29 17:56:46 +0000 | [diff] [blame] | 656 | if (g_strcmp0 (cat, "X-Controller") == 0) { |
| 657 | /* TRANSLATORS: the controller is a device that has other devices |
| 658 | * plugged into it, for example ThunderBolt, FireWire or USB, |
| 659 | * the first %s is the device name, e.g. 'Intel ThunderBolt` */ |
| 660 | return g_strdup_printf (_("%s Controller Update"), name); |
| 661 | } |
Richard Hughes | 011511e | 2019-08-06 09:49:06 +0100 | [diff] [blame] | 662 | if (g_strcmp0 (cat, "X-ThunderboltController") == 0) { |
| 663 | /* TRANSLATORS: the Thunderbolt controller is a device that |
| 664 | * has other high speed Thunderbolt devices plugged into it; |
| 665 | * the first %s is the system name, e.g. 'ThinkPad P50` */ |
| 666 | return g_strdup_printf (_("%s Thunderbolt Controller Update"), name); |
| 667 | } |
Mario Limonciello | 7a9bb7e | 2020-04-02 10:28:41 -0500 | [diff] [blame] | 668 | if (g_strcmp0 (cat, "X-CpuMicrocode") == 0) { |
| 669 | /* TRANSLATORS: the CPU microcode is firmware loaded onto the CPU |
| 670 | * at system bootup */ |
| 671 | return g_strdup_printf (_("%s CPU Microcode Update"), name); |
| 672 | } |
Richard Hughes | 2d3b8c8 | 2021-03-22 17:40:06 +0000 | [diff] [blame] | 673 | if (g_strcmp0 (cat, "X-Battery") == 0) { |
| 674 | /* TRANSLATORS: battery refers to the system power source */ |
| 675 | return g_strdup_printf (_("%s Battery Update"), name); |
| 676 | } |
| 677 | if (g_strcmp0 (cat, "X-Camera") == 0) { |
| 678 | /* TRANSLATORS: camera can refer to the laptop internal |
| 679 | * camera in the bezel or external USB webcam */ |
| 680 | return g_strdup_printf (_("%s Camera Update"), name); |
| 681 | } |
| 682 | if (g_strcmp0 (cat, "X-TPM") == 0) { |
| 683 | /* TRANSLATORS: TPM refers to a Trusted Platform Module */ |
| 684 | return g_strdup_printf (_("%s TPM Update"), name); |
| 685 | } |
| 686 | if (g_strcmp0 (cat, "X-Touchpad") == 0) { |
| 687 | /* TRANSLATORS: TouchPad refers to a flat input device */ |
| 688 | return g_strdup_printf (_("%s Touchpad Update"), name); |
| 689 | } |
| 690 | if (g_strcmp0 (cat, "X-Mouse") == 0) { |
| 691 | /* TRANSLATORS: Mouse refers to a handheld input device */ |
| 692 | return g_strdup_printf (_("%s Mouse Update"), name); |
| 693 | } |
| 694 | if (g_strcmp0 (cat, "X-Keyboard") == 0) { |
| 695 | /* TRANSLATORS: Keyboard refers to an input device for typing */ |
| 696 | return g_strdup_printf (_("%s Keyboard Update"), name); |
| 697 | } |
Richard Hughes | 02ac92c | 2019-03-29 17:56:46 +0000 | [diff] [blame] | 698 | } |
| 699 | |
| 700 | /* TRANSLATORS: this is the fallback where we don't know if the release |
| 701 | * is updating the system, the device, or a device class, or something else -- |
| 702 | * the first %s is the device name, e.g. 'ThinkPad P50` */ |
| 703 | return g_strdup_printf (_("%s Update"), name); |
| 704 | } |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 705 | |
| 706 | static GPtrArray * |
| 707 | fu_util_strsplit_words (const gchar *text, guint line_len) |
| 708 | { |
| 709 | g_auto(GStrv) tokens = NULL; |
| 710 | g_autoptr(GPtrArray) lines = g_ptr_array_new (); |
| 711 | g_autoptr(GString) curline = g_string_new (NULL); |
| 712 | |
| 713 | /* sanity check */ |
| 714 | if (text == NULL || text[0] == '\0') |
| 715 | return NULL; |
| 716 | if (line_len == 0) |
| 717 | return NULL; |
| 718 | |
| 719 | /* tokenize the string */ |
| 720 | tokens = g_strsplit (text, " ", -1); |
| 721 | for (guint i = 0; tokens[i] != NULL; i++) { |
| 722 | |
| 723 | /* current line plus new token is okay */ |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 724 | if (curline->len + fu_common_strwidth (tokens[i]) < line_len) { |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 725 | g_string_append_printf (curline, "%s ", tokens[i]); |
| 726 | continue; |
| 727 | } |
| 728 | |
| 729 | /* too long, so remove space, add newline and dump */ |
| 730 | if (curline->len > 0) |
| 731 | g_string_truncate (curline, curline->len - 1); |
| 732 | g_ptr_array_add (lines, g_strdup (curline->str)); |
| 733 | g_string_truncate (curline, 0); |
| 734 | g_string_append_printf (curline, "%s ", tokens[i]); |
| 735 | } |
| 736 | |
| 737 | /* any incomplete line? */ |
| 738 | if (curline->len > 0) { |
| 739 | g_string_truncate (curline, curline->len - 1); |
| 740 | g_ptr_array_add (lines, g_strdup (curline->str)); |
| 741 | } |
| 742 | return g_steal_pointer (&lines); |
| 743 | } |
| 744 | |
| 745 | static void |
| 746 | fu_util_warning_box_line (const gchar *start, |
| 747 | const gchar *text, |
| 748 | const gchar *end, |
| 749 | const gchar *padding, |
| 750 | guint width) |
| 751 | { |
| 752 | guint offset = 0; |
| 753 | if (start != NULL) { |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 754 | offset += fu_common_strwidth (start); |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 755 | g_print ("%s", start); |
| 756 | } |
| 757 | if (text != NULL) { |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 758 | offset += fu_common_strwidth (text); |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 759 | g_print ("%s", text); |
| 760 | } |
| 761 | if (end != NULL) |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 762 | offset += fu_common_strwidth (end); |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 763 | for (guint i = offset; i < width; i++) |
| 764 | g_print ("%s", padding); |
| 765 | if (end != NULL) |
| 766 | g_print ("%s\n", end); |
| 767 | } |
| 768 | |
| 769 | void |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 770 | fu_util_warning_box (const gchar *title, const gchar *body, guint width) |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 771 | { |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 772 | /* nothing to do */ |
| 773 | if (title == NULL && body == NULL) |
| 774 | return; |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 775 | |
| 776 | /* header */ |
| 777 | fu_util_warning_box_line ("╔", NULL, "╗", "═", width); |
| 778 | |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 779 | /* optional title */ |
| 780 | if (title != NULL) { |
| 781 | g_autoptr(GPtrArray) lines = fu_util_strsplit_words (title, width - 4); |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 782 | for (guint j = 0; j < lines->len; j++) { |
| 783 | const gchar *line = g_ptr_array_index (lines, j); |
| 784 | fu_util_warning_box_line ("║ ", line, " ║", " ", width); |
| 785 | } |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 786 | } |
| 787 | |
| 788 | /* join */ |
| 789 | if (title != NULL && body != NULL) |
| 790 | fu_util_warning_box_line ("╠", NULL, "╣", "═", width); |
| 791 | |
| 792 | /* optional body */ |
| 793 | if (body != NULL) { |
| 794 | g_auto(GStrv) split = g_strsplit (body, "\n", -1); |
| 795 | for (guint i = 0; split[i] != NULL; i++) { |
| 796 | g_autoptr(GPtrArray) lines = fu_util_strsplit_words (split[i], width - 4); |
| 797 | if (lines == NULL) |
| 798 | continue; |
| 799 | for (guint j = 0; j < lines->len; j++) { |
| 800 | const gchar *line = g_ptr_array_index (lines, j); |
| 801 | fu_util_warning_box_line ("║ ", line, " ║", " ", width); |
| 802 | } |
| 803 | } |
Richard Hughes | acfa4ef | 2019-05-01 12:18:25 +0100 | [diff] [blame] | 804 | } |
| 805 | |
| 806 | /* footer */ |
| 807 | fu_util_warning_box_line ("╚", NULL, "╝", "═", width); |
| 808 | } |
Richard Hughes | 747f570 | 2019-08-06 14:27:26 +0100 | [diff] [blame] | 809 | |
| 810 | gboolean |
| 811 | fu_util_parse_filter_flags (const gchar *filter, FwupdDeviceFlags *include, |
| 812 | FwupdDeviceFlags *exclude, GError **error) |
| 813 | { |
| 814 | FwupdDeviceFlags tmp; |
| 815 | g_auto(GStrv) strv = g_strsplit (filter, ",", -1); |
| 816 | |
| 817 | g_return_val_if_fail (include != NULL, FALSE); |
| 818 | g_return_val_if_fail (exclude != NULL, FALSE); |
| 819 | |
| 820 | for (guint i = 0; strv[i] != NULL; i++) { |
| 821 | if (g_str_has_prefix (strv[i], "~")) { |
| 822 | tmp = fwupd_device_flag_from_string (strv[i] + 1); |
| 823 | if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| 824 | g_set_error (error, |
| 825 | FWUPD_ERROR, |
| 826 | FWUPD_ERROR_NOT_SUPPORTED, |
| 827 | "Unknown device flag %s", |
| 828 | strv[i] + 1); |
| 829 | return FALSE; |
| 830 | } |
| 831 | if ((tmp & *include) > 0) { |
| 832 | g_set_error (error, |
| 833 | FWUPD_ERROR, |
| 834 | FWUPD_ERROR_NOT_SUPPORTED, |
| 835 | "Filter %s already included", |
| 836 | fwupd_device_flag_to_string (tmp)); |
| 837 | return FALSE; |
| 838 | } |
| 839 | if ((tmp & *exclude) > 0) { |
| 840 | g_set_error (error, |
| 841 | FWUPD_ERROR, |
| 842 | FWUPD_ERROR_NOT_SUPPORTED, |
| 843 | "Filter %s already excluded", |
| 844 | fwupd_device_flag_to_string (tmp)); |
| 845 | return FALSE; |
| 846 | } |
| 847 | *exclude |= tmp; |
| 848 | } else { |
| 849 | tmp = fwupd_device_flag_from_string (strv[i]); |
| 850 | if (tmp == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| 851 | g_set_error (error, |
| 852 | FWUPD_ERROR, |
| 853 | FWUPD_ERROR_NOT_SUPPORTED, |
| 854 | "Unknown device flag %s", |
| 855 | strv[i]); |
| 856 | return FALSE; |
| 857 | } |
| 858 | if ((tmp & *exclude) > 0) { |
| 859 | g_set_error (error, |
| 860 | FWUPD_ERROR, |
| 861 | FWUPD_ERROR_NOT_SUPPORTED, |
| 862 | "Filter %s already excluded", |
| 863 | fwupd_device_flag_to_string (tmp)); |
| 864 | return FALSE; |
| 865 | } |
| 866 | if ((tmp & *include) > 0) { |
| 867 | g_set_error (error, |
| 868 | FWUPD_ERROR, |
| 869 | FWUPD_ERROR_NOT_SUPPORTED, |
| 870 | "Filter %s already included", |
| 871 | fwupd_device_flag_to_string (tmp)); |
| 872 | return FALSE; |
| 873 | } |
| 874 | *include |= tmp; |
| 875 | } |
| 876 | } |
| 877 | |
| 878 | return TRUE; |
| 879 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 880 | |
Richard Hughes | 7286951 | 2020-09-01 15:07:20 +0100 | [diff] [blame] | 881 | typedef struct { |
| 882 | guint cnt; |
| 883 | GString *str; |
| 884 | } FuUtilConvertHelper; |
| 885 | |
| 886 | static gboolean |
| 887 | fu_util_convert_description_head_cb (XbNode *n, gpointer user_data) |
| 888 | { |
| 889 | FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data; |
| 890 | helper->cnt++; |
| 891 | |
| 892 | /* start */ |
| 893 | if (g_strcmp0 (xb_node_get_element (n), "em") == 0) { |
| 894 | g_string_append (helper->str, "\033[3m"); |
| 895 | } else if (g_strcmp0 (xb_node_get_element (n), "strong") == 0) { |
| 896 | g_string_append (helper->str, "\033[1m"); |
| 897 | } else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) { |
| 898 | g_string_append (helper->str, "`"); |
| 899 | } else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) { |
| 900 | g_string_append (helper->str, "• "); |
| 901 | } else if (g_strcmp0 (xb_node_get_element (n), "p") == 0 || |
| 902 | g_strcmp0 (xb_node_get_element (n), "ul") == 0 || |
| 903 | g_strcmp0 (xb_node_get_element (n), "ol") == 0) { |
| 904 | g_string_append (helper->str, "\n"); |
| 905 | } |
| 906 | |
| 907 | /* text */ |
| 908 | if (xb_node_get_text (n) != NULL) |
| 909 | g_string_append (helper->str, xb_node_get_text (n)); |
| 910 | |
| 911 | return FALSE; |
| 912 | } |
| 913 | |
| 914 | static gboolean |
| 915 | fu_util_convert_description_tail_cb (XbNode *n, gpointer user_data) |
| 916 | { |
| 917 | FuUtilConvertHelper *helper = (FuUtilConvertHelper *) user_data; |
| 918 | helper->cnt++; |
| 919 | |
| 920 | /* end */ |
| 921 | if (g_strcmp0 (xb_node_get_element (n), "em") == 0 || |
| 922 | g_strcmp0 (xb_node_get_element (n), "strong") == 0) { |
| 923 | g_string_append (helper->str, "\033[0m"); |
| 924 | } else if (g_strcmp0 (xb_node_get_element (n), "code") == 0) { |
| 925 | g_string_append (helper->str, "`"); |
| 926 | } else if (g_strcmp0 (xb_node_get_element (n), "li") == 0) { |
| 927 | g_string_append (helper->str, "\n"); |
| 928 | } else if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { |
| 929 | g_string_append (helper->str, "\n"); |
| 930 | } |
| 931 | |
| 932 | /* tail */ |
| 933 | if (xb_node_get_tail (n) != NULL) |
| 934 | g_string_append (helper->str, xb_node_get_tail (n)); |
| 935 | |
| 936 | return FALSE; |
| 937 | } |
| 938 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 939 | gchar * |
| 940 | fu_util_convert_description (const gchar *xml, GError **error) |
| 941 | { |
| 942 | g_autoptr(GString) str = g_string_new (NULL); |
| 943 | g_autoptr(XbNode) n = NULL; |
| 944 | g_autoptr(XbSilo) silo = NULL; |
Richard Hughes | 7286951 | 2020-09-01 15:07:20 +0100 | [diff] [blame] | 945 | FuUtilConvertHelper helper = { |
| 946 | .cnt = 0, |
| 947 | .str = str, |
| 948 | }; |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 949 | |
| 950 | /* parse XML */ |
| 951 | silo = xb_silo_new_from_xml (xml, error); |
| 952 | if (silo == NULL) |
| 953 | return NULL; |
| 954 | |
Richard Hughes | 7286951 | 2020-09-01 15:07:20 +0100 | [diff] [blame] | 955 | /* convert to something we can show on the console */ |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 956 | n = xb_silo_get_root (silo); |
Richard Hughes | 7286951 | 2020-09-01 15:07:20 +0100 | [diff] [blame] | 957 | xb_node_transmogrify (n, |
| 958 | fu_util_convert_description_head_cb, |
| 959 | fu_util_convert_description_tail_cb, |
| 960 | &helper); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 961 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 962 | /* success */ |
Mario Limonciello | 26563d7 | 2019-09-04 15:33:46 -0500 | [diff] [blame] | 963 | return fu_common_strstrip (str->str); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 964 | } |
| 965 | |
Mario Limonciello | eb442ea | 2020-01-10 10:56:05 -0600 | [diff] [blame] | 966 | /** |
| 967 | * fu_util_time_to_str: |
| 968 | * @tmp: the time in seconds |
| 969 | * |
| 970 | * Converts a timestamp to a 'pretty' translated string |
| 971 | * |
| 972 | * Return value: (transfer full): A string |
| 973 | * |
| 974 | * Since: 1.3.7 |
| 975 | **/ |
| 976 | gchar * |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 977 | fu_util_time_to_str (guint64 tmp) |
| 978 | { |
| 979 | g_return_val_if_fail (tmp != 0, NULL); |
| 980 | |
| 981 | /* seconds */ |
| 982 | if (tmp < 60) { |
| 983 | /* TRANSLATORS: duration in seconds */ |
| 984 | return g_strdup_printf (ngettext ("%u second", "%u seconds", |
| 985 | (gint) tmp), |
| 986 | (guint) tmp); |
| 987 | } |
| 988 | |
| 989 | /* minutes */ |
| 990 | tmp /= 60; |
| 991 | if (tmp < 60) { |
| 992 | /* TRANSLATORS: duration in minutes */ |
| 993 | return g_strdup_printf (ngettext ("%u minute", "%u minutes", |
| 994 | (gint) tmp), |
| 995 | (guint) tmp); |
| 996 | } |
| 997 | |
| 998 | /* hours */ |
| 999 | tmp /= 60; |
| 1000 | if (tmp < 60) { |
| 1001 | /* TRANSLATORS: duration in minutes */ |
| 1002 | return g_strdup_printf (ngettext ("%u hour", "%u hours", |
| 1003 | (gint) tmp), |
| 1004 | (guint) tmp); |
| 1005 | } |
| 1006 | |
| 1007 | /* days */ |
| 1008 | tmp /= 24; |
| 1009 | /* TRANSLATORS: duration in days! */ |
| 1010 | return g_strdup_printf (ngettext ("%u day", "%u days", |
| 1011 | (gint) tmp), |
| 1012 | (guint) tmp); |
| 1013 | } |
| 1014 | |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1015 | static gchar * |
| 1016 | fu_util_device_flag_to_string (guint64 device_flag) |
| 1017 | { |
| 1018 | if (device_flag == FWUPD_DEVICE_FLAG_NONE) { |
| 1019 | return NULL; |
| 1020 | } |
| 1021 | if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) { |
| 1022 | /* TRANSLATORS: Device cannot be removed easily*/ |
| 1023 | return _("Internal device"); |
| 1024 | } |
Richard Hughes | 8500b4f | 2020-04-17 11:47:04 +0100 | [diff] [blame] | 1025 | if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE || |
| 1026 | device_flag == FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN) { |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1027 | /* TRANSLATORS: Device is updatable in this or any other mode */ |
| 1028 | return _("Updatable"); |
| 1029 | } |
| 1030 | if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) { |
| 1031 | /* TRANSLATORS: Update can only be done from offline mode */ |
| 1032 | return _("Update requires a reboot"); |
| 1033 | } |
| 1034 | if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) { |
| 1035 | /* TRANSLATORS: Must be plugged in to an outlet */ |
mendel5 | f75ff42 | 2020-09-19 21:58:19 +0200 | [diff] [blame] | 1036 | return _("System requires external power source"); |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1037 | } |
| 1038 | if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) { |
| 1039 | /* TRANSLATORS: Is locked and can be unlocked */ |
| 1040 | return _("Device is locked"); |
| 1041 | } |
| 1042 | if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) { |
| 1043 | /* TRANSLATORS: Is found in current metadata */ |
| 1044 | return _("Supported on remote server"); |
| 1045 | } |
| 1046 | if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) { |
| 1047 | /* TRANSLATORS: Requires a bootloader mode to be manually enabled by the user */ |
| 1048 | return _("Requires a bootloader"); |
| 1049 | } |
| 1050 | if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { |
| 1051 | /* TRANSLATORS: Requires a reboot to apply firmware or to reload hardware */ |
| 1052 | return _("Needs a reboot after installation"); |
| 1053 | } |
| 1054 | if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { |
| 1055 | /* TRANSLATORS: Requires system shutdown to apply firmware */ |
| 1056 | return _("Needs shutdown after installation"); |
| 1057 | } |
| 1058 | if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) { |
| 1059 | /* TRANSLATORS: Has been reported to a metadata server */ |
| 1060 | return _("Reported to remote server"); |
| 1061 | } |
| 1062 | if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) { |
| 1063 | /* TRANSLATORS: User has been notified */ |
| 1064 | return _("User has been notified"); |
| 1065 | } |
| 1066 | if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) { |
| 1067 | /* skip */ |
| 1068 | return NULL; |
| 1069 | } |
| 1070 | if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) { |
| 1071 | /* TRANSLATORS: Install composite firmware on the parent before the child */ |
| 1072 | return _("Install to parent device first"); |
| 1073 | } |
| 1074 | if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) { |
| 1075 | /* TRANSLATORS: Is currently in bootloader mode */ |
| 1076 | return _("Is in bootloader mode"); |
| 1077 | } |
| 1078 | if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) { |
| 1079 | /* TRANSLATORS: The hardware is waiting to be replugged */ |
| 1080 | return _("Hardware is waiting to be replugged"); |
| 1081 | } |
| 1082 | if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) { |
| 1083 | /* TRANSLATORS: Ignore validation safety checks when flashing this device */ |
| 1084 | return _("Ignore validation safety checks"); |
| 1085 | } |
| 1086 | if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) { |
| 1087 | /* skip */ |
| 1088 | return NULL; |
| 1089 | } |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1090 | if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) { |
| 1091 | /* TRANSLATORS: Device update needs to be separately activated */ |
| 1092 | return _("Device update needs activation"); |
| 1093 | } |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1094 | if (device_flag == FWUPD_DEVICE_FLAG_HISTORICAL) { |
| 1095 | /* skip */ |
| 1096 | return NULL; |
| 1097 | } |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1098 | if (device_flag == FWUPD_DEVICE_FLAG_WILL_DISAPPEAR) { |
| 1099 | /* TRANSLATORS: Device will not return after update completes */ |
| 1100 | return _("Device will not re-appear after update completes"); |
| 1101 | } |
| 1102 | if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY) { |
| 1103 | /* TRANSLATORS: Device supports some form of checksum verification */ |
| 1104 | return _("Cryptographic hash verification is available"); |
| 1105 | } |
| 1106 | if (device_flag == FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE) { |
| 1107 | /* skip */ |
| 1108 | return NULL; |
| 1109 | } |
| 1110 | if (device_flag == FWUPD_DEVICE_FLAG_DUAL_IMAGE) { |
| 1111 | /* TRANSLATORS: Device supports a safety mechanism for flashing */ |
| 1112 | return _("Device stages updates"); |
| 1113 | } |
| 1114 | if (device_flag == FWUPD_DEVICE_FLAG_SELF_RECOVERY) { |
| 1115 | /* TRANSLATORS: Device supports a safety mechanism for flashing */ |
| 1116 | return _("Device can recover flash failures"); |
| 1117 | } |
| 1118 | if (device_flag == FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE) { |
| 1119 | /* TRANSLATORS: Device remains usable during update */ |
| 1120 | return _("Device is usable for the duration of the update"); |
| 1121 | } |
Richard Hughes | 1eb7c74 | 2020-01-07 10:58:32 +0000 | [diff] [blame] | 1122 | if (device_flag == FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED) { |
| 1123 | /* TRANSLATORS: a version check is required for all firmware */ |
| 1124 | return _("Device firmware is required to have a version check"); |
| 1125 | } |
Richard Hughes | af14073 | 2019-12-22 18:42:12 +0000 | [diff] [blame] | 1126 | if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES) { |
| 1127 | /* TRANSLATORS: a version check is required for all firmware */ |
| 1128 | return _("Device is required to install all provided releases"); |
| 1129 | } |
Richard Hughes | 460c4b7 | 2020-09-25 20:59:28 +0100 | [diff] [blame] | 1130 | if (device_flag == FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES) { |
| 1131 | /* TRANSLATORS: there is more than one supplier of the firmware */ |
| 1132 | return _("Device supports switching to a different branch of firmware"); |
| 1133 | } |
Richard Hughes | 1a61258 | 2020-09-29 19:39:38 +0100 | [diff] [blame] | 1134 | if (device_flag == FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL) { |
| 1135 | /* TRANSLATORS: save the old firmware to disk before installing the new one */ |
| 1136 | return _("Device will backup firmware before installing"); |
| 1137 | } |
Mario Limonciello | 768a268 | 2020-04-29 11:18:43 -0500 | [diff] [blame] | 1138 | if (device_flag == FWUPD_DEVICE_FLAG_SKIPS_RESTART) { |
| 1139 | /* skip */ |
| 1140 | return NULL; |
| 1141 | } |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1142 | if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { |
| 1143 | return NULL; |
| 1144 | } |
| 1145 | return NULL; |
| 1146 | } |
| 1147 | |
Richard Hughes | b1edfec | 2021-01-13 13:59:10 +0000 | [diff] [blame] | 1148 | static const gchar * |
| 1149 | fu_util_update_state_to_string (FwupdUpdateState update_state) |
| 1150 | { |
| 1151 | if (update_state == FWUPD_UPDATE_STATE_PENDING) { |
| 1152 | /* TRANSLATORS: The update state of the specific device */ |
| 1153 | return _("Pending"); |
| 1154 | } |
| 1155 | if (update_state == FWUPD_UPDATE_STATE_SUCCESS) { |
| 1156 | /* TRANSLATORS: The update state of the specific device */ |
| 1157 | return _("Success"); |
| 1158 | } |
| 1159 | if (update_state == FWUPD_UPDATE_STATE_FAILED) { |
| 1160 | /* TRANSLATORS: The update state of the specific device */ |
| 1161 | return _("Failed"); |
| 1162 | } |
| 1163 | if (update_state == FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { |
| 1164 | /* TRANSLATORS: The update state of the specific device */ |
| 1165 | return _("Transient failure"); |
| 1166 | } |
| 1167 | if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { |
| 1168 | /* TRANSLATORS: The update state of the specific device */ |
| 1169 | return _("Needs reboot"); |
| 1170 | } |
| 1171 | return NULL; |
| 1172 | } |
| 1173 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1174 | gchar * |
| 1175 | fu_util_device_to_string (FwupdDevice *dev, guint idt) |
| 1176 | { |
| 1177 | FwupdUpdateState state; |
Richard Hughes | 71db86f | 2019-09-11 10:25:47 +0100 | [diff] [blame] | 1178 | GPtrArray *guids = fwupd_device_get_guids (dev); |
Richard Hughes | e52e1b4 | 2021-01-26 09:59:15 +0000 | [diff] [blame] | 1179 | GPtrArray *vendor_ids = fwupd_device_get_vendor_ids (dev); |
Richard Hughes | 71db86f | 2019-09-11 10:25:47 +0100 | [diff] [blame] | 1180 | GPtrArray *instance_ids = fwupd_device_get_instance_ids (dev); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1181 | const gchar *tmp; |
| 1182 | const gchar *tmp2; |
| 1183 | guint64 flags = fwupd_device_get_flags (dev); |
Mario Limonciello | 3be596b | 2019-09-20 10:10:08 -0500 | [diff] [blame] | 1184 | guint64 modified = fwupd_device_get_modified (dev); |
Richard Hughes | 71db86f | 2019-09-11 10:25:47 +0100 | [diff] [blame] | 1185 | g_autoptr(GHashTable) ids = NULL; |
Richard Hughes | f68f531 | 2021-04-20 13:55:08 +0100 | [diff] [blame] | 1186 | g_autoptr(GString) str = g_string_new (NULL); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1187 | |
| 1188 | /* some fields are intentionally not included and are only shown in --verbose */ |
| 1189 | if (g_getenv ("FWUPD_VERBOSE") != NULL) { |
| 1190 | g_autofree gchar *debug_str = NULL; |
| 1191 | debug_str = fwupd_device_to_string (dev); |
Mario Limonciello | 17e9bf5 | 2019-08-27 09:03:19 -0500 | [diff] [blame] | 1192 | g_debug ("%s", debug_str); |
| 1193 | return NULL; |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1194 | } |
| 1195 | |
Mario Limonciello | 764fc2a | 2019-08-27 10:02:59 -0500 | [diff] [blame] | 1196 | tmp = fwupd_device_get_name (dev); |
| 1197 | if (tmp == NULL) { |
| 1198 | /* TRANSLATORS: Name of hardware */ |
| 1199 | tmp = _("Unknown Device"); |
| 1200 | } |
| 1201 | fu_common_string_append_kv (str, idt, tmp, NULL); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1202 | |
Mario Limonciello | 764fc2a | 2019-08-27 10:02:59 -0500 | [diff] [blame] | 1203 | tmp = fwupd_device_get_id (dev); |
| 1204 | if (tmp != NULL) { |
| 1205 | /* TRANSLATORS: ID for hardware, typically a SHA1 sum */ |
| 1206 | fu_common_string_append_kv (str, idt + 1, _("Device ID"), tmp); |
| 1207 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1208 | |
| 1209 | /* summary */ |
| 1210 | tmp = fwupd_device_get_summary (dev); |
| 1211 | if (tmp != NULL) { |
| 1212 | /* TRANSLATORS: one line summary of device */ |
| 1213 | fu_common_string_append_kv (str, idt + 1, _("Summary"), tmp); |
| 1214 | } |
| 1215 | |
| 1216 | /* description */ |
| 1217 | tmp = fwupd_device_get_description (dev); |
| 1218 | if (tmp != NULL) { |
| 1219 | g_autofree gchar *desc = NULL; |
| 1220 | desc = fu_util_convert_description (tmp, NULL); |
| 1221 | /* TRANSLATORS: multiline description of device */ |
| 1222 | fu_common_string_append_kv (str, idt + 1, _("Description"), desc); |
| 1223 | } |
| 1224 | |
| 1225 | /* versions */ |
| 1226 | tmp = fwupd_device_get_version (dev); |
| 1227 | if (tmp != NULL) { |
Mario Limonciello | 3be596b | 2019-09-20 10:10:08 -0500 | [diff] [blame] | 1228 | if (flags & FWUPD_DEVICE_FLAG_HISTORICAL) { |
| 1229 | /* TRANSLATORS: version number of previous firmware */ |
| 1230 | fu_common_string_append_kv (str, idt + 1, _("Previous version"), tmp); |
| 1231 | } else { |
| 1232 | /* TRANSLATORS: version number of current firmware */ |
| 1233 | fu_common_string_append_kv (str, idt + 1, _("Current version"), tmp); |
| 1234 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1235 | } |
| 1236 | tmp = fwupd_device_get_version_lowest (dev); |
| 1237 | if (tmp != NULL) { |
| 1238 | /* TRANSLATORS: smallest version number installable on device */ |
| 1239 | fu_common_string_append_kv (str, idt + 1, _("Minimum Version"), tmp); |
| 1240 | } |
| 1241 | tmp = fwupd_device_get_version_bootloader (dev); |
| 1242 | if (tmp != NULL) { |
| 1243 | /* TRANSLATORS: firmware version of bootloader */ |
| 1244 | fu_common_string_append_kv (str, idt + 1, _("Bootloader Version"), tmp); |
| 1245 | } |
| 1246 | |
| 1247 | /* vendor */ |
| 1248 | tmp = fwupd_device_get_vendor (dev); |
Richard Hughes | e52e1b4 | 2021-01-26 09:59:15 +0000 | [diff] [blame] | 1249 | if (tmp != NULL && vendor_ids->len > 0) { |
| 1250 | g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids); |
| 1251 | g_autofree gchar *both = g_strdup_printf ("%s (%s)", tmp, strv); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1252 | /* TRANSLATORS: manufacturer of hardware */ |
| 1253 | fu_common_string_append_kv (str, idt + 1, _("Vendor"), both); |
| 1254 | } else if (tmp != NULL) { |
| 1255 | /* TRANSLATORS: manufacturer of hardware */ |
| 1256 | fu_common_string_append_kv (str, idt + 1, _("Vendor"), tmp); |
Richard Hughes | e52e1b4 | 2021-01-26 09:59:15 +0000 | [diff] [blame] | 1257 | } else if (vendor_ids->len > 0) { |
| 1258 | g_autofree gchar *strv = fu_common_strjoin_array ("|", vendor_ids); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1259 | /* TRANSLATORS: manufacturer of hardware */ |
Richard Hughes | e52e1b4 | 2021-01-26 09:59:15 +0000 | [diff] [blame] | 1260 | fu_common_string_append_kv (str, idt + 1, _("Vendor"), strv); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1261 | } |
| 1262 | |
Richard Hughes | 71f713e | 2021-01-15 11:51:10 +0000 | [diff] [blame] | 1263 | /* branch */ |
| 1264 | if (fwupd_device_get_branch (dev) != NULL) { |
| 1265 | /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ |
| 1266 | fu_common_string_append_kv (str, idt + 1, _("Release Branch"), |
| 1267 | fwupd_device_get_branch (dev)); |
| 1268 | } |
| 1269 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1270 | /* install duration */ |
| 1271 | if (fwupd_device_get_install_duration (dev) > 0) { |
| 1272 | g_autofree gchar *time = fu_util_time_to_str (fwupd_device_get_install_duration (dev)); |
| 1273 | /* TRANSLATORS: length of time the update takes to apply */ |
| 1274 | fu_common_string_append_kv (str, idt + 1, _("Install Duration"), time); |
| 1275 | } |
| 1276 | |
| 1277 | /* serial # */ |
| 1278 | tmp = fwupd_device_get_serial (dev); |
| 1279 | if (tmp != NULL) { |
| 1280 | /* TRANSLATORS: serial number of hardware */ |
| 1281 | fu_common_string_append_kv (str, idt + 1, _("Serial Number"), tmp); |
| 1282 | } |
| 1283 | |
| 1284 | /* update state */ |
| 1285 | state = fwupd_device_get_update_state (dev); |
| 1286 | if (state != FWUPD_UPDATE_STATE_UNKNOWN) { |
| 1287 | /* TRANSLATORS: hardware state, e.g. "pending" */ |
| 1288 | fu_common_string_append_kv (str, idt + 1, _("Update State"), |
Richard Hughes | b1edfec | 2021-01-13 13:59:10 +0000 | [diff] [blame] | 1289 | fu_util_update_state_to_string (state)); |
Mario Limonciello | c2721c8 | 2020-06-25 09:42:12 -0500 | [diff] [blame] | 1290 | |
| 1291 | if (state == FWUPD_UPDATE_STATE_SUCCESS) { |
| 1292 | tmp = fwupd_device_get_update_message (dev); |
| 1293 | if (tmp != NULL) { |
| 1294 | /* TRANSLATORS: helpful messages from last update */ |
| 1295 | fu_common_string_append_kv (str, idt + 1, _("Update Message"), tmp); |
| 1296 | } |
| 1297 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1298 | } |
| 1299 | tmp = fwupd_device_get_update_error (dev); |
| 1300 | if (tmp != NULL) { |
| 1301 | /* TRANSLATORS: error message from last update attempt */ |
| 1302 | fu_common_string_append_kv (str, idt + 1, _("Update Error"), tmp); |
| 1303 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1304 | |
Mario Limonciello | 3be596b | 2019-09-20 10:10:08 -0500 | [diff] [blame] | 1305 | /* modified date: for history devices */ |
| 1306 | if (modified > 0) { |
| 1307 | g_autoptr(GDateTime) date = NULL; |
| 1308 | g_autofree gchar *time_str = NULL; |
| 1309 | date = g_date_time_new_from_unix_utc (modified); |
| 1310 | time_str = g_date_time_format (date, "%F %R"); |
| 1311 | /* TRANSLATORS: the original time/date the device was modified */ |
| 1312 | fu_common_string_append_kv (str, idt +1, _("Last modified"), time_str); |
| 1313 | } |
| 1314 | |
Richard Hughes | 71db86f | 2019-09-11 10:25:47 +0100 | [diff] [blame] | 1315 | /* all GUIDs for this hardware, with IDs if available */ |
| 1316 | ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); |
| 1317 | for (guint i = 0; i < instance_ids->len; i++) { |
| 1318 | const gchar *instance_id = g_ptr_array_index (instance_ids, i); |
| 1319 | g_hash_table_insert (ids, |
| 1320 | fwupd_guid_hash_string (instance_id), |
| 1321 | g_strdup (instance_id)); |
| 1322 | } |
| 1323 | for (guint i = 0; i < guids->len; i++) { |
| 1324 | const gchar *guid = g_ptr_array_index (guids, i); |
| 1325 | const gchar *instance_id = g_hash_table_lookup (ids, guid); |
| 1326 | g_autofree gchar *guid_src = NULL; |
| 1327 | |
| 1328 | /* instance IDs are only available as root */ |
| 1329 | if (instance_id == NULL) { |
| 1330 | guid_src = g_strdup (guid); |
| 1331 | } else { |
| 1332 | guid_src = g_strdup_printf ("%s ← %s", guid, instance_id); |
| 1333 | } |
| 1334 | if (i == 0) { |
| 1335 | /* TRANSLATORS: global ID common to all similar hardware */ |
| 1336 | fu_common_string_append_kv (str, idt + 1, ngettext ("GUID", "GUIDs", guids->len), guid_src); |
| 1337 | } else { |
| 1338 | fu_common_string_append_kv (str, idt + 1, "", guid_src); |
| 1339 | } |
| 1340 | } |
Mario Limonciello | fd877a0 | 2019-10-17 10:28:56 -0500 | [diff] [blame] | 1341 | |
| 1342 | /* TRANSLATORS: description of device ability */ |
| 1343 | tmp = _("Device Flags"); |
| 1344 | for (guint i = 0; i < 64; i++) { |
| 1345 | if ((flags & ((guint64) 1 << i)) == 0) |
| 1346 | continue; |
| 1347 | tmp2 = fu_util_device_flag_to_string ((guint64) 1 << i); |
| 1348 | if (tmp2 == NULL) |
| 1349 | continue; |
| 1350 | /* header */ |
| 1351 | if (tmp != NULL) { |
| 1352 | g_autofree gchar *bullet = NULL; |
| 1353 | bullet = g_strdup_printf ("• %s", tmp2); |
| 1354 | fu_common_string_append_kv (str, idt + 1, tmp, bullet); |
| 1355 | tmp = NULL; |
| 1356 | } else { |
| 1357 | g_autofree gchar *bullet = NULL; |
| 1358 | bullet = g_strdup_printf ("• %s", tmp2); |
| 1359 | fu_common_string_append_kv (str, idt + 1, "", bullet); |
| 1360 | } |
| 1361 | } |
| 1362 | |
Richard Hughes | f68f531 | 2021-04-20 13:55:08 +0100 | [diff] [blame] | 1363 | return g_string_free (g_steal_pointer (&str), FALSE); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1364 | } |
| 1365 | |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1366 | const gchar * |
| 1367 | fu_util_plugin_flag_to_string (FwupdPluginFlags plugin_flag) |
| 1368 | { |
| 1369 | if (plugin_flag == FWUPD_PLUGIN_FLAG_UNKNOWN) |
| 1370 | return NULL; |
| 1371 | if (plugin_flag == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE) |
| 1372 | return NULL; |
| 1373 | if (plugin_flag == FWUPD_PLUGIN_FLAG_USER_WARNING) |
| 1374 | return NULL; |
Richard Hughes | d94286b | 2021-03-01 21:12:18 +0000 | [diff] [blame] | 1375 | if (plugin_flag == FWUPD_PLUGIN_FLAG_REQUIRE_HWID) |
| 1376 | return NULL; |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1377 | if (plugin_flag == FWUPD_PLUGIN_FLAG_NONE) { |
| 1378 | /* TRANSLATORS: Plugin is active and in use */ |
| 1379 | return _("Enabled"); |
| 1380 | } |
| 1381 | if (plugin_flag == FWUPD_PLUGIN_FLAG_DISABLED) { |
| 1382 | /* TRANSLATORS: Plugin is inactive and not used */ |
| 1383 | return _("Disabled"); |
| 1384 | } |
| 1385 | if (plugin_flag == FWUPD_PLUGIN_FLAG_NO_HARDWARE) { |
| 1386 | /* TRANSLATORS: not required for this system */ |
| 1387 | return _("Required hardware was not found"); |
| 1388 | } |
| 1389 | if (plugin_flag == FWUPD_PLUGIN_FLAG_LEGACY_BIOS) { |
| 1390 | /* TRANSLATORS: system is not booted in UEFI mode */ |
Richard Hughes | e082272 | 2021-04-19 11:03:41 +0100 | [diff] [blame] | 1391 | return _("UEFI firmware can not be updated in legacy BIOS mode"); |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1392 | } |
| 1393 | if (plugin_flag == FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED) { |
| 1394 | /* TRANSLATORS: capsule updates are an optional BIOS feature */ |
Mario Limonciello | 797da4f | 2021-01-12 12:38:51 -0600 | [diff] [blame] | 1395 | return _("UEFI capsule updates not available or enabled in firmware setup"); |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1396 | } |
| 1397 | if (plugin_flag == FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED) { |
| 1398 | /* TRANSLATORS: user needs to run a command */ |
| 1399 | return _("Firmware updates disabled; run 'fwupdmgr unlock' to enable"); |
| 1400 | } |
| 1401 | if (plugin_flag == FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED) { |
| 1402 | /* TRANSLATORS: the user is using Gentoo/Arch and has screwed something up */ |
| 1403 | return _("Required efivarfs filesystem was not found"); |
| 1404 | } |
| 1405 | if (plugin_flag == FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND) { |
| 1406 | /* TRANSLATORS: partition refers to something on disk, again, hey Arch users */ |
| 1407 | return _("UEFI ESP partition not detected or configured"); |
| 1408 | } |
Mario Limonciello | c3a8173 | 2020-10-20 09:16:18 -0500 | [diff] [blame] | 1409 | if (plugin_flag == FWUPD_PLUGIN_FLAG_FAILED_OPEN) { |
| 1410 | /* TRANSLATORS: Failed to open plugin, hey Arch users */ |
| 1411 | return _("Plugin dependencies missing"); |
| 1412 | } |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1413 | |
| 1414 | /* fall back for unknown types */ |
| 1415 | return fwupd_plugin_flag_to_string (plugin_flag); |
| 1416 | } |
| 1417 | |
| 1418 | static gchar * |
| 1419 | fu_util_plugin_flag_to_cli_text (FwupdPluginFlags plugin_flag) |
| 1420 | { |
| 1421 | switch (plugin_flag) { |
| 1422 | case FWUPD_PLUGIN_FLAG_UNKNOWN: |
| 1423 | case FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE: |
| 1424 | case FWUPD_PLUGIN_FLAG_USER_WARNING: |
Richard Hughes | d94286b | 2021-03-01 21:12:18 +0000 | [diff] [blame] | 1425 | case FWUPD_PLUGIN_FLAG_REQUIRE_HWID: |
Richard Hughes | 7bcb8d4 | 2020-10-08 15:47:47 +0100 | [diff] [blame] | 1426 | return NULL; |
| 1427 | case FWUPD_PLUGIN_FLAG_NONE: |
| 1428 | return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| 1429 | FU_UTIL_CLI_COLOR_GREEN); |
| 1430 | case FWUPD_PLUGIN_FLAG_DISABLED: |
| 1431 | case FWUPD_PLUGIN_FLAG_NO_HARDWARE: |
| 1432 | return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| 1433 | FU_UTIL_CLI_COLOR_BLACK); |
| 1434 | case FWUPD_PLUGIN_FLAG_LEGACY_BIOS: |
| 1435 | case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: |
| 1436 | case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: |
| 1437 | case FWUPD_PLUGIN_FLAG_EFIVAR_NOT_MOUNTED: |
| 1438 | case FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND: |
| 1439 | return fu_util_term_format (fu_util_plugin_flag_to_string (plugin_flag), |
| 1440 | FU_UTIL_TERM_COLOR_RED); |
| 1441 | default: |
| 1442 | break; |
| 1443 | } |
| 1444 | |
| 1445 | /* fall back for unknown types */ |
| 1446 | return g_strdup (fwupd_plugin_flag_to_string (plugin_flag)); |
| 1447 | } |
| 1448 | |
| 1449 | gchar * |
| 1450 | fu_util_plugin_to_string (FwupdPlugin *plugin, guint idt) |
| 1451 | { |
| 1452 | GString *str = g_string_new (NULL); |
| 1453 | const gchar *hdr; |
| 1454 | guint64 flags = fwupd_plugin_get_flags (plugin); |
| 1455 | |
| 1456 | fu_common_string_append_kv (str, idt, fwupd_plugin_get_name (plugin), NULL); |
| 1457 | |
| 1458 | /* TRANSLATORS: description of plugin state, e.g. disabled */ |
| 1459 | hdr = _("Flags"); |
| 1460 | if (flags == 0x0) { |
| 1461 | const gchar *tmp = fu_util_plugin_flag_to_cli_text (flags); |
| 1462 | g_autofree gchar *li = g_strdup_printf ("• %s", tmp); |
| 1463 | fu_common_string_append_kv (str, idt + 1, hdr, li); |
| 1464 | } else { |
| 1465 | for (guint i = 0; i < 64; i++) { |
| 1466 | g_autofree gchar *li = NULL; |
| 1467 | g_autofree gchar *tmp = NULL; |
| 1468 | if ((flags & ((guint64) 1 << i)) == 0) |
| 1469 | continue; |
| 1470 | tmp = fu_util_plugin_flag_to_cli_text ((guint64) 1 << i); |
| 1471 | if (tmp == NULL) |
| 1472 | continue; |
| 1473 | li = g_strdup_printf ("• %s", tmp); |
| 1474 | fu_common_string_append_kv (str, idt + 1, hdr, li); |
| 1475 | |
| 1476 | /* clear header */ |
| 1477 | hdr = ""; |
| 1478 | } |
| 1479 | } |
| 1480 | |
| 1481 | return g_string_free (str, FALSE); |
| 1482 | } |
| 1483 | |
Richard Hughes | 07184d3 | 2019-09-27 08:57:28 +0100 | [diff] [blame] | 1484 | static const gchar * |
| 1485 | fu_util_license_to_string (const gchar *license) |
| 1486 | { |
| 1487 | if (license == NULL) { |
| 1488 | /* TRANSLATORS: we don't know the license of the update */ |
| 1489 | return _("Unknown"); |
| 1490 | } |
| 1491 | if (g_strcmp0 (license, "LicenseRef-proprietary") == 0 || |
| 1492 | g_strcmp0 (license, "proprietary") == 0) { |
| 1493 | /* TRANSLATORS: a non-free software license */ |
| 1494 | return _("Proprietary"); |
| 1495 | } |
| 1496 | return license; |
| 1497 | } |
| 1498 | |
Richard Hughes | 52c1a4d | 2020-04-02 11:21:06 +0100 | [diff] [blame] | 1499 | static const gchar * |
| 1500 | fu_util_release_urgency_to_string (FwupdReleaseUrgency release_urgency) |
| 1501 | { |
| 1502 | if (release_urgency == FWUPD_RELEASE_URGENCY_LOW) { |
| 1503 | /* TRANSLATORS: the release urgency */ |
| 1504 | return _("Low"); |
| 1505 | } |
| 1506 | if (release_urgency == FWUPD_RELEASE_URGENCY_MEDIUM) { |
| 1507 | /* TRANSLATORS: the release urgency */ |
| 1508 | return _("Medium"); |
| 1509 | } |
| 1510 | if (release_urgency == FWUPD_RELEASE_URGENCY_HIGH) { |
| 1511 | /* TRANSLATORS: the release urgency */ |
| 1512 | return _("High"); |
| 1513 | } |
| 1514 | if (release_urgency == FWUPD_RELEASE_URGENCY_CRITICAL) { |
| 1515 | /* TRANSLATORS: the release urgency */ |
| 1516 | return _("Critical"); |
| 1517 | } |
| 1518 | /* TRANSLATORS: unknown release urgency */ |
| 1519 | return _("Unknown"); |
| 1520 | } |
| 1521 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1522 | gchar * |
| 1523 | fu_util_release_to_string (FwupdRelease *rel, guint idt) |
| 1524 | { |
Richard Hughes | 0ad59cb | 2019-09-16 10:33:05 +0100 | [diff] [blame] | 1525 | GPtrArray *issues = fwupd_release_get_issues (rel); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1526 | GString *str = g_string_new (NULL); |
| 1527 | guint64 flags = fwupd_release_get_flags (rel); |
| 1528 | g_autoptr(GString) flags_str = g_string_new (NULL); |
| 1529 | |
| 1530 | g_return_val_if_fail (FWUPD_IS_RELEASE (rel), NULL); |
| 1531 | |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1532 | fu_common_string_append_kv (str, idt, fwupd_release_get_name (rel), NULL); |
| 1533 | |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1534 | /* TRANSLATORS: version number of new firmware */ |
Mario Limonciello | 3be596b | 2019-09-20 10:10:08 -0500 | [diff] [blame] | 1535 | fu_common_string_append_kv (str, idt + 1 , _("New version"), |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1536 | fwupd_release_get_version (rel)); |
| 1537 | |
| 1538 | if (fwupd_release_get_remote_id (rel) != NULL) { |
| 1539 | /* TRANSLATORS: the server the file is coming from */ |
| 1540 | fu_common_string_append_kv (str, idt + 1, _("Remote ID"), |
| 1541 | fwupd_release_get_remote_id (rel)); |
| 1542 | } |
Richard Hughes | 460c4b7 | 2020-09-25 20:59:28 +0100 | [diff] [blame] | 1543 | if (fwupd_release_get_branch (rel) != NULL) { |
| 1544 | /* TRANSLATORS: the stream of firmware, e.g. nonfree or open-source */ |
| 1545 | fu_common_string_append_kv (str, idt + 1, _("Branch"), |
| 1546 | fwupd_release_get_branch (rel)); |
| 1547 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1548 | if (fwupd_release_get_summary (rel) != NULL) { |
| 1549 | /* TRANSLATORS: one line summary of device */ |
| 1550 | fu_common_string_append_kv (str, idt + 1, _("Summary"), |
| 1551 | fwupd_release_get_summary (rel)); |
| 1552 | } |
Richard Hughes | f54ddf4 | 2019-09-21 12:36:14 +0100 | [diff] [blame] | 1553 | if (fwupd_release_get_name_variant_suffix (rel) != NULL) { |
| 1554 | /* TRANSLATORS: one line variant of release (e.g. 'Prerelease' or 'China') */ |
| 1555 | fu_common_string_append_kv (str, idt + 1, _("Variant"), |
| 1556 | fwupd_release_get_name_variant_suffix (rel)); |
| 1557 | } |
Richard Hughes | 07184d3 | 2019-09-27 08:57:28 +0100 | [diff] [blame] | 1558 | /* TRANSLATORS: e.g. GPLv2+, Proprietary etc */ |
| 1559 | fu_common_string_append_kv (str, idt + 1, _("License"), |
| 1560 | fu_util_license_to_string (fwupd_release_get_license (rel))); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1561 | if (fwupd_release_get_size (rel) != 0) { |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1562 | g_autofree gchar *tmp = NULL; |
| 1563 | tmp = g_format_size (fwupd_release_get_size (rel)); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1564 | /* TRANSLATORS: file size of the download */ |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1565 | fu_common_string_append_kv (str, idt + 1, _("Size"), tmp); |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1566 | } |
Richard Hughes | 14797f8 | 2020-04-02 10:52:04 +0100 | [diff] [blame] | 1567 | if (fwupd_release_get_created (rel) != 0) { |
| 1568 | gint64 value = (gint64) fwupd_release_get_created (rel); |
| 1569 | g_autoptr(GDateTime) date = g_date_time_new_from_unix_utc (value); |
| 1570 | g_autofree gchar *tmp = g_date_time_format (date, "%F"); |
| 1571 | /* TRANSLATORS: when the update was built */ |
| 1572 | fu_common_string_append_kv (str, idt + 1, _("Created"), tmp); |
| 1573 | } |
Richard Hughes | 52c1a4d | 2020-04-02 11:21:06 +0100 | [diff] [blame] | 1574 | if (fwupd_release_get_urgency (rel) != FWUPD_RELEASE_URGENCY_UNKNOWN) { |
| 1575 | FwupdReleaseUrgency tmp = fwupd_release_get_urgency (rel); |
| 1576 | /* TRANSLATORS: how important the release is */ |
| 1577 | fu_common_string_append_kv (str, idt + 1, _("Urgency"), |
| 1578 | fu_util_release_urgency_to_string (tmp)); |
| 1579 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1580 | if (fwupd_release_get_details_url (rel) != NULL) { |
| 1581 | /* TRANSLATORS: more details about the update link */ |
| 1582 | fu_common_string_append_kv (str, idt + 1, _("Details"), |
| 1583 | fwupd_release_get_details_url (rel)); |
| 1584 | } |
| 1585 | if (fwupd_release_get_source_url (rel) != NULL) { |
| 1586 | /* TRANSLATORS: source (as in code) link */ |
| 1587 | fu_common_string_append_kv (str, idt + 1, _("Source"), |
| 1588 | fwupd_release_get_source_url (rel)); |
| 1589 | } |
| 1590 | if (fwupd_release_get_vendor (rel) != NULL) { |
| 1591 | /* TRANSLATORS: manufacturer of hardware */ |
| 1592 | fu_common_string_append_kv (str, idt + 1, _("Vendor"), |
| 1593 | fwupd_release_get_vendor (rel)); |
| 1594 | } |
| 1595 | if (fwupd_release_get_install_duration (rel) != 0) { |
| 1596 | g_autofree gchar *tmp = fu_util_time_to_str (fwupd_release_get_install_duration (rel)); |
| 1597 | /* TRANSLATORS: length of time the update takes to apply */ |
| 1598 | fu_common_string_append_kv (str, idt + 1, _("Duration"), tmp); |
| 1599 | } |
| 1600 | if (fwupd_release_get_update_message (rel) != NULL) { |
| 1601 | /* TRANSLATORS: helpful messages for the update */ |
| 1602 | fu_common_string_append_kv (str, idt + 1, _("Update Message"), |
| 1603 | fwupd_release_get_update_message (rel)); |
| 1604 | } |
| 1605 | |
| 1606 | for (guint i = 0; i < 64; i++) { |
| 1607 | if ((flags & ((guint64) 1 << i)) == 0) |
| 1608 | continue; |
| 1609 | g_string_append_printf (flags_str, "%s|", |
| 1610 | fwupd_release_flag_to_string ((guint64) 1 << i)); |
| 1611 | } |
Mario Limonciello | 3be596b | 2019-09-20 10:10:08 -0500 | [diff] [blame] | 1612 | if (flags_str->len > 0) { |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1613 | g_string_truncate (flags_str, flags_str->len - 1); |
| 1614 | /* TRANSLATORS: release properties */ |
| 1615 | fu_common_string_append_kv (str, idt + 1, _("Flags"), flags_str->str); |
| 1616 | } |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1617 | if (fwupd_release_get_description (rel) != NULL) { |
| 1618 | g_autofree gchar *desc = NULL; |
| 1619 | desc = fu_util_convert_description (fwupd_release_get_description (rel), NULL); |
| 1620 | /* TRANSLATORS: multiline description of device */ |
| 1621 | fu_common_string_append_kv (str, idt + 1, _("Description"), desc); |
| 1622 | } |
Richard Hughes | 0ad59cb | 2019-09-16 10:33:05 +0100 | [diff] [blame] | 1623 | for (guint i = 0; i < issues->len; i++) { |
| 1624 | const gchar *issue = g_ptr_array_index (issues, i); |
| 1625 | if (i == 0) { |
| 1626 | /* TRANSLATORS: issue fixed with the release, e.g. CVE */ |
| 1627 | fu_common_string_append_kv (str, idt + 1, ngettext ("Issue", "Issues", issues->len), issue); |
| 1628 | } else { |
| 1629 | fu_common_string_append_kv (str, idt + 1, "", issue); |
| 1630 | } |
| 1631 | } |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1632 | |
| 1633 | return g_string_free (str, FALSE); |
| 1634 | } |
| 1635 | |
| 1636 | gchar * |
| 1637 | fu_util_remote_to_string (FwupdRemote *remote, guint idt) |
| 1638 | { |
| 1639 | GString *str = g_string_new (NULL); |
| 1640 | FwupdRemoteKind kind = fwupd_remote_get_kind (remote); |
| 1641 | FwupdKeyringKind keyring_kind = fwupd_remote_get_keyring_kind (remote); |
| 1642 | const gchar *tmp; |
| 1643 | gint priority; |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1644 | |
| 1645 | g_return_val_if_fail (FWUPD_IS_REMOTE (remote), NULL); |
| 1646 | |
| 1647 | fu_common_string_append_kv (str, idt, |
| 1648 | fwupd_remote_get_title (remote), NULL); |
| 1649 | |
| 1650 | /* TRANSLATORS: remote identifier, e.g. lvfs-testing */ |
| 1651 | fu_common_string_append_kv (str, idt + 1, _("Remote ID"), |
| 1652 | fwupd_remote_get_id (remote)); |
| 1653 | |
| 1654 | /* TRANSLATORS: remote type, e.g. remote or local */ |
| 1655 | fu_common_string_append_kv (str, idt + 1, _("Type"), |
| 1656 | fwupd_remote_kind_to_string (kind)); |
| 1657 | |
| 1658 | /* TRANSLATORS: keyring type, e.g. GPG or PKCS7 */ |
| 1659 | if (keyring_kind != FWUPD_KEYRING_KIND_UNKNOWN) { |
| 1660 | fu_common_string_append_kv (str, idt + 1, _("Keyring"), |
| 1661 | fwupd_keyring_kind_to_string (keyring_kind)); |
| 1662 | } |
| 1663 | |
| 1664 | /* TRANSLATORS: if the remote is enabled */ |
| 1665 | fu_common_string_append_kv (str, idt + 1, _("Enabled"), |
| 1666 | fwupd_remote_get_enabled (remote) ? "true" : "false"); |
| 1667 | |
| 1668 | tmp = fwupd_remote_get_checksum (remote); |
| 1669 | if (tmp != NULL) { |
| 1670 | /* TRANSLATORS: remote checksum */ |
| 1671 | fu_common_string_append_kv (str, idt + 1, _("Checksum"), tmp); |
| 1672 | } |
| 1673 | |
| 1674 | /* optional parameters */ |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1675 | if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && |
Richard Hughes | 1ca0316 | 2020-12-08 16:09:05 +0000 | [diff] [blame] | 1676 | fwupd_remote_get_age (remote) > 0 && |
| 1677 | fwupd_remote_get_age (remote) != G_MAXUINT64) { |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1678 | const gchar *unit = "s"; |
Richard Hughes | 1ca0316 | 2020-12-08 16:09:05 +0000 | [diff] [blame] | 1679 | gdouble age = fwupd_remote_get_age (remote); |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1680 | g_autofree gchar *age_str = NULL; |
| 1681 | if (age > 60) { |
| 1682 | age /= 60.f; |
| 1683 | unit = "m"; |
| 1684 | } |
| 1685 | if (age > 60) { |
| 1686 | age /= 60.f; |
| 1687 | unit = "h"; |
| 1688 | } |
| 1689 | if (age > 24) { |
| 1690 | age /= 24.f; |
| 1691 | unit = "d"; |
| 1692 | } |
| 1693 | if (age > 7) { |
| 1694 | age /= 7.f; |
| 1695 | unit = "w"; |
| 1696 | } |
| 1697 | age_str = g_strdup_printf ("%.2f%s", age, unit); |
| 1698 | /* TRANSLATORS: the age of the metadata */ |
| 1699 | fu_common_string_append_kv (str, idt + 1, _("Age"), age_str); |
| 1700 | } |
| 1701 | priority = fwupd_remote_get_priority (remote); |
| 1702 | if (priority != 0) { |
| 1703 | g_autofree gchar *priority_str = NULL; |
| 1704 | priority_str = g_strdup_printf ("%i", priority); |
| 1705 | /* TRANSLATORS: the numeric priority */ |
| 1706 | fu_common_string_append_kv (str, idt + 1, _("Priority"), priority_str); |
| 1707 | } |
| 1708 | tmp = fwupd_remote_get_username (remote); |
| 1709 | if (tmp != NULL) { |
| 1710 | /* TRANSLATORS: remote filename base */ |
| 1711 | fu_common_string_append_kv (str, idt + 1, _("Username"), tmp); |
| 1712 | } |
| 1713 | tmp = fwupd_remote_get_password (remote); |
| 1714 | if (tmp != NULL) { |
Richard Hughes | ae96a1f | 2019-09-23 11:16:36 +0100 | [diff] [blame] | 1715 | g_autofree gchar *hidden = g_strnfill (fu_common_strwidth (tmp), '*'); |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1716 | /* TRANSLATORS: remote filename base */ |
| 1717 | fu_common_string_append_kv (str, idt + 1, _("Password"), hidden); |
| 1718 | } |
| 1719 | tmp = fwupd_remote_get_filename_cache (remote); |
| 1720 | if (tmp != NULL) { |
| 1721 | /* TRANSLATORS: filename of the local file */ |
| 1722 | fu_common_string_append_kv (str, idt + 1, _("Filename"), tmp); |
| 1723 | } |
| 1724 | tmp = fwupd_remote_get_filename_cache_sig (remote); |
| 1725 | if (tmp != NULL) { |
| 1726 | /* TRANSLATORS: filename of the local file */ |
| 1727 | fu_common_string_append_kv (str, idt + 1, _("Filename Signature"), tmp); |
| 1728 | } |
| 1729 | tmp = fwupd_remote_get_metadata_uri (remote); |
| 1730 | if (tmp != NULL) { |
| 1731 | /* TRANSLATORS: remote URI */ |
| 1732 | fu_common_string_append_kv (str, idt + 1, _("Metadata URI"), tmp); |
| 1733 | } |
| 1734 | tmp = fwupd_remote_get_metadata_uri_sig (remote); |
| 1735 | if (tmp != NULL) { |
| 1736 | /* TRANSLATORS: remote URI */ |
| 1737 | fu_common_string_append_kv (str, idt + 1, _("Metadata Signature"), tmp); |
| 1738 | } |
| 1739 | tmp = fwupd_remote_get_firmware_base_uri (remote); |
| 1740 | if (tmp != NULL) { |
| 1741 | /* TRANSLATORS: remote URI */ |
| 1742 | fu_common_string_append_kv (str, idt + 1, _("Firmware Base URI"), tmp); |
| 1743 | } |
| 1744 | tmp = fwupd_remote_get_report_uri (remote); |
| 1745 | if (tmp != NULL) { |
| 1746 | /* TRANSLATORS: URI to send success/failure reports */ |
| 1747 | fu_common_string_append_kv (str, idt + 1, _("Report URI"), tmp); |
Mario Limonciello | 34c366a | 2019-09-24 11:24:24 -0500 | [diff] [blame] | 1748 | /* TRANSLATORS: Boolean value to automatically send reports */ |
| 1749 | fu_common_string_append_kv (str, idt + 1, _("Automatic Reporting"), |
| 1750 | fwupd_remote_get_automatic_reports (remote) ? "true" : "false"); |
Mario Limonciello | 4250d9d | 2019-08-29 09:53:44 -0500 | [diff] [blame] | 1751 | } |
Mario Limonciello | fee8f49 | 2019-08-18 12:16:07 -0500 | [diff] [blame] | 1752 | |
| 1753 | return g_string_free (str, FALSE); |
| 1754 | } |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1755 | |
| 1756 | static void |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1757 | fu_security_attr_append_str (FwupdSecurityAttr *attr, GString *str, FuSecurityAttrToStringFlags flags) |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1758 | { |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1759 | g_autofree gchar *name = NULL; |
| 1760 | |
| 1761 | /* hide obsoletes by default */ |
| 1762 | if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED) && |
| 1763 | (flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES) == 0) |
| 1764 | return; |
| 1765 | |
| 1766 | name = fu_security_attr_get_name (attr); |
Richard Hughes | 4d2c0f8 | 2020-07-07 12:02:30 +0100 | [diff] [blame] | 1767 | if (name == NULL) |
| 1768 | name = g_strdup (fwupd_security_attr_get_appstream_id (attr)); |
Richard Hughes | 1b97ee2 | 2020-05-14 20:48:06 +0100 | [diff] [blame] | 1769 | if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
Richard Hughes | dcd32eb | 2020-05-18 20:58:08 +0100 | [diff] [blame] | 1770 | g_string_append (str, "✦ "); |
Richard Hughes | 1b97ee2 | 2020-05-14 20:48:06 +0100 | [diff] [blame] | 1771 | } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 1772 | g_string_append (str, "✔ "); |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1773 | } else { |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 1774 | g_string_append (str, "✘ "); |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1775 | } |
Richard Hughes | b99df2e | 2020-07-01 19:28:35 +0100 | [diff] [blame] | 1776 | g_string_append_printf (str, "%s:", name); |
| 1777 | for (guint i = fu_common_strwidth (name); i < 30; i++) |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 1778 | g_string_append (str, " "); |
| 1779 | if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
| 1780 | g_string_append_printf (str, "\033[37m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
| 1781 | } else if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { |
| 1782 | g_string_append_printf (str, "\033[32m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1783 | } else { |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 1784 | g_string_append_printf (str, "\033[31m\033[1m%s\033[0m", fu_security_attr_get_result (attr)); |
| 1785 | } |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1786 | if ((flags & FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS) > 0 && |
| 1787 | fwupd_security_attr_get_url (attr) != NULL) { |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1788 | g_string_append_printf (str, ": %s", |
Richard Hughes | b246bca | 2020-05-18 14:31:35 +0100 | [diff] [blame] | 1789 | fwupd_security_attr_get_url (attr)); |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1790 | } |
Richard Hughes | fa34c31 | 2020-06-30 20:37:21 +0100 | [diff] [blame] | 1791 | if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED)) { |
| 1792 | /* TRANSLATORS: this is shown as a suffix for obsoleted tests */ |
| 1793 | g_string_append_printf (str, " %s", _("(obsoleted)")); |
| 1794 | } |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1795 | g_string_append_printf (str, "\n"); |
| 1796 | } |
| 1797 | |
| 1798 | gchar * |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1799 | fu_util_security_attrs_to_string (GPtrArray *attrs, FuSecurityAttrToStringFlags strflags) |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1800 | { |
| 1801 | FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE; |
| 1802 | const FwupdSecurityAttrFlags hpi_suffixes[] = { |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1803 | FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE, |
| 1804 | FWUPD_SECURITY_ATTR_FLAG_NONE, |
| 1805 | }; |
| 1806 | GString *str = g_string_new (NULL); |
Mario Limonciello | 6ed9cbd | 2020-05-13 10:40:45 -0500 | [diff] [blame] | 1807 | gboolean low_help = FALSE; |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1808 | gboolean runtime_help = FALSE; |
Mario Limonciello | b563840 | 2020-06-25 09:34:54 -0500 | [diff] [blame] | 1809 | gboolean pcr0_help = FALSE; |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1810 | |
| 1811 | for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) { |
| 1812 | gboolean has_header = FALSE; |
| 1813 | for (guint i = 0; i < attrs->len; i++) { |
| 1814 | FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| 1815 | if (fwupd_security_attr_get_level (attr) != j) |
| 1816 | continue; |
| 1817 | if (!has_header) { |
| 1818 | g_string_append_printf (str, "\n\033[1mHSI-%u\033[0m\n", j); |
| 1819 | has_header = TRUE; |
| 1820 | } |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1821 | fu_security_attr_append_str (attr, str, strflags); |
Mario Limonciello | 6ed9cbd | 2020-05-13 10:40:45 -0500 | [diff] [blame] | 1822 | /* make sure they have at least HSI-1 */ |
| 1823 | if (j < FWUPD_SECURITY_ATTR_LEVEL_IMPORTANT && |
| 1824 | !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) |
| 1825 | low_help = TRUE; |
Mario Limonciello | b563840 | 2020-06-25 09:34:54 -0500 | [diff] [blame] | 1826 | |
| 1827 | /* check for PCR0 not matching */ |
| 1828 | if (g_strcmp0 (fwupd_security_attr_get_appstream_id (attr), |
| 1829 | FWUPD_SECURITY_ATTR_ID_TPM_RECONSTRUCTION_PCR0) == 0 && |
| 1830 | fwupd_security_attr_get_result (attr) == FWUPD_SECURITY_ATTR_RESULT_NOT_VALID) |
| 1831 | pcr0_help = TRUE; |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1832 | } |
| 1833 | } |
| 1834 | for (guint i = 0; i < attrs->len; i++) { |
| 1835 | FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| 1836 | flags |= fwupd_security_attr_get_flags (attr); |
| 1837 | } |
| 1838 | for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) { |
| 1839 | if (flags & hpi_suffixes[j]) { |
| 1840 | g_string_append_printf (str, "\n\033[1m%s -%s\033[0m\n", |
| 1841 | /* TRANSLATORS: this is the HSI suffix */ |
| 1842 | _("Runtime Suffix"), |
| 1843 | fwupd_security_attr_flag_to_suffix (hpi_suffixes[j])); |
| 1844 | for (guint i = 0; i < attrs->len; i++) { |
| 1845 | FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i); |
| 1846 | if (!fwupd_security_attr_has_flag (attr, hpi_suffixes[j])) |
| 1847 | continue; |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1848 | if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE) && |
| 1849 | !fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) |
| 1850 | runtime_help = TRUE; |
Richard Hughes | 5c82b94 | 2020-09-14 12:24:06 +0100 | [diff] [blame] | 1851 | fu_security_attr_append_str (attr, str, strflags); |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1852 | } |
| 1853 | } |
| 1854 | } |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1855 | |
Mario Limonciello | 6ed9cbd | 2020-05-13 10:40:45 -0500 | [diff] [blame] | 1856 | if (low_help) { |
Richard Hughes | c11bed4 | 2020-05-19 20:10:45 +0100 | [diff] [blame] | 1857 | g_string_append_printf (str, "\n%s\n » %s\n", |
Mario Limonciello | 6ed9cbd | 2020-05-13 10:40:45 -0500 | [diff] [blame] | 1858 | /* TRANSLATORS: this is instructions on how to improve the HSI security level */ |
Richard Hughes | c11bed4 | 2020-05-19 20:10:45 +0100 | [diff] [blame] | 1859 | _("This system has a low HSI security level."), |
Mario Limonciello | 6ed9cbd | 2020-05-13 10:40:45 -0500 | [diff] [blame] | 1860 | "https://github.com/fwupd/fwupd/wiki/Low-host-security-level"); |
| 1861 | } |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1862 | if (runtime_help) { |
Richard Hughes | c11bed4 | 2020-05-19 20:10:45 +0100 | [diff] [blame] | 1863 | g_string_append_printf (str, "\n%s\n » %s\n", |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1864 | /* TRANSLATORS: this is instructions on how to improve the HSI suffix */ |
Richard Hughes | c11bed4 | 2020-05-19 20:10:45 +0100 | [diff] [blame] | 1865 | _("This system has HSI runtime issues."), |
Mario Limonciello | b8a57e5 | 2020-05-13 09:34:29 -0500 | [diff] [blame] | 1866 | "https://github.com/fwupd/fwupd/wiki/Host-security-ID-runtime-issues"); |
| 1867 | } |
| 1868 | |
Mario Limonciello | b563840 | 2020-06-25 09:34:54 -0500 | [diff] [blame] | 1869 | if (pcr0_help) { |
| 1870 | g_string_append_printf (str, "\n%s\n » %s\n", |
| 1871 | /* TRANSLATORS: this is more background on a security measurement problem */ |
Richard Hughes | 907fd15 | 2020-07-08 21:08:46 +0100 | [diff] [blame] | 1872 | _("The TPM PCR0 differs from reconstruction."), |
Mario Limonciello | b563840 | 2020-06-25 09:34:54 -0500 | [diff] [blame] | 1873 | "https://github.com/fwupd/fwupd/wiki/TPM-PCR0-differs-from-reconstruction"); |
| 1874 | |
| 1875 | } |
| 1876 | |
Richard Hughes | 196c6c6 | 2020-05-11 19:42:47 +0100 | [diff] [blame] | 1877 | return g_string_free (str, FALSE); |
| 1878 | } |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1879 | |
| 1880 | gboolean |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1881 | fu_util_send_report (FwupdClient *client, |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1882 | const gchar *report_uri, |
| 1883 | const gchar *data, |
| 1884 | const gchar *sig, |
| 1885 | gchar **uri, /* (nullable) (out) */ |
| 1886 | GError **error) |
| 1887 | { |
| 1888 | const gchar *server_msg = NULL; |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1889 | JsonNode *json_root; |
| 1890 | JsonObject *json_object; |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1891 | g_autofree gchar *str = NULL; |
| 1892 | g_autoptr(GBytes) upload_response = NULL; |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1893 | g_autoptr(JsonParser) json_parser = NULL; |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1894 | |
| 1895 | /* POST request */ |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1896 | upload_response = fwupd_client_upload_bytes (client, report_uri, data, sig, |
| 1897 | FWUPD_CLIENT_UPLOAD_FLAG_NONE, |
| 1898 | NULL, error); |
| 1899 | if (upload_response == NULL) |
| 1900 | return FALSE; |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1901 | |
| 1902 | /* server returned nothing, and probably exploded in a ball of flames */ |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1903 | if (g_bytes_get_size (upload_response) == 0) { |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1904 | g_set_error (error, |
| 1905 | FWUPD_ERROR, |
| 1906 | FWUPD_ERROR_INVALID_FILE, |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1907 | "Failed to upload to %s", |
| 1908 | report_uri); |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1909 | return FALSE; |
| 1910 | } |
| 1911 | |
| 1912 | /* parse JSON reply */ |
| 1913 | json_parser = json_parser_new (); |
Richard Hughes | 9b6d616 | 2020-07-07 16:24:57 +0100 | [diff] [blame] | 1914 | str = g_strndup (g_bytes_get_data (upload_response, NULL), |
| 1915 | g_bytes_get_size (upload_response)); |
| 1916 | if (!json_parser_load_from_data (json_parser, str, -1, error)) { |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1917 | g_prefix_error (error, "Failed to parse JSON response from '%s': ", str); |
| 1918 | return FALSE; |
| 1919 | } |
| 1920 | json_root = json_parser_get_root (json_parser); |
| 1921 | if (json_root == NULL) { |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1922 | g_set_error (error, |
| 1923 | FWUPD_ERROR, |
| 1924 | FWUPD_ERROR_PERMISSION_DENIED, |
| 1925 | "JSON response was malformed: '%s'", str); |
| 1926 | return FALSE; |
| 1927 | } |
| 1928 | json_object = json_node_get_object (json_root); |
| 1929 | if (json_object == NULL) { |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1930 | g_set_error (error, |
| 1931 | FWUPD_ERROR, |
| 1932 | FWUPD_ERROR_PERMISSION_DENIED, |
| 1933 | "JSON response object was malformed: '%s'", str); |
| 1934 | return FALSE; |
| 1935 | } |
| 1936 | |
| 1937 | /* get any optional server message */ |
| 1938 | if (json_object_has_member (json_object, "msg")) |
| 1939 | server_msg = json_object_get_string_member (json_object, "msg"); |
| 1940 | |
| 1941 | /* server reported failed */ |
| 1942 | if (!json_object_get_boolean_member (json_object, "success")) { |
| 1943 | g_set_error (error, |
| 1944 | FWUPD_ERROR, |
| 1945 | FWUPD_ERROR_PERMISSION_DENIED, |
| 1946 | "Server rejected report: %s", |
| 1947 | server_msg != NULL ? server_msg : "unspecified"); |
| 1948 | return FALSE; |
| 1949 | } |
| 1950 | |
| 1951 | /* server wanted us to see the message */ |
| 1952 | if (server_msg != NULL) { |
| 1953 | g_debug ("server message: %s", server_msg); |
| 1954 | if (g_strstr_len (server_msg, -1, "known issue") != NULL && |
| 1955 | json_object_has_member (json_object, "uri")) { |
| 1956 | if (uri != NULL) |
| 1957 | *uri = g_strdup (json_object_get_string_member (json_object, "uri")); |
| 1958 | } |
| 1959 | } |
| 1960 | |
Richard Hughes | 6d9ae62 | 2020-06-16 15:20:23 +0100 | [diff] [blame] | 1961 | /* success */ |
| 1962 | return TRUE; |
| 1963 | } |
Mario Limonciello | eb7be16 | 2020-07-01 15:38:47 -0500 | [diff] [blame] | 1964 | |
| 1965 | gint |
| 1966 | fu_util_sort_devices_by_flags_cb (gconstpointer a, gconstpointer b) |
| 1967 | { |
| 1968 | FuDevice *dev_a = *((FuDevice **) a); |
| 1969 | FuDevice *dev_b = *((FuDevice **) b); |
| 1970 | |
| 1971 | if ((!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && |
| 1972 | fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || |
| 1973 | (!fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && |
| 1974 | fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) |
| 1975 | return -1; |
| 1976 | if ((fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_UPDATABLE) && |
| 1977 | !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_UPDATABLE)) || |
| 1978 | (fu_device_has_flag (dev_a, FWUPD_DEVICE_FLAG_SUPPORTED) && |
| 1979 | !fu_device_has_flag (dev_b, FWUPD_DEVICE_FLAG_SUPPORTED))) |
| 1980 | return 1; |
| 1981 | |
| 1982 | return 0; |
| 1983 | } |
Mario Limonciello | 02085a0 | 2020-09-11 14:59:35 -0500 | [diff] [blame] | 1984 | |
| 1985 | static gint |
| 1986 | fu_util_device_order_compare (FuDevice *device1, FuDevice *device2) |
| 1987 | { |
| 1988 | if (fu_device_get_order (device1) < fu_device_get_order (device2)) |
| 1989 | return -1; |
| 1990 | if (fu_device_get_order (device1) > fu_device_get_order (device2)) |
| 1991 | return 1; |
| 1992 | return 0; |
| 1993 | } |
| 1994 | |
| 1995 | gint |
| 1996 | fu_util_device_order_sort_cb (gconstpointer a, gconstpointer b) |
| 1997 | { |
| 1998 | FuDevice *device_a = *((FuDevice **) a); |
| 1999 | FuDevice *device_b = *((FuDevice **) b); |
| 2000 | return fu_util_device_order_compare (device_a, device_b); |
| 2001 | } |
Mario Limonciello | 02f2cc3 | 2020-10-20 15:39:47 -0500 | [diff] [blame] | 2002 | |
| 2003 | |
| 2004 | gboolean |
| 2005 | fu_util_switch_branch_warning (FwupdDevice *dev, |
| 2006 | FwupdRelease *rel, |
| 2007 | gboolean assume_yes, |
| 2008 | GError **error) |
| 2009 | { |
| 2010 | const gchar *desc_markup = NULL; |
| 2011 | g_autofree gchar *desc_plain = NULL; |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 2012 | g_autofree gchar *title = NULL; |
Mario Limonciello | 02f2cc3 | 2020-10-20 15:39:47 -0500 | [diff] [blame] | 2013 | g_autoptr(GString) desc_full = g_string_new (NULL); |
| 2014 | |
| 2015 | /* warn the user if the vendor is different */ |
| 2016 | if (g_strcmp0 (fwupd_device_get_vendor (dev), fwupd_release_get_vendor (rel)) != 0) { |
| 2017 | /* TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name */ |
| 2018 | g_string_append_printf (desc_full, _("The firmware from %s is not " |
| 2019 | "supplied by %s, the hardware vendor."), |
| 2020 | fwupd_release_get_vendor (rel), |
| 2021 | fwupd_device_get_vendor (dev)); |
| 2022 | g_string_append (desc_full, "\n\n"); |
| 2023 | /* TRANSLATORS: %1 is the device vendor name */ |
| 2024 | g_string_append_printf (desc_full, _("Your hardware may be damaged using this firmware, " |
| 2025 | "and installing this release may void any warranty " |
| 2026 | "with %s."), |
| 2027 | fwupd_device_get_vendor (dev)); |
| 2028 | g_string_append (desc_full, "\n\n"); |
| 2029 | } |
| 2030 | |
| 2031 | /* from the <description> in the AppStream data */ |
| 2032 | desc_markup = fwupd_release_get_description (rel); |
| 2033 | if (desc_markup == NULL) |
| 2034 | return TRUE; |
| 2035 | desc_plain = fu_util_convert_description (desc_markup, error); |
| 2036 | if (desc_plain == NULL) |
| 2037 | return FALSE; |
| 2038 | g_string_append (desc_full, desc_plain); |
| 2039 | |
Richard Hughes | e69f0f5 | 2021-04-22 11:10:23 +0100 | [diff] [blame] | 2040 | /* TRANSLATORS: show and ask user to confirm -- |
| 2041 | * %1 is the old branch name, %2 is the new branch name */ |
| 2042 | title = g_strdup_printf (_("Switch branch from %s to %s?"), |
| 2043 | fu_util_branch_for_display (fwupd_device_get_branch (dev)), |
| 2044 | fu_util_branch_for_display (fwupd_release_get_branch (rel))); |
| 2045 | fu_util_warning_box (title, desc_full->str, 80); |
Mario Limonciello | 02f2cc3 | 2020-10-20 15:39:47 -0500 | [diff] [blame] | 2046 | if (!assume_yes) { |
| 2047 | /* ask for permission */ |
| 2048 | g_print ("\n%s [y|N]: ", |
| 2049 | /* TRANSLATORS: should the branch be changed */ |
| 2050 | _("Do you understand the consequences of changing the firmware branch?")); |
| 2051 | if (!fu_util_prompt_for_boolean (FALSE)) { |
| 2052 | g_set_error_literal (error, |
| 2053 | FWUPD_ERROR, |
| 2054 | FWUPD_ERROR_NOTHING_TO_DO, |
| 2055 | "Declined branch switch"); |
| 2056 | return FALSE; |
| 2057 | } |
| 2058 | } |
| 2059 | return TRUE; |
| 2060 | } |
Mario Limonciello | bd60de1 | 2020-11-11 13:09:43 -0600 | [diff] [blame] | 2061 | |
| 2062 | void |
| 2063 | fu_util_show_unsupported_warn (void) |
| 2064 | { |
| 2065 | #ifndef SUPPORTED_BUILD |
| 2066 | g_autofree gchar *fmt = NULL; |
| 2067 | /* TRANSLATORS: this is a prefix on the console */ |
| 2068 | fmt = fu_util_term_format (_("WARNING:"), FU_UTIL_CLI_COLOR_YELLOW); |
| 2069 | /* TRANSLATORS: unsupported build of the package */ |
| 2070 | g_printerr ("%s %s\n", fmt, _("This package has not been validated, it may not work properly.")); |
| 2071 | #endif |
| 2072 | } |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 2073 | |
Richard Hughes | 00640f4 | 2020-12-07 10:25:12 +0000 | [diff] [blame] | 2074 | #ifdef HAVE_LIBCURL_7_62_0 |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 2075 | G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURLU, curl_url_cleanup) |
Richard Hughes | 00640f4 | 2020-12-07 10:25:12 +0000 | [diff] [blame] | 2076 | #endif |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 2077 | |
| 2078 | gboolean |
| 2079 | fu_util_is_url (const gchar *perhaps_url) |
| 2080 | { |
Richard Hughes | 00640f4 | 2020-12-07 10:25:12 +0000 | [diff] [blame] | 2081 | #ifdef HAVE_LIBCURL_7_62_0 |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 2082 | g_autoptr(CURLU) h = curl_url (); |
| 2083 | return curl_url_set (h, CURLUPART_URL, perhaps_url, 0) == CURLUE_OK; |
Richard Hughes | 00640f4 | 2020-12-07 10:25:12 +0000 | [diff] [blame] | 2084 | #else |
| 2085 | return g_str_has_prefix (perhaps_url, "http://") || |
| 2086 | g_str_has_prefix (perhaps_url, "https://"); |
| 2087 | #endif |
Richard Hughes | 3a73c34 | 2020-11-13 13:25:22 +0000 | [diff] [blame] | 2088 | } |