diff --git a/bin/input-monitor.c b/bin/input-monitor.c index 6d5bc0f0136d83efd1a42f935adf3be744c2efae..6234d23a8ed4ca70cbf8f92b6c83926363f42d43 100644 --- a/bin/input-monitor.c +++ b/bin/input-monitor.c @@ -30,6 +30,7 @@ #include "steam-runtime-tools/glib-backports-internal.h" #include "steam-runtime-tools/input-device-internal.h" #include "steam-runtime-tools/json-glib-backports-internal.h" +#include "steam-runtime-tools/json-utils-internal.h" #include "steam-runtime-tools/utils-internal.h" #include <stdio.h> @@ -106,44 +107,6 @@ print_json (JsonBuilder *builder) g_warning ("Unable to write final newline: %s", g_strerror (errno)); } -static void -add_uevent_member (JsonBuilder *builder, - const char *name, - const char *value) -{ - const char *start = value; - const char *end; - - /* value is conceptually a single string, but we - * present it as an array of lines here, because that's - * a lot easier to read! */ - json_builder_set_member_name (builder, name); - json_builder_begin_array (builder); - - while (*start != '\0') - { - g_autofree gchar *valid = NULL; - - while (*start == '\n') - start++; - - if (*start == '\0') - break; - - end = strchrnul (start, '\n'); - - valid = g_utf8_make_valid (start, end - start); - json_builder_add_string_value (builder, valid); - - if (*end == '\0') - break; - - start = end + 1; - } - - json_builder_end_array (builder); -} - static void append_evdev_hex (GString *buf, const unsigned long *bits, @@ -576,7 +539,7 @@ added (SrtInputDeviceMonitor *monitor, g_autofree gchar *uevent = srt_input_device_dup_uevent (dev); if (uevent != NULL) - add_uevent_member (builder, "uevent", uevent); + _srt_json_builder_add_array_of_lines (builder, "uevent", uevent); } sys_path = srt_input_device_get_hid_sys_path (dev); @@ -628,7 +591,7 @@ added (SrtInputDeviceMonitor *monitor, json_builder_add_string_value (builder, id.phys); if (uevent != NULL) - add_uevent_member (builder, "uevent", uevent); + _srt_json_builder_add_array_of_lines (builder, "uevent", uevent); } json_builder_end_object (builder); @@ -686,7 +649,7 @@ added (SrtInputDeviceMonitor *monitor, json_builder_add_string_value (builder, id.phys); if (uevent != NULL) - add_uevent_member (builder, "uevent", uevent); + _srt_json_builder_add_array_of_lines (builder, "uevent", uevent); } json_builder_end_object (builder); @@ -739,7 +702,7 @@ added (SrtInputDeviceMonitor *monitor, g_autofree gchar *uevent = srt_input_device_dup_usb_device_uevent (dev); if (uevent != NULL) - add_uevent_member (builder, "uevent", uevent); + _srt_json_builder_add_array_of_lines (builder, "uevent", uevent); } json_builder_end_object (builder); diff --git a/bin/system-info.c b/bin/system-info.c index 346c0fde7cdfd29bc883375bbf6fea48a933c74c..69667a6de7c4869c96ab26503fcac226bb01f2fc 100644 --- a/bin/system-info.c +++ b/bin/system-info.c @@ -25,93 +25,7 @@ /* * Output basic information about the system on which the tool is run. - * The output is a JSON object with the following keys: - * - * can-write-uinput: - * The values are boolean: true if we can write to `/dev/uninput`, - * false if we are not able to do it. - * - * architectures: - * An object. The keys are multiarch tuples like %SRT_ABI_I386, - * as used in Debian and the freedesktop.org SDK runtime. - * The values are objects with more details of the architecture: - * - * can-run: - * The values are boolean: true if we can definitely run - * executables of this architecture, or false if we cannot prove - * that we can do so. - * - * library-issues-summary: - * A string array listing all the libraries problems that has been found - * in the running system. Possible values can be: "cannot-load", - * "missing-symbols", "misversioned-symbols" and "internal-error". - * If "can-run" for this architecture is false we skip the library check - * and this "library-issues-summary" will not be printed at all. - * - * library-details: - * An object. The keys are library SONAMEs, such as `libjpeg.so.62`. - * The values are objects with more details of the library: - * - * path: - * The value is a string representing the full path about where the - * @library has been found. The value is `null` if the library is - * not available in the system. - * - * messages: - * If present, the value is a string containing diagnostic messages - * that were encountered when attempting to load the library. - * This member will only be available if there were some messages. - * - * issues: - * A string array listing all the @library problems that has been - * found in the running system. For possible values check - * `library-issues-summary`. This object will be available only if - * there are some issues. - * - * missing-symbols: - * A string array listing all the symbols that were expected to be - * provided by @library but were not found. This object will be - * available only if there are sone missing symbols. - * - * misversioned-symbols: - * A string array listing all the symbols that were expected to be - * provided by @library but were available with a different version. - * This object will be available only if there are some misversioned - * symbols. - * - * graphics: - * An object. The keys are multiarch tuples like %SRT_ABI_I386, - * as used in Debian and the freedesktop.org SDK runtime. - * The values are objects with more details of the graphics results: - * - * locale-issues: - * A string array listing locale-related issues. - * - * locales: - * An object. The keys are either `<default>` (representing passing - * the empty string to `setlocale()`), or locale names that can be - * requested with `setlocale()`. They will include at least `C`, - * `C.UTF-8`, `en_US.UTF-8` and `<default>`, and may include more - * in future versions of steam-runtime-tools. The values are objects - * containing either: - * - * error: - * A string: The error that was encountered when trying to - * set this locale - * error-domain: - * A string: The GError domain - * error-code: - * An integer: The GError code - * - * or: - * - * resulting-name: - * A string: the locale name as returned by setlocale(), if - * different - * charset: - * A string: the character set - * is_utf8: - * A boolean: whether the character set is UTF-8 + * See system-info.md for details. */ #include <libglnx.h> @@ -364,10 +278,7 @@ print_libraries_details (JsonBuilder *builder, messages = srt_library_get_messages (l->data); if (messages != NULL) - { - json_builder_set_member_name (builder, "messages"); - json_builder_add_string_value (builder, messages); - } + _srt_json_builder_add_array_of_lines (builder, "messages", messages); json_builder_set_member_name (builder, "soname"); json_builder_add_string_value (builder, soname); @@ -434,10 +345,7 @@ print_graphics_details(JsonBuilder *builder, messages = srt_graphics_get_messages (g->data); if (messages != NULL) - { - json_builder_set_member_name (builder, "messages"); - json_builder_add_string_value (builder, messages); - } + _srt_json_builder_add_array_of_lines (builder, "messages", messages); json_builder_set_member_name (builder, "renderer"); json_builder_add_string_value (builder, srt_graphics_get_renderer_string (g->data)); @@ -1398,10 +1306,7 @@ main (int argc, json_builder_end_array (builder); if (xdg_portal_messages != NULL) - { - json_builder_set_member_name (builder, "messages"); - json_builder_add_string_value (builder, xdg_portal_messages); - } + _srt_json_builder_add_array_of_lines (builder, "messages", xdg_portal_messages); } json_builder_end_object (builder); diff --git a/bin/system-info.md b/bin/system-info.md index 94e04b7faa8867e3f542e8268bd7a93c60e297f5..88bdeea84726361f4d7fce62bbe3798c3928ab84 100644 --- a/bin/system-info.md +++ b/bin/system-info.md @@ -431,6 +431,9 @@ keys: symbol exists with an unexpected version or no version, they are listed here. + **messages** + : Human-readable array of diagnostic messages. + **graphics-details** : An object representing graphics stacks. The keys are strings such as **glx/gl**, **egl_x11/glesv2** or **x11/vulkan** @@ -498,6 +501,9 @@ keys: : The test to see if drawing works for the given graphics mode had an error and was unable to draw. + **messages** + : Human-readable array of diagnostic messages. + **dri_drivers** : An array of objects describing the Mesa DRI driver modules that have been found. These drivers are used by Mesa and by older versions of Xorg. @@ -777,7 +783,7 @@ keys: : There isn't a working XDG portal implementation. **messages** - : Human-readable string with error messages about the XDG portal + : Human-readable array of lines with error messages about the XDG portal support. This element will not be printed if there aren't any error messages. diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c index dbff33ee6f7bd2f3bf90b7c4465213553f154c75..20d1a99937dbfcdfbbcf4efa700d9e94609609e3 100644 --- a/steam-runtime-tools/graphics.c +++ b/steam-runtime-tools/graphics.c @@ -1498,7 +1498,7 @@ _srt_graphics_get_from_report (JsonObject *json_obj, graphics_members = json_object_get_members (json_graphics_obj); for (GList *l = graphics_members; l != NULL; l = l->next) { - const gchar *messages = NULL; + g_autofree gchar *messages = NULL; const gchar *renderer = NULL; const gchar *version = NULL; int exit_status; @@ -1545,8 +1545,7 @@ _srt_graphics_get_from_report (JsonObject *json_obj, continue; } - messages = json_object_get_string_member_with_default (json_stack_obj, "messages", - NULL); + messages = _srt_json_object_dup_array_of_lines_member (json_stack_obj, "messages"); renderer = json_object_get_string_member_with_default (json_stack_obj, "renderer", NULL); version = json_object_get_string_member_with_default (json_stack_obj, "version", diff --git a/steam-runtime-tools/json-utils-internal.h b/steam-runtime-tools/json-utils-internal.h index f01ddf524ac740d586b0ec347632a5f91cbedd66..32dced70604470d2f6b38e19f0a40c3dee5e9eb0 100644 --- a/steam-runtime-tools/json-utils-internal.h +++ b/steam-runtime-tools/json-utils-internal.h @@ -46,6 +46,9 @@ gchar ** _srt_json_object_dup_strv_member (JsonObject *json_obj, gchar *_srt_json_object_dup_array_of_lines_member (JsonObject *json_obj, const gchar *array_member); +void _srt_json_builder_add_array_of_lines (JsonBuilder *builder, + const char *name, + const char *value); void _srt_json_builder_add_strv_value (JsonBuilder *builder, const gchar *array_name, diff --git a/steam-runtime-tools/json-utils.c b/steam-runtime-tools/json-utils.c index e4f42687d2b3794e627378ae2993e77039e53118..09d7d67276a8214bab92361a81b7e7a0cf44c51e 100644 --- a/steam-runtime-tools/json-utils.c +++ b/steam-runtime-tools/json-utils.c @@ -148,8 +148,12 @@ _srt_json_object_dup_strv_member (JsonObject *json_obj, * * If @json_obj has a member named @array_member and it is an array * of strings, concatenate the strings (adding a trailing newline to - * each one if not already present) and return them. Otherwise, - * return %NULL. + * each one if not already present) and return them. + * + * For compatibility with the old representation of diagnostic messages, + * if @array_member exists but is a single string, return it. + * + * Otherwise, return %NULL. * * Returns: (transfer full) (element-type utf8) (nullable): * A string, or %NULL if not found. Free with g_free(). @@ -168,7 +172,13 @@ _srt_json_object_dup_array_of_lines_member (JsonObject *json_obj, arr_node = json_object_get_member (json_obj, array_member); - if (arr_node == NULL || !JSON_NODE_HOLDS_ARRAY (arr_node)) + if (arr_node == NULL || JSON_NODE_HOLDS_NULL (arr_node)) + return NULL; + + if (JSON_NODE_HOLDS_VALUE (arr_node)) + return json_node_dup_string (arr_node); + + if (!JSON_NODE_HOLDS_ARRAY (arr_node)) return NULL; array = json_node_get_array (arr_node); @@ -193,6 +203,77 @@ _srt_json_object_dup_array_of_lines_member (JsonObject *json_obj, return g_string_free (g_steal_pointer (&ret), FALSE); } +/** + * _srt_json_builder_add_array_of_lines: + * @builder: (not nullable): A JSON builder to which the provided @value + * will be added + * @name: (not nullable): The array member name to use + * @value: (nullable): String to be split into an array of lines, or %NULL + * + * Write an array of lines into a JSON object. + * + * A non-%NULL @value will be split into lines, removing any trailing + * newlines, so + * + * |[ + * if (g_file_get_contents ("/etc/passwd", &contents, ...)) + * _srt_json_builder_add_array_of_lines (builder, "/etc/passwd", contents); + * ]| + * + * might produce + * + * |[ + * "/etc/passwd" : [ + * "root:x:0:0:root:/root:/bin/bash", + * ..., + * "nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin", + * ], + * ]| + * + * A %NULL @value will be emitted as the special JSON constant `null`. + */ +void +_srt_json_builder_add_array_of_lines (JsonBuilder *builder, + const char *name, + const char *value) +{ + const char *start = value; + const char *end; + + json_builder_set_member_name (builder, name); + + if (value == NULL) + { + json_builder_add_string_value (builder, NULL); + return; + } + + json_builder_begin_array (builder); + + while (*start != '\0') + { + g_autofree gchar *valid = NULL; + + while (*start == '\n') + start++; + + if (*start == '\0') + break; + + end = strchrnul (start, '\n'); + + valid = g_utf8_make_valid (start, end - start); + json_builder_add_string_value (builder, valid); + + if (*end == '\0') + break; + + start = end + 1; + } + + json_builder_end_array (builder); +} + /** * _srt_json_builder_add_strv_value: * @builder: (not nullable): A JSON Builder where the provided @values diff --git a/steam-runtime-tools/xdg-portal.c b/steam-runtime-tools/xdg-portal.c index 53e196dcda37ed5a3c75a80cd3705ce1bc8e499d..8cb5aba1ceb2eeca9a0cd9d034445ff7affd2651 100644 --- a/steam-runtime-tools/xdg-portal.c +++ b/steam-runtime-tools/xdg-portal.c @@ -710,7 +710,7 @@ _srt_xdg_portal_get_info_from_report (JsonObject *json_obj) g_autoptr(GPtrArray) backends = g_ptr_array_new_full (2, g_object_unref); g_autoptr(GPtrArray) interfaces = g_ptr_array_new_full (2, g_object_unref); SrtXdgPortalIssues issues = SRT_XDG_PORTAL_ISSUES_NONE; - const gchar *messages = NULL; + g_autofree gchar *messages = NULL; g_return_val_if_fail (json_obj != NULL, NULL); @@ -727,7 +727,7 @@ _srt_xdg_portal_get_info_from_report (JsonObject *json_obj) "issues", SRT_XDG_PORTAL_ISSUES_UNKNOWN); - messages = json_object_get_string_member_with_default (json_portals_obj, "messages", NULL); + messages = _srt_json_object_dup_array_of_lines_member (json_portals_obj, "messages"); if (!json_object_has_member (json_portals_obj, "details")) return _srt_xdg_portal_new (messages, issues, NULL, NULL); diff --git a/tests/json-report/full-good-report.json b/tests/json-report/full-good-report.json index d926f4b07f7010e7a60a1a95699645f2d3745360..05b01afda1950c36f802cd68bc62394135c8e538 100644 --- a/tests/json-report/full-good-report.json +++ b/tests/json-report/full-good-report.json @@ -61,7 +61,10 @@ }, "graphics-details" : { "x11/vulkan" : { - "messages" : "ERROR: [Loader Message] Code 0 : /usr/lib/amdvlk64.so: wrong ELF class: ELFCLASS64\nCannot create Vulkan instance.\n", + "messages" : [ + "ERROR: [Loader Message] Code 0 : /usr/lib/amdvlk64.so: wrong ELF class: ELFCLASS64", + "Cannot create Vulkan instance." + ], "renderer" : null, "version" : null, "issues" : [ @@ -146,7 +149,11 @@ "version" : null }, "glx/gl" : { - "messages" : "libGL: Can't open configuration file /etc/drirc: No such file or directory.\n/usr/share/libdrm/amdgpu.ids version: 1.0.0\nlibGL: Using DRI3 for screen 0\n", + "messages" : [ + "libGL: Can't open configuration file /etc/drirc: No such file or directory.", + "/usr/share/libdrm/amdgpu.ids version: 1.0.0", + "libGL: Using DRI3 for screen 0" + ], "renderer" : "AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)", "version" : "4.6 (Compatibility Profile) Mesa 20.0.5", "library-vendor" : "glvnd",