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

Merge branch 'wip/denittis/t20491' into 'master'

Add check-requirements preflight check

See merge request steam/steam-runtime-tools!122
parents 6886bdf5 ff39939f
No related branches found
No related tags found
1 merge request!122Add check-requirements preflight check
Pipeline #2967 passed
/*
* 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:
......@@ -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.
......@@ -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
/*
* 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 ();
}
......@@ -29,6 +29,7 @@ test_env.prepend('PATH', join_paths(meson.current_build_dir(), '..', 'bin'))
tests = [
'architecture',
'check-requirements-cli',
'desktop-entry',
'graphics',
'library',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment