From 6d593bb9eeecf58c74c400ea011b07522cceb455 Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Wed, 14 Aug 2019 12:50:15 +0200 Subject: [PATCH] Add checks for problems with the Steam Installation and the Runtime Now we should be able to detect when the Steam Runtime environmental setup has been modified by the distro or user. Co-authored-by: Ludovico de Nittis <ludovico.denittis@collabora.com> --- bin/system-info.c | 96 ++++- docs/reference/steam-runtime-tools.xml | 2 + steam-runtime-tools/meson.build | 6 + steam-runtime-tools/runtime-internal.h | 39 ++ steam-runtime-tools/runtime.c | 458 ++++++++++++++++++++++ steam-runtime-tools/runtime.h | 76 ++++ steam-runtime-tools/steam-internal.h | 37 ++ steam-runtime-tools/steam-runtime-tools.h | 2 + steam-runtime-tools/steam.c | 202 ++++++++++ steam-runtime-tools/steam.h | 54 +++ steam-runtime-tools/system-info.c | 229 +++++++++++ steam-runtime-tools/system-info.h | 11 + 12 files changed, 1206 insertions(+), 6 deletions(-) create mode 100644 steam-runtime-tools/runtime-internal.h create mode 100644 steam-runtime-tools/runtime.c create mode 100644 steam-runtime-tools/runtime.h create mode 100644 steam-runtime-tools/steam-internal.h create mode 100644 steam-runtime-tools/steam.c create mode 100644 steam-runtime-tools/steam.h diff --git a/bin/system-info.c b/bin/system-info.c index b220516be..642cc84da 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 1eebb3f58..c9038ed68 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 5d3d0a0dc..852b880db 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 000000000..d79806419 --- /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 000000000..314b870c3 --- /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 000000000..96662d331 --- /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 000000000..73863cd7a --- /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 e7313d07b..bc51b02fb 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 000000000..c01e39d05 --- /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 000000000..68e64c200 --- /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 0b6179d1c..76d33e93c 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 33b6132b3..632e5df37 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); -- GitLab