diff --git a/bin/system-info.c b/bin/system-info.c index 264599791a0590b857135fe5bfde8b823ef4e602..dfcd43cff6e1c2149dc1f1ed9abf8d0fd0abf606 100644 --- a/bin/system-info.c +++ b/bin/system-info.c @@ -463,6 +463,9 @@ main (int argc, gchar *inst_path = NULL; gchar *data_path = NULL; gchar *rt_path = NULL; + gchar **overrides = NULL; + gchar **messages = NULL; + gchar **values = NULL; int opt; static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64, NULL }; GList *icds; @@ -530,17 +533,99 @@ main (int argc, json_builder_set_member_name (builder, "runtime"); json_builder_begin_object (builder); - json_builder_set_member_name (builder, "path"); - rt_path = srt_system_info_dup_runtime_path (info); - json_builder_add_string_value (builder, rt_path); - json_builder_set_member_name (builder, "version"); - version = srt_system_info_dup_runtime_version (info); - json_builder_add_string_value (builder, version); - json_builder_set_member_name (builder, "issues"); - json_builder_begin_array (builder); - runtime_issues = srt_system_info_get_runtime_issues (info); - jsonify_runtime_issues (builder, runtime_issues); - json_builder_end_array (builder); + { + json_builder_set_member_name (builder, "path"); + rt_path = srt_system_info_dup_runtime_path (info); + json_builder_add_string_value (builder, rt_path); + json_builder_set_member_name (builder, "version"); + version = srt_system_info_dup_runtime_version (info); + json_builder_add_string_value (builder, version); + json_builder_set_member_name (builder, "issues"); + json_builder_begin_array (builder); + runtime_issues = srt_system_info_get_runtime_issues (info); + jsonify_runtime_issues (builder, runtime_issues); + json_builder_end_array (builder); + + if (g_strcmp0 (rt_path, "/") == 0) + { + overrides = srt_system_info_list_pressure_vessel_overrides (info, &messages); + + json_builder_set_member_name (builder, "overrides"); + json_builder_begin_object (builder); + if (overrides != NULL && overrides[0] != NULL) + { + json_builder_set_member_name (builder, "list"); + json_builder_begin_array (builder); + for (gsize i = 0; overrides[i] != NULL; i++) + json_builder_add_string_value (builder, overrides[i]); + json_builder_end_array (builder); + } + if (messages != NULL && messages[0] != NULL) + { + json_builder_set_member_name (builder, "messages"); + json_builder_begin_array (builder); + for (gsize i = 0; messages[i] != NULL; i++) + json_builder_add_string_value (builder, messages[i]); + json_builder_end_array (builder); + } + json_builder_end_object (builder); + + g_strfreev (overrides); + g_strfreev (messages); + } + + if (rt_path != NULL && g_strcmp0 (rt_path, "/") != 0) + { + values = srt_system_info_list_pinned_libs_32 (info, &messages); + + json_builder_set_member_name (builder, "pinned_libs_32"); + json_builder_begin_object (builder); + if (values != NULL && values[0] != NULL) + { + json_builder_set_member_name (builder, "list"); + json_builder_begin_array (builder); + for (gsize i = 0; values[i] != NULL; i++) + json_builder_add_string_value (builder, values[i]); + json_builder_end_array (builder); + } + if (messages != NULL && messages[0] != NULL) + { + json_builder_set_member_name (builder, "messages"); + json_builder_begin_array (builder); + for (gsize i = 0; messages[i] != NULL; i++) + json_builder_add_string_value (builder, messages[i]); + json_builder_end_array (builder); + } + json_builder_end_object (builder); + + g_strfreev (values); + g_strfreev (messages); + values = srt_system_info_list_pinned_libs_64 (info, &messages); + + json_builder_set_member_name (builder, "pinned_libs_64"); + json_builder_begin_object (builder); + if (values != NULL && values[0] != NULL) + { + json_builder_set_member_name (builder, "list"); + json_builder_begin_array (builder); + for (gsize i = 0; values[i] != NULL; i++) + json_builder_add_string_value (builder, values[i]); + json_builder_end_array (builder); + } + if (messages != NULL && messages[0] != NULL) + { + json_builder_set_member_name (builder, "messages"); + json_builder_begin_array (builder); + for (gsize i = 0; messages[i] != NULL; i++) + json_builder_add_string_value (builder, messages[i]); + json_builder_end_array (builder); + } + json_builder_end_object (builder); + + g_strfreev (values); + g_strfreev (messages); + } + } json_builder_end_object (builder); json_builder_set_member_name (builder, "os-release"); diff --git a/bin/system-info.md b/bin/system-info.md index 114929ff7ae5ded8bb0b93d64ad9c73146b2cf49..80bbd3043b54997ba4efa2f141a0179487d3d056 100644 --- a/bin/system-info.md +++ b/bin/system-info.md @@ -138,6 +138,54 @@ keys: likely to break the use of libraries from the host system, in particular the Mesa graphics drivers. + **overrides** + : An object representing the libraries that have been + overridden when the Steam Runtime is running in a + `pressure-vessel` container. + These libraries are taken from the host system, instead of being + taken from the container like all other libraries. + + **list** + : A human-readable array of overridden libraries. + It is currently in `find -ls` format. + + **messages** + : A human-readable array of errors and warning messages about + the overridden libraries. + It is currently the output on standard error of `find -ls`. + + **pinned_libs_32** + : An object representing the %SRT_ABI_I386 libraries that have been + "pinned" when the Steam Runtime is running in an + `LD_LIBRARY_PATH`-based environment. + These libraries are taken from the Steam Runtime, even if a library + of the same SONAME exists on the host system. + + **list** + : A human-readable array of %SRT_ABI_I386 "pinned" libraries. + It is currently in `find -ls` format. + + **messages** + : A human-readable array of errors and warning messages about the + %SRT_ABI_I386 "pinned" libraries. + It is currently the output on standard error of `find -ls`. + + **pinned_libs_64** + : An object representing the %SRT_ABI_X86_64 libraries that have been + "pinned" when the Steam Runtime is running in an + `LD_LIBRARY_PATH`-based environment. + These libraries are taken from the Steam Runtime, even if a library + of the same SONAME exists on the host system. + + **list** + : A human-readable array of "pinned" %SRT_ABI_X86_64 libraries. + It is currently in `find -ls` format. + + **messages** + : A human-readable array of errors and warning messages about the + %SRT_ABI_X86_64 "pinned" libraries. + It is currently the output on standard error of `find -ls`. + **os-release** : An object describing the operating system or container in which **steam-runtime-system-info** was run. The keys and values are diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c index 7aef99565fccd8647e4ecbe0c332e24abf00a08f..14919653dc4566eaec776a63b5bd99a09b155220 100644 --- a/steam-runtime-tools/system-info.c +++ b/steam-runtime-tools/system-info.c @@ -124,6 +124,20 @@ struct _SrtSystemInfo gboolean have_egl; gboolean have_vulkan; } icds; + struct + { + gchar **values; + gchar **messages; + gboolean have_data; + } overrides; + struct + { + gchar **values_32; + gchar **messages_32; + gchar **values_64; + gchar **messages_64; + gboolean have_data; + } pinned_libs; SrtOsRelease os_release; SrtTestFlags test_flags; Tristate can_write_uinput; @@ -354,6 +368,30 @@ forget_icds (SrtSystemInfo *self) self->icds.vulkan = NULL; } +/* + * Forget any cached information about overrides. + */ +static void +forget_overrides (SrtSystemInfo *self) +{ + g_clear_pointer (&self->overrides.values, g_strfreev); + g_clear_pointer (&self->overrides.messages, g_strfreev); + self->overrides.have_data = FALSE; +} + +/* + * Forget any cached information about pinned libraries. + */ +static void +forget_pinned_libs (SrtSystemInfo *self) +{ + g_clear_pointer (&self->pinned_libs.values_32, g_strfreev); + g_clear_pointer (&self->pinned_libs.messages_32, g_strfreev); + g_clear_pointer (&self->pinned_libs.values_64, g_strfreev); + g_clear_pointer (&self->pinned_libs.messages_64, g_strfreev); + self->pinned_libs.have_data = FALSE; +} + static void srt_system_info_finalize (GObject *object) { @@ -362,6 +400,8 @@ srt_system_info_finalize (GObject *object) forget_icds (self); forget_locales (self); forget_os (self); + forget_overrides (self); + forget_pinned_libs (self); forget_runtime (self); forget_steam (self); @@ -567,6 +607,305 @@ ensure_expectations (SrtSystemInfo *self) return self->expectations[0] != '\0'; } +static void +ensure_overrides_cached (SrtSystemInfo *self) +{ + const gchar *argv[] = {"find", "overrides", "-ls", NULL}; + const char *sysroot = NULL; + gchar *output = NULL; + gchar *messages = NULL; + gchar *overrides_path = NULL; + gchar *runtime = NULL; + int exit_status = -1; + GError *error = NULL; + + if (!self->overrides.have_data) + { + if (self->env != NULL) + sysroot = g_environ_getenv (self->env, "SRT_TEST_SYSROOT"); + else + sysroot = g_getenv ("SRT_TEST_SYSROOT"); + + self->overrides.have_data = TRUE; + + runtime = srt_system_info_dup_runtime_path (self); + /* Skip checking the overridden folder if we are not in a pressure-vessel + * Steam Runtime container */ + if (g_strcmp0 (runtime, "/") != 0) + goto out; + + if (!g_spawn_sync (sysroot == NULL ? "/" : sysroot, /* working directory */ + (gchar **) argv, + get_environ (self), + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + &messages, /* stderr */ + &exit_status, + &error)) + { + g_debug ("An error occurred calling the \"find\" binary: %s", error->message); + self->overrides.messages = g_new0 (gchar *, 2); + self->overrides.messages[0] = g_strdup_printf ("%s %d: %s", g_quark_to_string (error->domain), error->code, error->message); + self->overrides.messages[1] = NULL; + goto out; + } + + if (exit_status != 0) + g_debug ("... wait status %d", exit_status); + + if (output != NULL) + { + g_strchomp (output); + self->overrides.values = g_strsplit (output, "\n", -1); + } + + if (messages != NULL) + { + g_strchomp (messages); + self->overrides.messages = g_strsplit (messages, "\n", -1); + } + } + + out: + g_free (output); + g_free (messages); + g_free (overrides_path); + g_free (runtime); + g_clear_error (&error); +} + +/** + * srt_system_info_list_pressure_vessel_overrides: + * @self: The #SrtSystemInfo object + * @messages: (optional) (out) (array zero-terminated=1) (transfer full): If + * not %NULL, used to return a %NULL-terminated array of diagnostic + * messages. Free with g_strfreev(). + * + * If running in a Steam Runtime container using `pressure-vessel`, + * list the libraries from the container that have been overridden + * with libraries from the host system. + * + * The output is intended to be human-readable debugging information, + * rather than something to use programmatically, and its format is + * not guaranteed. It is currently in `find -ls` format. + * + * Similarly, @messages is intended to be human-readable debugging + * information. It is currently whatever was output on standard error + * by `find -ls`. + * + * Returns: (array zero-terminated=1) (transfer full) (nullable): A + * %NULL-terminated array of libraries that have been overridden, + * or %NULL if this process does not appear to be in a pressure-vessel + * container. Free with g_strfreev(). + */ +gchar ** +srt_system_info_list_pressure_vessel_overrides (SrtSystemInfo *self, + gchar ***messages) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_overrides_cached (self); + + if (messages != NULL) + *messages = g_strdupv (self->overrides.messages); + + return g_strdupv (self->overrides.values); +} + +static void +ensure_pinned_libs_cached (SrtSystemInfo *self) +{ + gchar *output = NULL; + gchar *messages = NULL; + gchar *runtime = NULL; + int exit_status = -1; + GError *error = NULL; + + if (!self->pinned_libs.have_data) + { + runtime = srt_system_info_dup_runtime_path (self); + + self->pinned_libs.have_data = TRUE; + + if (runtime == NULL || g_strcmp0 (runtime, "/") == 0) + return; + + const gchar *argv[] = {"find", "pinned_libs_32", "-ls", NULL}; + + if (!g_spawn_sync (runtime, /* working directory */ + (gchar **) argv, + get_environ (self), + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + &messages, /* stderr */ + &exit_status, + &error)) + { + g_debug ("An error occurred calling the \"find\" binary: %s", error->message); + self->pinned_libs.messages_32 = g_new0 (gchar *, 2); + self->pinned_libs.messages_32[0] = g_strdup_printf ("%s %d: %s", g_quark_to_string (error->domain), error->code, error->message); + self->pinned_libs.messages_32[1] = NULL; + goto out; + } + + if (exit_status != 0) + g_debug ("... wait status %d", exit_status); + + if (output != NULL) + { + g_strchomp (output); + self->pinned_libs.values_32 = g_strsplit (output, "\n", -1); + } + + if (messages != NULL) + { + g_strchomp (messages); + self->pinned_libs.messages_32 = g_strsplit (messages, "\n", -1); + } + + g_free (output); + g_free (messages); + + /* Do the same check for `pinned_libs_64` */ + argv[1] = "pinned_libs_64"; + + if (!g_spawn_sync (runtime, /* working directory */ + (gchar **) argv, + get_environ (self), + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + &messages, /* stderr */ + &exit_status, + &error)) + { + g_debug ("An error occurred calling the \"find\" binary: %s", error->message); + self->pinned_libs.messages_64 = g_new0 (gchar *, 2); + self->pinned_libs.messages_64[0] = g_strdup_printf ("%s %d: %s", g_quark_to_string (error->domain), error->code, error->message); + self->pinned_libs.messages_64[1] = NULL; + goto out; + } + + if (exit_status != 0) + g_debug ("... wait status %d", exit_status); + + if (output != NULL) + { + g_strchomp (output); + self->pinned_libs.values_64 = g_strsplit (output, "\n", -1); + } + + if (messages != NULL) + { + g_strchomp (messages); + self->pinned_libs.messages_64 = g_strsplit (messages, "\n", -1); + } + } + + out: + g_clear_error (&error); + g_free (output); + g_free (messages); + g_free (runtime); +} + +/** + * srt_system_info_list_pinned_libs_32: + * @self: The #SrtSystemInfo object + * @messages: (optional) (out) (array zero-terminated=1) (transfer full): If + * not %NULL, used to return a %NULL-terminated array of diagnostic + * messages. Free with g_strfreev(). + * + * If running in an `LD_LIBRARY_PATH`-based Steam Runtime, return + * information about %SRT_ABI_I386 libraries that have been "pinned". + * Normally, the Steam Runtime infrastructure prefers to use shared + * libraries from the host OS, if available, rather than the + * library of the same `SONAME` from the Steam Runtime. However, if + * a library in the Steam Runtime is newer then the version in the + * host OS, or if it is known to be incompatible with newer + * libraries with the same `SONAME`, then the library from the + * Steam Runtime is said to have been "pinned": it is used with a + * higher precedence than libraries from the host OS. + * + * If not in an `LD_LIBRARY_PATH`-based Steam Runtime, return %NULL. + * + * The output is intended to be human-readable debugging information, + * rather than something to use programmatically, and its format is + * not guaranteed. It is currently in `find -ls` format. + * + * Similarly, @messages is intended to be human-readable debugging + * information. It is currently whatever was output on standard error + * by `find -ls`. + * + * Returns: (array zero-terminated=1) (transfer full) (element-type utf8) (nullable): + * An array of strings, or %NULL if we were unable to call "find". + * Free with g_strfreev(). + */ +gchar ** +srt_system_info_list_pinned_libs_32 (SrtSystemInfo *self, + gchar ***messages) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_pinned_libs_cached (self); + + if (messages != NULL) + *messages = g_strdupv (self->pinned_libs.messages_32); + + return g_strdupv (self->pinned_libs.values_32); +} + +/** + * srt_system_info_list_pinned_libs_64: + * @self: The #SrtSystemInfo object + * @messages: (optional) (out) (array zero-terminated=1) (transfer full): If + * not %NULL, used to return a %NULL-terminated array of diagnostic + * messages. Free with g_strfreev(). + * + * If running in an `LD_LIBRARY_PATH`-based Steam Runtime, return + * information about %SRT_ABI_X86_64 libraries that have been "pinned". + * Normally, the Steam Runtime infrastructure prefers to use shared + * libraries from the host OS, if available, rather than the + * library of the same `SONAME` from the Steam Runtime. However, if + * a library in the Steam Runtime is newer then the version in the + * host OS, or if it is known to be incompatible with newer + * libraries with the same `SONAME`, then the library from the + * Steam Runtime is said to have been "pinned": it is used with a + * higher precedence than libraries from the host OS. + * + * If not in an `LD_LIBRARY_PATH`-based Steam Runtime, return %NULL. + * + * The output is intended to be human-readable debugging information, + * rather than something to use programmatically, and its format is + * not guaranteed. It is currently in `find -ls` format. + * + * Similarly, @messages is intended to be human-readable debugging + * information. It is currently whatever was output on standard error + * by `find -ls`. + * + * Returns: (array zero-terminated=1) (transfer full) (element-type utf8) (nullable): + * An array of strings, or %NULL if we were unable to call "find". + * Free with g_strfreev(). + */ +gchar ** +srt_system_info_list_pinned_libs_64 (SrtSystemInfo *self, + gchar ***messages) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_pinned_libs_cached (self); + + if (messages != NULL) + *messages = g_strdupv (self->pinned_libs.messages_64); + + return g_strdupv (self->pinned_libs.values_64); +} + /** * srt_system_info_check_libraries: * @self: The #SrtSystemInfo object to use. @@ -1044,6 +1383,8 @@ srt_system_info_set_environ (SrtSystemInfo *self, forget_graphics_results (self); forget_locales (self); forget_os (self); + forget_overrides (self); + forget_pinned_libs (self); g_strfreev (self->env); self->env = g_strdupv ((gchar **) env); diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h index 4aabb3b7332dc2aeeac270d1c52f385ec2852cdf..2c04e8e280e2efd8c4b387f3b2f7ef6e99706e31 100644 --- a/steam-runtime-tools/system-info.h +++ b/steam-runtime-tools/system-info.h @@ -113,6 +113,14 @@ SrtSteamIssues srt_system_info_get_steam_issues (SrtSystemInfo *self); gchar *srt_system_info_dup_steam_installation_path (SrtSystemInfo *self); gchar *srt_system_info_dup_steam_data_path (SrtSystemInfo *self); +gchar ** srt_system_info_list_pressure_vessel_overrides (SrtSystemInfo *self, + gchar ***messages); + +gchar ** srt_system_info_list_pinned_libs_32 (SrtSystemInfo *self, + gchar ***messages); +gchar ** srt_system_info_list_pinned_libs_64 (SrtSystemInfo *self, + gchar ***messages); + SrtLocaleIssues srt_system_info_get_locale_issues (SrtSystemInfo *self); SrtLocale *srt_system_info_check_locale (SrtSystemInfo *self, const char *requested_name, diff --git a/tests/sysroots/steamrt-overrides-issues/etc/os-release b/tests/sysroots/steamrt-overrides-issues/etc/os-release new file mode 120000 index 0000000000000000000000000000000000000000..c4c75b419cfd1a831f48769e8b4ac8680f728654 --- /dev/null +++ b/tests/sysroots/steamrt-overrides-issues/etc/os-release @@ -0,0 +1 @@ +../usr/lib/os-release \ No newline at end of file diff --git a/tests/sysroots/steamrt-overrides-issues/overrides/bin/.keep b/tests/sysroots/steamrt-overrides-issues/overrides/bin/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/sysroots/steamrt-overrides-issues/overrides/lib/i386-linux-gnu/.keep b/tests/sysroots/steamrt-overrides-issues/overrides/lib/i386-linux-gnu/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/sysroots/steamrt-overrides-issues/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 b/tests/sysroots/steamrt-overrides-issues/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 new file mode 120000 index 0000000000000000000000000000000000000000..8aa9f9f730a56022549af3042c661c9e41f1c076 --- /dev/null +++ b/tests/sysroots/steamrt-overrides-issues/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 @@ -0,0 +1 @@ +/run/host/usr/lib/libgcc_s.so.1 \ No newline at end of file diff --git a/tests/sysroots/steamrt-overrides-issues/usr/lib/os-release b/tests/sysroots/steamrt-overrides-issues/usr/lib/os-release new file mode 100644 index 0000000000000000000000000000000000000000..064dec3e50ce858cccfac132e4b198ed87ef1bd4 --- /dev/null +++ b/tests/sysroots/steamrt-overrides-issues/usr/lib/os-release @@ -0,0 +1,9 @@ +NAME="Steam Runtime" +VERSION="1 (scout)" +ID=steamrt +ID_LIKE=ubuntu +PRETTY_NAME="Steam Runtime 1 (scout)" +VERSION_ID="1" +BUILD_ID="0.20190924.0" +VARIANT=Platform +VARIANT_ID="com.valvesoftware.steamruntime.platform-amd64_i386-scout" diff --git a/tests/sysroots/steamrt/overrides/bin/.keep b/tests/sysroots/steamrt/overrides/bin/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/sysroots/steamrt/overrides/lib/i386-linux-gnu/.keep b/tests/sysroots/steamrt/overrides/lib/i386-linux-gnu/.keep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/sysroots/steamrt/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 b/tests/sysroots/steamrt/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 new file mode 120000 index 0000000000000000000000000000000000000000..8aa9f9f730a56022549af3042c661c9e41f1c076 --- /dev/null +++ b/tests/sysroots/steamrt/overrides/lib/x86_64-linux-gnu/libgcc_s.so.1 @@ -0,0 +1 @@ +/run/host/usr/lib/libgcc_s.so.1 \ No newline at end of file diff --git a/tests/system-info-cli.c b/tests/system-info-cli.c index bb6afe0b3f4f66ed6d945c211f087778f1758b25..a50adc6d70db20669386b04792f9291dec7c9cbc 100644 --- a/tests/system-info-cli.c +++ b/tests/system-info-cli.c @@ -514,6 +514,10 @@ steam_presence (Fixture *f, array = json_object_get_array_member (json_sub_object, "issues"); g_assert_cmpint (json_array_get_length (array), ==, 0); + g_assert_false (json_object_has_member (json_sub_object, "overrides")); + g_assert_true (json_object_has_member (json_sub_object, "pinned_libs_32")); + g_assert_true (json_object_has_member (json_sub_object, "pinned_libs_64")); + g_assert_true (json_object_has_member (json, "architectures")); fake_home_clean_up (fake_home); diff --git a/tests/system-info.c b/tests/system-info.c index e74622c5e9a5db29f1c3899190ee420fb3785092..bff4ea2acb8c452e8d6decab7930831995aa468d 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -1664,6 +1664,456 @@ os_no_os_release (Fixture *f, g_strfreev (envp); } +static void +overrides (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar **envp; + gchar **output; + gchar **issues; + gchar *sysroot; + gchar *s; + gsize i; + gboolean seen_link; + + sysroot = g_build_filename (f->srcdir, "sysroots", "steamrt", NULL); + envp = g_get_environ (); + envp = g_environ_setenv (envp, "SRT_TEST_SYSROOT", sysroot, TRUE); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, envp); + + s = srt_system_info_dup_os_id (info); + g_assert_cmpstr (s, ==, "steamrt"); + g_free (s); + + output = srt_system_info_list_pressure_vessel_overrides (info, &issues); + + /* In the steamrt test overrides folder we expect to have a symbolic + * link to "/run/host/usr/lib/libgcc_s.so.1" */ + seen_link = FALSE; + /* The output is not guaranteed to be ordered */ + g_debug ("overrides content:"); + for (i = 0; output[i] != NULL; i++) + { + g_debug ("%s", output[i]); + + if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL) + seen_link = TRUE; + } + /* The overrides folder contains 4 folders plus the root folder, plus one symlink, + * plus 2 ".keep" files */ + g_assert_cmpint (i, ==, 8); + g_assert_true (seen_link); + g_strfreev (output); + + g_assert_nonnull (issues); + g_assert_cmpstr (issues[0], ==, NULL); + g_strfreev (issues); + + /* Repeat the same check, this time using the cached result */ + output = srt_system_info_list_pressure_vessel_overrides (info, &issues); + + seen_link = FALSE; + for (i = 0; output[i] != NULL; i++) + { + if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL) + seen_link = TRUE; + } + g_assert_cmpint (i, ==, 8); + g_assert_true (seen_link); + g_strfreev (output); + + g_assert_nonnull (issues); + g_assert_cmpstr (issues[0], ==, NULL); + g_strfreev (issues); + + g_object_unref (info); + g_free (sysroot); + g_strfreev (envp); +} + +static void +overrides_issues (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar **envp; + gchar **output; + gchar **issues; + gchar *sysroot; + gchar *lib_folder; + gsize i; + gboolean seen_link; + + sysroot = g_build_filename (f->srcdir, "sysroots", "steamrt-overrides-issues", NULL); + envp = g_get_environ (); + envp = g_environ_setenv (envp, "SRT_TEST_SYSROOT", sysroot, TRUE); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, envp); + + lib_folder = g_build_filename (sysroot, "overrides", "lib", NULL); + + /* Remove the read permission for the "lib" folder */ + g_assert_cmpint (g_chmod (lib_folder, 0200), ==, 0); + + if (g_access (lib_folder, R_OK) == 0) + { + g_test_skip ("This test can't be executed with elevated privileges"); + goto out; + } + + output = srt_system_info_list_pressure_vessel_overrides (info, &issues); + + /* In the steamrt test overrides folder we expect to have a symbolic + * link to "/run/host/usr/lib/libgcc_s.so.1" */ + seen_link = FALSE; + /* The output is not guaranteed to be ordered */ + g_debug ("overrides content:"); + for (i = 0; output[i] != NULL; i++) + { + g_debug ("%s", output[i]); + + if (strstr (output[i], "/run/host/usr/lib/libgcc_s.so.1") != NULL) + seen_link = TRUE; + } + /* The overrides folder contains 4 folders plus the root folder, plus one symlink, + * plus 2 ".keep" files. + * We expect to not be able to open the "lib" folder, so we should have 4 less items than + * a "normal" scenario */ + g_assert_cmpint (i, ==, 4); + /* We expect not to be able to reach the symlink */ + g_assert_false (seen_link); + g_strfreev (output); + + g_assert_nonnull (issues); + g_assert_cmpstr (strstr (issues[0], "overrides/lib"), !=, NULL); + g_strfreev (issues); + + out: + /* Re set the permissions for "lib" to the default 755 */ + g_chmod (lib_folder, 0755); + g_object_unref (info); + g_free (sysroot); + g_free (lib_folder); + g_strfreev (envp); +} + +static void +overrides_not_available (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar **envp; + gchar **output; + gchar **issues; + gchar *sysroot; + + sysroot = g_build_filename (f->srcdir, "sysroots", "debian10", NULL); + envp = g_get_environ (); + envp = g_environ_setenv (envp, "SRT_TEST_SYSROOT", sysroot, TRUE); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, envp); + + output = srt_system_info_list_pressure_vessel_overrides (info, &issues); + + g_assert_null (output); + + g_assert_null (issues); + + g_object_unref (info); + g_free (sysroot); + g_strfreev (envp); +} + +static void +pinned_libraries (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + FakeHome *fake_home; + gchar *target1 = NULL; + gchar *target2 = NULL; + gchar *start = NULL; + gchar *has_pins = NULL; + gchar **values = NULL; + gchar **messages = NULL; + GFile *symlink = NULL; + gboolean seen_pins; + gsize i; + GError *error = NULL; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + start = g_build_filename (fake_home->pinned_32, "libcurl.so.3", NULL); + target1 = g_build_filename (fake_home->pinned_32, "libcurl.so.4", NULL); + symlink = g_file_new_for_path (start); + + g_file_make_symbolic_link (symlink, target1, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + + target2 = g_build_filename (fake_home->i386_usr_lib_i386, "libcurl.so.4.2.0", NULL); + g_assert_cmpint (g_creat (target2, 0755), >, -1); + symlink = g_file_new_for_path (target1); + + g_file_make_symbolic_link (symlink, target2, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + + has_pins = g_build_filename (fake_home->pinned_32, "has_pins", NULL); + g_assert_cmpint (g_creat (has_pins, 0755), >, -1); + + values = srt_system_info_list_pinned_libs_32 (info, &messages); + g_assert_nonnull (values); + seen_pins = FALSE; + /* The output is not guaranteed to be ordered */ + g_debug ("pinned_libs_32 content:"); + for (i = 0; values[i] != NULL; i++) + { + g_debug ("%s", values[i]); + + if (strstr (values[i], "has_pins") != NULL) + seen_pins = TRUE; + } + /* We placed 3 files in `pinned_libs_32`. We expect to have 3 files plus the folder + * itself */ + g_assert_cmpint (i, ==, 4); + g_assert_true (seen_pins); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (messages[0], ==, NULL); + g_strfreev (messages); + + /* Repeat the same check, this time using the cached values */ + values = srt_system_info_list_pinned_libs_32 (info, &messages); + g_assert_nonnull (values); + seen_pins = FALSE; + for (i = 0; values[i] != NULL; i++) + { + if (strstr (values[i], "has_pins") != NULL) + seen_pins = TRUE; + } + g_assert_cmpint (i, ==, 4); + g_assert_true (seen_pins); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (messages[0], ==, NULL); + g_strfreev (messages); + + g_free (target1); + g_free (target2); + g_free (start); + g_free (has_pins); + + /* Check pinned_libs_64. + * Set again the environ to flush the cached values */ + srt_system_info_set_environ (info, fake_home->env); + start = g_build_filename (fake_home->pinned_64, "libcurl.so.3", NULL); + target1 = g_build_filename (fake_home->pinned_64, "libcurl.so.4", NULL); + symlink = g_file_new_for_path (start); + + g_file_make_symbolic_link (symlink, target1, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + + target2 = g_build_filename (fake_home->amd64_usr_lib_64, "libcurl.so.4.2.0", NULL); + g_assert_cmpint (g_creat (target2, 0755), >, -1); + symlink = g_file_new_for_path (target1); + + g_file_make_symbolic_link (symlink, target2, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + + has_pins = g_build_filename (fake_home->pinned_64, "has_pins", NULL); + g_assert_cmpint (g_creat (has_pins, 0755), >, -1); + + values = srt_system_info_list_pinned_libs_64 (info, &messages); + g_assert_nonnull (values); + seen_pins = FALSE; + g_debug ("pinned_libs_64 content:"); + for (i = 0; values[i] != NULL; i++) + { + g_debug ("%s", values[i]); + + if (strstr (values[i], "has_pins") != NULL) + seen_pins = TRUE; + } + g_assert_cmpint (i, ==, 4); + g_assert_true (seen_pins); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (messages[0], ==, NULL); + g_strfreev (messages); + + /* Repeat the same check, this time using the cached values */ + values = srt_system_info_list_pinned_libs_64 (info, &messages); + g_assert_nonnull (values); + seen_pins = FALSE; + for (i = 0; values[i] != NULL; i++) + { + if (strstr (values[i], "has_pins") != NULL) + seen_pins = TRUE; + } + g_assert_cmpint (i, ==, 4); + g_assert_true (seen_pins); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (messages[0], ==, NULL); + g_strfreev (messages); + + g_free (target1); + g_free (target2); + g_free (start); + g_free (has_pins); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + +static void +pinned_libraries_permission (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + FakeHome *fake_home; + gchar *no_access = NULL; + gchar **values = NULL; + gchar **messages = NULL; + gboolean seen_no_access; + gsize i; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + no_access = g_build_filename (fake_home->pinned_32, "no_access", NULL); + /* Creates a folder without read permissions */ + g_assert_cmpint (g_mkdir_with_parents (no_access, 0200), ==, 0); + + if (g_access (no_access, R_OK) == 0) + { + g_test_skip ("This test can't be executed with elevated privileges"); + goto out; + } + + values = srt_system_info_list_pinned_libs_32 (info, &messages); + g_assert_nonnull (values); + seen_no_access = FALSE; + /* The output is not guaranteed to be ordered */ + g_debug ("pinned_libs_32 content:"); + for (i = 0; values[i] != NULL; i++) + { + g_debug ("%s", values[i]); + + if (strstr (values[i], "no_access") != NULL) + seen_no_access = TRUE; + } + /* We placed 1 folder in `pinned_libs_32`. We expect to have 1 folder plus the + * parent folder itself */ + g_assert_cmpint (i, ==, 2); + g_assert_true (seen_no_access); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (strstr (messages[0], "no_access"), !=, NULL); + g_strfreev (messages); + + g_free (no_access); + + /* Check pinned_libs_64. + * Set again the environ to flush the cached values */ + srt_system_info_set_environ (info, fake_home->env); + + no_access = g_build_filename (fake_home->pinned_64, "no_access", NULL); + g_assert_cmpint (g_mkdir_with_parents (no_access, 0311), ==, 0); + + values = srt_system_info_list_pinned_libs_64 (info, &messages); + g_assert_nonnull (values); + seen_no_access = FALSE; + /* The output is not guaranteed to be ordered */ + g_debug ("pinned_libs_64 content:"); + for (i = 0; values[i] != NULL; i++) + { + g_debug ("%s", values[i]); + + if (strstr (values[i], "no_access") != NULL) + seen_no_access = TRUE; + } + /* We placed 1 folder in `pinned_libs_32`. We expect to have 1 folder plus the + * parent folder itself */ + g_assert_cmpint (i, ==, 2); + g_assert_true (seen_no_access); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (strstr (messages[0], "no_access"), !=, NULL); + g_strfreev (messages); + + out: + g_free (no_access); + fake_home_clean_up (fake_home); + g_object_unref (info); +} + +static void +pinned_libraries_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + FakeHome *fake_home; + gchar **values = NULL; + gchar **messages = NULL; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + g_assert_cmpint (g_rmdir (fake_home->pinned_32), ==, 0); + + values = srt_system_info_list_pinned_libs_32 (info, &messages); + g_assert_nonnull (values); + g_assert_cmpstr (values[0], ==, NULL); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (strstr (messages[0], "pinned_libs_32"), !=, NULL); + g_strfreev (messages); + + /* Check pinned_libs_64. + * Set again the environ to flush the cached values */ + srt_system_info_set_environ (info, fake_home->env); + + g_assert_cmpint (g_rmdir (fake_home->pinned_64), ==, 0); + + values = srt_system_info_list_pinned_libs_64 (info, &messages); + g_assert_nonnull (values); + g_assert_cmpstr (values[0], ==, NULL); + g_strfreev (values); + + g_assert_nonnull (messages); + g_assert_cmpstr (strstr (messages[0], "pinned_libs_64"), !=, NULL); + g_strfreev (messages); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + int main (int argc, char **argv) @@ -1718,5 +2168,18 @@ main (int argc, g_test_add ("/system-info/os/no-os-release", Fixture, NULL, setup, os_no_os_release, teardown); + g_test_add ("/system-info/libs/overrides", Fixture, NULL, + setup, overrides, teardown); + g_test_add ("/system-info/libs/overrides_issues", Fixture, NULL, + setup, overrides_issues, teardown); + g_test_add ("/system-info/libs/overrides_not_available", Fixture, NULL, + setup, overrides_not_available, teardown); + g_test_add ("/system-info/libs/pinned_libraries", Fixture, NULL, + setup, pinned_libraries, teardown); + g_test_add ("/system-info/libs/pinned_libraries_permission", Fixture, NULL, + setup, pinned_libraries_permission, teardown); + g_test_add ("/system-info/libs/pinned_libraries_missing", Fixture, NULL, + setup, pinned_libraries_missing, teardown); + return g_test_run (); }