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);