diff --git a/debian/control b/debian/control index 73f78ac811c0a5155a39093ccb46a53203e2439a..a6d229ae3b742047e751baf568270430b8edb829 100644 --- a/debian/control +++ b/debian/control @@ -86,6 +86,7 @@ Section: misc Depends: ${misc:Depends}, ${shlibs:Depends}, + zlib1g, Description: The Steam Runtime is the library stack used to run the Steam client on Linux. The Steam Runtime Tools utility library contains open-source diff --git a/steam-runtime-tools/architecture-internal.h b/steam-runtime-tools/architecture-internal.h new file mode 100644 index 0000000000000000000000000000000000000000..06825aad7859341b3c85105ecb545f9bb364090e --- /dev/null +++ b/steam-runtime-tools/architecture-internal.h @@ -0,0 +1,31 @@ +/*<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 <glib.h> + +G_GNUC_INTERNAL gboolean _srt_architecture_can_run (const char *multiarch); diff --git a/steam-runtime-tools/architecture.c b/steam-runtime-tools/architecture.c index e8a5f4390f34524bb440d39ec03c756440fda93c..44616d4d4c646a55aee1ca47dd51b7782d0454db 100644 --- a/steam-runtime-tools/architecture.c +++ b/steam-runtime-tools/architecture.c @@ -24,6 +24,7 @@ */ #include "steam-runtime-tools/architecture.h" +#include "steam-runtime-tools/architecture-internal.h" #include "steam-runtime-tools/utils-internal.h" @@ -38,7 +39,7 @@ * operating system. */ -static gboolean +gboolean _srt_architecture_can_run (const char *multiarch) { gchar *helper = NULL; diff --git a/steam-runtime-tools/glib-compat.h b/steam-runtime-tools/glib-compat.h index e41c71b4ae80518f1549ab7e7de31958d5fe7c33..51e3b08825a2d8ce3ac23c54b77c9a2daddf298f 100644 --- a/steam-runtime-tools/glib-compat.h +++ b/steam-runtime-tools/glib-compat.h @@ -42,7 +42,8 @@ _srt_steal_pointer (gpointer pointer_to_pointer) #endif #if !GLIB_CHECK_VERSION(2, 34, 0) -#define g_clear_pointer(x, destroy) _srt_clear_pointer (x, destroy) +#define g_clear_pointer(x, destroy) \ + _srt_clear_pointer (x, (GDestroyNotify) (void (*)(void)) destroy) /* A simplified version of g_clear_pointer without type-safety. */ static inline void _srt_clear_pointer (gpointer pointer_to_pointer, diff --git a/steam-runtime-tools/meson.build b/steam-runtime-tools/meson.build index 7431fef406310bce5c1c2d0a9c53797cc537c2bc..1211aebc96a0134578611dec3caa341dfba1d525 100644 --- a/steam-runtime-tools/meson.build +++ b/steam-runtime-tools/meson.build @@ -22,9 +22,11 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libsteamrt_sources = [ + 'architecture-internal.h', 'architecture.c', 'library-internal.h', 'library.c', + 'system-info.c', 'utils-internal.h', 'utils.c', ] @@ -33,6 +35,7 @@ libsteamrt_public_headers = [ 'architecture.h', 'library.h', 'steam-runtime-tools.h', + 'system-info.h', ] enums = gnome.mkenums_simple( diff --git a/steam-runtime-tools/steam-runtime-tools.h b/steam-runtime-tools/steam-runtime-tools.h index 844d550df34944668e29e68018d91f6e5d098b01..e7313d07bcf9f72676c70466ed54bb8367b2e334 100644 --- a/steam-runtime-tools/steam-runtime-tools.h +++ b/steam-runtime-tools/steam-runtime-tools.h @@ -30,5 +30,6 @@ #include <steam-runtime-tools/architecture.h> #include <steam-runtime-tools/enums.h> #include <steam-runtime-tools/library.h> +#include <steam-runtime-tools/system-info.h> #undef _SRT_IN_SINGLE_HEADER diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c new file mode 100644 index 0000000000000000000000000000000000000000..4a6ae3f062b5b1169073d91f8fb72f91ea3dd45c --- /dev/null +++ b/steam-runtime-tools/system-info.c @@ -0,0 +1,262 @@ +/* + * 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/system-info.h" + +#include "steam-runtime-tools/architecture.h" +#include "steam-runtime-tools/architecture-internal.h" +#include "steam-runtime-tools/glib-compat.h" +#include "steam-runtime-tools/utils-internal.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +/** + * SECTION:system-info + * @title: System information + * @short_description: Cached information about the system + * @include: steam-runtime-tools/steam-runtime-tools.h + * + * #SrtSystemInfo is an opaque object representing information about + * the system. Information is retrieved "lazily"; when it has been + * retrieved, it is cached until the #SrtSystemInfo is destroyed. + * + * This is a reference-counted object: use g_object_ref() and + * g_object_unref() to manage its lifecycle. + */ + +typedef enum +{ + TRI_NO = FALSE, + TRI_YES = TRUE, + TRI_MAYBE = -1 +} Tristate; + +struct _SrtSystemInfo +{ + /*< private >*/ + GObject parent; + gchar *expectations; + Tristate can_write_uinput; + /* (element-type Abi) */ + GPtrArray *abis; +}; + +struct _SrtSystemInfoClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +enum { + PROP_0, + PROP_EXPECTATIONS, + N_PROPERTIES +}; + +G_DEFINE_TYPE (SrtSystemInfo, srt_system_info, G_TYPE_OBJECT) + +typedef struct +{ + GQuark multiarch_tuple; + Tristate can_run; +} Abi; + +static Abi * +ensure_abi (SrtSystemInfo *self, + const char *multiarch_tuple) +{ + GQuark quark; + guint i; + Abi *abi = NULL; + + quark = g_quark_from_string (multiarch_tuple); + + for (i = 0; i < self->abis->len; i++) + { + abi = g_ptr_array_index (self->abis, i); + + if (abi->multiarch_tuple == quark) + return abi; + } + + abi = g_slice_new0 (Abi); + abi->multiarch_tuple = quark; + abi->can_run = TRI_MAYBE; + /* transfer ownership to self->abis */ + g_ptr_array_add (self->abis, abi); + return abi; +} + +static void +abi_free (gpointer self) +{ + g_slice_free (Abi, self); +} + +static void +srt_system_info_init (SrtSystemInfo *self) +{ + self->can_write_uinput = TRI_MAYBE; + + /* Assume that in practice we will usually add two ABIs: amd64 and i386 */ + self->abis = g_ptr_array_new_full (2, abi_free); +} + +static void +srt_system_info_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SrtSystemInfo *self = SRT_SYSTEM_INFO (object); + + switch (prop_id) + { + case PROP_EXPECTATIONS: + g_value_set_string (value, self->expectations); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +srt_system_info_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SrtSystemInfo *self = SRT_SYSTEM_INFO (object); + + switch (prop_id) + { + case PROP_EXPECTATIONS: + /* Construct-only */ + g_return_if_fail (self->expectations == NULL); + self->expectations = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +srt_system_info_finalize (GObject *object) +{ + SrtSystemInfo *self = SRT_SYSTEM_INFO (object); + + g_clear_pointer (&self->abis, g_ptr_array_unref); + g_free (self->expectations); + + G_OBJECT_CLASS (srt_system_info_parent_class)->finalize (object); +} + +static GParamSpec *properties[N_PROPERTIES] = { NULL }; + +static void +srt_system_info_class_init (SrtSystemInfoClass *cls) +{ + GObjectClass *object_class = G_OBJECT_CLASS (cls); + + object_class->get_property = srt_system_info_get_property; + object_class->set_property = srt_system_info_set_property; + object_class->finalize = srt_system_info_finalize; + + properties[PROP_EXPECTATIONS] = + g_param_spec_string ("expectations", "Expectations", + "Path to a directory containing information " + "about the properties we expect the system " + "to have, or NULL if unknown", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPERTIES, properties); +} + +SrtSystemInfo * +srt_system_info_new (const char *expectations) +{ + g_return_val_if_fail ((expectations == NULL || + g_file_test (expectations, G_FILE_TEST_IS_DIR)), + NULL); + return g_object_new (SRT_TYPE_SYSTEM_INFO, + "expectations", expectations, + NULL); +} + +gboolean +srt_system_info_can_run (SrtSystemInfo *self, + const char *multiarch_tuple) +{ + Abi *abi = NULL; + + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), FALSE); + g_return_val_if_fail (multiarch_tuple != NULL, FALSE); + + abi = ensure_abi (self, multiarch_tuple); + + if (abi->can_run == TRI_MAYBE) + { + if (_srt_architecture_can_run (multiarch_tuple)) + abi->can_run = TRI_YES; + else + abi->can_run = TRI_NO; + } + + return (abi->can_run == TRI_YES); +} + +gboolean +srt_system_info_can_write_to_uinput (SrtSystemInfo *self) +{ + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), FALSE); + + if (self->can_write_uinput == TRI_MAYBE) + { + int fd = open ("/dev/uinput", O_WRONLY | O_NONBLOCK); + + if (fd >= 0) + { + g_debug ("Successfully opened /dev/uinput for writing"); + self->can_write_uinput = TRI_YES; + close (fd); + } + else + { + g_debug ("Failed to open /dev/uinput for writing: %s", + g_strerror (errno)); + self->can_write_uinput = TRI_NO; + } + } + + return (self->can_write_uinput == TRI_YES); +} diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h new file mode 100644 index 0000000000000000000000000000000000000000..cc1f72b29c6772843558492c49383b0171e9e3ee --- /dev/null +++ b/steam-runtime-tools/system-info.h @@ -0,0 +1,53 @@ +/* + * 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 + +#include <glib.h> +#include <glib-object.h> + +#include <steam-runtime-tools/library.h> + +typedef struct _SrtSystemInfo SrtSystemInfo; +typedef struct _SrtSystemInfoClass SrtSystemInfoClass; + +#define SRT_TYPE_SYSTEM_INFO srt_system_info_get_type () +#define SRT_SYSTEM_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SRT_TYPE_SYSTEM_INFO, SrtSystemInfo)) +#define SRT_SYSTEM_INFO_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), SRT_TYPE_SYSTEM_INFO, SrtSystemInfoClass)) +#define SRT_IS_SYSTEM_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SRT_TYPE_SYSTEM_INFO)) +#define SRT_IS_SYSTEM_INFO_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE ((cls), SRT_TYPE_SYSTEM_INFO)) +#define SRT_SYSTEM_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SRT_TYPE_SYSTEM_INFO, SrtSystemInfoClass) + +GType srt_system_info_get_type (void); + +SrtSystemInfo *srt_system_info_new (const char *expectations); + +gboolean srt_system_info_can_run (SrtSystemInfo *self, + const char *multiarch_tuple); +gboolean srt_system_info_can_write_to_uinput (SrtSystemInfo *self); diff --git a/tests/expectations/i386-linux-gnu/zlib1g.symbols b/tests/expectations/i386-linux-gnu/zlib1g.symbols new file mode 100644 index 0000000000000000000000000000000000000000..63be790e3612555c3aae70b905be7b349964ee34 --- /dev/null +++ b/tests/expectations/i386-linux-gnu/zlib1g.symbols @@ -0,0 +1,4 @@ +# Cut-down version of zlib1g:amd64.symbols, to illustrate what we expect +# to find here +libz.so.1 zlib1g #MINVER# + adler32@Base 1:1.1.4 diff --git a/tests/expectations/x86_64-linux-gnu/zlib1g.symbols b/tests/expectations/x86_64-linux-gnu/zlib1g.symbols new file mode 100644 index 0000000000000000000000000000000000000000..63be790e3612555c3aae70b905be7b349964ee34 --- /dev/null +++ b/tests/expectations/x86_64-linux-gnu/zlib1g.symbols @@ -0,0 +1,4 @@ +# Cut-down version of zlib1g:amd64.symbols, to illustrate what we expect +# to find here +libz.so.1 zlib1g #MINVER# + adler32@Base 1:1.1.4 diff --git a/tests/meson.build b/tests/meson.build index 9f92740023fb106046b7045ccaa72b203f531023..d763b615b85c18a1893750a135745a06d822e7ec 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -29,6 +29,7 @@ test_env.set('SRT_HELPERS_PATH', join_paths(meson.current_build_dir(), '..', 'he tests = [ 'architecture', 'library', + 'system-info', ] tests_dir = join_paths( @@ -42,6 +43,8 @@ tests_metadir = join_paths( meson.project_name() + '-' + api_major, ) +install_subdir('expectations', install_dir : tests_dir) + foreach test_name : tests exe = executable( 'test-' + test_name, diff --git a/tests/system-info.c b/tests/system-info.c new file mode 100644 index 0000000000000000000000000000000000000000..451253c5889500ccc4c662525d4c0baca2333bb4 --- /dev/null +++ b/tests/system-info.c @@ -0,0 +1,151 @@ +/* + * 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-runtime-tools.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.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; + + 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); +} + +/* + * Test basic functionality of the SrtSystemInfo object. + */ +static void +test_object (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar *expectations_in = NULL; + gchar *expectations = NULL; + int fd; + + info = srt_system_info_new (NULL); + g_assert_nonnull (info); + g_object_get (info, + "expectations", &expectations, + NULL); + g_assert_cmpstr (expectations, ==, NULL); + + /* We try it twice, to exercise the cached and non-cached cases */ +#if defined(__x86_64__) && defined(__LP64__) + g_assert_true (srt_system_info_can_run (info, SRT_ABI_X86_64)); + g_assert_true (srt_system_info_can_run (info, SRT_ABI_X86_64)); +#endif + +#if defined(__i386__) + g_assert_true (srt_system_info_can_run (info, SRT_ABI_I386)); + g_assert_true (srt_system_info_can_run (info, SRT_ABI_I386)); +#endif + + g_assert_false (srt_system_info_can_run (info, "hal9000-linux-gnu")); + g_assert_false (srt_system_info_can_run (info, "hal9000-linux-gnu")); + + /* This is a little bit tautologous - we're using the same check + * that the production code does. */ + fd = open ("/dev/uinput", O_WRONLY | O_NONBLOCK); + + if (fd >= 0) + { + g_assert_true (srt_system_info_can_write_to_uinput (info)); + g_assert_true (srt_system_info_can_write_to_uinput (info)); + close (fd); + } + else + { + g_assert_false (srt_system_info_can_write_to_uinput (info)); + g_assert_false (srt_system_info_can_write_to_uinput (info)); + } + + g_object_unref (info); + + expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + info = srt_system_info_new (expectations_in); + g_assert_nonnull (info); + g_object_get (info, + "expectations", &expectations, + NULL); + g_assert_cmpstr (expectations, ==, expectations_in); + g_free (expectations_in); + g_free (expectations); + g_object_unref (info); +} + +int +main (int argc, + char **argv) +{ + argv0 = argv[0]; + + g_test_init (&argc, &argv, NULL); + g_test_add ("/system-info/object", Fixture, NULL, + setup, test_object, teardown); + + return g_test_run (); +}