diff --git a/bin/system-info.c b/bin/system-info.c index b220516be40399169e4a752388ca39889eafff9a..642cc84da9fe5319c45dab9d9b36948a0e79c8f4 100644 --- a/bin/system-info.c +++ b/bin/system-info.c @@ -190,6 +190,55 @@ jsonify_library_issues (JsonBuilder *builder, json_builder_add_string_value (builder, "unknown-expectations"); } +static void +jsonify_steam_issues (JsonBuilder *builder, + SrtSteamIssues issues) +{ + if ((issues & SRT_STEAM_ISSUES_INTERNAL_ERROR) != 0) + json_builder_add_string_value (builder, "internal-error"); + + if ((issues & SRT_STEAM_ISSUES_CANNOT_FIND) != 0) + json_builder_add_string_value (builder, "cannot-find"); + + if ((issues & SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK) != 0) + json_builder_add_string_value (builder, "dot-steam-steam-not-symlink"); +} + +static void +jsonify_runtime_issues (JsonBuilder *builder, + SrtRuntimeIssues issues) +{ + if ((issues & SRT_RUNTIME_ISSUES_INTERNAL_ERROR) != 0) + json_builder_add_string_value (builder, "internal-error"); + + if ((issues & SRT_RUNTIME_ISSUES_DISABLED) != 0) + json_builder_add_string_value (builder, "disabled"); + + if ((issues & SRT_RUNTIME_ISSUES_NOT_RUNTIME) != 0) + json_builder_add_string_value (builder, "not-runtime"); + + if ((issues & SRT_RUNTIME_ISSUES_UNOFFICIAL) != 0) + json_builder_add_string_value (builder, "unofficial"); + + if ((issues & SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION) != 0) + json_builder_add_string_value (builder, "unexpected-location"); + + if ((issues & SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION) != 0) + json_builder_add_string_value (builder, "unexpected-version"); + + if ((issues & SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH) != 0) + json_builder_add_string_value (builder, "not-in-ld-path"); + + if ((issues & SRT_RUNTIME_ISSUES_NOT_IN_PATH) != 0) + json_builder_add_string_value (builder, "not-in-path"); + + if ((issues & SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT) != 0) + json_builder_add_string_value (builder, "not-in-environment"); + + if ((issues & SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES) != 0) + json_builder_add_string_value (builder, "not-using-newer-host-libraries"); +} + static void print_libraries_details (JsonBuilder *builder, GList *libraries, @@ -251,13 +300,18 @@ main (int argc, FILE *original_stdout = NULL; GError *error = NULL; SrtSystemInfo *info; - SrtLibraryIssues issues = SRT_LIBRARY_ISSUES_NONE; + SrtLibraryIssues library_issues = SRT_LIBRARY_ISSUES_NONE; + SrtSteamIssues steam_issues = SRT_STEAM_ISSUES_NONE; + SrtRuntimeIssues runtime_issues = SRT_RUNTIME_ISSUES_NONE; char *expectations = NULL; gboolean verbose = FALSE; JsonBuilder *builder; JsonGenerator *generator; gboolean can_run = FALSE; gchar *json_output; + gchar *version = NULL; + gchar *inst_path = NULL; + gchar *rt_path = NULL; int opt; static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; @@ -306,6 +360,33 @@ main (int argc, json_builder_set_member_name (builder, "can-write-uinput"); json_builder_add_boolean_value (builder, srt_system_info_can_write_to_uinput (info)); + json_builder_set_member_name (builder, "steam-installation"); + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "path"); + inst_path = srt_system_info_dup_steam_installation_path (info); + json_builder_add_string_value (builder, inst_path); + json_builder_set_member_name (builder, "issues"); + json_builder_begin_array (builder); + steam_issues = srt_system_info_get_steam_issues (info); + jsonify_steam_issues (builder, steam_issues); + json_builder_end_array (builder); + json_builder_end_object (builder); + + 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_end_object (builder); + json_builder_set_member_name (builder, "architectures"); json_builder_begin_object (builder); @@ -323,14 +404,14 @@ main (int argc, { json_builder_set_member_name (builder, "library-issues-summary"); json_builder_begin_array (builder); - issues = srt_system_info_check_libraries (info, - multiarch_tuples[i], - &libraries); - jsonify_library_issues (builder, issues); + library_issues = srt_system_info_check_libraries (info, + multiarch_tuples[i], + &libraries); + jsonify_library_issues (builder, library_issues); json_builder_end_array (builder); } - if (libraries != NULL && (issues != SRT_LIBRARY_ISSUES_NONE || verbose)) + if (libraries != NULL && (library_issues != SRT_LIBRARY_ISSUES_NONE || verbose)) print_libraries_details (builder, libraries, verbose); json_builder_end_object (builder); @@ -360,6 +441,9 @@ main (int argc, json_node_free (root); g_object_unref (builder); g_object_unref (info); + g_free (rt_path); + g_free (inst_path); + g_free (version); return 0; } diff --git a/docs/reference/steam-runtime-tools.xml b/docs/reference/steam-runtime-tools.xml index 1eebb3f58343ae5973202ffd549c5248e2ff644b..c9038ed68fd40d39492ccd40f8668c1831ccf9ef 100644 --- a/docs/reference/steam-runtime-tools.xml +++ b/docs/reference/steam-runtime-tools.xml @@ -20,6 +20,8 @@ <title>Specific system information</title> <xi:include href="xml/architecture.xml"/> <xi:include href="xml/library.xml"/> + <xi:include href="xml/runtime.xml"/> + <xi:include href="xml/steam.xml"/> </chapter> <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> diff --git a/steam-runtime-tools/meson.build b/steam-runtime-tools/meson.build index 5d3d0a0dccc5873c31f09f7652067e18b79537e6..852b880db65d1999b002e0c03d6ccc58675eb1c0 100644 --- a/steam-runtime-tools/meson.build +++ b/steam-runtime-tools/meson.build @@ -26,6 +26,10 @@ libsteamrt_sources = [ 'architecture.c', 'library-internal.h', 'library.c', + 'runtime-internal.h', + 'runtime.c', + 'steam-internal.h', + 'steam.c', 'system-info.c', 'utils-internal.h', 'utils.c', @@ -34,7 +38,9 @@ libsteamrt_sources = [ libsteamrt_public_headers = [ 'architecture.h', 'library.h', + 'runtime.h', 'steam-runtime-tools.h', + 'steam.h', 'system-info.h', ] diff --git a/steam-runtime-tools/runtime-internal.h b/steam-runtime-tools/runtime-internal.h new file mode 100644 index 0000000000000000000000000000000000000000..d7980641986ef1b1b166c981d30d423e9d4f4760 --- /dev/null +++ b/steam-runtime-tools/runtime-internal.h @@ -0,0 +1,39 @@ +/*<private_header>*/ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "steam-runtime-tools/runtime.h" + +#include <glib.h> +#include <glib-object.h> + +G_GNUC_INTERNAL +SrtRuntimeIssues _srt_runtime_check (const char *bin32, + const char *expected_version, + const GStrv custom_environ, + gchar **version_out, + gchar **path_out); diff --git a/steam-runtime-tools/runtime.c b/steam-runtime-tools/runtime.c new file mode 100644 index 0000000000000000000000000000000000000000..314b870c35848e68437dc20aee3eb47a61f386e5 --- /dev/null +++ b/steam-runtime-tools/runtime.c @@ -0,0 +1,458 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "steam-runtime-tools/runtime.h" +#include "steam-runtime-tools/runtime-internal.h" + +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <glib/gstdio.h> + +#include "steam-runtime-tools/glib-compat.h" + +/** +* SECTION:runtime +* @title: LD_LIBRARY_PATH-based Steam Runtime +* @short_description: Information about the Steam Runtime +* @include: steam-runtime-tools/steam-runtime-tools.h +* +* #SrtRuntimeIssues represents problems encountered with the Steam +* Runtime. +*/ + +static void +should_be_executable (SrtRuntimeIssues *issues, + const char *path, + const char *filename) +{ + gchar *full = g_build_filename (path, filename, NULL); + + if (!g_file_test (full, G_FILE_TEST_IS_EXECUTABLE)) + { + g_debug ("%s is not executable", full); + *issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + } + + g_free (full); +} + +static void +should_be_dir (SrtRuntimeIssues *issues, + const char *path, + const char *filename) +{ + gchar *full = g_build_filename (path, filename, NULL); + + if (!g_file_test (full, G_FILE_TEST_IS_DIR)) + { + g_debug ("%s is not a regular file", full); + *issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + } + + g_free (full); +} + +static void +should_be_stattable (SrtRuntimeIssues *issues, + const char *path, + const char *filename, + GStatBuf *buf) +{ + gchar *full = g_build_filename (path, filename, NULL); + + if (g_stat (full, buf) != 0) + { + g_debug ("stat %s: %s", full, g_strerror (errno)); + *issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + } + + g_free (full); +} + +static void +might_be_stattable (const char *path, + const char *filename, + GStatBuf *buf) +{ + GStatBuf zeroed_stat = {}; + gchar *full = g_build_filename (path, filename, NULL); + + if (g_stat (full, buf) != 0) + { + g_debug ("stat %s: %s", full, g_strerror (errno)); + *buf = zeroed_stat; + } + + g_free (full); +} + +static gboolean +same_stat (GStatBuf *left, + GStatBuf *right) +{ + return left->st_dev == right->st_dev && left->st_ino == right->st_ino; +} + +/* + * _srt_runtime_check: + * @bin32: (nullable): The absolute path to `ubuntu12_32` + * @expected_version: (nullable): The expected version number of the + * Steam Runtime + * @custom_environ: (nullable): The list of environment variables to use. If NULL + * g_get_environ will be used instead. + * @version_out: (optional) (type utf8) (out): The actual version number + * @path_out: (optional) (type filename) (out): The absolute path of the + * Steam Runtime + */ +SrtRuntimeIssues +_srt_runtime_check (const char *bin32, + const char *expected_version, + const GStrv custom_environ, + gchar **version_out, + gchar **path_out) +{ + SrtRuntimeIssues issues = SRT_RUNTIME_ISSUES_NONE; + GStatBuf zeroed_stat = {}; + GStatBuf expected_stat, actual_stat; + GStatBuf pinned_libs_32; + GStatBuf pinned_libs_64; + GStatBuf lib_i386_linux_gnu; + GStatBuf lib_x86_64_linux_gnu; + GStatBuf usr_lib_i386_linux_gnu; + GStatBuf usr_lib_x86_64_linux_gnu; + GStatBuf amd64_bin; + GStatBuf i386_bin; + const gchar *env = NULL; + gchar *contents = NULL; + gchar *expected_path = NULL; + gchar *path = NULL; + gchar *version = NULL; + gchar *version_txt = NULL; + gsize len = 0; + GError *error = NULL; + GStrv environ = NULL; + + environ = (custom_environ == NULL) ? g_get_environ () : g_strdupv (custom_environ); + + env = g_environ_getenv (environ, "STEAM_RUNTIME"); + + if (bin32 != NULL) + expected_path = g_build_filename (bin32, "steam-runtime", NULL); + + if (g_strcmp0 (env, "0") == 0) + { + issues |= SRT_RUNTIME_ISSUES_DISABLED; + goto out; + } + + if (env == NULL || env[0] != '/') + { + issues |= SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT; + } + else if (g_stat (env, &actual_stat) != 0) + { + g_debug ("stat %s: %s", env, g_strerror (errno)); + issues |= SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT; + actual_stat = zeroed_stat; + } + + if (issues & SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT) + { + /* Try to recover by using the expected path */ + if (expected_path != NULL) + { + path = g_strdup (expected_path); + + if (g_stat (path, &actual_stat) != 0) + actual_stat = zeroed_stat; + } + } + else + { + g_assert (env != NULL); + g_assert (env[0] == '/'); + path = g_strdup (env); + } + + /* If we haven't found it yet, there is nothing else we can check */ + if (path == NULL) + goto out; + + if (expected_path != NULL && strcmp (path, expected_path) != 0) + { + if (g_stat (expected_path, &expected_stat) == 0) + { + if (!same_stat (&expected_stat, &actual_stat)) + { + g_debug ("%s and %s are different inodes", path, expected_path); + issues |= SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION; + } + } + else + { + g_debug ("stat %s: %s", expected_path, g_strerror (errno)); + /* If the expected location doesn't exist then logically + * the actual Steam Runtime in use can't be in the expected + * location... */ + issues |= SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION; + } + } + + version_txt = g_build_filename (path, "version.txt", NULL); + + if (g_file_get_contents (version_txt, &contents, &len, &error)) + { + const char *underscore = strrchr (contents, '_'); + + /* Remove trailing \n if any */ + if (contents[len - 1] == '\n') + contents[--len] = '\0'; + + if (len != strlen (contents) || + strchr (contents, '\n') != NULL || + underscore == NULL) + { + g_debug ("Corrupt runtime: contents of %s should be in the " + "format NAME_VERSION", + version_txt); + issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + } + else if (!g_str_has_prefix (contents, "steam-runtime_")) + { + g_debug ("Unofficial Steam Runtime build %s", contents); + issues |= SRT_RUNTIME_ISSUES_UNOFFICIAL; + } + + if (underscore != NULL) + { + version = g_strdup (underscore + 1); + + if (version[0] == '\0') + { + g_debug ("Corrupt runtime: contents of %s is missing the expected " + "runtime version number", + version_txt); + issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + } + + if (expected_version != NULL && + strcmp (expected_version, version) != 0) + { + g_debug ("Expected Steam Runtime v%s, got v%s", + expected_version, version); + issues |= SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION; + } + } + + g_clear_pointer (&contents, g_free); + } + else + { + issues |= SRT_RUNTIME_ISSUES_NOT_RUNTIME; + g_debug ("Unable to read %s: %s", version_txt, error->message); + g_clear_error (&error); + } + + should_be_dir (&issues, path, "scripts"); + should_be_executable (&issues, path, "run.sh"); + should_be_executable (&issues, path, "setup.sh"); + should_be_stattable (&issues, path, "amd64/lib/x86_64-linux-gnu", + &lib_x86_64_linux_gnu); + should_be_stattable (&issues, path, "amd64/usr/lib/x86_64-linux-gnu", + &usr_lib_x86_64_linux_gnu); + should_be_stattable (&issues, path, "i386/lib/i386-linux-gnu", + &lib_i386_linux_gnu); + should_be_stattable (&issues, path, "i386/usr/lib/i386-linux-gnu", + &usr_lib_i386_linux_gnu); + might_be_stattable (path, "pinned_libs_32", &pinned_libs_32); + might_be_stattable (path, "pinned_libs_64", &pinned_libs_64); + might_be_stattable (path, "amd64/usr/bin", &amd64_bin); + might_be_stattable (path, "i386/usr/bin", &i386_bin); + + env = g_environ_getenv (environ, "STEAM_RUNTIME_PREFER_HOST_LIBRARIES"); + + if (g_strcmp0 (env, "0") == 0) + issues |= SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES; + + env = g_environ_getenv (environ, "LD_LIBRARY_PATH"); + + if (env == NULL) + { + issues |= SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH; + } + else + { + gchar **entries = g_strsplit (env, ":", 0); + gchar **entry; + gboolean saw_lib_i386_linux_gnu = FALSE; + gboolean saw_lib_x86_64_linux_gnu = FALSE; + gboolean saw_usr_lib_i386_linux_gnu = FALSE; + gboolean saw_usr_lib_x86_64_linux_gnu = FALSE; + gboolean saw_pinned_libs_32 = FALSE; + gboolean saw_pinned_libs_64 = FALSE; + + for (entry = entries; entry != NULL && *entry != NULL; entry++) + { + /* Scripts that manipulate LD_LIBRARY_PATH have a habit of + * adding empty entries */ + if (*entry[0] == '\0') + continue; + + /* We compare by stat(), because the entries in the + * LD_LIBRARY_PATH might not have been canonicalized by + * chasing symlinks, replacing "/.." or "//", etc. */ + if (g_stat (*entry, &actual_stat) == 0) + { + if (same_stat (&actual_stat, &lib_i386_linux_gnu)) + saw_lib_i386_linux_gnu = TRUE; + + /* Don't use "else if": it would be legitimate for + * usr/lib/i386-linux-gnu and lib/i386-linux-gnu + * to be symlinks to the same place, in which case + * seeing one counts as seeing both. */ + if (same_stat (&actual_stat, &usr_lib_i386_linux_gnu)) + saw_usr_lib_i386_linux_gnu = TRUE; + + if (same_stat (&actual_stat, &lib_x86_64_linux_gnu)) + saw_lib_x86_64_linux_gnu = TRUE; + + if (same_stat (&actual_stat, &usr_lib_x86_64_linux_gnu)) + saw_usr_lib_x86_64_linux_gnu = TRUE; + + /* The pinned libraries only count if they are before the + * corresponding Steam Runtime directories */ + if (!saw_lib_i386_linux_gnu && + !saw_usr_lib_i386_linux_gnu && + pinned_libs_32.st_mode != 0 && + same_stat (&actual_stat, &pinned_libs_32)) + saw_pinned_libs_32 = TRUE; + + if (!saw_lib_x86_64_linux_gnu && + !saw_usr_lib_x86_64_linux_gnu && + pinned_libs_64.st_mode != 0 && + same_stat (&actual_stat, &pinned_libs_64)) + saw_pinned_libs_64 = TRUE; + } + else + { + g_debug ("stat LD_LIBRARY_PATH entry %s: %s", + *entry, g_strerror (errno)); + } + } + + if (!saw_lib_x86_64_linux_gnu || !saw_usr_lib_x86_64_linux_gnu) + { + g_debug ("STEAM_RUNTIME/amd64/[usr/]lib/x86_64-linux-gnu missing " + "from LD_LIBRARY_PATH"); + issues |= SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH; + } + + if (!saw_lib_i386_linux_gnu || !saw_usr_lib_i386_linux_gnu) + { + g_debug ("STEAM_RUNTIME/i386/[usr/]lib/i386-linux-gnu missing " + "from LD_LIBRARY_PATH"); + issues |= SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH; + } + + if (!saw_pinned_libs_64 || !saw_pinned_libs_32) + { + g_debug ("Pinned libraries missing from LD_LIBRARY_PATH"); + issues |= SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES; + } + + g_strfreev (entries); + } + + env = g_environ_getenv (environ, "PATH"); + + if (env == NULL) + { + issues |= SRT_RUNTIME_ISSUES_NOT_IN_PATH; + } + else + { + gchar **entries = g_strsplit (env, ":", 0); + gchar **entry; + gboolean saw_amd64_bin = FALSE; + gboolean saw_i386_bin = FALSE; + + for (entry = entries; entry != NULL && *entry != NULL; entry++) + { + /* Scripts that manipulate PATH have a habit of adding empty + * entries */ + if (*entry[0] == '\0') + continue; + + /* We compare by stat(), because the entries in the + * PATH might not have been canonicalized by + * chasing symlinks, replacing "/.." or "//", etc. */ + if (g_stat (*entry, &actual_stat) == 0) + { + if (amd64_bin.st_mode != 0 && + same_stat (&actual_stat, &amd64_bin)) + saw_amd64_bin = TRUE; + + if (i386_bin.st_mode != 0 && + same_stat (&actual_stat, &i386_bin)) + saw_i386_bin = TRUE; + + } + else + { + g_debug ("stat PATH entry %s: %s", + *entry, g_strerror (errno)); + } + } + + if (!saw_amd64_bin && !saw_i386_bin) + { + g_debug ("Neither STEAM_RUNTIME/amd64/usr/bin nor STEAM_RUNTIME/i386/usr/bin " + "are available in PATH"); + issues |= SRT_RUNTIME_ISSUES_NOT_IN_PATH; + } + + g_strfreev (entries); + } + +out: + if (path_out != NULL) + *path_out = g_steal_pointer (&path); + + if (version_out != NULL) + *version_out = g_steal_pointer (&version); + + g_free (contents); + g_free (expected_path); + g_free (path); + g_free (version); + g_free (version_txt); + g_strfreev (environ); + g_clear_error (&error); + return issues; +} diff --git a/steam-runtime-tools/runtime.h b/steam-runtime-tools/runtime.h new file mode 100644 index 0000000000000000000000000000000000000000..96662d331f61f0df5f11f50d08ff43dbef234f1b --- /dev/null +++ b/steam-runtime-tools/runtime.h @@ -0,0 +1,76 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#if !defined(_SRT_IN_SINGLE_HEADER) && !defined(_SRT_COMPILATION) +#error "Do not include directly, use <steam-runtime-tools/steam-runtime-tools.h>" +#endif + +/** + * 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_DISABLED: The Steam Runtime has been disabled + * @SRT_RUNTIME_ISSUES_NOT_RUNTIME: The Steam Runtime does not appear to + * have the correct structure + * @SRT_RUNTIME_ISSUES_UNOFFICIAL: The Steam Runtime is an unofficial build + * @SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION: The Steam Runtime is not in + * the location that was expected + * @SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION: The Steam Runtime is not the + * version that was expected + * @SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH: The Steam Runtime is not in the + * expected position in the `LD_LIBRARY_PATH` + * @SRT_RUNTIME_ISSUES_NOT_IN_PATH: The Steam Runtime is not in the + * expected position in the `PATH` + * @SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT: The environment variable + * `STEAM_RUNTIME` is not set to the absolute path to the Steam Runtime + * @SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES: The Steam Runtime has + * been configured to not use host libraries even if they are newer than + * the libraries in the Steam Runtime. This is likely to work + * acceptably with NVIDIA non-free graphics drivers, but is likely to + * break Mesa. + * + * A bitfield with flags representing problems with the Steam Runtime, or + * %SRT_RUNTIME_ISSUES_NONE (which is numerically zero) if no problems + * were detected. + * + * In general, more bits set means more problems. + */ +typedef enum +{ + SRT_RUNTIME_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_RUNTIME_ISSUES_DISABLED = (1 << 1), + SRT_RUNTIME_ISSUES_NOT_RUNTIME = (1 << 2), + SRT_RUNTIME_ISSUES_UNOFFICIAL = (1 << 3), + SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION = (1 << 4), + SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION = (1 << 5), + SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH = (1 << 6), + SRT_RUNTIME_ISSUES_NOT_IN_PATH = (1 << 7), + SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT = (1 << 8), + SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES = (1 << 9), + SRT_RUNTIME_ISSUES_NONE = 0 +} SrtRuntimeIssues; diff --git a/steam-runtime-tools/steam-internal.h b/steam-runtime-tools/steam-internal.h new file mode 100644 index 0000000000000000000000000000000000000000..73863cd7adb6395bd8e426a01c198706d51d5768 --- /dev/null +++ b/steam-runtime-tools/steam-internal.h @@ -0,0 +1,37 @@ +/*<private_header>*/ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "steam-runtime-tools/steam.h" + +#include <glib.h> +#include <glib-object.h> + +G_GNUC_INTERNAL +SrtSteamIssues _srt_steam_check (const GStrv env, + gchar **path_out, + gchar **bin32_out); diff --git a/steam-runtime-tools/steam-runtime-tools.h b/steam-runtime-tools/steam-runtime-tools.h index e7313d07bcf9f72676c70466ed54bb8367b2e334..bc51b02fb1a10e63acf1915f73aaed3d1eff8b5c 100644 --- a/steam-runtime-tools/steam-runtime-tools.h +++ b/steam-runtime-tools/steam-runtime-tools.h @@ -30,6 +30,8 @@ #include <steam-runtime-tools/architecture.h> #include <steam-runtime-tools/enums.h> #include <steam-runtime-tools/library.h> +#include <steam-runtime-tools/runtime.h> +#include <steam-runtime-tools/steam.h> #include <steam-runtime-tools/system-info.h> #undef _SRT_IN_SINGLE_HEADER diff --git a/steam-runtime-tools/steam.c b/steam-runtime-tools/steam.c new file mode 100644 index 0000000000000000000000000000000000000000..c01e39d05b080cdc7c7f31246c72095d3f5a854d --- /dev/null +++ b/steam-runtime-tools/steam.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "steam-runtime-tools/steam.h" +#include "steam-runtime-tools/steam-internal.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> + +#include "steam-runtime-tools/glib-compat.h" + +/** + * SECTION:steam + * @title: Steam installation + * @short_description: Information about the Steam installation + * @include: steam-runtime-tools/steam-runtime-tools.h + * + * #SrtSteamIssues represents problems encountered with the Steam + * installation. + */ + +/* + * _srt_steam_check: + * @environ: (nullable): The list of environment variables to use. + * @path_out: (out) (optional) (nullable) (type filename): + * Used to return the absolute path to the Steam installation + * @bin32_out: (out) (optional) (nullable) (type filename): + * Used to return the absolute path to `ubuntu12_32` + */ +SrtSteamIssues +_srt_steam_check (const GStrv environ, + gchar **path_out, + gchar **bin32_out) +{ + SrtSteamIssues issues = SRT_STEAM_ISSUES_NONE; + gchar *dot_steam_bin32 = NULL; + gchar *dot_steam_steam = NULL; + gchar *dot_steam_root = NULL; + gchar *default_steam_path = NULL; + char *path = NULL; + char *bin32 = NULL; + const char *home = NULL; + const char *user_data = NULL; + GStrv env = NULL; + + env = (environ == NULL) ? g_get_environ () : g_strdupv (environ); + + home = g_environ_getenv (env, "HOME"); + user_data = g_environ_getenv (env, "XDG_DATA_HOME"); + + if (home == NULL) + home = g_get_home_dir (); + + if (user_data == NULL) + user_data = g_get_user_data_dir (); + + default_steam_path = g_build_filename (user_data, + "Steam", NULL); + + dot_steam_bin32 = g_build_filename (home, ".steam", "bin32", NULL); + dot_steam_steam = g_build_filename (home, ".steam", "steam", NULL); + dot_steam_root = g_build_filename (home, ".steam", "root", NULL); + + g_return_val_if_fail (path_out == NULL || *path_out == NULL, + SRT_STEAM_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (bin32_out == NULL || *bin32_out == NULL, + SRT_STEAM_ISSUES_INTERNAL_ERROR); + + /* Canonically, ~/.steam/steam is a symlink to the Steam installation. + * (This is ignoring the Valve-internal "beta universe", which uses + * ~/.steam/steambeta instead.) */ + if (g_file_test (dot_steam_steam, G_FILE_TEST_IS_SYMLINK)) + { + path = realpath (dot_steam_steam, NULL); + + if (path == NULL) + g_debug ("realpath(%s): %s", dot_steam_steam, g_strerror (errno)); + } + else + { + /* e.g. https://bugs.debian.org/916303 */ + issues |= SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK; + } + + /* If that doesn't work, try ~/.steam/root, which points to whichever + * one of the public or beta universe was run most recently. */ + if (path == NULL && + g_file_test (dot_steam_root, G_FILE_TEST_IS_SYMLINK)) + { + path = realpath (dot_steam_root, NULL); + + if (path == NULL) + g_debug ("realpath(%s): %s", dot_steam_root, g_strerror (errno)); + } + + /* If *that* doesn't work, try going up one level from ubuntu12_32, + * to which ~/.steam/bin32 is a symlink */ + if (path == NULL && + g_file_test (dot_steam_bin32, G_FILE_TEST_IS_SYMLINK)) + { + bin32 = realpath (dot_steam_bin32, NULL); + + if (bin32 == NULL) + { + g_debug ("realpath(%s): %s", dot_steam_bin32, g_strerror (errno)); + } + else if (!g_str_has_suffix (bin32, "/ubuntu12_32")) + { + g_debug ("Unexpected bin32 path: %s -> %s", dot_steam_bin32, + bin32); + } + else + { + path = strndup (bin32, strlen (bin32) - strlen ("/ubuntu12_32")); + + /* We don't try to survive out-of-memory */ + if (path == NULL) + g_error ("strndup: %s", g_strerror (errno)); + } + } + + /* If *that* doesn't work, try the default installation location. */ + if (path == NULL) + { + path = realpath (default_steam_path, NULL); + + if (path == NULL) + g_debug ("realpath(%s): %s", default_steam_path, g_strerror (errno)); + } + + if (path == NULL) + { + g_debug ("Unable to find Steam installation"); + issues |= SRT_STEAM_ISSUES_CANNOT_FIND; + goto out; + } + + g_debug ("Found Steam installation at %s", path); + + /* If we haven't found ubuntu12_32 yet, it's a subdirectory of the + * Steam installation */ + if (bin32 == NULL) + { + /* We don't try to survive out-of-memory */ + if (asprintf (&bin32, "%s/ubuntu12_32", path) < 0) + g_error ("asprintf: %s", g_strerror (errno)); + } + + if (bin32 != NULL) + { + g_debug ("Found ubuntu12_32 directory at %s", bin32); + } + else + { + g_debug ("Unable to find ubuntu12_32 directory"); + } + + /* We can't just transfer ownership here, because in the older GLib + * that we're targeting, g_free() and free() are not guaranteed to be + * associated with the same memory pool. */ + if (path_out != NULL) + *path_out = g_strdup (path); + + if (bin32_out != NULL) + *bin32_out = g_strdup (bin32); + +out: + free (path); + free (bin32); + g_free (dot_steam_bin32); + g_free (dot_steam_steam); + g_free (dot_steam_root); + g_free (default_steam_path); + g_strfreev (env); + return issues; +} diff --git a/steam-runtime-tools/steam.h b/steam-runtime-tools/steam.h new file mode 100644 index 0000000000000000000000000000000000000000..68e64c200e66daff8fa58b19ec150efc491e111f --- /dev/null +++ b/steam-runtime-tools/steam.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#if !defined(_SRT_IN_SINGLE_HEADER) && !defined(_SRT_COMPILATION) +#error "Do not include directly, use <steam-runtime-tools/steam-runtime-tools.h>" +#endif + +/** + * 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_CANNOT_FIND: Unable to find the Steam installation + * @SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK: `~/.steam/steam` is not + * a symbolic link to a Steam installation, which for example can happen + * if Steam was installed on a system with https://bugs.debian.org/916303 + * + * A bitfield with flags representing problems with the Steam + * installation, or %SRT_STEAM_ISSUES_NONE (which is numerically zero) + * if no problems were detected. + * + * In general, more bits set means more problems. + */ +typedef enum +{ + SRT_STEAM_ISSUES_INTERNAL_ERROR = (1 << 0), + SRT_STEAM_ISSUES_CANNOT_FIND = (1 << 1), + SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK = (1 << 2), + SRT_STEAM_ISSUES_NONE = 0 +} SrtSteamIssues; diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c index 0b6179d1cee47b8d71c0c5e01977c7956611312c..76d33e93ca5fdff8eff1603eea3877f52cb6f0e0 100644 --- a/steam-runtime-tools/system-info.c +++ b/steam-runtime-tools/system-info.c @@ -28,6 +28,8 @@ #include "steam-runtime-tools/architecture.h" #include "steam-runtime-tools/architecture-internal.h" #include "steam-runtime-tools/glib-compat.h" +#include "steam-runtime-tools/runtime-internal.h" +#include "steam-runtime-tools/steam-internal.h" #include "steam-runtime-tools/utils-internal.h" #include <errno.h> @@ -79,6 +81,23 @@ struct _SrtSystemInfo gchar *expectations; /* Fake environment variables, or %NULL to use the real environment */ gchar **env; + struct + { + /* path != NULL or issues != NONE indicates we have already checked + * the Steam installation */ + gchar *path; + gchar *bin32; + SrtSteamIssues issues; + } steam; + struct + { + /* path != NULL or issues != NONE indicates we have already checked + * the Steam Runtime */ + gchar *path; + gchar *expected_version; + gchar *version; + SrtRuntimeIssues issues; + } runtime; Tristate can_write_uinput; /* (element-type Abi) */ GPtrArray *abis; @@ -195,11 +214,38 @@ srt_system_info_set_property (GObject *object, } } +/* + * Forget any cached information about the Steam Runtime. + */ +static void +forget_runtime (SrtSystemInfo *self) +{ + g_clear_pointer (&self->runtime.path, g_free); + g_clear_pointer (&self->runtime.version, g_free); + g_clear_pointer (&self->runtime.expected_version, g_free); + self->runtime.issues = SRT_RUNTIME_ISSUES_NONE; +} + +/* + * Forget any cached information about the Steam installation. + */ +static void +forget_steam (SrtSystemInfo *self) +{ + forget_runtime (self); + self->steam.issues = SRT_STEAM_ISSUES_NONE; + g_clear_pointer (&self->steam.path, g_free); + g_clear_pointer (&self->steam.bin32, g_free); +} + static void srt_system_info_finalize (GObject *object) { SrtSystemInfo *self = SRT_SYSTEM_INFO (object); + forget_runtime (self); + forget_steam (self); + g_clear_pointer (&self->abis, g_ptr_array_unref); g_free (self->expectations); g_strfreev (self->env); @@ -708,4 +754,187 @@ srt_system_info_set_environ (SrtSystemInfo *self, forget_libraries (self); g_strfreev (self->env); self->env = g_strdupv ((gchar **) env); + + /* Forget what we know about Steam because it is bounded to the environment. */ + forget_steam (self); +} + +static void +ensure_steam_cached (SrtSystemInfo *self) +{ + if (self->steam.issues == SRT_STEAM_ISSUES_NONE && + self->steam.path == NULL) + self->steam.issues = _srt_steam_check (self->env, + &self->steam.path, + &self->steam.bin32); +} + +/** + * srt_system_info_get_steam_issues: + * @self: The #SrtSystemInfo object + * + * Detect and return any problems encountered with the Steam installation. + * + * Returns: Any problems detected with the Steam installation, + * or %SRT_RUNTIME_ISSUES_NONE if no problems were detected + */ +SrtSteamIssues +srt_system_info_get_steam_issues (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), + SRT_STEAM_ISSUES_INTERNAL_ERROR); + + ensure_steam_cached (self); + return self->steam.issues; +} + +/** + * srt_system_info_dup_steam_installation_path: + * @self: The #SrtSystemInfo object + * + * Return the absolute path to the Steam installation in use (the + * directory containing `steam.sh` and `ubuntu12_32/` among other + * files and directories, analogous to `C:\Program Files\Steam` in a + * typical Windows installation of Steam). This is typically of the form + * `/home/me/.local/share/Steam`. + * + * If the Steam installation could not be found, at least one flag will + * be set in the result of srt_system_info_get_steam_issues() to indicate + * why. + * + * Returns: (transfer full) (type filename) (nullable): The absolute path + * to the Steam installation, or %NULL if it could not be determined. + * Free with g_free(). + */ +gchar * +srt_system_info_dup_steam_installation_path (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_steam_cached (self); + return g_strdup (self->steam.path); +} + +/** + * srt_system_info_set_expected_runtime_version: + * @self: The #SrtSystemInfo object + * @version (nullable): The expected version number, such as `0.20190711.3`, + * or %NULL if there is no particular expectation + * + * Set the expected version number of the Steam Runtime. Invalidate any + * cached information about the Steam Runtime if it differs from the + * previous expectation. + */ +void +srt_system_info_set_expected_runtime_version (SrtSystemInfo *self, + const char *version) +{ + g_return_if_fail (SRT_IS_SYSTEM_INFO (self)); + + 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); + } +} + +/** + * srt_system_info_dup_expected_runtime_version: + * @self: The #SrtSystemInfo object + * + * Returns: (transfer full) (type utf8): The expected version number of + * the Steam Runtime, or %NULL if no particular version is expected. + * Free with g_free(). + */ +gchar * +srt_system_info_dup_expected_runtime_version (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + return g_strdup (self->runtime.expected_version); +} + +static void +ensure_runtime_cached (SrtSystemInfo *self) +{ + ensure_steam_cached (self); + + if (self->runtime.issues == SRT_RUNTIME_ISSUES_NONE && + self->runtime.path == NULL) + self->runtime.issues = _srt_runtime_check (self->steam.bin32, + self->runtime.expected_version, + self->env, + &self->runtime.version, + &self->runtime.path); +} + +/** + * srt_system_info_get_runtime_issues: + * @self: The #SrtSystemInfo object + * + * Detect and return any problems encountered with the + * `LD_LIBRARY_PATH`-based Steam Runtime. + * + * Returns: Any problems detected with the Steam Runtime, + * or %SRT_RUNTIME_ISSUES_NONE if no problems were detected + */ +SrtRuntimeIssues +srt_system_info_get_runtime_issues (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), + SRT_RUNTIME_ISSUES_INTERNAL_ERROR); + + ensure_runtime_cached (self); + return self->runtime.issues; +} + +/** + * srt_system_info_dup_runtime_path: + * @self: The #SrtSystemInfo object + * + * Return the absolute path to the `LD_LIBRARY_PATH`-based Steam Runtime + * in use (the directory containing `run.sh`, `version.txt` and + * similar files). + * + * If the Steam Runtime has been disabled or could not be found, at + * least one flag will be set in the result of + * srt_system_info_get_runtime_issues() to indicate why. + * + * Returns: (transfer full) (type filename) (nullable): The absolute path + * to the Steam Runtime, or %NULL if it could not be determined. + * Free with g_free(). + */ +gchar * +srt_system_info_dup_runtime_path (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_runtime_cached (self); + return g_strdup (self->runtime.path); +} + +/** + * srt_system_info_dup_runtime_version: + * @self: The #SrtSystemInfo object + * + * Return the version number of the `LD_LIBRARY_PATH`-based Steam Runtime + * in use, for example `0.20190711.3`, or %NULL if it could not be + * determined. + * + * If the Steam Runtime has been disabled or could not be found, or its + * version number could not be read, then at least one flag will be set + * in the result of srt_system_info_get_runtime_issues() to indicate why. + * + * Returns: (transfer full) (type utf8) (nullable): The version number of + * the Steam Runtime, or %NULL if it could not be determined. + * Free with g_free(). + */ +gchar * +srt_system_info_dup_runtime_version (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL); + + ensure_runtime_cached (self); + return g_strdup (self->runtime.version); } diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h index 33b6132b3b25235e320bfd201b54b85dafd5bcdd..632e5df379ab39ae6d06ce5c3fd8ffd01539a78b 100644 --- a/steam-runtime-tools/system-info.h +++ b/steam-runtime-tools/system-info.h @@ -33,6 +33,8 @@ #include <glib-object.h> #include <steam-runtime-tools/library.h> +#include <steam-runtime-tools/runtime.h> +#include <steam-runtime-tools/steam.h> typedef struct _SrtSystemInfo SrtSystemInfo; typedef struct _SrtSystemInfoClass SrtSystemInfoClass; @@ -61,3 +63,12 @@ SrtLibraryIssues srt_system_info_check_library (SrtSystemInfo *self, void srt_system_info_set_environ (SrtSystemInfo *self, gchar * const *env); +void srt_system_info_set_expected_runtime_version (SrtSystemInfo *self, + const char *version); +gchar *srt_system_info_dup_expected_runtime_version (SrtSystemInfo *self); +SrtRuntimeIssues srt_system_info_get_runtime_issues (SrtSystemInfo *self); +gchar *srt_system_info_dup_runtime_path (SrtSystemInfo *self); +gchar *srt_system_info_dup_runtime_version (SrtSystemInfo *self); + +SrtSteamIssues srt_system_info_get_steam_issues (SrtSystemInfo *self); +gchar *srt_system_info_dup_steam_installation_path (SrtSystemInfo *self); diff --git a/tests/fake-home.c b/tests/fake-home.c new file mode 100644 index 0000000000000000000000000000000000000000..c60da94021c8acdb0e7e448af7eedd013bf785f8 --- /dev/null +++ b/tests/fake-home.c @@ -0,0 +1,295 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "fake-home.h" + +#include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <gio/gio.h> +#include <glib.h> +#include <glib/gstdio.h> + +#include "test-utils.h" + +/** + * fake_home_new: + * + * Create a new #FakeHome object and a temporary folder in the file system + * + * Returns: the newely created #FakeHome object + */ +FakeHome * +fake_home_new (void) +{ + GError *error = NULL; + FakeHome *fake_home = g_slice_new (FakeHome); + fake_home->home = g_dir_make_tmp ("fake-home-XXXXXX", &error); + g_assert_no_error (error); + + fake_home->create_pinning_libs = TRUE; + fake_home->create_i386_folders = TRUE; + fake_home->create_amd64_folders = TRUE; + fake_home->create_steam_symlink = TRUE; + fake_home->create_steamrt_files = TRUE; + fake_home->add_environments = TRUE; + fake_home->has_debian_bug_916303 = FALSE; + + return fake_home; +} + +/** + * fake_home_create_structure: + * @f: a #FakeHome object used to create the folder structure + * + * Create folders and files like a canonical Steam Installation. + * As the home directory, the temporary folder stored in `home` is used. + * A custom environ is also created and stored under `env`. + * + * Returns: %TRUE if the creation was successful + */ +gboolean +fake_home_create_structure (FakeHome *f) +{ + gboolean ret = FALSE; + GFile *symlink = NULL; + gchar *dot_steam = NULL; + gchar *scripts = NULL; + gchar *run = NULL; + gchar *setup = NULL; + gchar *version = NULL; + gchar *dot_steam_bin32 = NULL; + gchar *dot_steam_steam = NULL; + gchar *local_share = NULL; + gchar *ld_path = NULL; + gchar *prepended_path = NULL; + gchar *ubuntu12_32 = NULL; + GError *error = NULL; + int saved_errno; + + g_return_val_if_fail (f != NULL, FALSE); + + dot_steam = g_build_filename (f->home, ".steam", NULL); + + if (f->has_debian_bug_916303) + f->steam_base_folder = g_strdup (dot_steam); + else + f->steam_base_folder = g_build_filename (f->home, ".local", "share", + "Steam", NULL); + + f->runtime = g_build_filename (f->steam_base_folder, "ubuntu12_32", + "steam-runtime", NULL); + + scripts = g_build_filename (f->runtime, "scripts", NULL); + + if (g_mkdir_with_parents (dot_steam, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->steam_base_folder, 0755) != 0) + goto out; + if (g_mkdir_with_parents (scripts, 0755) != 0) + goto out; + + f->pinned_32 = g_build_filename (f->runtime, "pinned_libs_32", NULL); + f->pinned_64 = g_build_filename (f->runtime, "pinned_libs_64", NULL); + f->i386_lib_i386 = g_build_filename (f->runtime, "i386", "lib", "i386-linux-gnu", NULL); + f->i386_lib = g_build_filename (f->runtime, "i386", "lib", NULL); + f->i386_usr_lib_i386 = g_build_filename (f->runtime, "i386", "usr", "lib", "i386-linux-gnu", NULL); + f->i386_usr_lib = g_build_filename (f->runtime, "i386", "usr", "lib", NULL); + f->i386_usr_bin = g_build_filename (f->runtime, "i386", "usr", "bin", NULL); + f->amd64_lib_64 = g_build_filename (f->runtime, "amd64", "lib", "x86_64-linux-gnu", NULL); + f->amd64_lib = g_build_filename (f->runtime, "amd64", "lib", NULL); + f->amd64_usr_lib_64 = g_build_filename (f->runtime, "amd64", "usr", "lib", "x86_64-linux-gnu", NULL); + f->amd64_usr_lib = g_build_filename (f->runtime, "amd64", "usr", "lib", NULL); + f->amd64_bin = g_build_filename (f->runtime, "amd64", "bin", NULL); + f->amd64_usr_bin = g_build_filename (f->runtime, "amd64", "usr", "bin", NULL); + + if (f->create_pinning_libs) + { + if (g_mkdir_with_parents (f->pinned_32, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->pinned_64, 0755) != 0) + goto out; + } + + if (f->create_i386_folders) + { + if (g_mkdir_with_parents (f->i386_lib_i386, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->i386_usr_lib_i386, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->i386_usr_bin, 0755) != 0) + goto out; + } + + if (f->create_amd64_folders) + { + if (g_mkdir_with_parents (f->amd64_lib_64, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->amd64_usr_lib_64, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->amd64_usr_bin, 0755) != 0) + goto out; + } + + if (f->create_steamrt_files) + { + run = g_build_filename (f->runtime, "run.sh", NULL); + setup = g_build_filename (f->runtime, "setup.sh", NULL); + version = g_build_filename (f->runtime, "version.txt", NULL); + + if (g_creat (run, 0755) < 0) + goto out; + if (g_creat (setup, 0755) < 0) + goto out; + if (g_creat (version, 0755) < 0) + goto out; + + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + } + + if (f->has_debian_bug_916303) + { + dot_steam_steam = g_build_filename (dot_steam, "steam", NULL); + if (g_mkdir_with_parents (dot_steam_steam, 0755) != 0) + goto out; + } + + if (f->create_steam_symlink) + { + dot_steam_steam = g_build_filename (dot_steam, "steam", NULL); + symlink = g_file_new_for_path (dot_steam_steam); + + g_file_make_symbolic_link (symlink, f->steam_base_folder, NULL, &error); + g_object_unref (symlink); + if (f->has_debian_bug_916303) + { + g_error_free (error); + error = NULL; + } + else + { + g_assert_no_error (error); + } + + dot_steam_bin32 = g_build_filename (dot_steam, "bin32", NULL); + symlink = g_file_new_for_path (dot_steam_bin32); + + ubuntu12_32 = g_build_filename (f->steam_base_folder, "ubuntu12_32", NULL); + g_file_make_symbolic_link (symlink, ubuntu12_32, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + } + + local_share = g_build_filename (f->home, ".local", "share", NULL); + f->env = g_get_environ (); + f->env = g_environ_setenv (f->env, "HOME", f->home, TRUE); + f->env = g_environ_setenv (f->env, "XDG_DATA_HOME", local_share, TRUE); + + if (f->add_environments) + { + const gchar *path; + + f->env = g_environ_setenv (f->env, "STEAM_RUNTIME", f->runtime, TRUE); + + ld_path = g_strjoin (":", + f->pinned_32, + f->pinned_64, + f->i386_lib_i386, + f->i386_lib, + f->i386_usr_lib_i386, + f->i386_usr_lib, + f->amd64_lib_64, + f->amd64_lib, + f->amd64_usr_lib_64, + f->amd64_usr_lib, + NULL); + f->env = g_environ_setenv (f->env, "LD_LIBRARY_PATH", ld_path, TRUE); + + path = g_environ_getenv (f->env, "PATH"); + + prepended_path = g_strjoin (":", + f->amd64_bin, + f->amd64_usr_bin, + path, + NULL); + + f->env = g_environ_setenv (f->env, "PATH", prepended_path, TRUE); + } + + ret = TRUE; + + out: + saved_errno = errno; + g_free (dot_steam); + g_free (scripts); + g_free (run); + g_free (setup); + g_free (version); + g_free (dot_steam_steam); + g_free (local_share); + g_free (ld_path); + g_free (prepended_path); + + if (!ret) + g_warning ("Unable to create directories: %s", g_strerror (saved_errno)); + + return ret; + +} + +/** + * fake_home_clean_up: + * @f: the #FakeHome object to clean up and finalize + * + * Clean up a #FakeHome object recursively removing the created + * home directory and freeing @f. + */ +void +fake_home_clean_up (FakeHome *f) +{ + if (!rm_rf (f->home)) + g_debug ("Unable to remove the fake home directory: %s", f->home); + + g_free (f->home); + g_free (f->steam_base_folder); + g_free (f->runtime); + g_free (f->pinned_32); + g_free (f->pinned_64); + g_free (f->i386_lib_i386); + g_free (f->i386_lib); + g_free (f->i386_usr_lib_i386); + g_free (f->i386_usr_lib); + g_free (f->i386_usr_bin); + g_free (f->amd64_lib_64); + g_free (f->amd64_lib); + g_free (f->amd64_usr_lib_64); + g_free (f->amd64_usr_lib); + g_free (f->amd64_bin); + g_free (f->amd64_usr_bin); + g_strfreev (f->env); + + g_slice_free (FakeHome, f); +} diff --git a/tests/fake-home.h b/tests/fake-home.h new file mode 100644 index 0000000000000000000000000000000000000000..60a9846c0ace729554c7f7263eff9d0a3014d833 --- /dev/null +++ b/tests/fake-home.h @@ -0,0 +1,62 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include <glib.h> +#include <glib-object.h> + +typedef struct +{ + gboolean create_pinning_libs; + gboolean create_i386_folders; + gboolean create_amd64_folders; + gboolean create_steam_symlink; + gboolean create_steamrt_files; + gboolean add_environments; + gboolean has_debian_bug_916303; + + gchar *home; + gchar *steam_base_folder; + gchar *runtime; + gchar *pinned_32; + gchar *pinned_64; + gchar *i386_lib_i386; + gchar *i386_lib; + gchar *i386_usr_lib_i386; + gchar *i386_usr_lib; + gchar *i386_usr_bin; + gchar *amd64_lib_64; + gchar *amd64_lib; + gchar *amd64_usr_lib_64; + gchar *amd64_usr_lib; + gchar *amd64_bin; + gchar *amd64_usr_bin; + GStrv env; +} FakeHome; + +FakeHome * fake_home_new (void); +gboolean fake_home_create_structure (FakeHome *fake_home); +void fake_home_clean_up (FakeHome *f); diff --git a/tests/meson.build b/tests/meson.build index 53a323a8aaf07477412fdbcad6ae68cad1947451..12f2363e29678d044babd1916af99b14abe0f400 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -34,6 +34,11 @@ tests = [ 'system-info-cli', ] +tests_utils = [ + 'fake-home.c', + 'test-utils.c', +] + tests_dir = join_paths( get_option('libexecdir'), 'installed-tests', @@ -52,7 +57,7 @@ install_subdir('fake-steam-runtime', install_dir : tests_dir) foreach test_name : tests exe = executable( 'test-' + test_name, - files(test_name + '.c'), + files(test_name + '.c') + tests_utils, c_args : [ '-D_SRT_MULTIARCH="' + multiarch + '"', ], diff --git a/tests/system-info-cli.c b/tests/system-info-cli.c index f80e912f9f91daa7a0b87269f8a7504743ccfb22..93615bb96f8336ee7d0edafffd9a9d4860551b0d 100644 --- a/tests/system-info-cli.c +++ b/tests/system-info-cli.c @@ -39,6 +39,7 @@ #include <json-glib/json-glib.h> #include "test-utils.h" +#include "fake-home.h" static const char *argv0; static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; @@ -346,6 +347,10 @@ libraries_presence_verbose (Fixture *f, g_assert_true (json_object_has_member (json, "can-write-uinput")); + g_assert_true (json_object_has_member (json, "steam-installation")); + + g_assert_true (json_object_has_member (json, "runtime")); + g_assert_true (json_object_has_member (json, "architectures")); json = json_object_get_object_member (json, "architectures"); @@ -384,7 +389,6 @@ no_arguments (Fixture *f, GError *error = NULL; gchar *output = NULL; SrtSystemInfo *info = srt_system_info_new (NULL); - gchar *expectations_in = g_build_filename (f->srcdir, "expectations", NULL); const gchar *argv[] = { "steam-runtime-system-info", NULL }; g_assert_true (g_spawn_sync (NULL, /* working directory */ @@ -424,7 +428,186 @@ no_arguments (Fixture *f, g_object_unref (parser); g_object_unref (info); - g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +/* + * Test a system with a good Steam installation. + */ +static void +steam_presence (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_sub_object; + JsonArray *array; + GError *error = NULL; + gchar *output = NULL; + const gchar *path = NULL; + const gchar *version = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + fake_home->env, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + g_assert_true (json_object_has_member (json, "steam-installation")); + json_sub_object = json_object_get_object_member (json, "steam-installation"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + g_assert_true (json_object_has_member (json, "runtime")); + json_sub_object = json_object_get_object_member (json, "runtime"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "version")); + version = json_object_get_string_member (json_sub_object, "version"); + g_assert_cmpstr (version, !=, NULL); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + g_assert_true (json_object_has_member (json, "architectures")); + + fake_home_clean_up (fake_home); + g_object_unref (parser); + g_object_unref (info); + g_free (output); + g_clear_error (&error); +} + +/* + * Test a system with a Steam installation with issues. + */ +static void +steam_issues (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_sub_object; + JsonArray *array; + GError *error = NULL; + gchar *output = NULL; + const gchar *path = NULL; + const gchar *version = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + FakeHome *fake_home; + + g_assert_true (TRUE); + + fake_home = fake_home_new (); + fake_home->create_pinning_libs = FALSE; + fake_home->create_steam_symlink = FALSE; + fake_home->create_steamrt_files = FALSE; + fake_home_create_structure (fake_home); + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + fake_home->env, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + + g_assert_true (json_object_has_member (json, "steam-installation")); + json_sub_object = json_object_get_object_member (json, "steam-installation"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 1); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, + "dot-steam-steam-not-symlink"); + + g_assert_true (json_object_has_member (json, "runtime")); + json_sub_object = json_object_get_object_member (json, "runtime"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "version")); + version = json_object_get_string_member (json_sub_object, "version"); + g_assert_cmpstr (version, ==, NULL); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 2); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, + "not-runtime"); + g_assert_cmpstr (json_array_get_string_element (array, 1), ==, + "not-using-newer-host-libraries"); + + g_assert_true (json_object_has_member (json, "architectures")); + + fake_home_clean_up (fake_home); + g_object_unref (parser); + g_object_unref (info); g_free (output); g_clear_error (&error); } @@ -436,14 +619,18 @@ main (int argc, argv0 = argv[0]; g_test_init (&argc, &argv, NULL); - g_test_add ("/system-info/libraries_presence", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_presence", Fixture, NULL, setup, libraries_presence, teardown); - g_test_add ("/system-info/libraries_missing", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_missing", Fixture, NULL, setup, libraries_missing, teardown); - g_test_add ("/system-info/libraries_presence_verbose", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_presence_verbose", Fixture, NULL, setup, libraries_presence_verbose, teardown); - g_test_add ("/system-info/no_arguments", Fixture, NULL, + g_test_add ("/system-info-cli/no_arguments", Fixture, NULL, setup, no_arguments, teardown); + g_test_add ("/system-info-cli/steam_presence", Fixture, NULL, + setup, steam_presence, teardown); + g_test_add ("/system-info-cli/steam_issues", Fixture, NULL, + setup, steam_issues, teardown); return g_test_run (); } diff --git a/tests/system-info.c b/tests/system-info.c index afe4d58b1c1a9da6bc8c40b04630d6eb941dcda9..b7d73821e6c75483bd62cbc13609d329c32ed7bc 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -29,14 +29,17 @@ #include <glib.h> #include <glib/gstdio.h> +#include <gio/gio.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <ftw.h> #include "test-utils.h" +#include "fake-home.h" static const char *argv0; @@ -679,6 +682,497 @@ wrong_expectations (Fixture *f, g_object_unref (info); } +static void +steam_runtime (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues runtime_issues; + SrtSteamIssues steam_issues; + gchar *runtime_path = NULL; + gchar *installation_path = NULL; + FakeHome *fake_home; + + 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); + + /* Check for runtime issues */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NONE); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, fake_home->runtime); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, fake_home->steam_base_folder); + g_free (runtime_path); + g_free (installation_path); + + /* Do the check again, this time using the cache */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NONE); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, fake_home->runtime); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, fake_home->steam_base_folder); + + /* Check for Steam issues */ + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Do the check again, this time using the cache */ + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_RUNTIME_ISSUES_NONE); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (runtime_path); + g_free (installation_path); +} + +static void +steam_runtime_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues runtime_issues; + SrtSteamIssues steam_issues; + gchar *full_ld_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + full_ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + + info = srt_system_info_new (NULL); + + /* Unset LD_LIBRARY_PATH */ + fake_home->env = g_environ_unsetenv (fake_home->env, "LD_LIBRARY_PATH"); + srt_system_info_set_environ (info, fake_home->env); + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH); + + /* Re set LD_LIBRARY_PATH and remove a required folder from the runtime */ + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", full_ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + g_assert_cmpint (g_rmdir (fake_home->amd64_usr_lib_64), ==, 0); + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_RUNTIME, !=, 0); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH, !=, 0); + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_STEAM_ISSUES_NONE); + + /* Do the check again, this time using the cache */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_RUNTIME, !=, 0); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH, !=, 0); + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_STEAM_ISSUES_NONE); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (full_ld_path); +} + +static void +steam_runtime_pinned (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *full_ld_path = NULL; + gchar *ld_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + full_ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + + info = srt_system_info_new (NULL); + + /* Move the pinned libraries at the end of LD_LIBRARY_PATH */ + ld_path = g_strjoin (":", + fake_home->i386_lib_i386, + fake_home->i386_lib, + fake_home->i386_usr_lib_i386, + fake_home->i386_usr_lib, + fake_home->amd64_lib_64, + fake_home->amd64_lib, + fake_home->amd64_usr_lib_64, + fake_home->amd64_usr_lib, + fake_home->pinned_32, + fake_home->pinned_64, + NULL); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + g_free (ld_path); + + /* Remove the pinned library folder */ + g_assert_cmpint (g_rmdir (fake_home->pinned_32), ==, 0); + g_assert_cmpint (g_rmdir (fake_home->pinned_64), ==, 0); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", full_ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + + /* Remove pinned libraries from LD_LIBRARY_PATH */ + ld_path = g_strjoin (":", + fake_home->i386_lib_i386, + fake_home->i386_lib, + fake_home->i386_usr_lib_i386, + fake_home->i386_usr_lib, + fake_home->amd64_lib_64, + fake_home->amd64_lib, + fake_home->amd64_usr_lib_64, + fake_home->amd64_usr_lib, + NULL); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (full_ld_path); + g_free (ld_path); +} + +static void +runtime_disabled_or_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *runtime_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steamrt_files = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + + /* Completely disable the runtime */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", "0", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_DISABLED); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, NULL); + + /* Set the runtime to a relative position. + * Test if we can revover using the expected path. + * We didn't create SteamRT files so expect to receive a "not_runtime" + * issue. */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", "my/not/absolute/runtime/path", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + /* Remove the STEAM_RUNTIME environment. */ + fake_home->env = g_environ_unsetenv (fake_home->env, "STEAM_RUNTIME"); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + /* Disable prefer host libraries */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME_PREFER_HOST_LIBRARIES", "0", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES | + SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + +static void +runtime_version (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *version = NULL; + gchar *dup_version = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + version = g_build_filename (fake_home->runtime, "version.txt", NULL); + info = srt_system_info_new (NULL); + + /* Check version with a trailing new line */ + g_file_set_contents (version, "steam-runtime_0.20190711.3\n", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check version with an empty number */ + g_file_set_contents (version, "steam-runtime_", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + + /* Check version without underscore */ + g_file_set_contents (version, "steam-runtime", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, NULL); + + /* Check version with a custom prefix */ + g_file_set_contents (version, "custom-steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + + /* Check version with a custom prefix and multiple underscores */ + g_file_set_contents (version, "custom_steam_runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, "0.20190711.3"); + g_free (dup_version); + + /* Check an empty version file */ + g_file_set_contents (version, "", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, NULL); + + /* Check expected version */ + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check expected version with trailing new line */ + g_file_set_contents (version, "steam-runtime_0.20190711.3\n", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check wrong expected version */ + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20210813.4"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION); + + /* Check wrong expected version */ + g_file_set_contents (version, "steam-runtime_", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20180101.2"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_RUNTIME | + SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION, ==, issues); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, ""); + + /* Check expected version with custom prefix */ + g_file_set_contents (version, "my-custom_steam_runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (version); + g_free (dup_version); +} + +static void +runtime_unexpected_location (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *dot_steam_steam = NULL; + gchar *my_runtime = NULL; + gchar *ld_path = NULL; + gchar *env_path = NULL; + gchar **parts = NULL; + GFile *symlink = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steam_symlink = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + dot_steam_steam = g_build_filename (fake_home->home, ".steam", "steam", NULL); + + my_runtime = g_build_filename (fake_home->steam_base_folder, "ubuntu12_32", + "my-runtime", NULL); + + + /* Create a new homedir/.steam/steam symlink that doesn't point to + * the expected steam runtime path. */ + symlink = g_file_new_for_path (dot_steam_steam); + g_file_make_symbolic_link (symlink, fake_home->pinned_64, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION); + g_object_unref (symlink); + + /* Move the steam-runtime to another location called "my-runtime" and + * adjust all the environment variables accordingly. */ + ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + parts = g_strsplit (ld_path, "steam-runtime", -1); + g_free (ld_path); + ld_path = g_strjoinv ("my-runtime", parts); + g_strfreev (parts); + + env_path = g_strdup (g_environ_getenv (fake_home->env, "PATH")); + parts = g_strsplit (env_path, "steam-runtime", -1); + g_free (env_path); + env_path = g_strjoinv ("my-runtime", parts); + + g_rename (fake_home->runtime, my_runtime); + g_remove (dot_steam_steam); + symlink = g_file_new_for_path (dot_steam_steam); + g_file_make_symbolic_link (symlink, my_runtime, NULL, &error); + g_assert_no_error (error); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", my_runtime, TRUE); + fake_home->env = g_environ_setenv (fake_home->env, "PATH", env_path, TRUE); + + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION); + + fake_home_clean_up (fake_home); + g_object_unref (symlink); + g_object_unref (info); + g_free (dot_steam_steam); + g_free (my_runtime); + g_free (ld_path); + g_free (env_path); + g_strfreev (parts); +} + +static void +steam_symlink (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtSteamIssues issues; + gchar *data_home = NULL; + gchar *dot_steam_steam = NULL; + gchar *dot_steam_root = NULL; + gchar *dot_steam_bin32 = NULL; + gchar *installation_path = NULL; + gchar *ubuntu12_32 = NULL; + GFile *symlink = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steam_symlink = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + dot_steam_steam = g_build_filename (fake_home->home, ".steam", "steam", NULL); + dot_steam_root = g_build_filename (fake_home->home, ".steam", "root", NULL); + dot_steam_bin32 = g_build_filename (fake_home->home, ".steam", "bin32", NULL); + ubuntu12_32 = g_build_filename (fake_home->steam_base_folder, "ubuntu12_32", NULL); + + /* We don't have a homedir/.steam/steam symlink. */ + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + + /* Create the homedir/.steam/root symlink. */ + symlink = g_file_new_for_path (dot_steam_root); + g_file_make_symbolic_link (symlink, fake_home->steam_base_folder, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + g_object_unref (symlink); + + /* Remove homedir/.steam/root symlink and create homedir/.steam/bin32 symlink. */ + g_remove (dot_steam_root); + symlink = g_file_new_for_path (dot_steam_bin32); + g_file_make_symbolic_link (symlink, ubuntu12_32, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + + /* Remove the homedir/.steam/bin32 symlink and set XDG_DATA_HOME env to a + * folder that is not the expected homedir/.local/share */ + g_remove (dot_steam_bin32); + data_home = g_build_filename (fake_home->home, "DataHome", NULL); + fake_home->env = g_environ_setenv (fake_home->env, "XDG_DATA_HOME", data_home, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_CANNOT_FIND | + SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, NULL); + + fake_home_clean_up (fake_home); + g_object_unref (symlink); + g_object_unref (info); + g_free (data_home); + g_free (dot_steam_steam); + g_free (dot_steam_root); + g_free (dot_steam_bin32); + g_free (ubuntu12_32); +} + +/* Recreate the conditions that triggered the Debian bug 916303. + * Steam was installed into "~/.steam", which meant that the "steam/" + * directory inside the Steam installation collided with the "~/.steam/steam" + * symlink, preventing the symlink from being created. */ +static void +debian_bug_916303 (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtSteamIssues issues; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->has_debian_bug_916303 = TRUE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (issues, ==, SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + int main (int argc, char **argv) @@ -701,5 +1195,22 @@ main (int argc, g_test_add ("/system-info/wrong_expectations", Fixture, NULL, setup, wrong_expectations, teardown); + g_test_add ("/system-info/steam_runtime", Fixture, NULL, + setup, steam_runtime, teardown); + g_test_add ("/system-info/steam_runtime_missing", Fixture, NULL, + setup, steam_runtime_missing, teardown); + g_test_add ("/system-info/steam_runtime_pinned", Fixture, NULL, + setup, steam_runtime_pinned, teardown); + g_test_add ("/system-info/runtime_disabled_or_missing", Fixture, NULL, + setup, runtime_disabled_or_missing, teardown); + g_test_add ("/system-info/runtime_version", Fixture, NULL, + setup, runtime_version, teardown); + g_test_add ("/system-info/runtime_unexpected_location", Fixture, NULL, + setup, runtime_unexpected_location, teardown); + g_test_add ("/system-info/steam_symlink", Fixture, NULL, + setup, steam_symlink, teardown); + g_test_add ("/system-info/debian_bug_916303", Fixture, NULL, + setup, debian_bug_916303, teardown); + return g_test_run (); } diff --git a/tests/test-utils.c b/tests/test-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..969c790a8198b673764b0a9cec5f00be5a8ee28c --- /dev/null +++ b/tests/test-utils.c @@ -0,0 +1,61 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "test-utils.h" + +#include <ftw.h> +#include <glib.h> +#include <stdio.h> + +static gint +ftw_remove (const gchar *path, + const struct stat *sb, + gint typeflags, + struct FTW *ftwbuf) +{ + if (remove (path) < 0) + return -1; + + return 0; +} + +/** + * rm_rf: + * @directory: (type filename): The folder to remove. + * + * Recursively delete @directory within the same file system and + * without following symbolic links. + * + * Returns: %TRUE if the removal was successful + */ +gboolean rm_rf (char *directory) +{ + g_return_val_if_fail (directory != NULL, FALSE); + + if (nftw (directory, ftw_remove, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + return FALSE; + + return TRUE; +} \ No newline at end of file diff --git a/tests/test-utils.h b/tests/test-utils.h index e963a25495cc255fcff308d79ac4162e1ff079f8..8bb481e427984417e999b1d7dbf8836e10c45a44 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -64,3 +64,5 @@ #if !GLIB_CHECK_VERSION(2, 38, 0) #define g_test_skip(msg) g_test_message ("SKIP: %s", msg) #endif + +gboolean rm_rf (char *directory);