Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • steamrt/steam-runtime-tools
1 result
Show changes
Commits on Source (9)
Showing
with 1176 additions and 141 deletions
/*
* Copyright © 2020 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.
*/
/*
* Perform some checks to ensure that the Steam client requirements are met.
* Output a human-readable message on stdout if the current system does not
* meet every requirement.
*/
#include <steam-runtime-tools/steam-runtime-tools.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <glib.h>
#include <glib-object.h>
#include <steam-runtime-tools/utils-internal.h>
#define X86_FEATURES_REQUIRED (SRT_X86_FEATURE_X86_64 \
| SRT_X86_FEATURE_CMPXCHG16B \
| SRT_X86_FEATURE_SSE3)
enum
{
OPTION_HELP = 1,
OPTION_VERSION,
};
struct option long_options[] =
{
{ "version", no_argument, NULL, OPTION_VERSION },
{ "help", no_argument, NULL, OPTION_HELP },
{ NULL, 0, NULL, 0 }
};
static void usage (int code) __attribute__((__noreturn__));
/*
* Print usage information and exit with status @code.
*/
static void
usage (int code)
{
FILE *fp;
if (code == 0)
fp = stdout;
else
fp = stderr;
fprintf (fp, "Usage: %s [OPTIONS]\n",
program_invocation_short_name);
exit (code);
}
static FILE *
divert_stdout_to_stderr (GError **error)
{
int original_stdout_fd;
FILE *original_stdout;
/* Duplicate the original stdout so that we still have a way to write
* machine-readable output. */
original_stdout_fd = dup (STDOUT_FILENO);
if (original_stdout_fd < 0)
{
int saved_errno = errno;
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
"Unable to duplicate fd %d: %s",
STDOUT_FILENO, g_strerror (saved_errno));
return NULL;
}
/* If something like g_debug writes to stdout, make it come out of
* our original stderr. */
if (dup2 (STDERR_FILENO, STDOUT_FILENO) != STDOUT_FILENO)
{
int saved_errno = errno;
close (original_stdout_fd);
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
"Unable to make fd %d a copy of fd %d: %s",
STDOUT_FILENO, STDERR_FILENO, g_strerror (saved_errno));
return NULL;
}
/* original_stdout takes ownership of original_stdout_fd on success */
original_stdout = fdopen (original_stdout_fd, "w");
if (original_stdout == NULL)
{
int saved_errno = errno;
close (original_stdout_fd);
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
"Unable to create a stdio wrapper for fd %d: %s",
original_stdout_fd, g_strerror (saved_errno));
return NULL;
}
return original_stdout;
}
static gboolean
check_x86_features (SrtX86FeatureFlags features)
{
return ((features & X86_FEATURES_REQUIRED) == X86_FEATURES_REQUIRED);
}
int
main (int argc,
char **argv)
{
FILE *original_stdout = NULL;
GError *error = NULL;
SrtSystemInfo *info;
SrtX86FeatureFlags x86_features = SRT_X86_FEATURE_NONE;
const gchar *output = NULL;
gchar *version = NULL;
int opt;
int exit_code = EXIT_SUCCESS;
while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
{
switch (opt)
{
case OPTION_VERSION:
/* Output version number as YAML for machine-readability,
* inspired by `ostree --version` and `docker version` */
printf (
"%s:\n"
" Package: steam-runtime-tools\n"
" Version: %s\n",
argv[0], VERSION);
return EXIT_SUCCESS;
case OPTION_HELP:
usage (0);
break;
case '?':
default:
usage (EX_USAGE);
break; /* not reached */
}
}
if (optind != argc)
usage (EX_USAGE);
/* stdout is reserved for machine-readable output, so avoid having
* things like g_debug() pollute it. */
original_stdout = divert_stdout_to_stderr (&error);
if (original_stdout == NULL)
{
g_warning ("%s", error->message);
g_clear_error (&error);
return EXIT_FAILURE;
}
_srt_unblock_signals ();
info = srt_system_info_new (NULL);
/* This might be required for unit testing */
srt_system_info_set_sysroot (info, g_getenv ("SRT_TEST_SYSROOT"));
x86_features = srt_system_info_get_x86_features (info);
if (!check_x86_features (x86_features))
{
output = "Sorry, this computer's CPU is too old to run Steam.\n"
"\nSteam requires at least an Intel Pentium 4 or AMD Opteron, with the following features:\n"
"\t- x86-64 (AMD64) instruction set (lm in /proc/cpuinfo flags)\n"
"\t- CMPXCHG16B instruction support (cx16 in /proc/cpuinfo flags)\n"
"\t- SSE3 instruction support (pni in /proc/cpuinfo flags)\n";
exit_code = EX_OSERR;
goto out;
}
out:
if (output != NULL)
{
if (fputs (output, original_stdout) < 0)
g_warning ("Unable to write output: %s", g_strerror (errno));
if (fputs ("\n", original_stdout) < 0)
g_warning ("Unable to write final newline: %s", g_strerror (errno));
}
if (fclose (original_stdout) != 0)
g_warning ("Unable to close stdout: %s", g_strerror (errno));
g_object_unref (info);
g_free (version);
return exit_code;
}
---
title: steam-runtime-check-requirements
section: 1
...
# NAME
steam-runtime-check-requirements - perform checks to ensure that the Steam client requirements are met
# SYNOPSIS
**steam-runtime-check-requirements**
# DESCRIPTION
# OPTIONS
**--version**
: Instead of performing the checks, write in output the version number as
YAML.
# OUTPUT
If all the Steam client requirements are met the output will be empty.
Otherwise if some of the checks fails, the output will have a human-readable
message explaining what failed.
# EXIT STATUS
0
: **steam-runtime-check-requirements** ran successfully and all the Steam
client requirements are met.
71
: At least one of the requirements is not met. In this case the exit status
will be 71 (EX_OSERR).
Other Nonzero
: An error occurred.
<!-- vim:set sw=4 sts=4 et: -->
......@@ -32,23 +32,39 @@ executable(
install_rpath : bin_rpath,
)
executable(
'steam-runtime-check-requirements',
'check-requirements.c',
dependencies : [glib, gobject, libsteamrt_dep],
install : true,
# Use the adjacent libsteam-runtime-tools and json-glib, ignoring
# LD_LIBRARY_PATH even if set
build_rpath : bin_rpath,
install_rpath : bin_rpath,
)
if get_option('man')
custom_target(
'steam-runtime-system-info.1',
build_by_default : true,
command : [
pandoc,
'-s',
'-o', '@OUTPUT@',
'-f', pandoc_markdown_nosmart,
'-t', 'man',
'@INPUT@',
],
input : 'system-info.md',
output : 'steam-runtime-system-info.1',
install : true,
install_dir : join_paths(get_option('prefix'), get_option('mandir'), 'man1'),
)
foreach bin_name : [
'check-requirements',
'system-info',
]
custom_target(
'steam-runtime-' + bin_name + '.1',
build_by_default : true,
command : [
pandoc,
'-s',
'-o', '@OUTPUT@',
'-f', pandoc_markdown_nosmart,
'-t', 'man',
'@INPUT@',
],
input : bin_name + '.md',
output : 'steam-runtime-' + bin_name + '.1',
install : true,
install_dir : join_paths(get_option('prefix'), get_option('mandir'), 'man1'),
)
endforeach
endif
# vim:set sw=2 sts=2 et:
......@@ -251,6 +251,49 @@ jsonify_flags (JsonBuilder *builder,
g_type_class_unref (class);
}
static void
jsonify_flags_string_bool_map (JsonBuilder *builder,
GType flags_type,
unsigned int values)
{
GFlagsClass *class;
GFlagsValue *flags_value;
g_return_if_fail (G_TYPE_IS_FLAGS (flags_type));
class = g_type_class_ref (flags_type);
for (flags_value = class->values; flags_value->value_name; flags_value++)
{
/* Skip the numerically zero flag (usually "none") */
if (flags_value->value == 0)
continue;
json_builder_set_member_name (builder, flags_value->value_nick);
if ((flags_value->value & values) == flags_value->value)
{
json_builder_add_boolean_value (builder, TRUE);
values &= ~flags_value->value;
}
else
{
json_builder_add_boolean_value (builder, FALSE);
}
}
if (values)
{
gchar *rest = g_strdup_printf ("0x%x", values);
json_builder_set_member_name (builder, rest);
json_builder_add_boolean_value (builder, TRUE);
g_free (rest);
}
g_type_class_unref (class);
}
static void
jsonify_library_issues (JsonBuilder *builder,
SrtLibraryIssues issues)
......@@ -306,6 +349,13 @@ jsonify_locale_issues (JsonBuilder *builder,
jsonify_flags (builder, SRT_TYPE_LOCALE_ISSUES, issues);
}
static void
jsonify_x86_features (JsonBuilder *builder,
SrtX86FeatureFlags features)
{
jsonify_flags_string_bool_map (builder, SRT_TYPE_X86_FEATURE_FLAGS, features);
}
static void
print_libraries_details (JsonBuilder *builder,
GList *libraries,
......@@ -743,6 +793,7 @@ main (int argc,
SrtSteamIssues steam_issues = SRT_STEAM_ISSUES_NONE;
SrtRuntimeIssues runtime_issues = SRT_RUNTIME_ISSUES_NONE;
SrtLocaleIssues locale_issues = SRT_LOCALE_ISSUES_NONE;
SrtX86FeatureFlags x86_features = SRT_X86_FEATURE_NONE;
char *expectations = NULL;
gboolean verbose = FALSE;
JsonBuilder *builder;
......@@ -1213,6 +1264,14 @@ main (int argc,
}
json_builder_end_array (builder);
json_builder_set_member_name (builder, "cpu-features");
json_builder_begin_object (builder);
{
x86_features = srt_system_info_get_x86_features (info);
jsonify_x86_features (builder, x86_features);
}
json_builder_end_object (builder);
json_builder_end_object (builder); // End global object
JsonNode *root = json_builder_get_root (builder);
......
......@@ -652,6 +652,23 @@ keys:
**steam_uri_handler**
: A boolean value indicating whether this entry can open `steam:` URIs.
**cpu-features**
: An object decribing some of the features that the CPU in use supports.
Currently it has the following string keys, each with a boolean
value indicating whether the CPU feature is present or absent:
**x86-64**
: Whether the CPU supports the "Long mode", i.e. x86-64 architecture
(listed as `lm` in `/proc/cpuinfo`).
**sse3**
: Whether the CPU supports the SSE3 extension (Streaming SIMD Extensions
3, listed as `pni` (Prescott New Instructions) in `/proc/cpuinfo`).
**cmpxchg16b**
: Whether the CPU supports the CMPXCHG16B instruction
(listed as `cx16` in `/proc/cpuinfo`).
# EXIT STATUS
0
......
steam-runtime-tools (0.20200403.0) UNRELEASED; urgency=medium
steam-runtime-tools (0.20200415.0) scout; urgency=medium
[ Ludovico de Nittis ]
* Diagnose problems with "steam:" URL handler
(implements: T20052; diagnoses: steam-for-linux#6942)
* Create a new srt_system_info_get_steam_details function
* tests: Generate mock sysroots programmatically
* tests: Generate mock sysroots programmatically (fixes: T20177)
* Enumerate VDPAU drivers from LD_LIBRARY_PATH and system library
search path (implements: T19545)
* Avoid warnings when a graphics check returns empty JSON
* Check for required CPU features.
Check if the CPU supports the features we are interested in.
Right now they are: SSE3 (pni), x86_64 (lm) and CMPXCHG16B (cx16).
(implements: T20489; diagnoses: steam-for-linux#5164,
steam-for-linux#6812, steam-for-linux#6795, steam-for-linux#4196)
* Add check-requirements preflight check.
With check-requirements we can do a preflight check and ensure that the
Steam client requirements are met. (implements: T20491)
[ Simon McVittie ]
* tests: Factor out the directory containing mock sysroots
* tests: Add a script to generate the mock sysroots
* Build as a native package.
* Build as a native package
-- Simon McVittie <smcv@collabora.com> Tue, 03 Apr 2020 10:23:42 +0100
-- Simon McVittie <smcv@collabora.com> Wed, 15 Apr 2020 13:21:10 +0100
steam-runtime-tools (0.20200331.1-0+steamrt1.1) scout; urgency=medium
......
......@@ -107,9 +107,10 @@ Description:
supporting code used by the Steam client to discover system information.
.
This package contains symbolic links to libraries depended on by the
steam-runtime-system-info and libsteam-runtime-tools-0-helpers packages,
which make it possible to run those tools from an LD_LIBRARY_PATH-style
Steam Runtime even if the LD_LIBRARY_PATH is not correctly set.
steam-runtime-system-info, steam-runtime-check-requirements and
libsteam-runtime-tools-0-helpers packages, which make it possible to run
those tools from an LD_LIBRARY_PATH-style Steam Runtime even if the
LD_LIBRARY_PATH is not correctly set.
Package: libsteam-runtime-tools-0-tests
Architecture: any
......@@ -146,4 +147,5 @@ Description: Steam Runtime utility library - command-line tools
.
This package contains the command-line tool steam-runtime-system-info,
which summarizes everything that the libsteam-runtime-tools library
can find out.
can find out, and steam-runtime-check-requirements, which performs checks
to ensure that the Steam client requirements are met.
......@@ -7,6 +7,12 @@ libsteam-runtime-tools-0.so.0 libsteam-runtime-tools-0-0 #MINVER#
srt_architecture_can_run_x86_64@Base 0.20190717.0
srt_check_library_presence@Base 0.20190801.0
srt_container_type_get_type@Base 0.20200306.0
srt_desktop_entry_get_commandline@Base 0.20200415.0
srt_desktop_entry_get_filename@Base 0.20200415.0
srt_desktop_entry_get_id@Base 0.20200415.0
srt_desktop_entry_get_type@Base 0.20200415.0
srt_desktop_entry_is_default_handler@Base 0.20200415.0
srt_desktop_entry_is_steam_handler@Base 0.20200415.0
srt_dri_driver_get_library_path@Base 0.20200109.0
srt_dri_driver_get_type@Base 0.20200109.0
srt_dri_driver_is_extra@Base 0.20200109.0
......@@ -60,6 +66,11 @@ libsteam-runtime-tools-0.so.0 libsteam-runtime-tools-0-0 #MINVER#
srt_locale_issues_get_type@Base 0.20190909.0
srt_rendering_interface_get_type@Base 0.20190822.0
srt_runtime_issues_get_type@Base 0.20190816.0
srt_steam_get_bin32_path@Base 0.20200415.0
srt_steam_get_data_path@Base 0.20200415.0
srt_steam_get_install_path@Base 0.20200415.0
srt_steam_get_issues@Base 0.20200415.0
srt_steam_get_type@Base 0.20200415.0
srt_steam_issues_get_type@Base 0.20190816.0
srt_system_info_can_run@Base 0.20190801.0
srt_system_info_can_write_to_uinput@Base 0.20190801.0
......@@ -87,8 +98,11 @@ libsteam-runtime-tools-0.so.0 libsteam-runtime-tools-0-0 #MINVER#
srt_system_info_get_locale_issues@Base 0.20190909.0
srt_system_info_get_primary_multiarch_tuple@Base 0.20190909.0
srt_system_info_get_runtime_issues@Base 0.20190816.0
srt_system_info_get_steam_details@Base 0.20200415.0
srt_system_info_get_steam_issues@Base 0.20190816.0
srt_system_info_get_type@Base 0.20190801.0
srt_system_info_get_x86_features@Base 0.20200415.0
srt_system_info_list_desktop_entries@Base 0.20200415.0
srt_system_info_list_dri_drivers@Base 0.20200109.0
srt_system_info_list_driver_environment@Base 0.20200306.0
srt_system_info_list_egl_icds@Base 0.20190926.0
......@@ -125,3 +139,4 @@ libsteam-runtime-tools-0.so.0 libsteam-runtime-tools-0-0 #MINVER#
srt_vulkan_icd_resolve_library_path@Base 0.20190926.0
srt_vulkan_icd_write_to_file@Base 0.20190926.0
srt_window_system_get_type@Base 0.20190822.0
srt_x86_feature_flags_get_type@Base 0.20200415.0
......@@ -91,6 +91,7 @@ override_dh_shlibdeps:
-plibsteam-runtime-tools-0-relocatable-libs \
-- \
-prelocatable \
-e$(DESTDIR)/usr/bin/steam-runtime-check-requirements \
-e$(DESTDIR)/usr/bin/steam-runtime-system-info \
-e$(DESTDIR)/usr/$(pkglibexecdir)/$(DEB_HOST_MULTIARCH)-check-locale \
$(NULL)
......@@ -108,6 +109,7 @@ override_dh_link:
--link-target / \
--no-glibc \
--provider / \
only-dependencies:path:$(DESTDIR)/usr/bin/steam-runtime-check-requirements \
only-dependencies:path:$(DESTDIR)/usr/bin/steam-runtime-system-info \
only-dependencies:path:$(DESTDIR)/usr/$(pkglibexecdir)/$(DEB_HOST_MULTIARCH)-check-locale \
$(NULL)
......
usr/bin/steam-runtime-check-requirements
usr/bin/steam-runtime-system-info
usr/share/man/man1
......@@ -23,7 +23,7 @@
project(
'steam-runtime-tools', 'c',
version : '0.20200331.1',
version : '0.20200415.0',
default_options: [
'c_std=c99',
'cpp_std=c++11',
......@@ -35,7 +35,7 @@ add_languages('cpp')
api_major = '0'
abi_major = '0'
abi_minor = '20200331.1'
abi_minor = '20200415.0'
pkg = import('pkgconfig')
gnome = import('gnome')
......
/*<private_header>*/
/*
* Copyright © 2020 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/cpu-feature.h"
#include <glib.h>
#include <glib-object.h>
G_GNUC_INTERNAL
SrtX86FeatureFlags _srt_feature_get_x86_flags (void);
/*
* Copyright © 2020 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/cpu-feature.h"
#include "steam-runtime-tools/cpu-feature-internal.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <cpuid.h>
#include "steam-runtime-tools/glib-compat.h"
#include "steam-runtime-tools/utils.h"
/**
* SECTION:cpu-feature
* @title: CPU features
* @short_description: Information about supported CPU features
* @include: steam-runtime-tools/steam-runtime-tools.h
*
* #SrtX86FeatureFlags represents the features that the CPU supports.
*/
SrtX86FeatureFlags
_srt_feature_get_x86_flags (void)
{
guint eax = 0;
guint ebx = 0;
guint ecx = 0;
guint edx = 0;
int result;
SrtX86FeatureFlags features = SRT_X86_FEATURE_NONE;
/* Get the list of basic features (leaf 1) */
result = __get_cpuid (1, &eax, &ebx, &ecx, &edx);
if (result != 1)
{
g_debug ("Something went wrong trying to list supported x86 features");
return features;
}
if (ecx & bit_CMPXCHG16B)
features |= SRT_X86_FEATURE_CMPXCHG16B;
if (ecx & bit_SSE3)
features |= SRT_X86_FEATURE_SSE3;
result = __get_cpuid (0x80000001, &eax, &ebx, &ecx, &edx);
if (result != 1)
{
g_debug ("Something went wrong trying to list extended supported x86 features");
return features;
}
/* Long mode, 64-bit capable */
if (edx & bit_LM)
features |= SRT_X86_FEATURE_X86_64;
return features;
}
\ No newline at end of file
/*
* Copyright © 2020 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
/**
* SrtX86FeatureFlags:
* @SRT_X86_FEATURE_NONE: None of the features listed here are supported
* @SRT_X86_FEATURE_X86_64: The CPU supports the "Long mode", where an OS can
* access 64-bit instructions and registers (i.e. x86-64 architecture),
* indicated by `lm` in Linux `/proc/cpuinfo`
* @SRT_X86_FEATURE_SSE3: The CPU supports the SSE3 extension (Streaming SIMD
* Extensions 3, also known as Prescott New Instructions), indicated by
* `pni` in Linux `/proc/cpuinfo`
* @SRT_X86_FEATURE_CMPXCHG16B: The CPU supports the CMPXCHG16B instruction,
* indicated by `cx16` in Linux `/proc/cpuinfo`
*
* A bitfield with flags representing the features that the CPU supports, or
* %SRT_X86_FEATURE_NONE (which is numerically zero) if none of the features
* we checked are supported.
*
* In general, more bits set means more instructions are supported.
*
* At the time of writing, the Steam client requires %SRT_X86_FEATURE_X86_64,
* %SRT_X86_FEATURE_SSE3 and %SRT_X86_FEATURE_CMPXCHG16B.
*/
typedef enum
{
SRT_X86_FEATURE_X86_64 = (1 << 0),
SRT_X86_FEATURE_SSE3 = (1 << 1),
SRT_X86_FEATURE_CMPXCHG16B = (1 << 2),
SRT_X86_FEATURE_NONE = 0
} SrtX86FeatureFlags;
......@@ -641,6 +641,49 @@ _argv_for_check_gl (const char *helpers_path,
return argv;
}
static GPtrArray *
_argv_for_list_vdpau_drivers (gchar **envp,
const char *helpers_path,
const char *multiarch_tuple,
const char *temp_dir,
GError **error)
{
const gchar *vdpau_driver = NULL;
GPtrArray *argv;
if (envp != NULL)
vdpau_driver = g_environ_getenv (envp, "VDPAU_DRIVER");
else
vdpau_driver = g_getenv ("VDPAU_DRIVER");
argv = _srt_get_helper (helpers_path, multiarch_tuple, "capsule-capture-libs",
SRT_HELPER_FLAGS_SEARCH_PATH, error);
if (argv == NULL)
return NULL;
g_ptr_array_add (argv, g_strdup ("--dest"));
g_ptr_array_add (argv, g_strdup (temp_dir));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname-match:libvdpau_*.so"));
/* If the driver is not in the ld.so.cache the wildcard-matching will not find it.
* To increase our chances we specifically search for the chosen driver and some
* commonly used drivers. */
if (vdpau_driver != NULL)
{
g_ptr_array_add (argv, g_strjoin (NULL,
"no-dependencies:if-exists:even-if-older:soname:libvdpau_",
vdpau_driver, ".so", NULL));
}
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_nouveau.so"));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_nvidia.so"));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_r300.so"));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_r600.so"));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_radeonsi.so"));
g_ptr_array_add (argv, g_strdup ("no-dependencies:if-exists:even-if-older:soname:libvdpau_va_gl.so"));
g_ptr_array_add (argv, NULL);
return argv;
}
static GPtrArray *
_argv_for_list_glx_icds (const char *helpers_path,
const char *multiarch_tuple,
......@@ -2810,6 +2853,165 @@ _srt_get_modules_from_path (gchar **envp,
}
}
/**
* _srt_list_modules_from_directory:
* @envp: (array zero-terminated=1): Behave as though `environ` was this array
* @argv: (array zero-terminated=1) (not nullable): The `argv` of the helper to use
* @tmp_directory: (not nullable) (type filename): Full path to the destination
* directory used by the "capsule-capture-libs" helper
* @known_table: (not optional): set of library names, plus their links, that
* we already found. Newely found libraries will be added to this list.
* For VDPAU provide a set with just paths where we already looked into, and in
* the VDPAU case the set will not be changed by this function.
* @module: Which graphic module to search
* @is_extra: If this path should be considered an extra or not. This is used only if
* @module is #SRT_GRAPHICS_VDPAU_MODULE.
* @modules_out: (not optional) (inout): Prepend the found modules to this list.
* If @module is #SRT_GRAPHICS_GLX_MODULE, the element-type will be #SrtGlxIcd.
* Otherwise if @module is #SRT_GRAPHICS_VDPAU_MODULE, the element-type will be #SrtVdpauDriver.
*
* Modules are added to @modules_out in reverse lexicographic order
* (`libvdpau_r600.so` is before `libvdpau_r300.so`, which is before `libvdpau_nouveau.so`).
*/
static void
_srt_list_modules_from_directory (gchar **envp,
GPtrArray *argv,
const gchar *tmp_directory,
GHashTable *known_table,
SrtGraphicsModule module,
gboolean is_extra,
GList **modules_out)
{
int exit_status = -1;
GError *error = NULL;
gchar *stderr = NULL;
gchar *output = NULL;
GDir *dir_iter = NULL;
GPtrArray *members = NULL;
const gchar *member;
gchar *full_path = NULL;
gchar *driver_path = NULL;
gchar *driver_directory = NULL;
gchar *driver_link = NULL;
gchar *soname_path = NULL;
g_return_if_fail (argv != NULL);
g_return_if_fail (tmp_directory != NULL);
g_return_if_fail (known_table != NULL);
g_return_if_fail (modules_out != NULL);
if (!g_spawn_sync (NULL, /* working directory */
(gchar **) argv->pdata,
envp,
G_SPAWN_SEARCH_PATH, /* flags */
_srt_child_setup_unblock_signals,
NULL, /* user data */
&output, /* stdout */
&stderr,
&exit_status,
&error))
{
g_debug ("An error occurred calling the helper: %s", error->message);
goto out;
}
if (exit_status != 0)
{
g_debug ("... wait status %d", exit_status);
goto out;
}
dir_iter = g_dir_open (tmp_directory, 0, &error);
if (dir_iter == NULL)
{
g_debug ("Failed to open \"%s\": %s", tmp_directory, error->message);
goto out;
}
members = g_ptr_array_new_with_free_func (g_free);
while ((member = g_dir_read_name (dir_iter)) != NULL)
g_ptr_array_add (members, g_strdup (member));
g_ptr_array_sort (members, _srt_indirect_strcmp0);
for (gsize i = 0; i < members->len; i++)
{
member = g_ptr_array_index (members, i);
full_path = g_build_filename (tmp_directory, member, NULL);
driver_path = g_file_read_link (full_path, &error);
if (driver_path == NULL)
{
g_debug ("An error occurred trying to read the symlink: %s", error->message);
g_free (full_path);
goto out;
}
if (!g_path_is_absolute (driver_path))
{
g_free (full_path);
g_free (driver_path);
g_debug ("We were expecting an absolute path, instead we have: %s", driver_path);
goto out;
}
switch (module)
{
case SRT_GRAPHICS_GLX_MODULE:
/* Instead of just using just the library name to filter duplicates, we use it in
* combination with its path. Because in one of the multiple iterations we might
* find the same library that points to two different locations. And in this
* case we want to log both of them.
*
* `member` cannot contain `/`, so we know we can use `/` to make
* a composite key for deduplication. */
soname_path = g_strjoin ("/", member, driver_path, NULL);
if (!g_hash_table_contains (known_table, soname_path))
{
g_hash_table_add (known_table, g_strdup (soname_path));
*modules_out = g_list_prepend (*modules_out, srt_glx_icd_new (member, driver_path));
}
g_free (soname_path);
break;
case SRT_GRAPHICS_VDPAU_MODULE:
driver_directory = g_path_get_dirname (driver_path);
if (!g_hash_table_contains (known_table, driver_directory))
{
/* We do not add `driver_directory` to the hash table because it contains
* a list of directories where we already looked into. In this case we are
* just adding a single driver instead of searching for all the `libvdpau_*`
* files in `driver_directory`. */
driver_link = g_file_read_link (driver_path, NULL);
*modules_out = g_list_prepend (*modules_out, srt_vdpau_driver_new (driver_path,
driver_link,
is_extra));
g_free (driver_link);
}
g_free (driver_directory);
break;
case SRT_GRAPHICS_DRI_MODULE:
case SRT_GRAPHICS_VAAPI_MODULE:
case NUM_SRT_GRAPHICS_MODULES:
default:
g_return_if_reached ();
}
g_free (full_path);
g_free (driver_path);
}
out:
if (dir_iter != NULL)
g_dir_close (dir_iter);
g_clear_pointer (&members, g_ptr_array_unref);
g_free (output);
g_free (stderr);
g_clear_error (&error);
}
/**
* _srt_get_modules_full:
* @sysroot: (nullable): Look in this directory instead of the real root
......@@ -2844,10 +3046,14 @@ _srt_get_modules_full (const char *sysroot,
const gchar *env_override;
const gchar *drivers_path;
const gchar *force_elf_class = NULL;
const gchar *ld_library_path = NULL;
gchar *flatpak_info;
gchar *tmp_dir = NULL;
GHashTable *drivers_set;
gboolean is_extra = FALSE;
int driver_class;
GPtrArray *vdpau_argv = NULL;
GError *error = NULL;
g_return_if_fail (multiarch_tuple != NULL);
g_return_if_fail (drivers_out != NULL);
......@@ -2877,14 +3083,17 @@ _srt_get_modules_full (const char *sysroot,
}
if (envp != NULL)
drivers_path = g_environ_getenv (envp, env_override);
else
drivers_path = g_getenv (env_override);
if (envp != NULL)
force_elf_class = g_environ_getenv (envp, "SRT_TEST_FORCE_ELF");
{
drivers_path = g_environ_getenv (envp, env_override);
force_elf_class = g_environ_getenv (envp, "SRT_TEST_FORCE_ELF");
ld_library_path = g_environ_getenv (envp, "LD_LIBRARY_PATH");
}
else
force_elf_class = g_getenv ("SRT_TEST_FORCE_ELF");
{
drivers_path = g_getenv (env_override);
force_elf_class = g_getenv ("SRT_TEST_FORCE_ELF");
ld_library_path = g_getenv ("LD_LIBRARY_PATH");
}
if (sysroot == NULL)
sysroot = "/";
......@@ -3093,129 +3302,86 @@ _srt_get_modules_full (const char *sysroot,
g_list_free_full (extras, g_free);
}
/* Debian used to hardcode "/usr/lib/vdpau" as an additional search path for VDPAU.
* However since libvdpau 1.3-1 it has been removed; reference:
* <https://salsa.debian.org/nvidia-team/libvdpau/commit/11a3cd84>
* Just to be sure to not miss a potentially valid library path we search on it
* unconditionally, flagging it as extra. */
if (module == SRT_GRAPHICS_VDPAU_MODULE)
{
gchar *debian_additional = g_build_filename (sysroot, "usr", "lib", "vdpau", NULL);
if (!g_hash_table_contains (drivers_set, debian_additional))
{
_srt_get_modules_from_path (envp, helpers_path, multiarch_tuple,
debian_additional, TRUE, module,
drivers_out);
}
g_free (debian_additional);
}
g_hash_table_unref (drivers_set);
g_free (flatpak_info);
}
/*
* _srt_list_glx_icds_from_directory:
* @envp: (array zero-terminated=1): Behave as though `environ` was this array
* @argv: (array zero-terminated=1) (not nullable): The `argv` of the helper to use
* @tmp_directory: (not nullable) (type filename): Full path to the destination
* directory used by the "capsule-capture-libs" helper
* @known_libs: (not optional): set of library names, plus their links, that
* we already found. Newely found libraries will be added to this list
* @glxs: (element-type SrtGlxIcd) (not optional) (inout): Prepend all the unique
* #SrtGlxIcd found to this list in an unspecified order.
*/
static void
_srt_list_glx_icds_from_directory (gchar **envp,
GPtrArray *argv,
const gchar *tmp_directory,
GHashTable *known_libs,
GList **glxs)
{
int exit_status = -1;
GError *error = NULL;
gchar *stderr = NULL;
gchar *output = NULL;
GDir *dir_iter = NULL;
const gchar *member;
gchar *full_path = NULL;
gchar *glx_path = NULL;
gchar *soname_path = NULL;
g_return_if_fail (argv != NULL);
g_return_if_fail (tmp_directory != NULL);
g_return_if_fail (known_libs != NULL);
g_return_if_fail (glxs != NULL);
if (!g_spawn_sync (NULL, /* working directory */
(gchar **) argv->pdata,
envp,
G_SPAWN_SEARCH_PATH, /* flags */
_srt_child_setup_unblock_signals,
NULL, /* user data */
&output, /* stdout */
&stderr,
&exit_status,
&error))
{
g_debug ("An error occurred calling the helper: %s", error->message);
goto out;
}
if (exit_status != 0)
if (module == SRT_GRAPHICS_VDPAU_MODULE)
{
g_debug ("... wait status %d", exit_status);
goto out;
}
/* VDPAU modules are also loaded by just dlopening the bare filename
* libvdpau_${VDPAU_DRIVER}.so
* To cover that we search in all directories listed in LD_LIBRARY_PATH. */
if (ld_library_path != NULL)
{
gchar **entries = g_strsplit (ld_library_path, ":", 0);
gchar **entry;
char *entry_realpath;
dir_iter = g_dir_open (tmp_directory, 0, &error);
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;
if (dir_iter == NULL)
{
g_debug ("Failed to open \"%s\": %s", tmp_directory, error->message);
goto out;
}
entry_realpath = realpath (*entry, NULL);
if (entry_realpath == NULL)
{
g_debug ("realpath(%s): %s", *entry, g_strerror (errno));
continue;
}
if (!g_hash_table_contains (drivers_set, entry_realpath))
{
g_hash_table_add (drivers_set, g_strdup (entry_realpath));
_srt_get_modules_from_path (envp, helpers_path, multiarch_tuple,
entry_realpath, is_extra, module,
drivers_out);
}
free (entry_realpath);
}
g_strfreev (entries);
}
while ((member = g_dir_read_name (dir_iter)) != NULL)
{
full_path = g_build_filename (tmp_directory, member, NULL);
glx_path = g_file_read_link (full_path, &error);
if (glx_path == NULL)
/* Also use "capsule-capture-libs" to search for VDPAU drivers that we might have
* missed */
tmp_dir = g_dir_make_tmp ("vdpau-drivers-XXXXXX", &error);
if (tmp_dir == NULL)
{
g_debug ("An error occurred trying to read the symlink: %s", error->message);
g_free (full_path);
g_debug ("An error occurred trying to create a temporary folder: %s", error->message);
goto out;
}
if (!g_path_is_absolute (glx_path))
vdpau_argv = _argv_for_list_vdpau_drivers (envp, helpers_path, multiarch_tuple, tmp_dir, &error);
if (vdpau_argv == NULL)
{
g_free (full_path);
g_free (glx_path);
g_debug ("We were expecting an absolute path, instead we have: %s", glx_path);
g_debug ("An error occurred trying to capture VDPAU drivers: %s", error->message);
goto out;
}
/* Instead of just using just the library name to filter duplicates, we use it in
* combination with its path. Because in one of the multiple iterations we might
* find the same library that points to two different locations. And in this
* case we want to log both of them.
*
* `member` cannot contain `/`, so we know we can use `/` to make
* a composite key for deduplication. */
soname_path = g_strjoin ("/", member, glx_path, NULL);
if (!g_hash_table_contains (known_libs, soname_path))
_srt_list_modules_from_directory (envp, vdpau_argv, tmp_dir, drivers_set,
SRT_GRAPHICS_VDPAU_MODULE, is_extra, drivers_out);
/* Debian used to hardcode "/usr/lib/vdpau" as an additional search path for VDPAU.
* However since libvdpau 1.3-1 it has been removed; reference:
* <https://salsa.debian.org/nvidia-team/libvdpau/commit/11a3cd84>
* Just to be sure to not miss a potentially valid library path we search on it
* unconditionally, flagging it as extra. */
gchar *debian_additional = g_build_filename (sysroot, "usr", "lib", "vdpau", NULL);
if (!g_hash_table_contains (drivers_set, debian_additional))
{
g_hash_table_add (known_libs, g_strdup (soname_path));
*glxs = g_list_prepend (*glxs, srt_glx_icd_new (member, glx_path));
_srt_get_modules_from_path (envp, helpers_path, multiarch_tuple,
debian_additional, TRUE, module,
drivers_out);
}
g_free (soname_path);
g_free (full_path);
g_free (glx_path);
g_free (debian_additional);
}
out:
if (dir_iter != NULL)
g_dir_close (dir_iter);
g_free (output);
g_free (stderr);
g_clear_pointer (&vdpau_argv, g_ptr_array_unref);
if (tmp_dir)
{
if (!_srt_rm_rf (tmp_dir))
g_debug ("Unable to remove the temporary directory: %s", tmp_dir);
}
g_free (tmp_dir);
g_hash_table_unref (drivers_set);
g_free (flatpak_info);
g_clear_error (&error);
}
......@@ -3267,7 +3433,8 @@ _srt_list_glx_icds (const char *sysroot,
goto out;
}
_srt_list_glx_icds_from_directory (envp, by_soname_argv, by_soname_tmp_dir, known_libs, drivers_out);
_srt_list_modules_from_directory (envp, by_soname_argv, by_soname_tmp_dir, known_libs,
SRT_GRAPHICS_GLX_MODULE, FALSE, drivers_out);
/* When in a container we might miss valid GLX drivers because the `ld.so.cache` in
* use doesn't have a reference about them. To fix that we also include every
......@@ -3290,7 +3457,8 @@ _srt_list_glx_icds (const char *sysroot,
goto out;
}
_srt_list_glx_icds_from_directory (envp, overrides_argv, overrides_tmp_dir, known_libs, drivers_out);
_srt_list_modules_from_directory (envp, overrides_argv, overrides_tmp_dir, known_libs,
SRT_GRAPHICS_GLX_MODULE, FALSE, drivers_out);
}
out:
......
......@@ -24,6 +24,8 @@
libsteamrt_sources = [
'architecture-internal.h',
'architecture.c',
'cpu-feature-internal.h',
'cpu-feature.c',
'desktop-entry-internal.h',
'desktop-entry.c',
'graphics-internal.h',
......@@ -45,6 +47,7 @@ libsteamrt_sources = [
libsteamrt_public_headers = [
'architecture.h',
'cpu-feature.h',
'desktop-entry.h',
'graphics.h',
'library.h',
......
......@@ -28,6 +28,7 @@
#define _SRT_IN_SINGLE_HEADER
#include <steam-runtime-tools/architecture.h>
#include <steam-runtime-tools/cpu-feature.h>
#include <steam-runtime-tools/desktop-entry.h>
#include <steam-runtime-tools/enums.h>
#include <steam-runtime-tools/graphics.h>
......
......@@ -27,6 +27,7 @@
#include "steam-runtime-tools/architecture.h"
#include "steam-runtime-tools/architecture-internal.h"
#include "steam-runtime-tools/cpu-feature-internal.h"
#include "steam-runtime-tools/desktop-entry-internal.h"
#include "steam-runtime-tools/glib-compat.h"
#include "steam-runtime-tools/graphics.h"
......@@ -154,6 +155,11 @@ struct _SrtSystemInfo
GList *values;
gboolean have_data;
} desktop_entry;
struct
{
SrtX86FeatureFlags x86_features;
gboolean have_x86;
} cpu_features;
SrtOsRelease os_release;
SrtTestFlags test_flags;
Tristate can_write_uinput;
......@@ -3052,3 +3058,32 @@ srt_system_info_list_desktop_entries (SrtSystemInfo *self)
return g_list_reverse (ret);
}
static void
ensure_x86_features_cached (SrtSystemInfo *self)
{
if (self->cpu_features.have_x86)
return;
self->cpu_features.x86_features = _srt_feature_get_x86_flags ();
self->cpu_features.have_x86 = TRUE;
}
/**
* srt_system_info_get_x86_features:
* @self: The #SrtSystemInfo object
*
* Detect and return a list of x86 features that the CPU supports.
*
* Returns: x86 CPU supported features, or %SRT_X86_FEATURE_NONE
* if none of the checked features are supported.
*/
SrtX86FeatureFlags
srt_system_info_get_x86_features (SrtSystemInfo *self)
{
g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_X86_FEATURE_NONE);
ensure_x86_features_cached (self);
return self->cpu_features.x86_features;
}
......@@ -32,6 +32,7 @@
#include <glib.h>
#include <glib-object.h>
#include <steam-runtime-tools/cpu-feature.h>
#include <steam-runtime-tools/graphics.h>
#include <steam-runtime-tools/library.h>
#include <steam-runtime-tools/locale.h>
......@@ -197,6 +198,8 @@ gchar **srt_system_info_list_driver_environment (SrtSystemInfo *self);
GList *srt_system_info_list_desktop_entries (SrtSystemInfo *self);
SrtX86FeatureFlags srt_system_info_get_x86_features (SrtSystemInfo *self);
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtSystemInfo, g_object_unref)
#endif
/*
* Copyright © 2019-2020 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-runtime-tools.h>
#include <steam-runtime-tools/glib-compat.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <fcntl.h>
#include <string.h>
#include <sysexits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "test-utils.h"
static const char *argv0;
typedef struct
{
gchar *srcdir;
gchar *builddir;
} Fixture;
typedef struct
{
int unused;
} Config;
static void
setup (Fixture *f,
gconstpointer context)
{
G_GNUC_UNUSED const Config *config = context;
/* For the tests we currently have they are not used yet */
f->srcdir = g_strdup (g_getenv ("G_TEST_SRCDIR"));
f->builddir = g_strdup (g_getenv ("G_TEST_BUILDDIR"));
if (f->srcdir == NULL)
f->srcdir = g_path_get_dirname (argv0);
if (f->builddir == NULL)
f->builddir = g_path_get_dirname (argv0);
}
static void
teardown (Fixture *f,
gconstpointer context)
{
G_GNUC_UNUSED const Config *config = context;
g_free (f->srcdir);
g_free (f->builddir);
}
static void
test_arguments_validation (Fixture *f,
gconstpointer context)
{
gboolean ret;
int exit_status = -1;
GError *error = NULL;
gchar *output = NULL;
gchar *diagnostics = NULL;
const gchar *argv[] = { "steam-runtime-check-requirements", NULL, NULL };
ret = g_spawn_sync (NULL, /* working directory */
(gchar **) argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH,
NULL, /* child setup */
NULL, /* user data */
&output,
&diagnostics,
&exit_status,
&error);
g_assert_no_error (error);
g_assert_true (ret);
/* Do not assume the CI workers hardware. So we expect either a success or
* an EX_OSERR status */
if (exit_status != 0 && WIFEXITED (exit_status))
g_assert_cmpint (WEXITSTATUS (exit_status), ==, EX_OSERR);
else
g_assert_cmpint (exit_status, ==, 0);
g_assert_nonnull (output);
g_assert_true (g_utf8_validate (output, -1, NULL));
if (exit_status != 0)
g_assert_cmpstr (output, !=, "");
g_free (output);
g_free (diagnostics);
argv[1] = "--this-option-is-unsupported";
ret = g_spawn_sync (NULL, /* working directory */
(gchar **) argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH,
NULL, /* child setup */
NULL, /* user data */
&output,
&diagnostics,
&exit_status,
&error);
g_assert_no_error (error);
g_assert_true (ret);
g_assert_true (WIFEXITED (exit_status));
g_assert_cmpint (WEXITSTATUS (exit_status), ==, EX_USAGE);
g_assert_nonnull (output);
g_assert_cmpstr (output, ==, "");
g_assert_true (g_utf8_validate (output, -1, NULL));
g_assert_nonnull (diagnostics);
g_assert_cmpstr (diagnostics, !=, "");
g_assert_true (g_utf8_validate (diagnostics, -1, NULL));
g_free (output);
g_free (diagnostics);
argv[1] = "this-argument-is-unsupported";
ret = g_spawn_sync (NULL, /* working directory */
(gchar **) argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH,
NULL, /* child setup */
NULL, /* user data */
&output,
&diagnostics,
&exit_status,
&error);
g_assert_no_error (error);
g_assert_true (ret);
g_assert_true (WIFEXITED (exit_status));
g_assert_cmpint (WEXITSTATUS (exit_status), ==, EX_USAGE);
g_assert_nonnull (output);
g_assert_cmpstr (output, ==, "");
g_assert_true (g_utf8_validate (output, -1, NULL));
g_assert_nonnull (diagnostics);
g_assert_cmpstr (diagnostics, !=, "");
g_assert_true (g_utf8_validate (diagnostics, -1, NULL));
g_free (output);
g_free (diagnostics);
g_clear_error (&error);
}
/*
* Test `steam-runtime-check-requirements --help` and `--version`.
*/
static void
test_help_and_version (Fixture *f,
gconstpointer context)
{
gboolean ret;
int exit_status = -1;
GError *error = NULL;
gchar *output = NULL;
gchar *diagnostics = NULL;
const gchar *argv[] = {
"env",
"LC_ALL=C",
"steam-runtime-check-requirements",
"--version",
NULL
};
ret = g_spawn_sync (NULL, /* working directory */
(gchar **) argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH,
NULL, /* child setup */
NULL, /* user data */
&output,
&diagnostics,
&exit_status,
&error);
g_assert_no_error (error);
g_assert_true (ret);
g_assert_cmpint (exit_status, ==, 0);
g_assert_nonnull (output);
g_assert_cmpstr (output, !=, "");
g_assert_true (g_utf8_validate (output, -1, NULL));
g_assert_nonnull (diagnostics);
g_assert_nonnull (strstr (output, VERSION));
g_free (output);
g_free (diagnostics);
g_clear_error (&error);
argv[3] = "--help";
ret = g_spawn_sync (NULL, /* working directory */
(gchar **) argv,
NULL, /* envp */
G_SPAWN_SEARCH_PATH,
NULL, /* child setup */
NULL, /* user data */
&output,
&diagnostics,
&exit_status,
&error);
g_assert_no_error (error);
g_assert_true (ret);
g_assert_cmpint (exit_status, ==, 0);
g_assert_nonnull (output);
g_assert_cmpstr (output, !=, "");
g_assert_true (g_utf8_validate (output, -1, NULL));
g_assert_nonnull (diagnostics);
g_assert_nonnull (strstr (output, "OPTIONS"));
g_free (output);
g_free (diagnostics);
g_clear_error (&error);
}
int
main (int argc,
char **argv)
{
argv0 = argv[0];
g_test_init (&argc, &argv, NULL);
g_test_add ("/check-requirements-cli/arguments_validation", Fixture, NULL,
setup, test_arguments_validation, teardown);
g_test_add ("/check-requirements-cli/help-and-version", Fixture, NULL,
setup, test_help_and_version, teardown);
return g_test_run ();
}