diff --git a/bin/meson.build b/bin/meson.build index 1e7d45f37e0ef3e112eda0ef71600fd1c17976e2..d299b130a91b832b42837cf8eb4d17df410eb9ef 100644 --- a/bin/meson.build +++ b/bin/meson.build @@ -35,7 +35,7 @@ executable( executable( 'steam-runtime-check-requirements', 'check-requirements.c', - dependencies : [glib, gobject, libsteamrt_dep], + dependencies : [glib, json_glib, gobject, libsteamrt_dep], install : true, # Use the adjacent libsteam-runtime-tools and json-glib, ignoring # LD_LIBRARY_PATH even if set diff --git a/bin/system-info.c b/bin/system-info.c index de092fc91f5383ccabe2ea55b63d2357f01ba791..0127ad20a41cbdb786abecf94d2756e68b75e071 100644 --- a/bin/system-info.c +++ b/bin/system-info.c @@ -254,7 +254,8 @@ jsonify_flags (JsonBuilder *builder, static void jsonify_flags_string_bool_map (JsonBuilder *builder, GType flags_type, - unsigned int values) + unsigned int present, + unsigned int known) { GFlagsClass *class; GFlagsValue *flags_value; @@ -269,21 +270,32 @@ jsonify_flags_string_bool_map (JsonBuilder *builder, if (flags_value->value == 0) continue; - json_builder_set_member_name (builder, flags_value->value_nick); - if ((flags_value->value & values) == flags_value->value) + /* Skip the unknown flag */ + if (g_strcmp0 (flags_value->value_nick, "unknown") == 0) { + if ((flags_value->value & present) == flags_value->value) + present &= ~flags_value->value; + continue; + } + + if ((flags_value->value & present) == flags_value->value) + { + json_builder_set_member_name (builder, flags_value->value_nick); json_builder_add_boolean_value (builder, TRUE); - values &= ~flags_value->value; + present &= ~flags_value->value; + known &= ~flags_value->value; } - else + else if ((flags_value->value & known) == flags_value->value) { + json_builder_set_member_name (builder, flags_value->value_nick); json_builder_add_boolean_value (builder, FALSE); + known &= ~flags_value->value; } } - if (values) + if (present) { - gchar *rest = g_strdup_printf ("0x%x", values); + gchar *rest = g_strdup_printf ("0x%x", present); json_builder_set_member_name (builder, rest); json_builder_add_boolean_value (builder, TRUE); @@ -291,6 +303,16 @@ jsonify_flags_string_bool_map (JsonBuilder *builder, g_free (rest); } + if (known) + { + gchar *rest = g_strdup_printf ("0x%x", known); + + json_builder_set_member_name (builder, rest); + json_builder_add_boolean_value (builder, FALSE); + + g_free (rest); + } + g_type_class_unref (class); } @@ -351,9 +373,10 @@ jsonify_locale_issues (JsonBuilder *builder, static void jsonify_x86_features (JsonBuilder *builder, - SrtX86FeatureFlags features) + SrtX86FeatureFlags present, + SrtX86FeatureFlags known) { - jsonify_flags_string_bool_map (builder, SRT_TYPE_X86_FEATURE_FLAGS, features); + jsonify_flags_string_bool_map (builder, SRT_TYPE_X86_FEATURE_FLAGS, present, known); } static void @@ -392,7 +415,7 @@ print_libraries_details (JsonBuilder *builder, json_builder_end_array (builder); int exit_status = srt_library_get_exit_status (l->data); - if (exit_status != 0 && exit_status != -1) + if (exit_status != 0) { json_builder_set_member_name (builder, "exit-status"); json_builder_add_int_value (builder, exit_status); @@ -484,7 +507,7 @@ print_graphics_details(JsonBuilder *builder, jsonify_graphics_issues (builder, srt_graphics_get_issues (g->data)); json_builder_end_array (builder); int exit_status = srt_graphics_get_exit_status (g->data); - if (exit_status != 0 && exit_status != -1) + if (exit_status != 0) { json_builder_set_member_name (builder, "exit-status"); json_builder_add_int_value (builder, exit_status); @@ -799,11 +822,13 @@ main (int argc, SrtRuntimeIssues runtime_issues = SRT_RUNTIME_ISSUES_NONE; SrtLocaleIssues locale_issues = SRT_LOCALE_ISSUES_NONE; SrtX86FeatureFlags x86_features = SRT_X86_FEATURE_NONE; + SrtX86FeatureFlags known_x86_features = SRT_X86_FEATURE_NONE; char *expectations = NULL; gboolean verbose = FALSE; JsonBuilder *builder; JsonGenerator *generator; gboolean can_run = FALSE; + const gchar *test_json_path = NULL; gchar *json_output; gchar *version = NULL; gchar *inst_path = NULL; @@ -873,10 +898,26 @@ main (int argc, _srt_unblock_signals (); - info = srt_system_info_new (expectations); + test_json_path = g_getenv ("SRT_TEST_PARSE_JSON"); - /* For unit testing */ - srt_system_info_set_sysroot (info, g_getenv ("SRT_TEST_SYSROOT")); + if (test_json_path) + { + /* Get the system info from a JSON, used for unit testing */ + info = srt_system_info_new_from_json (test_json_path, &error); + if (info == NULL) + { + g_warning ("%s", error->message); + g_clear_error (&error); + return 1; + } + } + else + { + info = srt_system_info_new (expectations); + + /* For unit testing */ + srt_system_info_set_sysroot (info, g_getenv ("SRT_TEST_SYSROOT")); + } builder = json_builder_new (); json_builder_begin_object (builder); @@ -1276,8 +1317,9 @@ main (int argc, json_builder_set_member_name (builder, "cpu-features"); json_builder_begin_object (builder); { + known_x86_features = srt_system_info_get_known_x86_features (info); x86_features = srt_system_info_get_x86_features (info); - jsonify_x86_features (builder, x86_features); + jsonify_x86_features (builder, x86_features, known_x86_features); } json_builder_end_object (builder); diff --git a/steam-runtime-tools/architecture-internal.h b/steam-runtime-tools/architecture-internal.h index f0d28543e2c2f4fa7d0b7108b671c792e87dd7bf..0ec66b4f20a4a25e5592c1bfd45d1917093682bc 100644 --- a/steam-runtime-tools/architecture-internal.h +++ b/steam-runtime-tools/architecture-internal.h @@ -27,6 +27,9 @@ #pragma once #include <glib.h> +#include <json-glib/json-glib.h> G_GNUC_INTERNAL gboolean _srt_architecture_can_run (const char *helpers_path, const char *multiarch); + +gboolean _srt_architecture_can_run_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/architecture.c b/steam-runtime-tools/architecture.c index 04ae1529431062f0133aa95a6e01554d267c29a3..3c1fadf75d10d4990f397fb691e9edc304d2d1d5 100644 --- a/steam-runtime-tools/architecture.c +++ b/steam-runtime-tools/architecture.c @@ -158,3 +158,24 @@ srt_architecture_can_run_x86_64 (void) { return _srt_architecture_can_run (NULL, SRT_ABI_X86_64); } + +/** + * _srt_architecture_can_run_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "can-run" + * property + * + * Returns: %TRUE if the provided @json_obj has the "can-run" member with a + * positive boolean value. + */ +gboolean +_srt_architecture_can_run_from_report (JsonObject *json_obj) +{ + gboolean can_run = FALSE; + + g_return_val_if_fail (json_obj != NULL, FALSE); + + if (json_object_has_member (json_obj, "can-run")) + can_run = json_object_get_boolean_member (json_obj, "can-run"); + + return can_run; +} diff --git a/steam-runtime-tools/cpu-feature-internal.h b/steam-runtime-tools/cpu-feature-internal.h index 76f1ecc5fd9c1ec673e83bd2187484e955f4e567..944bf6dbcc88f00c030d60ce800b4781531dd533 100644 --- a/steam-runtime-tools/cpu-feature-internal.h +++ b/steam-runtime-tools/cpu-feature-internal.h @@ -31,5 +31,10 @@ #include <glib.h> #include <glib-object.h> +#include <json-glib/json-glib.h> + G_GNUC_INTERNAL -SrtX86FeatureFlags _srt_feature_get_x86_flags (void); +SrtX86FeatureFlags _srt_feature_get_x86_flags (SrtX86FeatureFlags *known); + +SrtX86FeatureFlags _srt_feature_get_x86_flags_from_report (JsonObject *json_obj, + SrtX86FeatureFlags *known); diff --git a/steam-runtime-tools/cpu-feature.c b/steam-runtime-tools/cpu-feature.c index 79579b38a6b6822b68b2c031223ce816a0167b72..b231f850f96bf28840f44d7fff5ceec4d3352442 100644 --- a/steam-runtime-tools/cpu-feature.c +++ b/steam-runtime-tools/cpu-feature.c @@ -25,6 +25,7 @@ #include "steam-runtime-tools/cpu-feature.h" #include "steam-runtime-tools/cpu-feature-internal.h" +#include <steam-runtime-tools/enums.h> #include <sys/stat.h> #include <sys/types.h> @@ -32,6 +33,7 @@ #include "steam-runtime-tools/glib-compat.h" #include "steam-runtime-tools/utils.h" +#include "steam-runtime-tools/utils-internal.h" /** * SECTION:cpu-feature @@ -42,40 +44,112 @@ * #SrtX86FeatureFlags represents the features that the CPU supports. */ +/** + * _srt_feature_get_x86_flags: + * @known: (not optional): Used to return the #SrtX86FeatureFlags that have + * been checked + * + * Returns: A #SrtX86FeatureFlags with the available X86 CPU flags. + */ SrtX86FeatureFlags -_srt_feature_get_x86_flags (void) +_srt_feature_get_x86_flags (SrtX86FeatureFlags *known) { guint eax = 0; guint ebx = 0; guint ecx = 0; guint edx = 0; - int result; - SrtX86FeatureFlags features = SRT_X86_FEATURE_NONE; + SrtX86FeatureFlags present = SRT_X86_FEATURE_NONE; + + g_return_val_if_fail (known != NULL, SRT_X86_FEATURE_NONE); + + *known = SRT_X86_FEATURE_NONE; /* Get the list of basic features (leaf 1) */ - result = __get_cpuid (1, &eax, &ebx, &ecx, &edx); - if (result != 1) + if (__get_cpuid (1, &eax, &ebx, &ecx, &edx) == 1) + { + *known |= (SRT_X86_FEATURE_CMPXCHG16B | SRT_X86_FEATURE_SSE3); + + if (ecx & bit_CMPXCHG16B) + present |= SRT_X86_FEATURE_CMPXCHG16B; + + if (ecx & bit_SSE3) + present |= SRT_X86_FEATURE_SSE3; + } + else { g_debug ("Something went wrong trying to list supported x86 features"); - return features; + return present; } - - if (ecx & bit_CMPXCHG16B) - features |= SRT_X86_FEATURE_CMPXCHG16B; - - if (ecx & bit_SSE3) - features |= SRT_X86_FEATURE_SSE3; - - result = __get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx); - if (result != 1) + + if (__get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx) == 1) + { + *known |= SRT_X86_FEATURE_X86_64; + + /* Long mode, 64-bit capable */ + if (edx & bit_LM) + present |= SRT_X86_FEATURE_X86_64; + } + else { g_debug ("Something went wrong trying to list extended supported x86 features"); - return features; + return present; } - /* Long mode, 64-bit capable */ - if (edx & bit_LM) - features |= SRT_X86_FEATURE_X86_64; + return present; +} + +/** + * _srt_feature_get_x86_flags_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "cpu-features" + * property + * @known: (not nullable): Used to return the #SrtX86FeatureFlags that are + * known + * + * If the provided @json_obj doesn't have a "cpu-features" member, or it is + * malformed, @known and the return value will be set to + * %SRT_X86_FEATURE_NONE. + * If @json_obj has some elements that we can't parse, + * %SRT_X86_FEATURE_UNKNOWN will be added to the @known and, if they are of + * positive value, to the return value too. + * + * Returns: the #SrtX86FeatureFlags that has been found + */ +SrtX86FeatureFlags +_srt_feature_get_x86_flags_from_report (JsonObject *json_obj, + SrtX86FeatureFlags *known) +{ + JsonObject *json_sub_obj; + + g_return_val_if_fail (json_obj != NULL, SRT_X86_FEATURE_NONE); + g_return_val_if_fail (known != NULL, SRT_X86_FEATURE_NONE); + + SrtX86FeatureFlags present = SRT_X86_FEATURE_NONE; + *known = SRT_X86_FEATURE_NONE; + + if (json_object_has_member (json_obj, "cpu-features")) + { + GList *features_members = NULL; + gboolean value = FALSE; + json_sub_obj = json_object_get_object_member (json_obj, "cpu-features"); + + if (json_sub_obj == NULL) + goto out; + + features_members = json_object_get_members (json_sub_obj); + + for (GList *l = features_members; l != NULL; l = l->next) + { + if (!srt_add_flag_from_nick (SRT_TYPE_X86_FEATURE_FLAGS, l->data, known, NULL)) + *known |= SRT_X86_FEATURE_UNKNOWN; + + value = json_object_get_boolean_member (json_sub_obj, l->data); + if (value) + if (!srt_add_flag_from_nick (SRT_TYPE_X86_FEATURE_FLAGS, l->data, &present, NULL)) + present |= SRT_X86_FEATURE_UNKNOWN; + } + g_list_free (features_members); + } - return features; -} \ No newline at end of file +out: + return present; +} diff --git a/steam-runtime-tools/cpu-feature.h b/steam-runtime-tools/cpu-feature.h index 111c19287c249ffb605f7b3e2ff7a590b5524b10..594aa33487f8b6fe17bdc75f591e2a2162bc609a 100644 --- a/steam-runtime-tools/cpu-feature.h +++ b/steam-runtime-tools/cpu-feature.h @@ -40,12 +40,15 @@ * `pni` in Linux `/proc/cpuinfo` * @SRT_X86_FEATURE_CMPXCHG16B: The CPU supports the CMPXCHG16B instruction, * indicated by `cx16` in Linux `/proc/cpuinfo` + * @SRT_X86_FEATURE_UNKNOWN: An unknown CPU feature was encountered when + * loading a report * * A bitfield with flags representing the features that the CPU supports, or * %SRT_X86_FEATURE_NONE (which is numerically zero) if none of the features * we checked are supported. * - * In general, more bits set means more instructions are supported. + * In general, more bits set means more instructions are supported, with the + * only exception for %SRT_X86_FEATURE_UNKNOWN. * * At the time of writing, the Steam client requires %SRT_X86_FEATURE_X86_64, * %SRT_X86_FEATURE_SSE3 and %SRT_X86_FEATURE_CMPXCHG16B. @@ -55,5 +58,6 @@ typedef enum SRT_X86_FEATURE_X86_64 = (1 << 0), SRT_X86_FEATURE_SSE3 = (1 << 1), SRT_X86_FEATURE_CMPXCHG16B = (1 << 2), + SRT_X86_FEATURE_UNKNOWN = (1 << 3), SRT_X86_FEATURE_NONE = 0 } SrtX86FeatureFlags; diff --git a/steam-runtime-tools/desktop-entry-internal.h b/steam-runtime-tools/desktop-entry-internal.h index ffa49466c926c21c3c44515f7a3a5280fb8b3d4a..260787e4499494d73ed6014376f3aea2b882aa57 100644 --- a/steam-runtime-tools/desktop-entry-internal.h +++ b/steam-runtime-tools/desktop-entry-internal.h @@ -29,6 +29,8 @@ #include "steam-runtime-tools/steam-runtime-tools.h" #include "steam-runtime-tools/utils-internal.h" +#include <json-glib/json-glib.h> + /* * _srt_desktop_entry_new: * @id: (nullable) @@ -68,3 +70,5 @@ _srt_desktop_entry_new (const gchar *id, G_GNUC_INTERNAL GList *_srt_list_steam_desktop_entries (void); + +GList *_srt_get_steam_desktop_entries_from_json_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/desktop-entry.c b/steam-runtime-tools/desktop-entry.c index 433494e7697cdd8c6898c53192332df360a3c6e4..bb5b1af1230d0045e06f70a59a7be2b4b0092533 100644 --- a/steam-runtime-tools/desktop-entry.c +++ b/steam-runtime-tools/desktop-entry.c @@ -390,3 +390,66 @@ _srt_list_steam_desktop_entries (void) return g_list_reverse (ret); } + +/** + * _srt_get_steam_desktop_entries_from_json_report: + * @json_obj: (not nullable): A JSON Object used to search for "desktop-entries" + * property + * + * If the provided @json_obj doesn't have a "desktop-entries" member, or it is + * malformed, %NULL will be returned. + * + * Returns: A list with all the #SrtDesktopEntry that have been found, or %NULL + * if none has been found. + */ +GList * +_srt_get_steam_desktop_entries_from_json_report (JsonObject *json_obj) +{ + JsonArray *array; + JsonObject *json_sub_obj; + GList *desktop_entries = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "desktop-entries")) + { + array = json_object_get_array_member (json_obj, "desktop-entries"); + + if (array == NULL) + goto out; + + guint length = json_array_get_length (array); + for (guint i = 0; i < length; i++) + { + const gchar *id = NULL; + const gchar *commandline = NULL; + const gchar *filename = NULL; + gboolean is_default = FALSE; + gboolean is_steam_handler = FALSE; + json_sub_obj = json_array_get_object_element (array, i); + if (json_object_has_member (json_sub_obj, "id")) + id = json_object_get_string_member (json_sub_obj, "id"); + + if (json_object_has_member (json_sub_obj, "commandline")) + commandline = json_object_get_string_member (json_sub_obj, "commandline"); + + if (json_object_has_member (json_sub_obj, "filename")) + filename = json_object_get_string_member (json_sub_obj, "filename"); + + if (json_object_has_member (json_sub_obj, "default_steam_uri_handler")) + is_default = json_object_get_boolean_member (json_sub_obj, "default_steam_uri_handler"); + + if (json_object_has_member (json_sub_obj, "steam_uri_handler")) + is_steam_handler = json_object_get_boolean_member (json_sub_obj, "steam_uri_handler"); + + desktop_entries = g_list_prepend (desktop_entries, _srt_desktop_entry_new (id, + commandline, + filename, + is_default, + is_steam_handler)); + } + } + +out: + return desktop_entries; +} diff --git a/steam-runtime-tools/graphics-internal.h b/steam-runtime-tools/graphics-internal.h index 6b31220ca091cbdb179b4167a521c3ceffaef816..3f29332cfb11d265f1310a22bb4616a7b6c972b7 100644 --- a/steam-runtime-tools/graphics-internal.h +++ b/steam-runtime-tools/graphics-internal.h @@ -29,6 +29,8 @@ #include "steam-runtime-tools/steam-runtime-tools.h" #include "steam-runtime-tools/utils-internal.h" +#include <json-glib/json-glib.h> + /* * _srt_graphics_new: * @multiarch_tuple: A multiarch tuple like %SRT_ABI_I386, @@ -129,6 +131,46 @@ _srt_graphics_window_system_string (SrtWindowSystem window_system) return "unknown window system"; } +/** + * _srt_graphics_window_system_nick_to_enum: + * @nick: (not nullable): Nick of the window system enum + * @window_system_out: (not nullable): Used to return the #SrtWindowSystem + * associated with the provided @nick + * @error: Used to return an error on failure + * + * Returns: %TRUE on success + */ +static inline gboolean +_srt_graphics_window_system_nick_to_enum (const gchar *nick, + SrtWindowSystem *window_system_out, + GError **error) +{ + g_return_val_if_fail (nick != NULL, FALSE); + g_return_val_if_fail (window_system_out != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_strcmp0 (nick, "egl_x11") == 0) + { + *window_system_out = SRT_WINDOW_SYSTEM_EGL_X11; + return TRUE; + } + + G_STATIC_ASSERT (sizeof (SrtWindowSystem) == sizeof (gint)); + + return srt_enum_from_nick (SRT_TYPE_WINDOW_SYSTEM, + nick, + (gint *) window_system_out, + error); +} + +/** + * _srt_graphics_rendering_interface_string: + * @rendering_interface: A #SrtRenderingInterface + * + * Returns: The string (nick) associated with the provided + * @rendering_interface, or "unknown rendering interface" if nothing was + * found. + */ static inline const gchar * _srt_graphics_rendering_interface_string (SrtRenderingInterface rendering_interface) { @@ -140,6 +182,32 @@ _srt_graphics_rendering_interface_string (SrtRenderingInterface rendering_interf return "unknown rendering interface"; } +/** + * _srt_graphics_rendering_interface_nick_to_enum: + * @nick: (not nullable): Nick of the rendering interface enum + * @rendering_interface_out: (not nullable): Used to return the #SrtRenderingInterface + * associated with the provided @nick + * @error: Used to return an error on failure + * + * Returns: %TRUE on success + */ +static inline gboolean +_srt_graphics_rendering_interface_nick_to_enum (const gchar *nick, + SrtRenderingInterface *rendering_interface_out, + GError **error) +{ + g_return_val_if_fail (nick != NULL, FALSE); + g_return_val_if_fail (rendering_interface_out != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + G_STATIC_ASSERT (sizeof (SrtRenderingInterface) == sizeof (gint)); + + return srt_enum_from_nick (SRT_TYPE_RENDERING_INTERFACE, + nick, + (gint *) rendering_interface_out, + error); +} + #endif /** @@ -174,3 +242,14 @@ GList *_srt_list_graphics_modules (const gchar *sysroot, const char *helpers_path, const char *multiarch_tuple, SrtGraphicsModule which); + +void _srt_graphics_get_from_report (JsonObject *json_obj, + const gchar *multiarch_tuple, + GHashTable **cached_graphics); + +GList *_srt_get_egl_from_json_report (JsonObject *json_obj); +GList *_srt_get_vulkan_from_json_report (JsonObject *json_obj); +GList *_srt_dri_driver_get_from_report (JsonObject *json_obj); +GList *_srt_va_api_driver_get_from_report (JsonObject *json_obj); +GList *_srt_vdpau_driver_get_from_report (JsonObject *json_obj); +GList *_srt_glx_icd_get_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c index cc971ebbf160fab1a7302903418b5e2d8c4653b8..1a092536c6be0453437984e5dddc91267501aa8d 100644 --- a/steam-runtime-tools/graphics.c +++ b/steam-runtime-tools/graphics.c @@ -351,8 +351,8 @@ srt_graphics_class_init (SrtGraphicsClass *cls) static SrtGraphicsIssues _srt_process_wflinfo (JsonParser *parser, const gchar **version_string, const gchar **renderer_string) { - g_return_val_if_fail (version_string != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (version_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE; @@ -408,8 +408,8 @@ _srt_process_wflinfo (JsonParser *parser, const gchar **version_string, const gc static SrtGraphicsIssues _srt_process_vulkaninfo (JsonParser *parser, gchar **new_version_string, const gchar **renderer_string) { - g_return_val_if_fail (new_version_string != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (new_version_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (renderer_string != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE; JsonNode *node = json_parser_get_root (parser); @@ -892,11 +892,11 @@ _srt_run_helper (GStrv *my_environ, // non_zero_wait_status_issue needs to be something other than NONE, otherwise // we get no indication in caller that there was any error g_return_val_if_fail (non_zero_wait_status_issue != SRT_GRAPHICS_ISSUES_NONE, - SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + SRT_GRAPHICS_ISSUES_UNKNOWN); - g_return_val_if_fail (wait_status != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (exit_status != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (terminating_signal != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (wait_status != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (exit_status != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (terminating_signal != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); SrtGraphicsIssues issues = SRT_GRAPHICS_ISSUES_NONE; GError *error = NULL; @@ -988,10 +988,10 @@ _srt_check_graphics (const char *helpers_path, gchar *filtered_preload = NULL; SrtGraphicsLibraryVendor library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN; - g_return_val_if_fail (details_out == NULL || *details_out == NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (((unsigned) window_system) < SRT_N_WINDOW_SYSTEMS, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (((unsigned) rendering_interface) < SRT_N_RENDERING_INTERFACES, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (_srt_check_not_setuid (), SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (details_out == NULL || *details_out == NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (((unsigned) window_system) < SRT_N_WINDOW_SYSTEMS, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (((unsigned) rendering_interface) < SRT_N_RENDERING_INTERFACES, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (_srt_check_not_setuid (), SRT_GRAPHICS_ISSUES_UNKNOWN); GPtrArray *argv = _argv_for_graphics_test (helpers_path, test_flags, @@ -1033,7 +1033,7 @@ _srt_check_graphics (const char *helpers_path, break; default: - g_return_val_if_reached (SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_reached (SRT_GRAPHICS_ISSUES_UNKNOWN); } issues |= _srt_run_helper (&my_environ, @@ -1097,7 +1097,7 @@ _srt_check_graphics (const char *helpers_path, break; default: - g_return_val_if_reached (SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_reached (SRT_GRAPHICS_ISSUES_UNKNOWN); } switch (rendering_interface) @@ -1210,7 +1210,7 @@ _srt_check_graphics (const char *helpers_path, break; default: - g_return_val_if_reached (SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_reached (SRT_GRAPHICS_ISSUES_UNKNOWN); } out: @@ -1263,7 +1263,7 @@ out: SrtGraphicsIssues srt_graphics_get_issues (SrtGraphics *self) { - g_return_val_if_fail (SRT_IS_GRAPHICS (self), SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_GRAPHICS (self), SRT_GRAPHICS_ISSUES_UNKNOWN); return self->issues; } @@ -1442,6 +1442,156 @@ srt_graphics_get_messages (SrtGraphics *self) return self->messages; } +/** + * _srt_graphics_get_from_report: + * @json_obj: (not nullable): A JSON Object representing an ABI, + * which is checked for a "graphics-details" member + * @multiarch_tuple: (not nullable) (type filename): A Debian-style multiarch tuple + * such as %SRT_ABI_X86_64 + * @cached_graphics: (not optional) (inout): An hash table with the #SrtGraphics + * objects that have been found. The hash key is an int generated combining + * the graphics window system and rendering interface. + */ +void +_srt_graphics_get_from_report (JsonObject *json_obj, + const gchar *multiarch_tuple, + GHashTable **cached_graphics) +{ + JsonObject *json_graphics_obj; + JsonObject *json_stack_obj; + JsonArray *array; + + g_return_if_fail (json_obj != NULL); + g_return_if_fail (multiarch_tuple != NULL); + g_return_if_fail (cached_graphics != NULL && *cached_graphics != NULL); + + if (json_object_has_member (json_obj, "graphics-details")) + { + GList *graphics_members; + json_graphics_obj = json_object_get_object_member (json_obj, "graphics-details"); + + if (json_graphics_obj == NULL) + return; + + graphics_members = json_object_get_members (json_graphics_obj); + for (GList *l = graphics_members; l != NULL; l = l->next) + { + const gchar *messages = NULL; + const gchar *renderer = NULL; + const gchar *version = NULL; + /* A missing exit_status means that its value is the default zero */ + int exit_status = 0; + int terminating_signal = 0; + SrtGraphics *graphics; + SrtGraphicsLibraryVendor library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN; + SrtGraphicsIssues graphics_issues = SRT_GRAPHICS_ISSUES_NONE; + SrtWindowSystem window_system = SRT_WINDOW_SYSTEM_X11; + SrtRenderingInterface rendering_interface = SRT_RENDERING_INTERFACE_GL; + + gchar **stack_parts = g_strsplit (l->data, "/", 2); + if (stack_parts[1] == NULL) + { + g_debug ("Expected to find a parameter divided by a slash, instead we got: %s", + (gchar *) l->data); + g_strfreev (stack_parts); + continue; + } + + if (!_srt_graphics_window_system_nick_to_enum (stack_parts[0], &window_system, NULL)) + { + g_debug ("Unable to get the window system from the parsed enum: %s", + stack_parts[0]); + g_strfreev (stack_parts); + continue; + } + if (!_srt_graphics_rendering_interface_nick_to_enum (stack_parts[1], + &rendering_interface, + NULL)) + { + g_debug ("Unable to get the rendering interface from the parsed enum: %s", + stack_parts[1]); + g_strfreev (stack_parts); + continue; + } + + g_strfreev (stack_parts); + + json_stack_obj = json_object_get_object_member (json_graphics_obj, l->data); + + if (json_stack_obj == NULL) + { + g_debug ("'%s' is not a JSON object as expected", (gchar *) l->data); + continue; + } + + if (json_object_has_member (json_stack_obj, "messages")) + messages = json_object_get_string_member (json_stack_obj, "messages"); + + if (json_object_has_member (json_stack_obj, "renderer")) + renderer = json_object_get_string_member (json_stack_obj, "renderer"); + + if (json_object_has_member (json_stack_obj, "version")) + version = json_object_get_string_member (json_stack_obj, "version"); + + /* In graphics, a missing "issues" array means that no issues were found */ + if (json_object_has_member (json_stack_obj, "issues")) + { + array = json_object_get_array_member (json_stack_obj, "issues"); + + /* We are expecting an array of issues here */ + if (array == NULL) + { + g_debug ("'issues' in 'graphics-details' is not an array as expected"); + graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN; + } + else + { + for (guint j = 0; j < json_array_get_length (array); j++) + { + const gchar *issue_string = json_array_get_string_element (array, j); + if (!srt_add_flag_from_nick (SRT_TYPE_GRAPHICS_ISSUES, + issue_string, + &graphics_issues, + NULL)) + graphics_issues |= SRT_GRAPHICS_ISSUES_UNKNOWN; + } + } + } + + if (json_object_has_member (json_stack_obj, "exit-status")) + exit_status = json_object_get_int_member (json_stack_obj, "exit-status"); + + if (json_object_has_member (json_stack_obj, "terminating-signal")) + terminating_signal = json_object_get_int_member (json_stack_obj, "terminating-signal"); + + if (json_object_has_member (json_stack_obj, "library-vendor")) + { + const gchar *vendor_string = json_object_get_string_member (json_stack_obj, "library-vendor"); + G_STATIC_ASSERT (sizeof (SrtGraphicsLibraryVendor) == sizeof (gint)); + if (!srt_enum_from_nick (SRT_TYPE_GRAPHICS_LIBRARY_VENDOR, + vendor_string, + (gint *) &library_vendor, + NULL)) + library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN; + } + + graphics = _srt_graphics_new (multiarch_tuple, + window_system, + rendering_interface, + library_vendor, + renderer, + version, + graphics_issues, + messages, + exit_status, + terminating_signal); + + int hash_key = _srt_graphics_hash_key (window_system, rendering_interface); + g_hash_table_insert (*cached_graphics, GINT_TO_POINTER(hash_key), graphics); + } + } +} + /* EGL and Vulkan ICDs are actually basically the same, but we don't * hard-code that in the API. */ typedef struct @@ -2449,6 +2599,23 @@ _srt_resolve_library_path (const gchar *library_path) return ret; } +static GList * +get_driver_icds_from_json_report (JsonObject *json_obj, + GType which); + +/** + * _srt_get_egl_from_json_report: + * @json_obj: (not nullable): A JSON Object used to search for "egl" property + * + * Returns: A list of #SrtEglIcd that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_get_egl_from_json_report (JsonObject *json_obj) +{ + return get_driver_icds_from_json_report (json_obj, SRT_TYPE_EGL_ICD); +} + /** * SrtDriDriver: * @@ -2654,6 +2821,52 @@ srt_dri_driver_resolve_library_path (SrtDriDriver *self) return _srt_resolve_library_path (self->library_path); } +/** + * _srt_dri_driver_get_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "dri_drivers" + * property + * + * If the provided @json_obj doesn't have a "dri_drivers" member, or it is + * malformed, %NULL will be returned. + * + * Returns: A list of #SrtDriDriver that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_dri_driver_get_from_report (JsonObject *json_obj) +{ + JsonArray *array; + GList *dri_drivers = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "dri_drivers")) + { + array = json_object_get_array_member (json_obj, "dri_drivers"); + + if (array == NULL) + goto out; + + guint length = json_array_get_length (array); + for (guint j = 0; j < length; j++) + { + const gchar *dri_path = NULL; + gboolean is_extra = FALSE; + JsonObject *json_dri_obj = json_array_get_object_element (array, j); + if (json_object_has_member (json_dri_obj, "library_path")) + dri_path = json_object_get_string_member (json_dri_obj, "library_path"); + + if (json_object_has_member (json_dri_obj, "is_extra")) + is_extra = json_object_get_boolean_member (json_dri_obj, "is_extra"); + + dri_drivers = g_list_prepend (dri_drivers, srt_dri_driver_new (dri_path, is_extra)); + } + } + +out: + return g_list_reverse (dri_drivers); +} + /** * _srt_get_library_class: * @library: (not nullable) (type filename): The library path to use @@ -3296,7 +3509,7 @@ _srt_get_modules_full (const char *sysroot, &library_details); if (issues & (SRT_LIBRARY_ISSUES_CANNOT_LOAD | - SRT_LIBRARY_ISSUES_INTERNAL_ERROR | + SRT_LIBRARY_ISSUES_UNKNOWN | SRT_LIBRARY_ISSUES_TIMEOUT)) { const char *messages = srt_library_get_messages (library_details); @@ -3801,6 +4014,52 @@ srt_va_api_driver_resolve_library_path (SrtVaApiDriver *self) return _srt_resolve_library_path (self->library_path); } +/** + * _srt_va_api_driver_get_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "va-api_drivers" + * property + * + * If the provided @json_obj doesn't have a "va-api_drivers" member, or it is + * malformed, %NULL will be returned. + * + * Returns: A list of #SrtVaApiDriver that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_va_api_driver_get_from_report (JsonObject *json_obj) +{ + JsonArray *array; + GList *va_api_drivers = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "va-api_drivers")) + { + array = json_object_get_array_member (json_obj, "va-api_drivers"); + + if (array == NULL) + goto out; + + guint length = json_array_get_length (array); + for (guint j = 0; j < length; j++) + { + const gchar *va_api_path = NULL; + gboolean is_extra = FALSE; + JsonObject *json_va_api_obj = json_array_get_object_element (array, j); + if (json_object_has_member (json_va_api_obj, "library_path")) + va_api_path = json_object_get_string_member (json_va_api_obj, "library_path"); + + if (json_object_has_member (json_va_api_obj, "is_extra")) + is_extra = json_object_get_boolean_member (json_va_api_obj, "is_extra"); + + va_api_drivers = g_list_prepend (va_api_drivers, srt_va_api_driver_new (va_api_path, is_extra)); + } + } + +out: + return g_list_reverse (va_api_drivers); +} + /** * SrtVdpauDriver: * @@ -4044,6 +4303,56 @@ srt_vdpau_driver_resolve_library_path (SrtVdpauDriver *self) return _srt_resolve_library_path (self->library_path); } +/** + * _srt_vdpau_driver_get_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "vdpau_drivers" + * property + * + * If the provided @json_obj doesn't have a "vdpau_drivers" member, or it is + * malformed, %NULL will be returned. + * + * Returns: A list of #SrtVdpauDriver that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_vdpau_driver_get_from_report (JsonObject *json_obj) +{ + JsonArray *array; + GList *vdpau_drivers = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "vdpau_drivers")) + { + array = json_object_get_array_member (json_obj, "vdpau_drivers"); + + if (array == NULL) + goto out; + + guint length = json_array_get_length (array); + for (guint j = 0; j < length; j++) + { + const gchar *vdpau_path = NULL; + const gchar *vdpau_link = NULL; + gboolean is_extra = FALSE; + JsonObject *json_vdpau_obj = json_array_get_object_element (array, j); + if (json_object_has_member (json_vdpau_obj, "library_path")) + vdpau_path = json_object_get_string_member (json_vdpau_obj, "library_path"); + + if (json_object_has_member (json_vdpau_obj, "library_link")) + vdpau_link = json_object_get_string_member (json_vdpau_obj, "library_link"); + + if (json_object_has_member (json_vdpau_obj, "is_extra")) + is_extra = json_object_get_boolean_member (json_vdpau_obj, "is_extra"); + + vdpau_drivers = g_list_prepend (vdpau_drivers, srt_vdpau_driver_new (vdpau_path, vdpau_link, is_extra)); + } + } + +out: + return g_list_reverse (vdpau_drivers); +} + /** * SrtVulkanIcd: * @@ -4682,6 +4991,144 @@ _srt_load_vulkan_icds (const char *sysroot, return g_list_reverse (ret); } +/** + * _srt_get_vulkan_from_json_report: + * @json_obj: (not nullable): A JSON Object used to search for "vulkan" property + * + * Returns: A list of #SrtVulkanIcd that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_get_vulkan_from_json_report (JsonObject *json_obj) +{ + return get_driver_icds_from_json_report (json_obj, SRT_TYPE_VULKAN_ICD); +} + +/** + * get_driver_icds_from_json_report: + * @json_obj: (not nullable): A JSON Object used to search for Icd properties + * @which: Used to choose which ICD to search, it can be either + * %SRT_TYPE_EGL_ICD or %SRT_TYPE_VULKAN_ICD + * + * Returns: A list of #SrtEglIcd (if @which is %SRT_TYPE_EGL_ICD) or + * #SrtVulkanIcd (if @which is %SRT_TYPE_VULKAN_ICD) that have been found, or + * %NULL if none has been found. + */ +static GList * +get_driver_icds_from_json_report (JsonObject *json_obj, + GType which) +{ + const gchar *member; + JsonObject *json_sub_obj; + JsonArray *array; + GList *driver_icds = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (which == SRT_TYPE_EGL_ICD) + member = "egl"; + else if (which == SRT_TYPE_VULKAN_ICD) + member = "vulkan"; + else + g_return_val_if_reached (NULL); + + if (json_object_has_member (json_obj, member)) + { + json_sub_obj = json_object_get_object_member (json_obj, member); + + /* We are expecting an object here */ + if (json_sub_obj == NULL) + { + g_debug ("'%s' is not a JSON object as expected", member); + goto out; + } + + if (json_object_has_member (json_sub_obj, "icds")) + { + array = json_object_get_array_member (json_sub_obj, "icds"); + + /* We are expecting an array of icds here */ + if (array == NULL) + { + g_debug ("'icds' is not an array as expected"); + goto out; + } + + for (guint i = 0; i < json_array_get_length (array); i++) + { + const gchar *json_path = NULL; + const gchar *library_path = NULL; + const gchar *api_version = NULL; + GQuark error_domain = 0; + gint error_code = -1; + const gchar *error_message = "(missing error message)"; + GError *icd_error = NULL; + JsonObject *json_icd_obj = json_array_get_object_element (array, i); + if (json_object_has_member (json_icd_obj, "json_path")) + json_path = json_object_get_string_member (json_icd_obj, "json_path"); + else + { + g_debug ("The parsed 'icd' is missing the expected 'json_path' member, skipping..."); + continue; + } + + if (json_object_has_member (json_icd_obj, "library_path")) + library_path = json_object_get_string_member (json_icd_obj, "library_path"); + + if (json_object_has_member (json_icd_obj, "api_version")) + api_version = json_object_get_string_member (json_icd_obj, "api_version"); + + if (json_object_has_member (json_icd_obj, "error-domain")) + error_domain = g_quark_from_string (json_object_get_string_member (json_icd_obj, + "error-domain")); + + if (json_object_has_member (json_icd_obj, "error-code")) + error_code = json_object_get_int_member (json_icd_obj, "error-code"); + + if (json_object_has_member (json_icd_obj, "error")) + error_message = json_object_get_string_member (json_icd_obj, "error"); + + if (library_path != NULL) + { + if (which == SRT_TYPE_EGL_ICD) + driver_icds = g_list_prepend (driver_icds, srt_egl_icd_new (json_path, + library_path)); + else if (which == SRT_TYPE_VULKAN_ICD) + driver_icds = g_list_prepend (driver_icds, srt_vulkan_icd_new (json_path, + api_version, + library_path)); + else + g_return_val_if_reached (NULL); + } + else + { + if (error_domain == 0) + { + error_domain = G_IO_ERROR; + error_code = G_IO_ERROR_FAILED; + } + g_set_error_literal (&icd_error, + error_domain, + error_code, + error_message); + if (which == SRT_TYPE_EGL_ICD) + driver_icds = g_list_prepend (driver_icds, srt_egl_icd_new_error (json_path, + icd_error)); + else if (which == SRT_TYPE_VULKAN_ICD) + driver_icds = g_list_prepend (driver_icds, srt_vulkan_icd_new_error (json_path, + icd_error)); + else + g_return_val_if_reached (NULL); + + g_clear_error (&icd_error); + } + } + } + } +out: + return driver_icds; +} + /** * SrtGlxIcd: * @@ -4855,3 +5302,49 @@ srt_glx_icd_get_library_path (SrtGlxIcd *self) g_return_val_if_fail (SRT_IS_GLX_ICD (self), NULL); return self->library_path; } + +/** + * _srt_glx_icd_get_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "glx_drivers" + * property + * + * If the provided @json_obj doesn't have a "glx_drivers" member, or it is + * malformed, %NULL will be returned. + * + * Returns: A list of #SrtGlxIcd that have been found, or %NULL if none + * has been found. + */ +GList * +_srt_glx_icd_get_from_report (JsonObject *json_obj) +{ + JsonArray *array; + GList *glx_drivers = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "glx_drivers")) + { + array = json_object_get_array_member (json_obj, "glx_drivers"); + + if (array == NULL) + goto out; + + guint length = json_array_get_length (array); + for (guint j = 0; j < length; j++) + { + const gchar *glx_path = NULL; + const gchar *glx_soname = NULL; + JsonObject *json_glx_obj = json_array_get_object_element (array, j); + if (json_object_has_member (json_glx_obj, "library_path")) + glx_path = json_object_get_string_member (json_glx_obj, "library_path"); + + if (json_object_has_member (json_glx_obj, "library_soname")) + glx_soname = json_object_get_string_member (json_glx_obj, "library_soname"); + + glx_drivers = g_list_prepend (glx_drivers, srt_glx_icd_new (glx_soname, glx_path)); + } + } + +out: + return g_list_reverse (glx_drivers); +} diff --git a/steam-runtime-tools/graphics.h b/steam-runtime-tools/graphics.h index 31400d7505cd8a5e41282d884dc65878f5febe64..06d9a9554a13e01a81adb163d06d9b36fbbed8a2 100644 --- a/steam-runtime-tools/graphics.h +++ b/steam-runtime-tools/graphics.h @@ -44,10 +44,14 @@ typedef struct _SrtGraphicsClass SrtGraphicsClass; GType srt_graphics_get_type (void); +/* Backward compatibility with previous steam-runtime-tools naming */ +#define SRT_GRAPHICS_ISSUES_INTERNAL_ERROR SRT_GRAPHICS_ISSUES_UNKNOWN + /** * SrtGraphicsIssues: * @SRT_GRAPHICS_ISSUES_NONE: There are no problems - * @SRT_GRAPHICS_ISSUES_INTERNAL_ERROR: An internal error of some kind has occurred + * @SRT_GRAPHICS_ISSUES_UNKNOWN: An internal error occurred while checking + * graphics, or an unknown issue flag was encountered while reading a report * @SRT_GRAPHICS_ISSUES_CANNOT_LOAD: Unable to load the necessary libraries and create rendering context * @SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING: The graphics renderer is software based * @SRT_GRAPHICS_ISSUES_TIMEOUT: The check for this graphics stack took @@ -64,11 +68,11 @@ GType srt_graphics_get_type (void); typedef enum { SRT_GRAPHICS_ISSUES_NONE = 0, - SRT_GRAPHICS_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_GRAPHICS_ISSUES_UNKNOWN = (1 << 0), SRT_GRAPHICS_ISSUES_CANNOT_LOAD = (1 << 1), SRT_GRAPHICS_ISSUES_SOFTWARE_RENDERING = (1 << 2), SRT_GRAPHICS_ISSUES_TIMEOUT = (1 << 3), - SRT_GRAPHICS_ISSUES_CANNOT_DRAW = (1 << 4) + SRT_GRAPHICS_ISSUES_CANNOT_DRAW = (1 << 4), } SrtGraphicsIssues; /** diff --git a/steam-runtime-tools/library-internal.h b/steam-runtime-tools/library-internal.h index 4e9f3be147126f9c93ff4bb4739259bbf8612634..aff1658ec9232182706332aec5ece5b2ed2a5fe1 100644 --- a/steam-runtime-tools/library-internal.h +++ b/steam-runtime-tools/library-internal.h @@ -28,6 +28,8 @@ #include "steam-runtime-tools/library.h" +#include <json-glib/json-glib.h> + /* * _srt_library_new: * @multiarch_tuple: A multiarch tuple like %SRT_ABI_I386, @@ -101,3 +103,5 @@ SrtLibraryIssues _srt_check_library_presence (const char *helpers_path, gchar **envp, SrtLibrarySymbolsFormat symbols_format, SrtLibrary **more_details_out); + +SrtLibraryIssues _srt_library_get_issues_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/library.c b/steam-runtime-tools/library.c index 2c5fd28a3137fc873d54336a95a489cbee6a72d3..ebbdfd014d2bdd1830c45b9a30f42b23ad14c028 100644 --- a/steam-runtime-tools/library.c +++ b/steam-runtime-tools/library.c @@ -580,11 +580,11 @@ _srt_check_library_presence (const char *helpers_path, SrtHelperFlags flags = SRT_HELPER_FLAGS_TIME_OUT; GString *log_args = NULL; - g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); + g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, - SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (_srt_check_not_setuid (), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + SRT_LIBRARY_ISSUES_UNKNOWN); + g_return_val_if_fail (_srt_check_not_setuid (), SRT_LIBRARY_ISSUES_UNKNOWN); if (symbols_path == NULL) issues |= SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS; @@ -629,7 +629,7 @@ _srt_check_library_presence (const char *helpers_path, break; default: - g_return_val_if_reached (SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_reached (SRT_LIBRARY_ISSUES_UNKNOWN); } for (gsize i = 0; hidden_deps != NULL && hidden_deps[i] != NULL; i++) @@ -779,3 +779,26 @@ out: g_clear_error (&error); return issues; } + +/** + * _srt_library_get_issues_from_report: + * @json_obj: (not nullable): A JSON Object used to search for + * "library-issues-summary" property + * + * If the provided @json_obj doesn't have a "library-issues-summary" member, + * or it is malformed, %SRT_LIBRARY_ISSUES_UNKNOWN will be returned. + * If @json_obj has some elements that we can't parse, + * %SRT_LIBRARY_ISSUES_UNKNOWN will be added to the returned #SrtLibraryIssues. + * + * Returns: The #SrtLibraryIssues that has been found + */ +SrtLibraryIssues +_srt_library_get_issues_from_report (JsonObject *json_obj) +{ + g_return_val_if_fail (json_obj != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); + + return srt_get_flags_from_json_array (SRT_TYPE_LIBRARY_ISSUES, + json_obj, + "library-issues-summary", + SRT_LIBRARY_ISSUES_UNKNOWN); +} diff --git a/steam-runtime-tools/library.h b/steam-runtime-tools/library.h index 011f9c16ce9503545415fcdd2ea54f10775dc385..a4f727f9987709b0c4cc41ec06863766b19882cc 100644 --- a/steam-runtime-tools/library.h +++ b/steam-runtime-tools/library.h @@ -44,6 +44,9 @@ typedef struct _SrtLibraryClass SrtLibraryClass; GType srt_library_get_type (void); +/* Backward compatibility with previous steam-runtime-tools naming */ +#define SRT_LIBRARY_ISSUES_INTERNAL_ERROR SRT_LIBRARY_ISSUES_UNKNOWN + /** * SrtLibraryIssues: * @SRT_LIBRARY_ISSUES_NONE: There are no problems @@ -52,8 +55,8 @@ GType srt_library_get_type (void); * were not present * @SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS: Some of the expected symbols * were available with a different version - * @SRT_LIBRARY_ISSUES_INTERNAL_ERROR: Generic internal error, for example - * a function has been called with a missing required parameter + * @SRT_LIBRARY_ISSUES_UNKNOWN: A generic internal error occurred, or an + * unknown issue flag was encountered while reading a report * @SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS: No directory containing * expected ABIs has been set, so we cannot know which libraries we are * meant to have found @@ -70,7 +73,7 @@ typedef enum SRT_LIBRARY_ISSUES_CANNOT_LOAD = (1 << 0), SRT_LIBRARY_ISSUES_MISSING_SYMBOLS = (1 << 1), SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS = (1 << 2), - SRT_LIBRARY_ISSUES_INTERNAL_ERROR = (1 << 3), + SRT_LIBRARY_ISSUES_UNKNOWN = (1 << 3), SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS = (1 << 4), SRT_LIBRARY_ISSUES_TIMEOUT = (1 << 5), SRT_LIBRARY_ISSUES_NONE = 0 diff --git a/steam-runtime-tools/locale-internal.h b/steam-runtime-tools/locale-internal.h index 9c3070c0b03a06a4789e8007a114988c69b9ca59..0a036744f2831ee7d03d2c93a7507db89cc994b8 100644 --- a/steam-runtime-tools/locale-internal.h +++ b/steam-runtime-tools/locale-internal.h @@ -28,6 +28,8 @@ #include "steam-runtime-tools/steam-runtime-tools.h" +#include <json-glib/json-glib.h> + #ifndef __GTK_DOC_IGNORE__ static inline SrtLocale *_srt_locale_new (const char *requested_name, const char *resulting_name, @@ -58,3 +60,9 @@ SrtLocale *_srt_check_locale (const char *helpers_path, const char *requested_name, GError **error); #endif + +SrtLocale *_srt_locale_get_locale_from_report (JsonObject *json_obj, + const gchar *requested_name, + GError **error); + +SrtLocaleIssues _srt_locale_get_issues_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/locale.c b/steam-runtime-tools/locale.c index dc5cab1ed9d64126249e5c3af7858e9fa0efa5da..a4b9f9bab8edf927c58bd9c1bcaaff033d385e4e 100644 --- a/steam-runtime-tools/locale.c +++ b/steam-runtime-tools/locale.c @@ -356,6 +356,97 @@ out: return ret; } +/** + * _srt_locale_get_locale_from_report: + * @json_obj: (not nullable): A JSON Object used to search for the locale's + * properties, e.g. "resulting-name" and "charset" + * @requested_name: (not nullable): The locale name related to the provided + * @json_obj + * @error: Used to raise an error on failure + * + * Returns: A new #SrtLocale or %NULL if an error occurred. + */ +SrtLocale * +_srt_locale_get_locale_from_report (JsonObject *json_obj, + const gchar *requested_name, + GError **error) +{ + SrtLocale *locale = NULL; + const gchar *resulting_name = NULL; + const gchar *charset = NULL; + gboolean is_utf8 = FALSE; + GQuark error_domain = 0; + gint error_code = -1; + const gchar *error_message = "(missing error message)"; + + g_return_val_if_fail (json_obj != NULL, NULL); + g_return_val_if_fail (requested_name != NULL, NULL); + g_return_val_if_fail (error != NULL && *error == NULL, NULL); + + if (json_object_has_member (json_obj, "resulting-name")) + resulting_name = json_object_get_string_member (json_obj, "resulting-name"); + + if (json_object_has_member (json_obj, "charset")) + charset = json_object_get_string_member (json_obj, "charset"); + + if (json_object_has_member (json_obj, "is_utf8")) + is_utf8 = json_object_get_boolean_member (json_obj, "is_utf8"); + + if (json_object_has_member (json_obj, "error-domain")) + error_domain = g_quark_from_string (json_object_get_string_member (json_obj, "error-domain")); + + if (json_object_has_member (json_obj, "error-code")) + error_code = json_object_get_int_member (json_obj, "error-code"); + + if (json_object_has_member (json_obj, "error")) + error_message = json_object_get_string_member (json_obj, "error"); + + if (resulting_name != NULL && charset != NULL) + { + locale = _srt_locale_new (requested_name, + resulting_name, + charset, + is_utf8); + } + else + { + if (error_domain == 0) + { + error_domain = G_IO_ERROR; + error_code = G_IO_ERROR_FAILED; + } + g_set_error_literal (error, + error_domain, + error_code, + error_message); + } + + return locale; +} + +/** + * _srt_locale_get_issues_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "locale-issues" + * property + * + * If the provided @json_obj doesn't have a "locale-issues" member, or it is + * malformed, %SRT_LOCALE_ISSUES_UNKNOWN will be returned. + * If @json_obj has some elements that we can't parse, + * %SRT_LOCALE_ISSUES_UNKNOWN will be added to the returned #SrtLocaleIssues. + * + * Returns: The %SrtLocaleIssues that has been found + */ +SrtLocaleIssues +_srt_locale_get_issues_from_report (JsonObject *json_obj) +{ + g_return_val_if_fail (json_obj != NULL, SRT_LOCALE_ISSUES_UNKNOWN); + + return srt_get_flags_from_json_array (SRT_TYPE_LOCALE_ISSUES, + json_obj, + "locale-issues", + SRT_LOCALE_ISSUES_UNKNOWN); +} + /** * srt_locale_get_requested_name: * @self: a locale object diff --git a/steam-runtime-tools/locale.h b/steam-runtime-tools/locale.h index 2415dd060e2ff8593cefde5a29cec12c7aef77c2..cf0c4a66220c2f2b8ce4844b8ad5b707cc709f8a 100644 --- a/steam-runtime-tools/locale.h +++ b/steam-runtime-tools/locale.h @@ -63,10 +63,14 @@ typedef enum GQuark srt_locale_error_quark (void); +/* Backward compatibility with previous steam-runtime-tools naming */ +#define SRT_LOCALE_ISSUES_INTERNAL_ERROR SRT_LOCALE_ISSUES_UNKNOWN + /** * SrtLocaleIssues: * @SRT_LOCALE_ISSUES_NONE: There are no problems - * @SRT_LOCALE_ISSUES_INTERNAL_ERROR: An internal error of some kind has occurred + * @SRT_LOCALE_ISSUES_UNKNOWN: An internal error of some kind has occurred, + * or an unknown issue flag was encountered while reading a report. * @SRT_LOCALE_ISSUES_DEFAULT_MISSING: `setlocale (LC_ALL, "")` fails. * This indicates that environment variables like LANGUAGE and LC_ALL * are set to values that do not match the locales available on the @@ -101,12 +105,13 @@ GQuark srt_locale_error_quark (void); * %SRT_LOCALE_ISSUES_NONE (which is numerically zero) if no problems * were detected. * - * In general, more bits set means more problems. + * In general, more bits set means more problems, with the only exception + * for %SRT_LOCALE_ISSUES_UNKNOWN. */ typedef enum { SRT_LOCALE_ISSUES_NONE = 0, - SRT_LOCALE_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_LOCALE_ISSUES_UNKNOWN = (1 << 0), SRT_LOCALE_ISSUES_DEFAULT_MISSING = (1 << 1), SRT_LOCALE_ISSUES_DEFAULT_NOT_UTF8 = (1 << 2), SRT_LOCALE_ISSUES_C_UTF8_MISSING = (1 << 3), diff --git a/steam-runtime-tools/os-internal.h b/steam-runtime-tools/os-internal.h index 1b4a1c2d331f3396dbcde56a82a506fccd0772d4..01cd0cfa567762b0c8bcb8e3bba7322397fd3d27 100644 --- a/steam-runtime-tools/os-internal.h +++ b/steam-runtime-tools/os-internal.h @@ -28,6 +28,7 @@ #include <glib.h> #include <glib-object.h> +#include <json-glib/json-glib.h> typedef struct { @@ -47,3 +48,5 @@ G_GNUC_INTERNAL void _srt_os_release_init (SrtOsRelease *self); G_GNUC_INTERNAL void _srt_os_release_populate (SrtOsRelease *self, const char *sysroot); G_GNUC_INTERNAL void _srt_os_release_clear (SrtOsRelease *self); +void _srt_os_release_populate_from_report (JsonObject *json_obj, + SrtOsRelease *self); diff --git a/steam-runtime-tools/os.c b/steam-runtime-tools/os.c index 6d5d5ba394575dcface490986880c758a511bd2d..4fba109c5e67cab14c780e953d875696d3e379ba 100644 --- a/steam-runtime-tools/os.c +++ b/steam-runtime-tools/os.c @@ -230,3 +230,94 @@ _srt_os_release_clear (SrtOsRelease *self) g_clear_pointer (&self->version_id, g_free); self->populated = FALSE; } + +/** + * _srt_os_release_populate_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "os-release" + * property + * @self: (not nullable): A #SrtOsRelease object to populate + * + * If the provided @json_obj doesn't have a "os-release" member, + * @self will be left untouched. + */ +void +_srt_os_release_populate_from_report (JsonObject *json_obj, + SrtOsRelease *self) +{ + JsonObject *json_sub_obj; + JsonArray *array; + + g_return_if_fail (json_obj != NULL); + g_return_if_fail (self != NULL); + g_return_if_fail (self->build_id == NULL); + g_return_if_fail (self->id == NULL); + g_return_if_fail (self->id_like == NULL); + g_return_if_fail (self->name == NULL); + g_return_if_fail (self->pretty_name == NULL); + g_return_if_fail (self->variant == NULL); + g_return_if_fail (self->variant_id == NULL); + g_return_if_fail (self->version_codename == NULL); + g_return_if_fail (self->version_id == NULL); + + if (json_object_has_member (json_obj, "os-release")) + { + json_sub_obj = json_object_get_object_member (json_obj, "os-release"); + + if (json_sub_obj == NULL) + { + g_debug ("'os-release' is not a JSON object as expected"); + return; + } + + self->populated = TRUE; + + if (json_object_has_member (json_sub_obj, "id")) + self->id = g_strdup (json_object_get_string_member (json_sub_obj, "id")); + + if (json_object_has_member (json_sub_obj, "id_like")) + { + array = json_object_get_array_member (json_sub_obj, "id_like"); + /* We are expecting an array of OS IDs here */ + if (array == NULL) + { + g_debug ("'id_like' in 'os-release' is not an array as expected"); + } + else + { + GString *str = g_string_new (""); + guint length = json_array_get_length (array); + for (guint i = 0; i < length; i++) + { + const char *temp_id = json_array_get_string_element (array, i); + + if (str->len > 0) + g_string_append_c (str, ' '); + + g_string_append (str, temp_id); + } + self->id_like = g_string_free (str, FALSE); + } + } + + if (json_object_has_member (json_sub_obj, "name")) + self->name = g_strdup (json_object_get_string_member (json_sub_obj, "name")); + + if (json_object_has_member (json_sub_obj, "pretty_name")) + self->pretty_name = g_strdup (json_object_get_string_member (json_sub_obj, "pretty_name")); + + if (json_object_has_member (json_sub_obj, "version_id")) + self->version_id = g_strdup (json_object_get_string_member (json_sub_obj, "version_id")); + + if (json_object_has_member (json_sub_obj, "version_codename")) + self->version_codename = g_strdup (json_object_get_string_member (json_sub_obj, "version_codename")); + + if (json_object_has_member (json_sub_obj, "build_id")) + self->build_id = g_strdup (json_object_get_string_member (json_sub_obj, "build_id")); + + if (json_object_has_member (json_sub_obj, "variant_id")) + self->variant_id = g_strdup (json_object_get_string_member (json_sub_obj, "variant_id")); + + if (json_object_has_member (json_sub_obj, "variant")) + self->variant = g_strdup (json_object_get_string_member (json_sub_obj, "variant")); + } +} diff --git a/steam-runtime-tools/runtime-internal.h b/steam-runtime-tools/runtime-internal.h index d7980641986ef1b1b166c981d30d423e9d4f4760..061f38c461d6142c2c7082d6cd41999747beb2b3 100644 --- a/steam-runtime-tools/runtime-internal.h +++ b/steam-runtime-tools/runtime-internal.h @@ -30,6 +30,7 @@ #include <glib.h> #include <glib-object.h> +#include <json-glib/json-glib.h> G_GNUC_INTERNAL SrtRuntimeIssues _srt_runtime_check (const char *bin32, @@ -37,3 +38,5 @@ SrtRuntimeIssues _srt_runtime_check (const char *bin32, const GStrv custom_environ, gchar **version_out, gchar **path_out); + +SrtRuntimeIssues _srt_runtime_get_issues_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/runtime.c b/steam-runtime-tools/runtime.c index f12f383c2573b8153b796656de58cebdb00a7ee6..2578a44e74ceadf0e15a6e11b7e7f68cc4ec252d 100644 --- a/steam-runtime-tools/runtime.c +++ b/steam-runtime-tools/runtime.c @@ -23,8 +23,10 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "steam-runtime-tools/enums.h" #include "steam-runtime-tools/runtime.h" #include "steam-runtime-tools/runtime-internal.h" +#include "steam-runtime-tools/utils-internal.h" #include <errno.h> #include <string.h> @@ -160,11 +162,10 @@ _srt_runtime_check (const char *bin32, GStrv my_environ = NULL; g_return_val_if_fail (version_out == NULL || *version_out == NULL, - SRT_RUNTIME_ISSUES_INTERNAL_ERROR); + SRT_RUNTIME_ISSUES_UNKNOWN); g_return_val_if_fail (path_out == NULL || *path_out == NULL, - SRT_RUNTIME_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (_srt_check_not_setuid (), - SRT_RUNTIME_ISSUES_INTERNAL_ERROR); + SRT_RUNTIME_ISSUES_UNKNOWN); + g_return_val_if_fail (_srt_check_not_setuid (), SRT_RUNTIME_ISSUES_UNKNOWN); if (custom_environ == NULL) my_environ = g_get_environ (); @@ -467,3 +468,26 @@ out: g_clear_error (&error); return issues; } + +/** + * _srt_runtime_get_issues_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "issues" + * property + * + * If the provided @json_obj doesn't have a "issues" member, or it is + * malformed, %SRT_RUNTIME_ISSUES_UNKNOWN will be returned. + * If @json_obj has some elements that we can't parse, + * %SRT_RUNTIME_ISSUES_UNKNOWN will be added to the returned #SrtRuntimeIssues. + * + * Returns: The #SrtRuntimeIssues that has been found + */ +SrtRuntimeIssues +_srt_runtime_get_issues_from_report (JsonObject *json_obj) +{ + g_return_val_if_fail (json_obj != NULL, SRT_RUNTIME_ISSUES_UNKNOWN); + + return srt_get_flags_from_json_array (SRT_TYPE_RUNTIME_ISSUES, + json_obj, + "issues", + SRT_RUNTIME_ISSUES_UNKNOWN); +} diff --git a/steam-runtime-tools/runtime.h b/steam-runtime-tools/runtime.h index 96662d331f61f0df5f11f50d08ff43dbef234f1b..5442176c67c01760c902809a2833f7d987321c5e 100644 --- a/steam-runtime-tools/runtime.h +++ b/steam-runtime-tools/runtime.h @@ -29,11 +29,16 @@ #error "Do not include directly, use <steam-runtime-tools/steam-runtime-tools.h>" #endif +/* Backward compatibility with previous steam-runtime-tools naming */ +#define SRT_RUNTIME_ISSUES_INTERNAL_ERROR SRT_RUNTIME_ISSUES_UNKNOWN + /** * SrtRuntimeIssues: * @SRT_RUNTIME_ISSUES_NONE: There are no problems - * @SRT_RUNTIME_ISSUES_INTERNAL_ERROR: Unable to detect the status of the - * `LD_LIBRARY_PATH`-based Steam Runtime + * @SRT_RUNTIME_ISSUES_UNKNOWN: A generic internal error occurred while + * trying to detect the status of the `LD_LIBRARY_PATH`-based Steam Runtime, + * or, while reading a report, either an unknown issue flag was encountered + * or the runtime issues field was missing * @SRT_RUNTIME_ISSUES_DISABLED: The Steam Runtime has been disabled * @SRT_RUNTIME_ISSUES_NOT_RUNTIME: The Steam Runtime does not appear to * have the correct structure @@ -62,7 +67,7 @@ */ typedef enum { - SRT_RUNTIME_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_RUNTIME_ISSUES_UNKNOWN = (1 << 0), SRT_RUNTIME_ISSUES_DISABLED = (1 << 1), SRT_RUNTIME_ISSUES_NOT_RUNTIME = (1 << 2), SRT_RUNTIME_ISSUES_UNOFFICIAL = (1 << 3), diff --git a/steam-runtime-tools/steam-internal.h b/steam-runtime-tools/steam-internal.h index 1845f2268bfe0c82c4612a6aa548676c7f4ba074..10ec22f19dbec680ae54264849f17cd44c0687f7 100644 --- a/steam-runtime-tools/steam-internal.h +++ b/steam-runtime-tools/steam-internal.h @@ -30,6 +30,7 @@ #include <glib.h> #include <glib-object.h> +#include <json-glib/json-glib.h> /* * _srt_library_new: @@ -60,3 +61,5 @@ _srt_steam_new (SrtSteamIssues issues, G_GNUC_INTERNAL SrtSteamIssues _srt_steam_check (const GStrv env, SrtSteam **more_details_out); + +SrtSteam *_srt_steam_get_from_report (JsonObject *json_obj); diff --git a/steam-runtime-tools/steam.c b/steam-runtime-tools/steam.c index 861e973c6f2d46757a798730897f3e7653dd789c..e2207dc8c29416f525c10e36f8bca60e1a9f64f0 100644 --- a/steam-runtime-tools/steam.c +++ b/steam-runtime-tools/steam.c @@ -37,6 +37,7 @@ #include "steam-runtime-tools/enums.h" #include "steam-runtime-tools/glib-compat.h" #include "steam-runtime-tools/utils.h" +#include "steam-runtime-tools/utils-internal.h" /** * SECTION:steam @@ -217,7 +218,7 @@ srt_steam_class_init (SrtSteamClass *cls) SrtSteamIssues srt_steam_get_issues (SrtSteam *self) { - g_return_val_if_fail (SRT_IS_STEAM (self), SRT_STEAM_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_STEAM (self), SRT_STEAM_ISSUES_UNKNOWN); return self->issues; } /** @@ -301,10 +302,9 @@ _srt_steam_check (const GStrv my_environ, GStrv env = NULL; GError *error = NULL; - g_return_val_if_fail (_srt_check_not_setuid (), - SRT_STEAM_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (_srt_check_not_setuid (), SRT_STEAM_ISSUES_UNKNOWN); g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, - SRT_STEAM_ISSUES_INTERNAL_ERROR); + SRT_STEAM_ISSUES_UNKNOWN); env = (my_environ == NULL) ? g_get_environ () : g_strdupv (my_environ); @@ -570,3 +570,72 @@ _srt_steam_check (const GStrv my_environ, g_strfreev (env); return issues; } + +/** + * _srt_steam_get_from_report: + * @json_obj: (not nullable): A JSON Object used to search for + * "steam-installation" property + * + * If the provided @json_obj doesn't have a "steam-installation" member, + * #SrtSteamIssues of the returned #SrtSteam will be set to + * %SRT_STEAM_ISSUES_UNKNOWN. + * + * Returns: (transfer full): a new #StrSteam object. + */ +SrtSteam * +_srt_steam_get_from_report (JsonObject *json_obj) +{ + JsonObject *json_sub_obj; + JsonArray *array; + SrtSteamIssues issues = SRT_STEAM_ISSUES_UNKNOWN; + const gchar *install_path = NULL; + const gchar *data_path = NULL; + const gchar *bin32_path = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + + if (json_object_has_member (json_obj, "steam-installation")) + { + json_sub_obj = json_object_get_object_member (json_obj, "steam-installation"); + + if (json_sub_obj == NULL) + goto out; + + if (json_object_has_member (json_sub_obj, "issues")) + { + issues = SRT_STEAM_ISSUES_NONE; + array = json_object_get_array_member (json_sub_obj, "issues"); + + /* We are expecting an array of issues here */ + if (array == NULL) + { + g_debug ("'issues' in 'steam-installation' is not an array as expected"); + issues |= SRT_STEAM_ISSUES_UNKNOWN; + } + else + { + for (guint i = 0; i < json_array_get_length (array); i++) + { + const gchar *issue_string = json_array_get_string_element (array, i); + if (!srt_add_flag_from_nick (SRT_TYPE_STEAM_ISSUES, issue_string, &issues, NULL)) + issues |= SRT_STEAM_ISSUES_UNKNOWN; + } + } + } + + if (json_object_has_member (json_sub_obj, "path")) + install_path = json_object_get_string_member (json_sub_obj, "path"); + + if (json_object_has_member (json_sub_obj, "data_path")) + data_path = json_object_get_string_member (json_sub_obj, "data_path"); + + if (json_object_has_member (json_sub_obj, "bin32_path")) + bin32_path = json_object_get_string_member (json_sub_obj, "bin32_path"); + } + +out: + return _srt_steam_new (issues, + install_path, + data_path, + bin32_path); +} diff --git a/steam-runtime-tools/steam.h b/steam-runtime-tools/steam.h index 1037209edba8710c35ef40c03761d629a1f0958f..ad8537e3dbb065133dc16eb940d9bc5761bb8cc8 100644 --- a/steam-runtime-tools/steam.h +++ b/steam-runtime-tools/steam.h @@ -43,11 +43,16 @@ typedef struct _SrtSteamClass SrtSteamClass; #define SRT_STEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SRT_TYPE_STEAM, SrtSteamClass) GType srt_steam_get_type (void); +/* Backward compatibility with previous steam-runtime-tools naming */ +#define SRT_STEAM_ISSUES_INTERNAL_ERROR SRT_STEAM_ISSUES_UNKNOWN + /** * SrtSteamIssues: * @SRT_STEAM_ISSUES_NONE: There are no problems - * @SRT_STEAM_ISSUES_INTERNAL_ERROR: Unable to detect the status of the - * Steam installation + * @SRT_STEAM_ISSUES_UNKNOWN: A generic internal error occurred while trying + * to detect the status of the Steam installation, or, while reading a + * report, either an unknown issue flag was encountered or the Steam issues + * field was missing * @SRT_STEAM_ISSUES_CANNOT_FIND: Unable to find the Steam installation, * either via its canonical symlink `~/.steam/root` or various fallback * methods. See srt_system_info_dup_steam_installation_path(). @@ -80,6 +85,7 @@ GType srt_steam_get_type (void); * one `STEAMSCRIPT` points to. * @SRT_STEAM_ISSUES_UNEXPECTED_STEAM_DESKTOP_ID: The default Steam desktop * application ID is not what we expected. + * @SRT_STEAM_ISSUES_UNKNOWN: The Steam problems are not known * * A bitfield with flags representing problems with the Steam * installation, or %SRT_STEAM_ISSUES_NONE (which is numerically zero) @@ -89,7 +95,7 @@ GType srt_steam_get_type (void); */ typedef enum { - SRT_STEAM_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_STEAM_ISSUES_UNKNOWN = (1 << 0), SRT_STEAM_ISSUES_CANNOT_FIND = (1 << 1), SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK = (1 << 2), SRT_STEAM_ISSUES_CANNOT_FIND_DATA = (1 << 3), diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c index 6325dcfd8c1ab850c10f4ff021730c11d3eadb98..328ff7a4e921bccf7fc9e50ab0a16fc610f05927 100644 --- a/steam-runtime-tools/system-info.c +++ b/steam-runtime-tools/system-info.c @@ -105,6 +105,8 @@ struct _SrtSystemInfo /* Multiarch tuple to use for helper executables in cases where it * shouldn't matter, or %NULL to use the compiled-in default */ GQuark primary_multiarch_tuple; + /* If %TRUE, #SrtSystemInfo cannot be changed anymore */ + gboolean immutable_values; GHashTable *cached_hidden_deps; SrtSteam *steam_data; struct @@ -158,7 +160,7 @@ struct _SrtSystemInfo struct { SrtX86FeatureFlags x86_features; - gboolean have_x86; + SrtX86FeatureFlags x86_known; } cpu_features; SrtOsRelease os_release; SrtTestFlags test_flags; @@ -246,8 +248,8 @@ typedef struct } Abi; static Abi * -ensure_abi (SrtSystemInfo *self, - const char *multiarch_tuple) +ensure_abi_unless_immutable (SrtSystemInfo *self, + const char *multiarch_tuple) { GQuark quark; guint i; @@ -263,6 +265,9 @@ ensure_abi (SrtSystemInfo *self, return abi; } + if (self->immutable_values) + return NULL; + abi = g_slice_new0 (Abi); abi->multiarch_tuple = quark; abi->can_run = TRI_MAYBE; @@ -537,6 +542,199 @@ srt_system_info_new (const char *expectations) NULL); } +static gchar ** _srt_system_info_driver_environment_from_report (JsonObject *json_obj); +static SrtContainerType _srt_system_info_get_container_info_from_report (JsonObject *json_obj, + gchar **host_path); +static gchar ** _srt_system_info_get_pinned_libs_from_report (JsonObject *json_obj, + const gchar *which, + gchar ***messages); + +/** + * srt_system_info_new_from_json: + * @path: (not nullable) (type filename): Path to a JSON report + * + * Return a new #SrtSystemInfo with the info parsed from an existing JSON + * report. + * The #SrtSystemInfo will be immutable: whatever information was in the JSON + * report, that will be the only information that is available. + * + * Returns: (transfer full): A new #SrtSystemInfo. Free with g_object_unref() + */ +SrtSystemInfo * +srt_system_info_new_from_json (const char *path, + GError **error) +{ + SrtSystemInfo *info = NULL; + JsonObject *json_obj = NULL; + JsonObject *json_sub_obj = NULL; + JsonParser *parser = NULL; + JsonNode *node = NULL; + + g_return_val_if_fail (_srt_check_not_setuid (), NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + info = srt_system_info_new (NULL); + + parser = json_parser_new (); + + if (!json_parser_load_from_file (parser, path, error)) + { + g_clear_object (&info); + goto out; + } + + node = json_parser_get_root (parser); + if (node == NULL || !JSON_NODE_HOLDS_OBJECT (node)) + { + if (error) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Expected to find a JSON object in the provided JSON"); + g_clear_object (&info); + goto out; + } + + json_obj = json_node_get_object (node); + + if (json_object_has_member (json_obj, "can-write-uinput")) + info->can_write_uinput = json_object_get_boolean_member (json_obj, "can-write-uinput"); + + info->steam_data = _srt_steam_get_from_report (json_obj); + + info->runtime.issues = SRT_RUNTIME_ISSUES_UNKNOWN; + if (json_object_has_member (json_obj, "runtime")) + { + json_sub_obj = json_object_get_object_member (json_obj, "runtime"); + + if (json_object_has_member (json_sub_obj, "path")) + info->runtime.path = g_strdup (json_object_get_string_member (json_sub_obj, "path")); + + if (json_object_has_member (json_sub_obj, "version")) + info->runtime.version = g_strdup (json_object_get_string_member (json_sub_obj, "version")); + + info->pinned_libs.have_data = TRUE; + + info->runtime.issues = _srt_runtime_get_issues_from_report (json_sub_obj); + + info->pinned_libs.values_32 = _srt_system_info_get_pinned_libs_from_report (json_sub_obj, + "pinned_libs_32", + &info->pinned_libs.messages_32); + info->pinned_libs.values_64 = _srt_system_info_get_pinned_libs_from_report (json_sub_obj, + "pinned_libs_64", + &info->pinned_libs.messages_64); + } + + _srt_os_release_populate_from_report (json_obj, &info->os_release); + + info->container.type = _srt_system_info_get_container_info_from_report (json_obj, + &info->container.host_directory); + + info->cached_driver_environment = _srt_system_info_driver_environment_from_report (json_obj); + + if (json_object_has_member (json_obj, "architectures")) + { + JsonObject *json_arch_obj = NULL; + GList *multiarch_tuples = NULL; + json_sub_obj = json_object_get_object_member (json_obj, "architectures"); + + multiarch_tuples = json_object_get_members (json_sub_obj); + for (GList *l = multiarch_tuples; l != NULL; l = l->next) + { + Abi *abi = NULL; + + if (!json_object_has_member (json_sub_obj, l->data)) + continue; + + json_arch_obj = json_object_get_object_member (json_sub_obj, l->data); + + abi = ensure_abi_unless_immutable (info, l->data); + + abi->can_run = _srt_architecture_can_run_from_report (json_arch_obj); + + abi->libraries_cache_available = TRUE; + abi->cached_combined_issues = _srt_library_get_issues_from_report (json_arch_obj); + + _srt_graphics_get_from_report (json_arch_obj, + l->data, + &abi->cached_graphics_results); + + abi->graphics_modules[SRT_GRAPHICS_DRI_MODULE].modules = _srt_dri_driver_get_from_report (json_arch_obj); + abi->graphics_modules[SRT_GRAPHICS_DRI_MODULE].available = TRUE; + + abi->graphics_modules[SRT_GRAPHICS_VAAPI_MODULE].modules = _srt_va_api_driver_get_from_report (json_arch_obj); + abi->graphics_modules[SRT_GRAPHICS_VAAPI_MODULE].available = TRUE; + + abi->graphics_modules[SRT_GRAPHICS_VDPAU_MODULE].modules = _srt_vdpau_driver_get_from_report (json_arch_obj); + abi->graphics_modules[SRT_GRAPHICS_VDPAU_MODULE].available = TRUE; + + abi->graphics_modules[SRT_GRAPHICS_GLX_MODULE].modules = _srt_glx_icd_get_from_report (json_arch_obj); + abi->graphics_modules[SRT_GRAPHICS_GLX_MODULE].available = TRUE; + } + } + + info->locales.have_issues = TRUE; + info->locales.issues = _srt_locale_get_issues_from_report (json_obj); + + if (info->locales.cached_locales == NULL) + info->locales.cached_locales = g_hash_table_new_full (NULL, NULL, NULL, + maybe_locale_free); + + if (json_object_has_member (json_obj, "locales")) + { + JsonObject *json_locale_obj = NULL; + GList *locales_members = NULL; + json_sub_obj = json_object_get_object_member (json_obj, "locales"); + + locales_members = json_object_get_members (json_sub_obj); + for (GList *l = locales_members; l != NULL; l = l->next) + { + SrtLocale *locale = NULL; + MaybeLocale *maybe; + const gchar *requested_name = NULL; + GError *locale_error = NULL; + json_locale_obj = json_object_get_object_member (json_sub_obj, l->data); + + requested_name = g_strcmp0 (l->data, "<default>") == 0 ? "" : l->data; + + locale = _srt_locale_get_locale_from_report (json_locale_obj, l->data, &locale_error); + if (locale != NULL) + { + maybe = maybe_locale_new_positive (locale); + g_object_unref (locale); + } + else + { + maybe = maybe_locale_new_negative (locale_error); + g_clear_error (&locale_error); + } + + g_hash_table_replace (info->locales.cached_locales, + GUINT_TO_POINTER (g_quark_from_string (requested_name)), + maybe); + } + } + + info->icds.have_egl = TRUE; + info->icds.egl = _srt_get_egl_from_json_report (json_obj); + + info->icds.have_vulkan = TRUE; + info->icds.vulkan = _srt_get_vulkan_from_json_report (json_obj); + + info->desktop_entry.have_data = TRUE; + info->desktop_entry.values = _srt_get_steam_desktop_entries_from_json_report (json_obj); + + info->cpu_features.x86_features = _srt_feature_get_x86_flags_from_report (json_obj, + &info->cpu_features.x86_known); + + info->immutable_values = TRUE; + +out: + if (parser != NULL) + g_object_unref (parser); + + return info; +} + /** * srt_system_info_can_run: * @self: A #SrtSystemInfo object @@ -572,7 +770,10 @@ srt_system_info_can_run (SrtSystemInfo *self, g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), FALSE); g_return_val_if_fail (multiarch_tuple != NULL, FALSE); - abi = ensure_abi (self, multiarch_tuple); + abi = ensure_abi_unless_immutable (self, multiarch_tuple); + + if (abi == NULL) + return FALSE; if (abi->can_run == TRI_MAYBE) { @@ -601,7 +802,7 @@ srt_system_info_can_write_to_uinput (SrtSystemInfo *self) { g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), FALSE); - if (self->can_write_uinput == TRI_MAYBE) + if (self->can_write_uinput == TRI_MAYBE && !self->immutable_values) { int fd = open ("/dev/uinput", O_WRONLY | O_NONBLOCK); @@ -654,6 +855,7 @@ static gboolean ensure_expectations (SrtSystemInfo *self) { g_return_val_if_fail (_srt_check_not_setuid (), FALSE); + g_return_val_if_fail (!self->immutable_values, FALSE); if (self->expectations == NULL) { @@ -693,6 +895,8 @@ ensure_expectations (SrtSystemInfo *self) static void ensure_hidden_deps (SrtSystemInfo *self) { + g_return_if_fail (!self->immutable_values); + if (self->cached_hidden_deps == NULL) { JsonParser *parser = NULL; @@ -807,6 +1011,7 @@ ensure_overrides_cached (SrtSystemInfo *self) GError *error = NULL; g_return_if_fail (_srt_check_not_setuid ()); + g_return_if_fail (!self->immutable_values); if (!self->overrides.have_data) { @@ -890,7 +1095,8 @@ srt_system_info_list_pressure_vessel_overrides (SrtSystemInfo *self, { g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); - ensure_overrides_cached (self); + if (!self->immutable_values) + ensure_overrides_cached (self); if (messages != NULL) *messages = g_strdupv (self->overrides.messages); @@ -909,7 +1115,7 @@ ensure_pinned_libs_cached (SrtSystemInfo *self) g_return_if_fail (_srt_check_not_setuid ()); - if (!self->pinned_libs.have_data) + if (!self->pinned_libs.have_data && !self->immutable_values) { runtime = srt_system_info_dup_runtime_path (self); @@ -1092,6 +1298,41 @@ srt_system_info_list_pinned_libs_64 (SrtSystemInfo *self, return g_strdupv (self->pinned_libs.values_64); } +/** + * _srt_system_info_get_pinned_libs_from_report: + * @json_obj: (not nullable): A JSON Object used to search for @which + * property + * @which: (not nullable): The member to look up + * @messages: (not nullable): Used to return human-readable debug information + * + * Returns: (array zero-terminated=1) (transfer full) (element-type utf8) (nullable): + * An array of strings with the found pinned libs from the @json_obj, or %NULL + * if @json_obj doesn't have a @which member. Free with g_strfreev(). + */ +static gchar ** +_srt_system_info_get_pinned_libs_from_report (JsonObject *json_obj, + const gchar *which, + gchar ***messages) +{ + JsonObject *json_pinned_obj = NULL; + gchar **pinned_list = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + g_return_val_if_fail (which != NULL, NULL); + g_return_val_if_fail (messages != NULL && *messages == NULL, NULL); + + if (json_object_has_member (json_obj, which)) + { + json_pinned_obj = json_object_get_object_member (json_obj, which); + + pinned_list = _srt_json_array_to_strv (json_pinned_obj, "list"); + + *messages = _srt_json_array_to_strv (json_pinned_obj, "messages"); + } + + return pinned_list; +} + /** * srt_system_info_check_libraries: * @self: The #SrtSystemInfo object to use. @@ -1122,20 +1363,17 @@ srt_system_info_check_libraries (SrtSystemInfo *self, GDir *dir = NULL; FILE *fp = NULL; GError *error = NULL; - SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_INTERNAL_ERROR; + SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_UNKNOWN; - g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_UNKNOWN); + g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); g_return_val_if_fail (libraries_out == NULL || *libraries_out == NULL, - SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + SRT_LIBRARY_ISSUES_UNKNOWN); - if (!ensure_expectations (self)) - { - /* We don't know which libraries to check. */ - return SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS; - } + abi = ensure_abi_unless_immutable (self, multiarch_tuple); - abi = ensure_abi (self, multiarch_tuple); + if (abi == NULL) + return SRT_LIBRARY_ISSUES_CANNOT_LOAD; /* If we cached already the result, we return it */ if (abi->libraries_cache_available) @@ -1150,6 +1388,15 @@ srt_system_info_check_libraries (SrtSystemInfo *self, return abi->cached_combined_issues; } + if (self->immutable_values) + return SRT_LIBRARY_ISSUES_UNKNOWN; + + if (!ensure_expectations (self)) + { + /* We don't know which libraries to check. */ + return SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS; + } + dir_path = g_build_filename (self->expectations, multiarch_tuple, NULL); dir = g_dir_open (dir_path, 0, &error); if (error) @@ -1269,15 +1516,18 @@ srt_system_info_check_library (SrtSystemInfo *self, SrtLibraryIssues issues; GDir *dir = NULL; GError *error = NULL; - SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_INTERNAL_ERROR; + SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_UNKNOWN; - g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_UNKNOWN); + g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); + g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_UNKNOWN); g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, - SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + SRT_LIBRARY_ISSUES_UNKNOWN); + + abi = ensure_abi_unless_immutable (self, multiarch_tuple); - abi = ensure_abi (self, multiarch_tuple); + if (abi == NULL) + return SRT_LIBRARY_ISSUES_CANNOT_LOAD; /* If we have the result already in cache, we return it */ library = g_hash_table_lookup (abi->cached_results, soname); @@ -1288,6 +1538,9 @@ srt_system_info_check_library (SrtSystemInfo *self, return srt_library_get_issues (library); } + if (self->immutable_values) + return SRT_LIBRARY_ISSUES_UNKNOWN; + if (ensure_expectations (self)) { dir_path = g_build_filename (self->expectations, multiarch_tuple, NULL); @@ -1460,22 +1713,25 @@ forget_graphics_modules (SrtSystemInfo *self) */ SrtGraphicsIssues srt_system_info_check_graphics (SrtSystemInfo *self, - const char *multiarch_tuple, - SrtWindowSystem window_system, - SrtRenderingInterface rendering_interface, - SrtGraphics **details_out) + const char *multiarch_tuple, + SrtWindowSystem window_system, + SrtRenderingInterface rendering_interface, + SrtGraphics **details_out) { Abi *abi = NULL; SrtGraphicsIssues issues; - g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (multiarch_tuple != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (multiarch_tuple != NULL, SRT_GRAPHICS_ISSUES_UNKNOWN); g_return_val_if_fail (details_out == NULL || *details_out == NULL, - SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (((unsigned) window_system) < SRT_N_WINDOW_SYSTEMS, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); - g_return_val_if_fail (((unsigned) rendering_interface) < SRT_N_RENDERING_INTERFACES, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (((unsigned) window_system) < SRT_N_WINDOW_SYSTEMS, SRT_GRAPHICS_ISSUES_UNKNOWN); + g_return_val_if_fail (((unsigned) rendering_interface) < SRT_N_RENDERING_INTERFACES, SRT_GRAPHICS_ISSUES_UNKNOWN); + + abi = ensure_abi_unless_immutable (self, multiarch_tuple); - abi = ensure_abi (self, multiarch_tuple); + if (abi == NULL) + return SRT_GRAPHICS_ISSUES_UNKNOWN; /* If we have the result already in cache, we return it */ int hash_key = _srt_graphics_hash_key (window_system, rendering_interface); @@ -1487,6 +1743,9 @@ srt_system_info_check_graphics (SrtSystemInfo *self, return srt_graphics_get_issues (graphics); } + if (self->immutable_values) + return SRT_GRAPHICS_ISSUES_UNKNOWN; + graphics = NULL; issues = _srt_check_graphics (self->helpers_path, self->test_flags, @@ -1518,14 +1777,18 @@ srt_system_info_check_graphics (SrtSystemInfo *self, * Free with 'g_list_free_full(list, g_object_unref)`. */ GList * srt_system_info_check_all_graphics (SrtSystemInfo *self, - const char *multiarch_tuple) + const char *multiarch_tuple) { Abi *abi = NULL; g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); g_return_val_if_fail (multiarch_tuple != NULL, NULL); - abi = ensure_abi (self, multiarch_tuple); + abi = ensure_abi_unless_immutable (self, multiarch_tuple); + + if (abi == NULL) + return NULL; + GList *list = NULL; /* If we cached already the result, we return it */ @@ -1602,12 +1865,16 @@ GList * srt_system_info_check_all_graphics (SrtSystemInfo *self, * when locating the Steam Runtime. * * If @env is %NULL, go back to using the real environment variables. + * + * This method is not valid to call on a #SrtSystemInfo that was + * constructed with srt_system_info_new_from_json(). */ void srt_system_info_set_environ (SrtSystemInfo *self, gchar * const *env) { g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); + g_return_if_fail (!self->immutable_values); forget_graphics_modules (self); forget_libraries (self); @@ -1631,12 +1898,16 @@ srt_system_info_set_environ (SrtSystemInfo *self, * system properties. * * If @root is %NULL, go back to using the real root. + * + * This method is not valid to call on a #SrtSystemInfo that was + * constructed with srt_system_info_new_from_json(). */ void srt_system_info_set_sysroot (SrtSystemInfo *self, const char *root) { g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); + g_return_if_fail (!self->immutable_values); forget_container_info (self); forget_graphics_modules (self); @@ -1652,7 +1923,7 @@ srt_system_info_set_sysroot (SrtSystemInfo *self, static void ensure_steam_cached (SrtSystemInfo *self) { - if (self->steam_data == NULL) + if (self->steam_data == NULL && !self->immutable_values) _srt_steam_check (self->env, &self->steam_data); } @@ -1668,8 +1939,7 @@ ensure_steam_cached (SrtSystemInfo *self) SrtSteamIssues srt_system_info_get_steam_issues (SrtSystemInfo *self) { - g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), - SRT_STEAM_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_STEAM_ISSUES_UNKNOWN); ensure_steam_cached (self); return srt_steam_get_issues (self->steam_data); @@ -1806,7 +2076,7 @@ srt_system_info_dup_steam_bin32_path (SrtSystemInfo *self) static void ensure_os_cached (SrtSystemInfo *self) { - if (!self->os_release.populated) + if (!self->os_release.populated && !self->immutable_values) _srt_os_release_populate (&self->os_release, self->sysroot); } @@ -2087,12 +2357,27 @@ srt_system_info_set_expected_runtime_version (SrtSystemInfo *self, { g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); - if (g_strcmp0 (version, self->runtime.expected_version) != 0) + if (self->immutable_values) + { + g_clear_pointer (&self->runtime.expected_version, g_free); + self->runtime.expected_version = g_strdup (version); + if (self->runtime.expected_version == NULL + || self->runtime.version == NULL + || g_strcmp0 (self->runtime.expected_version, self->runtime.version) == 0) { - forget_runtime (self); - g_clear_pointer (&self->runtime.expected_version, g_free); - self->runtime.expected_version = g_strdup (version); + self->runtime.issues &= ~SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION; } + else + { + self->runtime.issues |= SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION; + } + } + else if (g_strcmp0 (version, self->runtime.expected_version) != 0) + { + forget_runtime (self); + g_clear_pointer (&self->runtime.expected_version, g_free); + self->runtime.expected_version = g_strdup (version); + } } /** @@ -2114,6 +2399,9 @@ srt_system_info_dup_expected_runtime_version (SrtSystemInfo *self) static void ensure_runtime_cached (SrtSystemInfo *self) { + if (self->immutable_values) + return; + ensure_os_cached (self); ensure_steam_cached (self); @@ -2170,7 +2458,7 @@ SrtRuntimeIssues srt_system_info_get_runtime_issues (SrtSystemInfo *self) { g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), - SRT_RUNTIME_ISSUES_INTERNAL_ERROR); + SRT_RUNTIME_ISSUES_UNKNOWN); ensure_runtime_cached (self); return self->runtime.issues; @@ -2243,12 +2531,16 @@ srt_system_info_dup_runtime_version (SrtSystemInfo *self) * instead of the normal installed location. * * If @path is %NULL, go back to using the installed location. + * + * This method is not valid to call on a #SrtSystemInfo that was + * constructed with srt_system_info_new_from_json(). */ void srt_system_info_set_helpers_path (SrtSystemInfo *self, const gchar *path) { g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); + g_return_if_fail (!self->immutable_values); forget_graphics_modules (self); forget_libraries (self); @@ -2297,12 +2589,16 @@ srt_system_info_get_primary_multiarch_tuple (SrtSystemInfo *self) * during regression tests. * * If @path is %NULL, go back to using the compiled-in default. + * + * This method is not valid to call on a #SrtSystemInfo that was + * constructed with srt_system_info_new_from_json(). */ void srt_system_info_set_primary_multiarch_tuple (SrtSystemInfo *self, const gchar *tuple) { g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); + g_return_if_fail (!self->immutable_values); forget_locales (self); self->primary_multiarch_tuple = g_quark_from_string (tuple); @@ -2321,10 +2617,9 @@ srt_system_info_set_primary_multiarch_tuple (SrtSystemInfo *self, SrtLocaleIssues srt_system_info_get_locale_issues (SrtSystemInfo *self) { - g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), - SRT_LOCALE_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LOCALE_ISSUES_UNKNOWN); - if (!self->locales.have_issues) + if (!self->locales.have_issues && !self->immutable_values) { SrtLocale *locale = NULL; @@ -2414,6 +2709,12 @@ srt_system_info_check_locale (SrtSystemInfo *self, { maybe = value; } + else if (self->immutable_values) + { + g_set_error (error, SRT_LOCALE_ERROR, SRT_LOCALE_ERROR_INTERNAL_ERROR, + "Information about the requested locale is missing"); + return NULL; + } else { GError *local_error = NULL; @@ -2515,7 +2816,7 @@ srt_system_info_list_egl_icds (SrtSystemInfo *self, g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); - if (!self->icds.have_egl) + if (!self->icds.have_egl && !self->immutable_values) { g_assert (self->icds.egl == NULL); self->icds.egl = _srt_load_egl_icds (self->sysroot, self->env, @@ -2571,7 +2872,7 @@ srt_system_info_list_vulkan_icds (SrtSystemInfo *self, g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); - if (!self->icds.have_vulkan) + if (!self->icds.have_vulkan && !self->immutable_values) { g_assert (self->icds.vulkan == NULL); self->icds.vulkan = _srt_load_vulkan_icds (self->sysroot, self->env, @@ -2627,9 +2928,12 @@ _srt_system_info_list_graphics_modules (SrtSystemInfo *self, g_return_val_if_fail ((int) which >= 0, NULL); g_return_val_if_fail ((int) which < NUM_SRT_GRAPHICS_MODULES, NULL); - abi = ensure_abi (self, multiarch_tuple); + abi = ensure_abi_unless_immutable (self, multiarch_tuple); - if (!abi->graphics_modules[which].available) + if (abi == NULL) + return NULL; + + if (!abi->graphics_modules[which].available && !self->immutable_values) { abi->graphics_modules[which].modules = _srt_list_graphics_modules (self->sysroot, self->env, @@ -2785,7 +3089,7 @@ ensure_driver_environment (SrtSystemInfo *self) { g_return_if_fail (_srt_check_not_setuid ()); - if (self->cached_driver_environment == NULL) + if (self->cached_driver_environment == NULL && !self->immutable_values) { GPtrArray *builder; GRegex *regex; @@ -2864,6 +3168,21 @@ srt_system_info_list_driver_environment (SrtSystemInfo *self) return g_strdupv (self->cached_driver_environment); } +/** + * _srt_system_info_driver_environment_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "driver_environment" + * property + * + * Returns: (array zero-terminated=1) (element-type utf8) (nullable): An array + * of strings, or %NULL if the provided @json_obj doesn't have a + * "driver_environment" member. Free with g_strfreev(). + */ +static gchar ** +_srt_system_info_driver_environment_from_report (JsonObject *json_obj) +{ + return _srt_json_array_to_strv (json_obj, "driver_environment"); +} + typedef struct { SrtContainerType type; @@ -2898,7 +3217,7 @@ ensure_container_info (SrtSystemInfo *self) gchar *contents = NULL; gchar *filename = NULL; - if (self->container.have_data) + if (self->container.have_data || self->immutable_values) return; g_assert (self->container.host_directory == NULL); @@ -3049,9 +3368,64 @@ srt_system_info_dup_container_host_directory (SrtSystemInfo *self) return g_strdup (self->container.host_directory); } +/** + * _srt_system_info_get_container_info_from_report: + * @json_obj: (not nullable): A JSON Object used to search for "container" + * property + * @host_path: (not nullable): Used to return the host path + * + * If the provided @json_obj doesn't have a "container" member, + * %SRT_CONTAINER_TYPE_UNKNOWN will be returned. + * If @json_obj has some elements that we can't parse, the returned + * #SrtContainerType will be set to %SRT_CONTAINER_TYPE_UNKNOWN. + * + * Returns: The found container type + */ +static SrtContainerType +_srt_system_info_get_container_info_from_report (JsonObject *json_obj, + gchar **host_path) +{ + JsonObject *json_sub_obj; + JsonObject *json_host_obj = NULL; + const gchar *type_string = NULL; + SrtContainerType ret = SRT_CONTAINER_TYPE_UNKNOWN; + + g_return_val_if_fail (host_path != NULL && *host_path == NULL, SRT_CONTAINER_TYPE_UNKNOWN); + + if (json_object_has_member (json_obj, "container")) + { + json_sub_obj = json_object_get_object_member (json_obj, "container"); + + if (json_sub_obj == NULL) + goto out; + + if (json_object_has_member (json_sub_obj, "type")) + { + type_string = json_object_get_string_member (json_sub_obj, "type"); + if (!srt_enum_from_nick (SRT_TYPE_CONTAINER_TYPE, type_string, &ret, NULL)) + { + g_debug ("The parsed container type '%s' is not a known element", type_string); + ret = SRT_CONTAINER_TYPE_UNKNOWN; + } + } + + if (json_object_has_member (json_sub_obj, "host")) + { + json_host_obj = json_object_get_object_member (json_sub_obj, "host"); + if (json_object_has_member (json_host_obj, "path")) + *host_path = g_strdup (json_object_get_string_member (json_host_obj, "path")); + } + } + +out: + return ret; +} + static void ensure_desktop_entries (SrtSystemInfo *self) { + g_return_if_fail (!self->immutable_values); + if (self->desktop_entry.have_data) return; @@ -3091,7 +3465,8 @@ srt_system_info_list_desktop_entries (SrtSystemInfo *self) g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); - ensure_desktop_entries (self); + if (!self->immutable_values) + ensure_desktop_entries (self); for (iter = self->desktop_entry.values; iter != NULL; iter = iter->next) ret = g_list_prepend (ret, g_object_ref (iter->data)); @@ -3102,11 +3477,12 @@ srt_system_info_list_desktop_entries (SrtSystemInfo *self) static void ensure_x86_features_cached (SrtSystemInfo *self) { - if (self->cpu_features.have_x86) + g_return_if_fail (!self->immutable_values); + + if (self->cpu_features.x86_known != SRT_X86_FEATURE_NONE) return; - self->cpu_features.x86_features = _srt_feature_get_x86_flags (); - self->cpu_features.have_x86 = TRUE; + self->cpu_features.x86_features = _srt_feature_get_x86_flags (&self->cpu_features.x86_known); } /** @@ -3123,7 +3499,28 @@ srt_system_info_get_x86_features (SrtSystemInfo *self) { g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_X86_FEATURE_NONE); - ensure_x86_features_cached (self); + if (!self->immutable_values) + ensure_x86_features_cached (self); return self->cpu_features.x86_features; } + +/** + * srt_system_info_get_known_x86_features: + * @self: The #SrtSystemInfo object + * + * Return a list of x86 CPU features that has been checked. + * + * Returns: x86 CPU checked features, or %SRT_X86_FEATURE_NONE + * if no features were checked, e.g. when the CPU is not x86 based. + */ +SrtX86FeatureFlags +srt_system_info_get_known_x86_features (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_X86_FEATURE_NONE); + + if (!self->immutable_values) + ensure_x86_features_cached (self); + + return self->cpu_features.x86_known; +} diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h index cb3717b90f4176d3b20b76feec82cc3152392ae2..cefd6f7dc94ed1460acafe30947b239bb4bdca22 100644 --- a/steam-runtime-tools/system-info.h +++ b/steam-runtime-tools/system-info.h @@ -106,6 +106,9 @@ GType srt_system_info_get_type (void); SrtSystemInfo *srt_system_info_new (const char *expectations); +SrtSystemInfo *srt_system_info_new_from_json (const char *path, + GError **error); + gboolean srt_system_info_can_run (SrtSystemInfo *self, const char *multiarch_tuple); SrtContainerType srt_system_info_get_container_type (SrtSystemInfo *self); @@ -200,6 +203,7 @@ gchar **srt_system_info_list_driver_environment (SrtSystemInfo *self); GList *srt_system_info_list_desktop_entries (SrtSystemInfo *self); SrtX86FeatureFlags srt_system_info_get_x86_features (SrtSystemInfo *self); +SrtX86FeatureFlags srt_system_info_get_known_x86_features (SrtSystemInfo *self); #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtSystemInfo, g_object_unref) diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h index 1088f7c7f9eca499506f290b415f1ca3f6d8ae9f..b56a2d99b175944ab04a05e02fa04ce8a83477d1 100644 --- a/steam-runtime-tools/utils-internal.h +++ b/steam-runtime-tools/utils-internal.h @@ -28,6 +28,8 @@ #include <glib.h> +#include <json-glib/json-glib.h> + typedef enum { SRT_HELPER_FLAGS_SEARCH_PATH = (1 << 0), @@ -52,6 +54,21 @@ G_GNUC_INTERNAL gboolean _srt_process_timeout_wait_status (int wait_status, const char *srt_enum_value_to_nick (GType enum_type, int value); +gboolean srt_enum_from_nick (GType enum_type, + const gchar *nick, + gint *value_out, + GError **error); + +gboolean srt_add_flag_from_nick (GType flags_type, + const gchar *string, + guint *value_out, + GError **error); + +guint srt_get_flags_from_json_array (GType flags_type, + JsonObject *json_obj, + const gchar *array_member, + guint flag_if_unknown); + G_GNUC_INTERNAL void _srt_child_setup_unblock_signals (gpointer ignored); /* not G_GNUC_INTERNAL because s-r-s-i calls it */ @@ -59,3 +76,6 @@ void _srt_unblock_signals (void); G_GNUC_INTERNAL int _srt_indirect_strcmp0 (gconstpointer left, gconstpointer right); + +gchar ** _srt_json_array_to_strv (JsonObject *json_obj, + const gchar *array_member); diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c index 37704ec463bc89bd897dde23f5b7c3ecf9657531..525a9ccf7f851178ae64aefdfa669c6b1f81361b 100644 --- a/steam-runtime-tools/utils.c +++ b/steam-runtime-tools/utils.c @@ -446,6 +446,157 @@ srt_enum_value_to_nick (GType enum_type, return result; } +/** + * srt_enum_from_nick: + * @enum_type: The type of the enumeration + * @nick: The nickname to look up + * @value_out: (not nullable): Used to return the enumeration that has been + * found from the provided @nick + * @error: Used to raise an error on failure + * + * Get the enumeration from a given #GEnumValue.value-nick. + * For example: + * `srt_enum_from_nick (SRT_TYPE_GRAPHICS_LIBRARY_VENDOR, "glvnd", (gint *) &library_vendor, NULL)` + * will set `library_vendor` to SRT_GRAPHICS_LIBRARY_VENDOR_GLVND. + * + * Returns: %TRUE if no errors have been found. + */ +gboolean +srt_enum_from_nick (GType enum_type, + const gchar *nick, + gint *value_out, + GError **error) +{ + GEnumClass *class; + GEnumValue *enum_value; + gboolean result = TRUE; + + g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), FALSE); + g_return_val_if_fail (nick != NULL, FALSE); + g_return_val_if_fail (value_out != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + class = g_type_class_ref (enum_type); + + enum_value = g_enum_get_value_by_nick (class, nick); + if (enum_value) + { + *value_out = enum_value->value; + } + else + { + if (error != NULL) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "\"%s\" is not a known member of %s", + nick, g_type_name (enum_type)); + result = FALSE; + } + + g_type_class_unref (class); + return result; +} + +/** + * srt_add_flag_from_nick: + * @flags_type: The type of the flag + * @nick: The nickname to look up + * @value_out: (not nullable) (inout): The flag, from the provided @nick, + * will be added to @value_out + * @error: Used to raise an error on failure + * + * Get the flag from a given #GEnumValue.value-nick. + * For example: + * `srt_add_flag_from_nick (SRT_TYPE_STEAM_ISSUES, "cannot-find", &issues, error)` + * will add SRT_STEAM_ISSUES_CANNOT_FIND to `issues`. + * + * Returns: %TRUE if no errors have been found. + */ +gboolean +srt_add_flag_from_nick (GType flags_type, + const gchar *nick, + guint *value_out, + GError **error) +{ + GFlagsClass *class; + GFlagsValue *flags_value; + gboolean result = TRUE; + + g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), FALSE); + g_return_val_if_fail (nick != NULL, FALSE); + g_return_val_if_fail (value_out != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + class = g_type_class_ref (flags_type); + + flags_value = g_flags_get_value_by_nick (class, nick); + if (flags_value) + { + *value_out |= flags_value->value; + } + else + { + if (error != NULL) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "\"%s\" is not a known member of %s", + nick, g_type_name (flags_type)); + result = FALSE; + } + + g_type_class_unref (class); + return result; +} + +/** + * srt_get_flags_from_json_array: + * @flags_type: The type of the flag + * @json_obj: (not nullable): A JSON Object used to search for + * @array_member property + * @array_member: (not nullable): The JSON member to look up + * @flag_if_unknown: flag to use in case of parsing error + * + * Get the flags from a given JSON object array member. + * If @json_obj doesn't have the provided @member, or it is malformed, the + * @flag_if_unknown will be returned. + * If the parsed JSON array_member has some elements that we can't parse, + * @flag_if_unknown will be added to the returned flags. + * + * Returns: the found flags from the provided @json_obj + */ +guint +srt_get_flags_from_json_array (GType flags_type, + JsonObject *json_obj, + const gchar *array_member, + guint flag_if_unknown) +{ + JsonArray *array; + guint ret = flag_if_unknown; + + g_return_val_if_fail (G_TYPE_IS_FLAGS (flags_type), 0); + g_return_val_if_fail (json_obj != NULL, 0); + g_return_val_if_fail (array_member != NULL, 0); + + if (json_object_has_member (json_obj, array_member)) + { + array = json_object_get_array_member (json_obj, array_member); + + if (array == NULL) + goto out; + + /* We reset the value out because we found the member we were looking for */ + ret = 0; + + for (guint j = 0; j < json_array_get_length (array); j++) + { + const gchar *issue_string = json_array_get_string_element (array, j); + if (!srt_add_flag_from_nick (flags_type, issue_string, &ret, NULL)) + ret |= flag_if_unknown; + } + } + +out: + return ret; +} + static void _srt_constructor (void) __attribute__((__constructor__)); static void _srt_constructor (void) @@ -605,3 +756,49 @@ _srt_rm_rf (const char *directory) return TRUE; } + +/** + * _srt_json_array_to_strv: + * @json_obj: (not nullable): A JSON Object used to search for + * @array_member property + * @array_member: (not nullable): The JSON member to look up + * + * Every %NULL or non string members of the array are replaced with the value + * "<invalid>". + * + * Returns: (transfer full) (array zero-terminated=1) (element-type utf8) (nullable): + * A string array from the given @json_obj, or %NULL if it doesn't have a + * property @array_member + */ +gchar ** +_srt_json_array_to_strv (JsonObject *json_obj, + const gchar *array_member) +{ + JsonArray *array; + guint length; + const gchar *element; + gchar **ret = NULL; + + g_return_val_if_fail (json_obj != NULL, NULL); + g_return_val_if_fail (array_member != NULL, NULL); + + if (json_object_has_member (json_obj, array_member)) + { + array = json_object_get_array_member (json_obj, array_member); + if (array == NULL) + return ret; + + length = json_array_get_length (array); + ret = g_new0 (gchar *, length + 1); + for (guint i = 0; i < length; i++) + { + element = json_array_get_string_element (array, i); + if (element == NULL) + element = "<invalid>"; + ret[i] = g_strdup (element); + } + ret[length] = NULL; + } + + return ret; +} diff --git a/tests/json-report/empty-report.json b/tests/json-report/empty-report.json new file mode 100644 index 0000000000000000000000000000000000000000..2c63c0851048d8f7bff41ecf0f8cee05f52fd120 --- /dev/null +++ b/tests/json-report/empty-report.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/json-report/full-good-report.json b/tests/json-report/full-good-report.json new file mode 100644 index 0000000000000000000000000000000000000000..20fd76cf2e89b6ac52a0774f1eeeb3fd8c65300a --- /dev/null +++ b/tests/json-report/full-good-report.json @@ -0,0 +1,253 @@ +{ + "can-write-uinput" : true, + "steam-installation" : { + "path" : "/home/me/.local/share/Steam", + "data_path" : "/home/me/.local/share/Steam", + "bin32_path" : "/home/me/.local/share/Steam/ubuntu12_32", + "issues" : [ + "steamscript-not-in-environment" + ] + }, + "runtime" : { + "path" : "/home/me/.steam/root/ubuntu12_32/steam-runtime", + "version" : "0.20200123.4", + "issues" : [ + ], + "pinned_libs_32" : { + "list" : [ + " 13902790 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_32", + " 13902813 4 -rw-r--r-- 1 me me 50 Apr 24 21:32 pinned_libs_32/system_libGLU.so.1", + " 13902814 4 lrwxrwxrwx 1 me me 107 Apr 24 21:32 pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13" + ] + }, + "pinned_libs_64" : { + "list" : [ + " 13902791 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_64", + " 13902805 4 -rw-r--r-- 1 me me 46 Apr 24 21:32 pinned_libs_64/system_libGLU.so.1", + " 13902795 4 lrwxrwxrwx 1 me me 100 Apr 24 21:32 pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0" + ] + } + }, + "os-release" : { + "id" : "arch", + "id_like" : [ + "ubuntu", + "debian" + ], + "name" : "Arch Linux", + "pretty_name" : "Arch Linux", + "build_id" : "rolling" + }, + "container" : { + "type" : "docker", + "host" : { + "path" : "/the/host/path", + "os-release" : { + } + } + }, + "driver_environment" : [ + ], + "architectures" : { + "i386-linux-gnu" : { + "can-run" : false, + "graphics-details" : { + "x11/vulkan" : { + "messages" : "ERROR: [Loader Message] Code 0 : /usr/lib/amdvlk64.so: wrong ELF class: ELFCLASS64\nCannot create Vulkan instance.\n", + "renderer" : null, + "version" : null, + "issues" : [ + "cannot-load", + "cannot-draw" + ], + "exit-status" : 1 + }, + "x11/vdpau" : { + "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n", + "version" : null + }, + "x11/vaapi" : { + "renderer" : "Mesa Gallium driver 20.0.5 for AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)\n", + "version" : null + }, + "glx/gl" : { + "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" + }, + "egl_x11/gl" : { + "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" + }, + "egl_x11/glesv2" : { + "renderer" : "AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)", + "version" : "OpenGL ES 3.2 Mesa 20.0.5", + "library-vendor" : "glvnd" + } + }, + "dri_drivers" : [ + { + "library_path" : "/usr/lib32/dri/radeonsi_dri.so" + }, + { + "library_path" : "/usr/lib32/dri/vmwgfx_dri.so" + } + ], + "va-api_drivers" : [ + { + "library_path" : "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/dri/dummy_drv_video.so" + } + ], + "vdpau_drivers" : [ + { + "library_path" : "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/vdpau/libvdpau_trace.so.1", + "library_link" : "libvdpau_trace.so.1.0.0" + } + ], + "glx_drivers" : [ + { + "library_soname" : "libGLX_indirect.so.0", + "library_path" : "/usr/lib32/libGLX_mesa.so.0.0.0" + }, + { + "library_soname" : "libGLX_mesa.so.0", + "library_path" : "/usr/lib32/libGLX_mesa.so.0.0.0" + } + ] + }, + "x86_64-linux-gnu" : { + "can-run" : true, + "library-issues-summary" : [ + ], + "graphics-details" : { + "x11/vulkan" : { + "renderer" : "AMD Radeon RX 5700 XT", + "version" : "1.2.136 (device 1002:731f) (driver 2.0.140)" + }, + "x11/vdpau" : { + "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n", + "version" : null + }, + "x11/vaapi" : { + "renderer" : "Mesa Gallium driver 20.0.5 for AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)\n", + "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", + "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", + "issues" : [ + "cannot-draw" + ], + "terminating-signal" : 6, + "terminating-signal-name" : "Aborted" + }, + "egl_x11/gl" : { + "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" + }, + "egl_x11/glesv2" : { + "renderer" : "AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)", + "version" : "OpenGL ES 3.2 Mesa 20.0.5", + "library-vendor" : "glvnd" + } + }, + "dri_drivers" : [ + { + "library_path" : "/usr/lib/dri/i915_dri.so" + }, + { + "library_path" : "/usr/lib/dri/radeonsi_dri.so" + } + ], + "va-api_drivers" : [ + { + "library_path" : "/usr/lib/dri/vdpau_drv_video.so" + }, + { + "library_path" : "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/dri/dummy_drv_video.so" + } + ], + "vdpau_drivers" : [ + { + "library_path" : "/usr/lib/vdpau/libvdpau_radeonsi.so", + "library_link" : "libvdpau_radeonsi.so.1.0.0" + }, + { + "library_path" : "/usr/lib/vdpau/libvdpau_radeonsi.so.1", + "library_link" : "libvdpau_radeonsi.so.1.0.0" + } + ], + "glx_drivers" : [ + { + "library_soname" : "libGLX_indirect.so.0", + "library_path" : "/usr/lib/libGLX_mesa.so.0.0.0" + }, + { + "library_soname" : "libGLX_mesa.so.0", + "library_path" : "/usr/lib/libGLX_mesa.so.0.0.0" + } + ] + } + }, + "locale-issues" : [ + "c-utf8-missing", + "i18n-supported-missing" + ], + "locales" : { + "<default>" : { + "resulting-name" : "en_US.UTF-8", + "charset" : "UTF-8", + "is_utf8" : true + }, + "C" : { + "resulting-name" : "C", + "charset" : "ANSI_X3.4-1968", + "is_utf8" : false + }, + "C.UTF-8" : { + "error-domain" : "srt-locale-error-quark", + "error-code" : 0, + "error" : "No such file or directory" + }, + "en_US.UTF-8" : { + "resulting-name" : "en_US.UTF-8", + "charset" : "UTF-8", + "is_utf8" : true + } + }, + "egl" : { + "icds" : [ + { + "json_path" : "/usr/share/glvnd/egl_vendor.d/51_mesa.json", + "library_path" : "libEGL_mesa.so.0" + } + ] + }, + "vulkan" : { + "icds" : [ + { + "json_path" : "/usr/share/vulkan/icd.d/amd_icd64.json", + "library_path" : "/usr/lib/amdvlk64.so", + "api_version" : "1.2.136" + } + ] + }, + "desktop-entries" : [ + { + "id" : "steam.desktop", + "commandline" : "/usr/bin/steam-runtime %U", + "filename" : "/usr/share/applications/steam.desktop", + "default_steam_uri_handler" : true, + "steam_uri_handler" : true + } + ], + "cpu-features" : { + "x86-64" : true, + "sse3" : false, + "cmpxchg16b" : true + } +} diff --git a/tests/json-report/newer-report.json b/tests/json-report/newer-report.json new file mode 100644 index 0000000000000000000000000000000000000000..b0c0b9de57fd38c5c4f047b1e157fa2a466a4bbc --- /dev/null +++ b/tests/json-report/newer-report.json @@ -0,0 +1,35 @@ +{ + "container" : { + "type" : "better_docker" + }, + "architectures" : { + "i386-linux-gnu" : { + "can-run" : false, + "library-issues-summary" : [ + "cannot-load", + "future-issue" + ], + "graphics-details" : { + "x11/vdpau" : { + "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n", + "library-vendor" : "futuristic", + "issues" : [ + "cannot-draw", + "another-future-issue" + ] + } + } + } + }, + "locale-issues" : [ + "c-utf8-missing", + "future-missing" + ], + "cpu-features" : { + "x86-64" : true, + "sse3" : true, + "cmpxchg16b" : false, + "sse9000" : true, + "sse9001" : false + } +} diff --git a/tests/json-report/partial-report-2.json b/tests/json-report/partial-report-2.json new file mode 100644 index 0000000000000000000000000000000000000000..7297874d29a4c2c2ec7e8cedf5032b244cca1a0c --- /dev/null +++ b/tests/json-report/partial-report-2.json @@ -0,0 +1,44 @@ +{ + "steam-installation" : { + "path" : "/home/me/.local/share/Steam" + }, + "runtime" : { + "path" : "/home/me/.steam/root/ubuntu12_32/steam-runtime", + "issues" : [ + ], + "pinned_libs_32" : { + }, + "pinned_libs_64" : { + "list" : [ + ] + } + }, + "os-release" : { + "id" : "arch" + }, + "container" : { + "type" : "docker" + }, + "architectures" : { + "x86_64-linux-gnu" : { + "can-run" : true, + "graphics-details" : { + "x11/vulkan" : { + } + }, + "dri_drivers" : [ + ] + } + }, + "locales" : { + "<default>" : { + "resulting-name" : "en_US.UTF-8" + } + }, + "egl" : { + "icds" : [ + ] + }, + "vulkan" : { + } +} diff --git a/tests/json-report/partial-report.json b/tests/json-report/partial-report.json new file mode 100644 index 0000000000000000000000000000000000000000..a7fec110712b1f7fd6f2dfc9f6329f85b7afa6df --- /dev/null +++ b/tests/json-report/partial-report.json @@ -0,0 +1,45 @@ +{ + "container" : { + "type" : "flatpak", + "host" : { + "path" : null + } + }, + "driver_environment" : [ + "LIBVA_DRIVER_NAME=vava", + 5, + "MESA_LOADER_DRIVER_OVERRIDE=radeonsi" + ], + "architectures" : { + "i386-linux-gnu" : { + "can-run" : false, + "graphics-details" : { + "x11/vdpau" : { + "renderer" : "G3DVL VDPAU Driver Shared Library version 1.0\n", + "library-vendor" : "futuristic" + }, + "x12/missing" : { + "renderer" : "G3DVL VDPAU Driver Shared Library version 5.0\n" + } + } + }, + "x86_64-linux-gnu" : { + "can-run" : true + } + }, + "locales" : { + "<default>" : { + "resulting-name" : "en_US.UTF-8", + "charset" : "UTF-8", + "is_utf8" : true + } + }, + "vulkan" : { + "icds" : [ + { + "json_path" : "/usr/share/vulkan/icd.d/amd_icd64.json", + "error" : "Something went wrong" + } + ] + } +} diff --git a/tests/meson.build b/tests/meson.build index f45f8bee9d2a3a62ad2fe1f84f3a8c5528489395..fd481934e4e4ebf598866fd7960c130f78609a5b 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -62,6 +62,7 @@ install_subdir('expectations_with_missings', install_dir : tests_dir) # Vulkan *.json files. (The order of EGL *.json files is well-defined.) install_subdir('fake-icds', install_dir : tests_dir) install_subdir('fake-steam-runtime', install_dir : tests_dir) +install_subdir('json-report', install_dir : tests_dir) meson.add_postconf_script( python.path(), diff --git a/tests/system-info-cli.c b/tests/system-info-cli.c index 7b03d1c8b5f1531a7cefbd18a4d0be663d84e760..ea6cfea7eee10bcefd20fc1221075868396a351a 100644 --- a/tests/system-info-cli.c +++ b/tests/system-info-cli.c @@ -686,6 +686,76 @@ steam_issues (Fixture *f, g_clear_error (&error); } +typedef struct +{ + const gchar *description; + const gchar *input_name; + const gchar *output_name; +} JsonTest; + +static const JsonTest json_test[] = +{ + { + .description = "full JSON parsing", + .input_name = "full-good-report.json", + .output_name = "full-good-report.json", + }, +}; + +static void +json_parsing (Fixture *f, + gconstpointer context) +{ + for (gsize i = 0; i < G_N_ELEMENTS (json_test); i++) + { + const JsonTest *test = &json_test[i]; + gboolean result; + int exit_status = -1; + gchar *input_json; + gchar *output_json; + gchar *output = NULL; + gchar *expectation = NULL; + gchar **envp; + GError *error = NULL; + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + + g_test_message ("%s: input=%s output=%s", test->description, test->input_name, test->output_name); + + input_json = g_build_filename (f->srcdir, "json-report", test->input_name, NULL); + output_json = g_build_filename (f->srcdir, "json-report", test->output_name, NULL); + + envp = g_get_environ (); + envp = g_environ_setenv (envp, "SRT_TEST_PARSE_JSON", input_json, TRUE); + + result = g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + envp, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error); + g_assert_no_error (error); + g_assert_true (result); + g_assert_cmpint (exit_status, ==, 0); + g_assert_nonnull (output); + + g_assert_true (g_file_get_contents (output_json, &expectation, NULL, &error)); + g_assert_no_error (error); + + g_assert_cmpstr (output, ==, expectation); + + g_free (input_json); + g_free (output_json); + g_free (output); + g_free (expectation); + g_strfreev (envp); + g_clear_error (&error); + } +} + /* * Test `steam-runtime-system-info --help` and `--version`. */ @@ -852,6 +922,8 @@ main (int argc, setup, steam_presence, teardown); g_test_add ("/system-info-cli/steam_issues", Fixture, NULL, setup, steam_issues, teardown); + g_test_add ("/system-info-cli/json_parsing", Fixture, NULL, + setup, json_parsing, teardown); g_test_add ("/system-info-cli/help-and-version", Fixture, NULL, setup, test_help_and_version, teardown); g_test_add ("/system-info-cli/unblocks_sigchld", Fixture, NULL, diff --git a/tests/system-info.c b/tests/system-info.c index d894b517ee7d0fce6fca1fd03c8d4b0828b2d7f9..dd2e4a0847d89e56132b2abc3b024f921a51b7f3 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -2276,6 +2276,911 @@ test_containers (Fixture *f, } } +/* For the purpose of this test an array that is NULL, and one with just one + * NULL element, are considered to be equal */ +static void +assert_equal_strings_arrays (const gchar * const *array1, + const gchar * const *array2) +{ + gsize i = 0; + + if (array1 == NULL) + { + if (array2 != NULL) + g_assert_cmpstr (array2[0], ==, NULL); + return; + } + + if (array2 == NULL) + { + if (array1 != NULL) + g_assert_cmpstr (array1[0], ==, NULL); + return; + } + + for (i = 0; array1[i] != NULL; i++) + g_assert_cmpstr (array1[i], ==, array2[i]); + + g_assert_cmpstr (array2[i], ==, NULL); +} + +static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; + +typedef struct +{ + const gchar *path; + const gchar *data_path; + SrtSteamIssues issues; +} SteamInstallationTest; + +typedef struct +{ + const gchar *path; + const gchar *version; + SrtRuntimeIssues issues; + const gchar *pinned_libs_32[5]; + const gchar *pinned_libs_64[5]; + const gchar *messages_32[5]; + const gchar *messages_64[5]; +} RuntimeTest; + +typedef struct +{ + const gchar *build_id; + const gchar *id; + const gchar *id_like[5]; + const gchar *name; + const gchar *pretty_name; +} OsReleaseTest; + +typedef struct +{ + SrtContainerType type; + const gchar *host_path; +} ContTest; + +typedef struct +{ + const gchar *library_path; + const gchar *library_link; + const gchar *library_soname; + gboolean is_extra; +} DriverTest; + +typedef struct +{ + SrtWindowSystem window_system; + SrtRenderingInterface rendering_interface; + const gchar *renderer; + const gchar *version; + SrtGraphicsLibraryVendor library_vendor; + SrtGraphicsIssues issues; + const gchar *messages; + int exit_status; + int terminating_signal; + gboolean is_available; +} GraphicsTest; + +typedef struct +{ + gboolean can_run; + SrtLibraryIssues issues; + DriverTest dri_drivers[5]; + DriverTest va_api_drivers[5]; + DriverTest vdpau_drivers[5]; + DriverTest glx_drivers[5]; + GraphicsTest graphics[10]; +} ArchitectureTest; + +typedef struct +{ + const gchar *name; + const gchar *resulting_name; + const gchar *charset; + gboolean is_utf8; + const gchar *error_domain; + const gchar *error_message; + int error_code; +} LocaleTest; + +typedef struct +{ + const gchar *json_path; + const gchar *library_path; + const gchar *api_version; + const gchar *error_domain; + const gchar *error_message; + int error_code; +} IcdTest; + +typedef struct +{ + const gchar *id; + const gchar *commandline; + const gchar *filename; + gboolean default_handler; + gboolean steam_handler; +} DesktopEntryTest; + +typedef struct +{ + const gchar *description; + const gchar *input_name; + gboolean can_write_uinput; + SteamInstallationTest steam_installation; + RuntimeTest runtime; + OsReleaseTest os_release; + ContTest container; + const gchar *driver_environment[5]; + ArchitectureTest architecture[G_N_ELEMENTS (multiarch_tuples)]; + SrtLocaleIssues locale_issues; + LocaleTest locale[5]; + IcdTest egl_icd[3]; + IcdTest vulkan_icd[3]; + DesktopEntryTest desktop_entry[3]; + SrtX86FeatureFlags x86_features; + SrtX86FeatureFlags x86_known; +} JsonTest; + +static const JsonTest json_test[] = +{ + { /* Begin Full JSON report */ + .description = "full JSON parsing", + .input_name = "full-good-report.json", + .can_write_uinput = TRUE, + .steam_installation = + { + .path = "/home/me/.local/share/Steam", + .data_path = "/home/me/.local/share/Steam", + .issues = SRT_STEAM_ISSUES_STEAMSCRIPT_NOT_IN_ENVIRONMENT, + }, + + .runtime = + { + .path = "/home/me/.steam/root/ubuntu12_32/steam-runtime", + .version = "0.20200123.4", + .issues = SRT_RUNTIME_ISSUES_NONE, + .pinned_libs_32 = + { + " 13902790 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_32", + " 13902813 4 -rw-r--r-- 1 me me 50 Apr 24 21:32 pinned_libs_32/system_libGLU.so.1", + " 13902814 4 lrwxrwxrwx 1 me me 107 Apr 24 21:32 pinned_libs_32/libdbusmenu-gtk.so.4 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/libdbusmenu-gtk.so.4.0.13", + NULL, + }, + .pinned_libs_64 = + { + " 13902791 4 drwxr-xr-x 2 me me 4096 Apr 24 21:32 pinned_libs_64", + " 13902805 4 -rw-r--r-- 1 me me 46 Apr 24 21:32 pinned_libs_64/system_libGLU.so.1", + " 13902795 4 lrwxrwxrwx 1 me me 100 Apr 24 21:32 pinned_libs_64/libjack.so.0 -> /home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/libjack.so.0.1.0", + NULL, + }, + }, + + .os_release = + { + .id = "arch", + .id_like = {"ubuntu", "debian", NULL}, + .name = "Arch Linux", + .pretty_name = "Arch Linux", + .build_id = "rolling", + }, + + .container = + { + .type = SRT_CONTAINER_TYPE_DOCKER, + .host_path = "/the/host/path", + }, + + .architecture = + { + { + .can_run = FALSE, + .issues = SRT_LIBRARY_ISSUES_UNKNOWN, + .graphics = + { + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN, + .messages = "ERROR: [Loader Message] Code 0 : /usr/lib/amdvlk64.so: wrong ELF class: ELFCLASS64\nCannot create Vulkan instance.\n", + .issues = SRT_GRAPHICS_ISSUES_CANNOT_LOAD | SRT_GRAPHICS_ISSUES_CANNOT_DRAW, + .exit_status = 1, + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VDPAU, + .renderer = "G3DVL VDPAU Driver Shared Library version 1.0\n", + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VAAPI, + .renderer = "Mesa Gallium driver 20.0.5 for AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)\n", + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_GLX, + .rendering_interface = SRT_RENDERING_INTERFACE_GL, + .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 = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_EGL_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_GL, + .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 = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_EGL_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_GLESV2, + .renderer = "AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)", + .version = "OpenGL ES 3.2 Mesa 20.0.5", + .library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .is_available = TRUE, + }, + }, + .dri_drivers = + { + { + .library_path = "/usr/lib32/dri/radeonsi_dri.so", + }, + { + .library_path = "/usr/lib32/dri/vmwgfx_dri.so", + }, + }, + .va_api_drivers = + { + { + .library_path = "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/dri/dummy_drv_video.so", + }, + }, + .vdpau_drivers = + { + { + .library_path = "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/i386-linux-gnu/vdpau/libvdpau_trace.so.1", + .library_link = "libvdpau_trace.so.1.0.0", + }, + }, + .glx_drivers = + { + { + .library_soname = "libGLX_indirect.so.0", + .library_path = "/usr/lib32/libGLX_mesa.so.0.0.0", + }, + { + .library_soname = "libGLX_mesa.so.0", + .library_path = "/usr/lib32/libGLX_mesa.so.0.0.0", + }, + }, + }, + + { + .can_run = TRUE, + .graphics = + { + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VULKAN, + .renderer = "AMD Radeon RX 5700 XT", + .version = "1.2.136 (device 1002:731f) (driver 2.0.140)", + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VDPAU, + .renderer = "G3DVL VDPAU Driver Shared Library version 1.0\n", + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VAAPI, + .renderer = "Mesa Gallium driver 20.0.5 for AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)\n", + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_GLX, + .rendering_interface = SRT_RENDERING_INTERFACE_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", + .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 = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW, + .terminating_signal = 6, + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_EGL_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_GL, + .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 = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .is_available = TRUE, + }, + { + .window_system = SRT_WINDOW_SYSTEM_EGL_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_GLESV2, + .renderer = "AMD Radeon RX 5700 XT (NAVI10, DRM 3.36.0, 5.6.7-arch1-1, LLVM 10.0.0)", + .version = "OpenGL ES 3.2 Mesa 20.0.5", + .library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_GLVND, + .is_available = TRUE, + }, + }, + .dri_drivers = + { + { + .library_path = "/usr/lib/dri/i915_dri.so", + }, + { + .library_path = "/usr/lib/dri/radeonsi_dri.so", + }, + }, + .va_api_drivers = + { + { + .library_path = "/usr/lib/dri/vdpau_drv_video.so", + }, + { + .library_path = "/home/me/.local/share/Steam/ubuntu12_32/steam-runtime/usr/lib/x86_64-linux-gnu/dri/dummy_drv_video.so", + }, + }, + .vdpau_drivers = + { + { + .library_path = "/usr/lib/vdpau/libvdpau_radeonsi.so", + .library_link = "libvdpau_radeonsi.so.1.0.0", + }, + { + .library_path = "/usr/lib/vdpau/libvdpau_radeonsi.so.1", + .library_link = "libvdpau_radeonsi.so.1.0.0", + }, + }, + .glx_drivers = + { + { + .library_soname = "libGLX_indirect.so.0", + .library_path = "/usr/lib/libGLX_mesa.so.0.0.0", + }, + { + .library_soname = "libGLX_mesa.so.0", + .library_path = "/usr/lib/libGLX_mesa.so.0.0.0", + }, + }, + }, + }, + + .locale_issues = SRT_LOCALE_ISSUES_C_UTF8_MISSING | SRT_LOCALE_ISSUES_I18N_SUPPORTED_MISSING, + .locale = + { + { + .name = "", // <default> + .resulting_name = "en_US.UTF-8", + .charset = "UTF-8", + .is_utf8 = TRUE, + }, + { + .name = "C", + .resulting_name = "C", + .charset = "ANSI_X3.4-1968", + .is_utf8 = FALSE, + }, + { + .name = "C.UTF-8", + .error_domain = "srt-locale-error-quark", + .error_code = 0, + .error_message = "No such file or directory", + }, + { + .name = "en_US.UTF-8", + .resulting_name = "en_US.UTF-8", + .charset = "UTF-8", + .is_utf8 = TRUE, + }, + }, + + .egl_icd = + { + { + .json_path = "/usr/share/glvnd/egl_vendor.d/51_mesa.json", + .library_path = "libEGL_mesa.so.0", + }, + }, + + .vulkan_icd = + { + { + .json_path = "/usr/share/vulkan/icd.d/amd_icd64.json", + .library_path = "/usr/lib/amdvlk64.so", + .api_version = "1.2.136", + }, + }, + + .desktop_entry = + { + { + .id = "steam.desktop", + .commandline = "/usr/bin/steam-runtime %U", + .filename = "/usr/share/applications/steam.desktop", + .default_handler = TRUE, + .steam_handler = TRUE, + }, + }, + + .x86_features = SRT_X86_FEATURE_X86_64 | SRT_X86_FEATURE_CMPXCHG16B, + .x86_known = SRT_X86_FEATURE_X86_64 | SRT_X86_FEATURE_SSE3 | SRT_X86_FEATURE_CMPXCHG16B, + + }, /* End Full JSON report */ + + { /* Begin Partial JSON report */ + .description = "partial JSON parsing", + .input_name = "partial-report.json", + .steam_installation = + { + .issues = SRT_STEAM_ISSUES_UNKNOWN, + }, + .runtime = + { + .issues = SRT_RUNTIME_ISSUES_UNKNOWN, + }, + .container = + { + .type = SRT_CONTAINER_TYPE_FLATPAK, + }, + .driver_environment = + { + "LIBVA_DRIVER_NAME=vava", + "<invalid>", + "MESA_LOADER_DRIVER_OVERRIDE=radeonsi", + }, + .architecture = + { + { + .can_run = FALSE, + .issues = SRT_LIBRARY_ISSUES_UNKNOWN, + .graphics = + { + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VDPAU, + .renderer = "G3DVL VDPAU Driver Shared Library version 1.0\n", + .is_available = TRUE, + }, + }, + }, + { + .can_run = TRUE, + .issues = SRT_LIBRARY_ISSUES_UNKNOWN, + }, + }, + .locale_issues = SRT_LOCALE_ISSUES_UNKNOWN, + .locale = + { + { + .name = "", // <default> + .resulting_name = "en_US.UTF-8", + .charset = "UTF-8", + .is_utf8 = TRUE, + }, + { + .name = "C", + .error_domain = "srt-locale-error-quark", + .error_code = 1, + .error_message = "Information about the requested locale is missing", + }, + }, + .vulkan_icd = + { + { + .json_path = "/usr/share/vulkan/icd.d/amd_icd64.json", + .error_domain = "g-io-error-quark", /* Default domain */ + .error_code = G_IO_ERROR_FAILED, /* Default error code */ + .error_message = "Something went wrong", + }, + }, + .x86_features = SRT_X86_FEATURE_NONE, + .x86_known = SRT_X86_FEATURE_NONE, + }, /* End Partial JSON report */ + + { /* Begin Partial-2 JSON report */ + .description = "partial-2 JSON parsing", + .input_name = "partial-report-2.json", + .steam_installation = + { + .path = "/home/me/.local/share/Steam", + .issues = SRT_STEAM_ISSUES_UNKNOWN, + }, + .runtime = + { + .path = "/home/me/.steam/root/ubuntu12_32/steam-runtime", + }, + .os_release = + { + .id = "arch", + }, + .container = + { + .type = SRT_CONTAINER_TYPE_DOCKER, + }, + .architecture = + { + { + /* i386 */ + .issues = SRT_LIBRARY_ISSUES_CANNOT_LOAD, + }, + { + .can_run = TRUE, + .issues = SRT_LIBRARY_ISSUES_UNKNOWN, + }, + }, + .locale_issues = SRT_LOCALE_ISSUES_UNKNOWN, + .locale = + { + { + .name = "", // <default> + /* Missing the required "charset" */ + .error_domain = "g-io-error-quark", /* Default domain */ + .error_code = G_IO_ERROR_FAILED, /* Default error code */ + .error_message = "(missing error message)", + }, + }, + .x86_features = SRT_X86_FEATURE_NONE, + .x86_known = SRT_X86_FEATURE_NONE, + }, /* End Partial-2 JSON report */ + + { /* Begin Empty JSON report */ + .description = "empty JSON parsing", + .input_name = "empty-report.json", + .steam_installation = + { + .issues = SRT_STEAM_ISSUES_UNKNOWN, + }, + .runtime = + { + .issues = SRT_RUNTIME_ISSUES_UNKNOWN, + }, + .container = + { + .type = SRT_CONTAINER_TYPE_UNKNOWN, + }, + .architecture = + { + { + .issues = SRT_LIBRARY_ISSUES_CANNOT_LOAD, + }, + { + .issues = SRT_LIBRARY_ISSUES_CANNOT_LOAD, + }, + }, + .locale_issues = SRT_LOCALE_ISSUES_UNKNOWN, + .x86_features = SRT_X86_FEATURE_NONE, + .x86_known = SRT_X86_FEATURE_NONE, + }, /* End Empty JSON report */ + + { /* Begin Newer JSON report */ + .description = "newer JSON parsing", + .input_name = "newer-report.json", + .steam_installation = + { + .issues = SRT_STEAM_ISSUES_UNKNOWN, + }, + .runtime = + { + .issues = SRT_RUNTIME_ISSUES_UNKNOWN, + }, + .container = + { + .type = SRT_CONTAINER_TYPE_UNKNOWN, + }, + .architecture = + { + { + .can_run = FALSE, + .issues = SRT_LIBRARY_ISSUES_CANNOT_LOAD | SRT_LIBRARY_ISSUES_UNKNOWN, + .graphics = + { + { + .window_system = SRT_WINDOW_SYSTEM_X11, + .rendering_interface = SRT_RENDERING_INTERFACE_VDPAU, + .renderer = "G3DVL VDPAU Driver Shared Library version 1.0\n", + .library_vendor = SRT_GRAPHICS_LIBRARY_VENDOR_UNKNOWN, + .issues = SRT_GRAPHICS_ISSUES_CANNOT_DRAW | SRT_GRAPHICS_ISSUES_UNKNOWN, + .is_available = TRUE, + }, + }, + }, + { + /* x86_64 */ + .issues = SRT_LIBRARY_ISSUES_CANNOT_LOAD, + } + }, + .locale_issues = SRT_LOCALE_ISSUES_C_UTF8_MISSING | SRT_LOCALE_ISSUES_UNKNOWN, + .x86_features = SRT_X86_FEATURE_X86_64 | SRT_X86_FEATURE_SSE3 | SRT_X86_FEATURE_UNKNOWN, + .x86_known = SRT_X86_FEATURE_X86_64 | SRT_X86_FEATURE_SSE3 | SRT_X86_FEATURE_CMPXCHG16B | SRT_X86_FEATURE_UNKNOWN, + }, /* End Newer JSON report */ +}; + +static void +json_parsing (Fixture *f, + gconstpointer context) +{ + for (gsize i = 0; i < G_N_ELEMENTS (json_test); i++) + { + const JsonTest *t = &json_test[i]; + SrtSystemInfo *info; + gchar *input_json; + gchar **driver_environment_list; + gsize j; + gsize jj; + GList *icds; + GList *iter; + GList *desktop_entries; + gchar *steam_path = NULL; + gchar *steam_data_path = NULL; + gchar *runtime_path = NULL; + gchar *runtime_v = NULL; + gchar *build_id; + gchar *id; + gchar *name; + gchar *pretty_name; + gchar *host_directory; + gchar **pinned_32 = NULL; + gchar **messages_32 = NULL; + gchar **pinned_64 = NULL; + gchar **messages_64 = NULL; + gchar **id_like = NULL; + GError *error = NULL; + + g_test_message ("%s: %s", t->input_name, t->description); + + input_json = g_build_filename (f->srcdir, "json-report", t->input_name, NULL); + + info = srt_system_info_new_from_json (input_json, &error); + + g_assert_no_error (error); + + g_assert_cmpint (t->can_write_uinput, ==, srt_system_info_can_write_to_uinput (info)); + + steam_path = srt_system_info_dup_steam_installation_path (info); + steam_data_path = srt_system_info_dup_steam_data_path (info); + g_assert_cmpstr (t->steam_installation.path, ==, steam_path); + g_assert_cmpstr (t->steam_installation.data_path, ==, steam_data_path); + g_assert_cmpint (t->steam_installation.issues, ==, srt_system_info_get_steam_issues (info)); + + runtime_path = srt_system_info_dup_runtime_path (info); + runtime_v = srt_system_info_dup_runtime_version (info); + pinned_32 = srt_system_info_list_pinned_libs_32 (info, &messages_32); + pinned_64 = srt_system_info_list_pinned_libs_64 (info, &messages_64); + g_assert_cmpstr (t->runtime.path, ==, runtime_path); + g_assert_cmpstr (t->runtime.version, ==, runtime_v); + g_assert_cmpint (t->runtime.issues, ==, srt_system_info_get_runtime_issues (info)); + assert_equal_strings_arrays (t->runtime.pinned_libs_32, (const gchar * const *)pinned_32); + assert_equal_strings_arrays (t->runtime.pinned_libs_64, (const gchar * const *)pinned_64); + assert_equal_strings_arrays (t->runtime.messages_32, (const gchar * const *)messages_32); + assert_equal_strings_arrays (t->runtime.messages_64, (const gchar * const *)messages_64); + + build_id = srt_system_info_dup_os_build_id (info); + id = srt_system_info_dup_os_id (info); + id_like = srt_system_info_dup_os_id_like (info, FALSE); + name = srt_system_info_dup_os_name (info); + pretty_name = srt_system_info_dup_os_pretty_name (info); + g_assert_cmpstr (t->os_release.build_id, ==, build_id); + g_assert_cmpstr (t->os_release.id, ==, id); + assert_equal_strings_arrays (t->os_release.id_like, (const gchar * const *)id_like); + g_assert_cmpstr (t->os_release.name, ==, name); + g_assert_cmpstr (t->os_release.pretty_name, ==, pretty_name); + + host_directory = srt_system_info_dup_container_host_directory (info); + g_assert_cmpint (t->container.type, ==, srt_system_info_get_container_type (info)); + g_assert_cmpstr (t->container.host_path, ==, host_directory); + + driver_environment_list = srt_system_info_list_driver_environment (info); + assert_equal_strings_arrays (t->driver_environment, + (const gchar * const *)driver_environment_list); + + for (j = 0; j < G_N_ELEMENTS (multiarch_tuples); j++) + { + ArchitectureTest this_arch = t->architecture[j]; + GList *dri_list; + GList *va_api_list; + GList *vdpau_list; + GList *glx_list; + + g_assert_cmpint (this_arch.can_run, ==, srt_system_info_can_run (info, multiarch_tuples[j])); + if (this_arch.can_run) + { + g_assert_cmpint (this_arch.issues, ==, + srt_system_info_check_libraries (info, multiarch_tuples[j], NULL)); + } + + g_assert_cmpint (this_arch.issues, ==, + srt_system_info_check_libraries (info, multiarch_tuples[j], NULL)); + + for (jj = 0; this_arch.graphics[jj].is_available; jj++) + { + SrtGraphics *graphics = NULL; + SrtGraphicsLibraryVendor library_vendor; + srt_system_info_check_graphics (info, + multiarch_tuples[j], + this_arch.graphics[jj].window_system, + this_arch.graphics[jj].rendering_interface, + &graphics); + g_assert_cmpstr (this_arch.graphics[jj].messages, ==, + srt_graphics_get_messages (graphics)); + g_assert_cmpstr (this_arch.graphics[jj].renderer, ==, + srt_graphics_get_renderer_string (graphics)); + g_assert_cmpstr (this_arch.graphics[jj].version, ==, + srt_graphics_get_version_string (graphics)); + g_assert_cmpint (this_arch.graphics[jj].issues, ==, + srt_graphics_get_issues (graphics)); + srt_graphics_library_is_vendor_neutral (graphics, &library_vendor); + g_assert_cmpint (this_arch.graphics[jj].library_vendor, ==, library_vendor); + g_assert_cmpint (this_arch.graphics[jj].exit_status, ==, + srt_graphics_get_exit_status (graphics)); + g_assert_cmpint (this_arch.graphics[jj].terminating_signal, ==, + srt_graphics_get_terminating_signal (graphics)); + g_object_unref (graphics); + } + + dri_list = srt_system_info_list_dri_drivers (info, multiarch_tuples[j], + SRT_DRIVER_FLAGS_INCLUDE_ALL); + for (jj = 0, iter = dri_list; iter != NULL; iter = iter->next, jj++) + { + g_assert_cmpstr (this_arch.dri_drivers[jj].library_path, ==, + srt_dri_driver_get_library_path (iter->data)); + g_assert_cmpint (this_arch.dri_drivers[jj].is_extra, ==, + srt_dri_driver_is_extra (iter->data)); + } + g_assert_cmpstr (this_arch.dri_drivers[jj].library_path, ==, NULL); + + va_api_list = srt_system_info_list_va_api_drivers (info, multiarch_tuples[j], + SRT_DRIVER_FLAGS_INCLUDE_ALL); + for (jj = 0, iter = va_api_list; iter != NULL; iter = iter->next, jj++) + { + g_assert_cmpstr (this_arch.va_api_drivers[jj].library_path, ==, + srt_va_api_driver_get_library_path (iter->data)); + g_assert_cmpint (this_arch.va_api_drivers[jj].is_extra, ==, + srt_va_api_driver_is_extra (iter->data)); + } + g_assert_cmpstr (this_arch.va_api_drivers[jj].library_path, ==, NULL); + + vdpau_list = srt_system_info_list_vdpau_drivers (info, multiarch_tuples[j], + SRT_DRIVER_FLAGS_INCLUDE_ALL); + for (jj = 0, iter = vdpau_list; iter != NULL; iter = iter->next, jj++) + { + g_assert_cmpstr (this_arch.vdpau_drivers[jj].library_path, ==, + srt_vdpau_driver_get_library_path (iter->data)); + g_assert_cmpstr (this_arch.vdpau_drivers[jj].library_link, ==, + srt_vdpau_driver_get_library_link (iter->data)); + g_assert_cmpint (this_arch.va_api_drivers[jj].is_extra, ==, + srt_vdpau_driver_is_extra (iter->data)); + } + g_assert_cmpstr (this_arch.vdpau_drivers[jj].library_path, ==, NULL); + g_assert_cmpstr (this_arch.vdpau_drivers[jj].library_link, ==, NULL); + + glx_list = srt_system_info_list_glx_icds (info, multiarch_tuples[j], SRT_DRIVER_FLAGS_INCLUDE_ALL); + for (jj = 0, iter = glx_list; iter != NULL; iter = iter->next, jj++) + { + g_assert_cmpstr (this_arch.glx_drivers[jj].library_path, ==, + srt_glx_icd_get_library_path (iter->data)); + g_assert_cmpstr (this_arch.glx_drivers[jj].library_soname, ==, + srt_glx_icd_get_library_soname (iter->data)); + } + g_assert_cmpstr (this_arch.glx_drivers[jj].library_path, ==, NULL); + g_assert_cmpstr (this_arch.glx_drivers[jj].library_soname, ==, NULL); + + g_list_free_full (dri_list, g_object_unref); + g_list_free_full (va_api_list, g_object_unref); + g_list_free_full (vdpau_list, g_object_unref); + g_list_free_full (glx_list, g_object_unref); + } + + g_assert_cmpint (t->locale_issues, ==, srt_system_info_get_locale_issues (info)); + for (j = 0; t->locale[j].name != NULL; j++) + { + error = NULL; + SrtLocale *this_locale = srt_system_info_check_locale (info, t->locale[j].name, &error); + if (this_locale != NULL) + { + g_assert_cmpstr (t->locale[j].resulting_name, ==, + srt_locale_get_resulting_name (this_locale)); + g_assert_cmpstr (t->locale[j].charset, ==, + srt_locale_get_charset (this_locale)); + g_assert_cmpint (t->locale[j].is_utf8, ==, + srt_locale_is_utf8 (this_locale)); + } + else + { + g_assert_cmpstr (t->locale[j].error_domain, ==, + g_quark_to_string (error->domain)); + g_assert_cmpint (t->locale[j].error_code, ==, error->code); + g_assert_cmpstr (t->locale[j].error_message, ==, error->message); + } + g_clear_object (&this_locale); + g_clear_error (&error); + } + + icds = srt_system_info_list_egl_icds (info, multiarch_tuples); + for (j = 0, iter = icds; iter != NULL; iter = iter->next, j++) + { + error = NULL; + g_assert_cmpstr (t->egl_icd[j].json_path, ==, srt_egl_icd_get_json_path (iter->data)); + if (srt_egl_icd_check_error (iter->data, &error)) + { + g_assert_cmpstr (t->egl_icd[j].library_path, ==, srt_egl_icd_get_library_path (iter->data)); + } + else + { + g_assert_cmpstr (t->egl_icd[j].error_domain, ==, + g_quark_to_string (error->domain)); + g_assert_cmpint (t->egl_icd[j].error_code, ==, error->code); + g_assert_cmpstr (t->egl_icd[j].error_message, ==, error->message); + } + g_clear_error (&error); + } + g_list_free_full (icds, g_object_unref); + + icds = srt_system_info_list_vulkan_icds (info, multiarch_tuples); + for (j = 0, iter = icds; iter != NULL; iter = iter->next, j++) + { + error = NULL; + g_assert_cmpstr (t->vulkan_icd[j].json_path, ==, srt_vulkan_icd_get_json_path (iter->data)); + if (srt_vulkan_icd_check_error (iter->data, &error)) + { + g_assert_cmpstr (t->vulkan_icd[j].library_path, ==, + srt_vulkan_icd_get_library_path (iter->data)); + g_assert_cmpstr (t->vulkan_icd[j].api_version, ==, + srt_vulkan_icd_get_api_version (iter->data)); + } + else + { + g_assert_cmpstr (t->vulkan_icd[j].error_domain, ==, + g_quark_to_string (error->domain)); + g_assert_cmpint (t->vulkan_icd[j].error_code, ==, error->code); + g_assert_cmpstr (t->vulkan_icd[j].error_message, ==, error->message); + } + } + + desktop_entries = srt_system_info_list_desktop_entries (info); + for (j = 0, iter = desktop_entries; iter != NULL; iter = iter->next, j++) + { + g_assert_cmpstr (t->desktop_entry[j].id, ==, srt_desktop_entry_get_id (iter->data)); + g_assert_cmpstr (t->desktop_entry[j].commandline, ==, + srt_desktop_entry_get_commandline (iter->data)); + g_assert_cmpstr (t->desktop_entry[j].filename, ==, srt_desktop_entry_get_filename (iter->data)); + g_assert_cmpint (t->desktop_entry[j].default_handler, ==, + srt_desktop_entry_is_default_handler (iter->data)); + g_assert_cmpint (t->desktop_entry[j].steam_handler, ==, + srt_desktop_entry_is_steam_handler (iter->data)); + } + + g_assert_cmpint (t->x86_features, ==, srt_system_info_get_x86_features (info)); + g_assert_cmpint (t->x86_known, ==, srt_system_info_get_known_x86_features (info)); + + g_list_free_full (icds, g_object_unref); + g_free (steam_path); + g_free (steam_data_path); + g_free (runtime_path); + g_free (runtime_v); + g_free (build_id); + g_free (id); + g_free (name); + g_free (pretty_name); + g_free (host_directory); + g_strfreev (pinned_32); + g_strfreev (pinned_64); + g_strfreev (messages_32); + g_strfreev (messages_64); + g_strfreev (id_like); + g_strfreev (driver_environment_list); + g_free (input_json); + g_clear_error (&error); + g_object_unref (info); + } +} + int main (int argc, char **argv) @@ -2356,6 +3261,9 @@ main (int argc, g_test_add ("/system-info/containers", Fixture, NULL, setup, test_containers, teardown); + g_test_add ("/system-info/json_parsing", Fixture, NULL, + setup, json_parsing, teardown); + status = g_test_run (); if (!_srt_global_teardown_private_xdg_dirs ())