Skip to content
Snippets Groups Projects
Commit 58a56a85 authored by Simon McVittie's avatar Simon McVittie
Browse files

Merge branch 'wip/t16071' into 'master'

Add checks for brokenness in the Steam installation or Steam Runtime

See merge request steam/steam-runtime-tools!30
parents e769c869 1d9be65c
No related branches found
No related tags found
1 merge request!30Add checks for brokenness in the Steam installation or Steam Runtime
Pipeline #1449 passed
Showing
with 2336 additions and 13 deletions
......@@ -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;
}
......@@ -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>
......
......@@ -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',
]
......
/*<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);
/*
* 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;
}
/*
* 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;
/*<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);
......@@ -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
/*
* 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;
}
/*
* 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;
......@@ -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);
}
......@@ -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);
/*
* 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);
}
/*
* 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);
......@@ -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 + '"',
],
......
......@@ -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 ();
}
This diff is collapsed.
/*
* 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
......@@ -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);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment