From 06f27e651c748f4a05b2eb73dca8c7451be77d18 Mon Sep 17 00:00:00 2001 From: Ludovico de Nittis <ludovico.denittis@collabora.com> Date: Fri, 2 Aug 2019 14:26:48 +0200 Subject: [PATCH] Add libraries check to SrtSystemInfo This provides a high-level API to wrap SrtLibrary and check if the current system has the expected libraries and symbols. Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com> --- steam-runtime-tools/library.c | 7 +- steam-runtime-tools/library.h | 3 + steam-runtime-tools/system-info.c | 287 ++++++++++ steam-runtime-tools/system-info.h | 7 + .../i386-linux-gnu/libglib2.0-0.symbols | 11 + .../x86_64-linux-gnu/libglib2.0-0.symbols | 11 + .../i386-linux-gnu/libglib2.0-0.symbols | 9 + .../i386-linux-gnu/zlib1g.symbols | 7 + .../x86_64-linux-gnu/libglib2.0-0.symbols | 9 + .../x86_64-linux-gnu/zlib1g.symbols | 7 + tests/meson.build | 1 + tests/system-info.c | 509 ++++++++++++++++++ 12 files changed, 865 insertions(+), 3 deletions(-) create mode 100644 tests/expectations/i386-linux-gnu/libglib2.0-0.symbols create mode 100644 tests/expectations/x86_64-linux-gnu/libglib2.0-0.symbols create mode 100644 tests/expectations_with_missings/i386-linux-gnu/libglib2.0-0.symbols create mode 100644 tests/expectations_with_missings/i386-linux-gnu/zlib1g.symbols create mode 100644 tests/expectations_with_missings/x86_64-linux-gnu/libglib2.0-0.symbols create mode 100644 tests/expectations_with_missings/x86_64-linux-gnu/zlib1g.symbols diff --git a/steam-runtime-tools/library.c b/steam-runtime-tools/library.c index d0b39fc8d..4294f32b0 100644 --- a/steam-runtime-tools/library.c +++ b/steam-runtime-tools/library.c @@ -449,9 +449,10 @@ srt_check_library_presence (const char *soname, GStrv dependencies = NULL; SrtLibraryIssues issues = SRT_LIBRARY_ISSUES_NONE; - g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_CANNOT_LOAD); - g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_CANNOT_LOAD); - g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, SRT_LIBRARY_ISSUES_CANNOT_LOAD); + g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, + SRT_LIBRARY_ISSUES_INTERNAL_ERROR); switch (symbols_format) { diff --git a/steam-runtime-tools/library.h b/steam-runtime-tools/library.h index 4a3db271f..a5608ca93 100644 --- a/steam-runtime-tools/library.h +++ b/steam-runtime-tools/library.h @@ -52,6 +52,8 @@ GType srt_library_get_type (void); * were not present * @SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS: Some of the expected symbols * were available with a different version + * @SRT_LIBRARY_ISSUES_INTERNAL_ERROR: Generic internal error, for example + * a function has been called with a missing required parameter * * A bitfield with flags representing problems with a library, or * %SRT_LIBRARY_ISSUES_NONE (which is numerically zero) if no problems @@ -64,6 +66,7 @@ typedef enum SRT_LIBRARY_ISSUES_CANNOT_LOAD = (1 << 0), SRT_LIBRARY_ISSUES_MISSING_SYMBOLS = (1 << 1), SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS = (1 << 2), + SRT_LIBRARY_ISSUES_INTERNAL_ERROR = (1 << 3), SRT_LIBRARY_ISSUES_NONE = 0 } SrtLibraryIssues; diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c index 4a6ae3f06..f3174bdb7 100644 --- a/steam-runtime-tools/system-info.c +++ b/steam-runtime-tools/system-info.c @@ -32,6 +32,7 @@ #include <errno.h> #include <fcntl.h> +#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -85,6 +86,9 @@ typedef struct { GQuark multiarch_tuple; Tristate can_run; + GHashTable *cached_results; + SrtLibraryIssues cached_combined_issues; + gboolean libraries_cache_available; } Abi; static Abi * @@ -108,6 +112,9 @@ ensure_abi (SrtSystemInfo *self, abi = g_slice_new0 (Abi); abi->multiarch_tuple = quark; abi->can_run = TRI_MAYBE; + abi->cached_results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + abi->cached_combined_issues = SRT_LIBRARY_ISSUES_NONE; + abi->libraries_cache_available = FALSE; /* transfer ownership to self->abis */ g_ptr_array_add (self->abis, abi); return abi; @@ -116,6 +123,10 @@ ensure_abi (SrtSystemInfo *self, static void abi_free (gpointer self) { + Abi *abi = self; + if (abi->cached_results != NULL) + g_hash_table_unref (abi->cached_results); + g_slice_free (Abi, self); } @@ -260,3 +271,279 @@ srt_system_info_can_write_to_uinput (SrtSystemInfo *self) return (self->can_write_uinput == TRI_YES); } + +static gint +library_compare (SrtLibrary *a, SrtLibrary *b) +{ + return g_strcmp0 (srt_library_get_soname (a), srt_library_get_soname (b)); +} + +/** + * srt_system_info_check_libraries: + * @self: The #SrtSystemInfo object to use. + * @multiarch_tuple: A multiarch tuple like %SRT_ABI_I386, representing an ABI. + * @libraries_out: (out) (optional) (element-type SrtLibrary) (transfer full): + * Used to return a #GList object where every element of said list is an + * #SrtLibrary object, representing every `SONAME` found from the expectations + * folder. Free with `g_list_free_full(libraries, g_object_unref)`. + * + * Check if the running system has all the expected libraries, and related symbols, + * as listed in the `deb-symbols(5)` files `*.symbols` in the @multiarch + * subdirectory of #SrtSystemInfo:expectations. + * + * Returns: A bitfield containing problems, or %SRT_LIBRARY_ISSUES_NONE + * if no problems were found. + */ +SrtLibraryIssues +srt_system_info_check_libraries (SrtSystemInfo *self, + const gchar *multiarch_tuple, + GList **libraries_out) +{ + Abi *abi = NULL; + gchar *dir_path = NULL; + const gchar *filename = NULL; + gchar *symbols_file = NULL; + size_t len = 0; + ssize_t chars; + GDir *dir = NULL; + FILE *fp = NULL; + GError *error = NULL; + SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_INTERNAL_ERROR; + + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (libraries_out == NULL || *libraries_out == NULL, + SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + + abi = ensure_abi (self, multiarch_tuple); + + /* If we cached already the result, we return it */ + if (abi->libraries_cache_available) + { + if (libraries_out != NULL) + { + *libraries_out = g_list_sort (g_hash_table_get_values (abi->cached_results), + (GCompareFunc) library_compare); + g_list_foreach (*libraries_out, (GFunc) G_CALLBACK (g_object_ref), NULL); + } + + return abi->cached_combined_issues; + } + + dir_path = g_build_filename (self->expectations, multiarch_tuple, NULL); + dir = g_dir_open (dir_path, 0, &error); + if (error) + { + g_debug ("An error occurred while opening the symbols directory: %s", error->message); + g_clear_error (&error); + goto out; + } + while ((filename = g_dir_read_name (dir))) + { + char *line = NULL; + + if (!g_str_has_suffix (filename, ".symbols")) + continue; + + symbols_file = g_build_filename (dir_path, filename, NULL); + fp = fopen(symbols_file, "r"); + + if (fp == NULL) + { + int saved_errno = errno; + g_debug ("Error reading \"%s\": %s\n", symbols_file, strerror (saved_errno)); + goto out; + } + + while ((chars = getline(&line, &len, fp)) != -1) + { + char *pointer_into_line = line; + if (line[chars - 1] == '\n') + line[chars - 1] = '\0'; + + if (line[0] == '\0') + continue; + + if (line[0] != '#' && line[0] != '*' && line[0] != '|' && line[0] != ' ') + { + /* This line introduces a new SONAME. We extract it and call + * `srt_check_library_presence` with the symbols file where we + * found it, as an argument. */ + SrtLibrary *library = NULL; + char *soname = g_strdup (strsep (&pointer_into_line, " \t")); + abi->cached_combined_issues |= srt_check_library_presence (soname, + multiarch_tuple, + symbols_file, + SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS, + &library); + g_hash_table_insert (abi->cached_results, soname, library); + } + } + free (line); + g_clear_pointer (&symbols_file, g_free); + g_clear_pointer (&fp, fclose); + } + + abi->libraries_cache_available = TRUE; + if (libraries_out != NULL) + { + *libraries_out = g_list_sort (g_hash_table_get_values (abi->cached_results), + (GCompareFunc) library_compare); + g_list_foreach (*libraries_out, (GFunc) G_CALLBACK (g_object_ref), NULL); + } + + ret = abi->cached_combined_issues; + + out: + g_clear_pointer (&symbols_file, g_free); + g_clear_pointer (&dir_path, g_free); + + if (fp != NULL) + g_clear_pointer (&fp, fclose); + + if (dir != NULL) + g_dir_close (dir); + + return ret; + +} + +/** + * srt_system_info_check_library: + * @self: The #SrtSystemInfo object to use. + * @multiarch_tuple: A multiarch tuple like %SRT_ABI_I386, representing an ABI. + * @soname: (type filename): The `SONAME` of a shared library, for example `libjpeg.so.62`. + * @more_details_out: (out) (optional) (transfer full): Used to return an + * #SrtLibrary object representing the shared library provided by @soname. + * Free with `g_object_unref()`. + * + * Check if @soname is available in the running system and whether it conforms + * to the `deb-symbols(5)` files `*.symbols` in the @multiarch + * subdirectory of #SrtSystemInfo:expectations. + * + * Returns: A bitfield containing problems, or %SRT_LIBRARY_ISSUES_NONE + * if no problems were found. + */ +SrtLibraryIssues +srt_system_info_check_library (SrtSystemInfo *self, + const gchar *multiarch_tuple, + const gchar *soname, + SrtLibrary **more_details_out) +{ + Abi *abi = NULL; + SrtLibrary *library = NULL; + const gchar *filename = NULL; + gchar *symbols_file = NULL; + gchar *dir_path = NULL; + gchar *line = NULL; + size_t len = 0; + ssize_t chars; + FILE *fp = NULL; + SrtLibraryIssues issues; + GDir *dir; + GError *error = NULL; + SrtLibraryIssues ret = SRT_LIBRARY_ISSUES_INTERNAL_ERROR; + + g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (multiarch_tuple != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + g_return_val_if_fail (more_details_out == NULL || *more_details_out == NULL, + SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + + abi = ensure_abi (self, multiarch_tuple); + + /* If we have the result already in cache, we return it */ + library = g_hash_table_lookup (abi->cached_results, soname); + if (library != NULL) + { + if (more_details_out != NULL) + *more_details_out = g_object_ref (library); + return srt_library_get_issues (library); + } + + + dir_path = g_build_filename (self->expectations, multiarch_tuple, NULL); + dir = g_dir_open (dir_path, 0, &error); + if (error) + { + g_debug ("An error occurred while opening the symbols directory: %s", error->message); + g_clear_error (&error); + goto out; + } + while ((filename = g_dir_read_name (dir))) + { + if (!g_str_has_suffix (filename, ".symbols")) + continue; + + symbols_file = g_build_filename (dir_path, filename, NULL); + fp = fopen(symbols_file, "r"); + + if (fp == NULL) + { + int saved_errno = errno; + g_debug ("Error reading \"%s\": %s\n", symbols_file, strerror (saved_errno)); + goto out; + } + + while ((chars = getline(&line, &len, fp)) != -1) + { + char *pointer_into_line = line; + if (line[chars - 1] == '\n') + line[chars - 1] = '\0'; + + if (line[0] == '\0') + continue; + + if (line[0] != '#' && line[0] != '*' && line[0] != '|' && line[0] != ' ') + { + /* This line introduces a new SONAME, which might + * be the one we are interested in. */ + char *soname_found = g_strdup (strsep (&pointer_into_line, " \t")); + if (g_strcmp0 (soname_found, soname) == 0) + { + issues = srt_check_library_presence (soname_found, + multiarch_tuple, + symbols_file, + SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS, + &library); + g_hash_table_insert (abi->cached_results, soname_found, library); + abi->cached_combined_issues |= issues; + if (more_details_out != NULL) + *more_details_out = g_object_ref (library); + free (line); + ret = issues; + goto out; + } + free (soname_found); + } + } + g_clear_pointer (&symbols_file, g_free); + g_clear_pointer (&line, g_free); + g_clear_pointer (&fp, fclose); + } + /* The SONAME's symbols file is not available. + * We do instead a simple absence/presence check. */ + issues = srt_check_library_presence (soname, + multiarch_tuple, + NULL, + SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS, + &library); + g_hash_table_insert (abi->cached_results, g_strdup (soname), library); + abi->cached_combined_issues |= issues; + if (more_details_out != NULL) + *more_details_out = g_object_ref (library); + + ret = issues; + + out: + g_clear_pointer (&symbols_file, g_free); + g_clear_pointer (&dir_path, g_free); + + if (fp != NULL) + g_clear_pointer (&fp, fclose); + + if (dir != NULL) + g_dir_close (dir); + + return ret; +} diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h index cc1f72b29..6213b2d14 100644 --- a/steam-runtime-tools/system-info.h +++ b/steam-runtime-tools/system-info.h @@ -51,3 +51,10 @@ 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); +SrtLibraryIssues srt_system_info_check_libraries (SrtSystemInfo *self, + const gchar *multiarch_tuple, + GList **libraries_out); +SrtLibraryIssues srt_system_info_check_library (SrtSystemInfo *self, + const gchar *multiarch_tuple, + const gchar *soname, + SrtLibrary **more_details_out); diff --git a/tests/expectations/i386-linux-gnu/libglib2.0-0.symbols b/tests/expectations/i386-linux-gnu/libglib2.0-0.symbols new file mode 100644 index 000000000..17fc854ac --- /dev/null +++ b/tests/expectations/i386-linux-gnu/libglib2.0-0.symbols @@ -0,0 +1,11 @@ +# Cut-down version of libglib2.0-0:amd64.symbols, to illustrate what we expect +# to find here +libgio-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_action_activate@Base 2.28.0 + g_action_change_state@Base 2.30.0 +libglib-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_access@Base 2.12.0 + g_allocator_free@Base 2.12.0 + g_allocator_new@Base 2.12.0 diff --git a/tests/expectations/x86_64-linux-gnu/libglib2.0-0.symbols b/tests/expectations/x86_64-linux-gnu/libglib2.0-0.symbols new file mode 100644 index 000000000..17fc854ac --- /dev/null +++ b/tests/expectations/x86_64-linux-gnu/libglib2.0-0.symbols @@ -0,0 +1,11 @@ +# Cut-down version of libglib2.0-0:amd64.symbols, to illustrate what we expect +# to find here +libgio-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_action_activate@Base 2.28.0 + g_action_change_state@Base 2.30.0 +libglib-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_access@Base 2.12.0 + g_allocator_free@Base 2.12.0 + g_allocator_new@Base 2.12.0 diff --git a/tests/expectations_with_missings/i386-linux-gnu/libglib2.0-0.symbols b/tests/expectations_with_missings/i386-linux-gnu/libglib2.0-0.symbols new file mode 100644 index 000000000..9d92f3bf0 --- /dev/null +++ b/tests/expectations_with_missings/i386-linux-gnu/libglib2.0-0.symbols @@ -0,0 +1,9 @@ +# Cut-down version of libglib2.0-0:amd64.symbols. +# Also libgio-2.0.so.0 has been renamed to libgio-MISSING-2.0.so.0 +# In this way we can simulate a missing library. +libgio-MISSING-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_action_activate@Base 2.28.0 +libglib-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_access@Base 2.12.0 diff --git a/tests/expectations_with_missings/i386-linux-gnu/zlib1g.symbols b/tests/expectations_with_missings/i386-linux-gnu/zlib1g.symbols new file mode 100644 index 000000000..6ceb33235 --- /dev/null +++ b/tests/expectations_with_missings/i386-linux-gnu/zlib1g.symbols @@ -0,0 +1,7 @@ +# Cut-down version of zlib1g:amd64.symbols. +# It has been appended also a missing symbol `missing@NotAvailable` +# In this way we can simulate an available library with a missing symbol +libz.so.1 zlib1g #MINVER# + adler32@Base 1:1.1.4 + crc32@WRONG_VERSION 1:1.1.4 + missing@NotAvailable 1:1.1.4 diff --git a/tests/expectations_with_missings/x86_64-linux-gnu/libglib2.0-0.symbols b/tests/expectations_with_missings/x86_64-linux-gnu/libglib2.0-0.symbols new file mode 100644 index 000000000..9d92f3bf0 --- /dev/null +++ b/tests/expectations_with_missings/x86_64-linux-gnu/libglib2.0-0.symbols @@ -0,0 +1,9 @@ +# Cut-down version of libglib2.0-0:amd64.symbols. +# Also libgio-2.0.so.0 has been renamed to libgio-MISSING-2.0.so.0 +# In this way we can simulate a missing library. +libgio-MISSING-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_action_activate@Base 2.28.0 +libglib-2.0.so.0 libglib2.0-0 #MINVER# +* Build-Depends-Package: libglib2.0-dev + g_access@Base 2.12.0 diff --git a/tests/expectations_with_missings/x86_64-linux-gnu/zlib1g.symbols b/tests/expectations_with_missings/x86_64-linux-gnu/zlib1g.symbols new file mode 100644 index 000000000..6ceb33235 --- /dev/null +++ b/tests/expectations_with_missings/x86_64-linux-gnu/zlib1g.symbols @@ -0,0 +1,7 @@ +# Cut-down version of zlib1g:amd64.symbols. +# It has been appended also a missing symbol `missing@NotAvailable` +# In this way we can simulate an available library with a missing symbol +libz.so.1 zlib1g #MINVER# + adler32@Base 1:1.1.4 + crc32@WRONG_VERSION 1:1.1.4 + missing@NotAvailable 1:1.1.4 diff --git a/tests/meson.build b/tests/meson.build index d763b615b..5ff5cf52e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -44,6 +44,7 @@ tests_metadir = join_paths( ) install_subdir('expectations', install_dir : tests_dir) +install_subdir('expectations_with_missings', install_dir : tests_dir) foreach test_name : tests exe = executable( diff --git a/tests/system-info.c b/tests/system-info.c index 451253c58..a86d43447 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -25,6 +25,8 @@ #include <steam-runtime-tools/steam-runtime-tools.h> +#include <steam-runtime-tools/glib-compat.h> + #include <glib.h> #include <glib/gstdio.h> @@ -137,6 +139,503 @@ test_object (Fixture *f, g_object_unref (info); } +static void +check_libraries_result (GList *libraries) +{ + SrtLibrary *library = NULL; + const char * const *missing_symbols; + const char * const *misversioned_symbols; + const char * const *dependencies; + gboolean seen_libc; + GList *l; + + g_assert_nonnull (libraries); + l = libraries; + + /* Test first library. Alphabetical order is an API guarantee, so we know + * which one it should be. */ + library = l->data; + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libgio-2.0.so.0"); + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_NONE); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + seen_libc = FALSE; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + g_debug ("libgio-2.0.so.0 depends on %s", dependencies[i]); + + if (strstr (dependencies[i], "/libc.so.") != NULL) + seen_libc = TRUE; + } + + g_assert_true (seen_libc); + + /* Test second library */ + l = g_list_next (l); + library = l->data; + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libglib-2.0.so.0"); + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_NONE); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + seen_libc = FALSE; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + g_debug ("libglib-2.0.so.0 depends on %s", dependencies[i]); + + if (strstr (dependencies[i], "/libc.so.") != NULL) + seen_libc = TRUE; + } + + g_assert_true (seen_libc); + + /* Test last library */ + l = g_list_next (l); + library = l->data; + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libz.so.1"); + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_NONE); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + seen_libc = FALSE; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + g_debug ("libz.so.1 depends on %s", dependencies[i]); + + if (strstr (dependencies[i], "/libc.so.") != NULL) + seen_libc = TRUE; + } + + g_assert_true (seen_libc); + g_assert_null (g_list_next (l)); +} + +/* + * Test if the expected libraries are available in the + * running system. + */ +static void +libraries_presence (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar *expectations_in = NULL; + GList *libraries = NULL; + SrtLibraryIssues issues; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + info = srt_system_info_new (expectations_in); + issues = srt_system_info_check_libraries (info, + _SRT_MULTIARCH, + &libraries); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); + check_libraries_result (libraries); + g_list_free_full (libraries, g_object_unref); + libraries = NULL; + + /* Do the check again, this time using the cache */ + issues = srt_system_info_check_libraries (info, + _SRT_MULTIARCH, + &libraries); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); + check_libraries_result (libraries); + + g_list_free_full (libraries, g_object_unref); + g_free (expectations_in); + g_object_unref (info); +} + +static void +check_library_result (SrtLibrary *library) +{ + const char * const *missing_symbols; + const char * const *misversioned_symbols; + const char * const *dependencies; + gboolean seen_libc; + + g_assert_cmpstr (srt_library_get_soname (library), ==, "libz.so.1"); + + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + seen_libc = FALSE; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + g_debug ("libz.so.1 depends on %s", dependencies[i]); + + if (strstr (dependencies[i], "/libc.so.") != NULL) + seen_libc = TRUE; + } + + g_assert_true (seen_libc); +} + +/* + * Test if `libz.so.1` is available in the running system and + * if it has the expected symbols. + */ +static void +library_presence (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar *expectations_in = NULL; + SrtLibrary *library = NULL; + SrtLibraryIssues issues; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + info = srt_system_info_new (expectations_in); + + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libz.so.1", + &library); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); + + check_library_result (library); + + g_clear_pointer (&library, g_object_unref); + /* Do the check again, this time using the cache */ + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libz.so.1", + &library); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_NONE); + + check_library_result (library); + + g_object_unref (library); + g_free (expectations_in); + g_object_unref (info); +} + +static void +check_library_libz_missing_sym_result (SrtLibrary *library) +{ + const char * const *missing_symbols; + const char * const *misversioned_symbols; + const char * const *dependencies; + gboolean seen_libc; + + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libz.so.1"); + g_debug ("path to libz.so.1 is %s", srt_library_get_absolute_path (library)); + g_assert_true (srt_library_get_absolute_path (library)[0] == '/'); + g_assert_true (g_file_test (srt_library_get_absolute_path (library), G_FILE_TEST_EXISTS)); + + g_assert_cmpint (srt_library_get_issues (library) & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); + g_assert_cmpint (srt_library_get_issues (library) & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); + + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, "missing@NotAvailable"); + g_assert_cmpstr (missing_symbols[1], ==, NULL); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, "crc32@WRONG_VERSION"); + g_assert_cmpstr (misversioned_symbols[1], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + seen_libc = FALSE; + + for (gsize i = 0; dependencies[i] != NULL; i++) + { + g_debug ("libz.so.1 depends on %s", dependencies[i]); + + if (strstr (dependencies[i], "/libc.so.") != NULL) + seen_libc = TRUE; + } + + g_assert_true (seen_libc); +} + +static void +check_missing_libraries_result (GList *libraries) +{ + SrtLibrary *library = NULL; + const char * const *missing_symbols; + const char * const *misversioned_symbols; + const char * const *dependencies; + GList *l; + + g_assert_nonnull (libraries); + l = libraries; + + /* Test first library. Alphabetical order is an API guarantee, so we know + * which one it should be. */ + library = l->data; + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libgio-MISSING-2.0.so.0"); + g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); + + g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); + + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], ==, NULL); + + /* Test second library */ + l = g_list_next (l); + library = l->data; + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libglib-2.0.so.0"); + g_debug ("path to libglib-2.0.so.0 is %s", srt_library_get_absolute_path (library)); + g_assert_true (srt_library_get_absolute_path (library)[0] == '/'); + g_assert_true (g_file_test (srt_library_get_absolute_path (library), G_FILE_TEST_EXISTS)); + + g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_NONE); + + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], !=, NULL); + + /* Test last library */ + l = g_list_next (l); + library = l->data; + check_library_libz_missing_sym_result (library); +} + +/* + * Test libraries that are either not available or with missing and + * misversioned symbols. + */ +static void +libraries_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar *expectations_in = NULL; + GList *libraries = NULL; + SrtLibraryIssues issues; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + expectations_in = g_build_filename (f->srcdir, "expectations_with_missings", NULL); + info = srt_system_info_new (expectations_in); + issues = srt_system_info_check_libraries (info, + _SRT_MULTIARCH, + &libraries); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_CANNOT_LOAD, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); + check_missing_libraries_result (libraries); + g_list_free_full (libraries, g_object_unref); + libraries = NULL; + + /* Do the check again, this time using the cache */ + issues = srt_system_info_check_libraries (info, + _SRT_MULTIARCH, + &libraries); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_CANNOT_LOAD, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); + check_missing_libraries_result (libraries); + + g_list_free_full (libraries, g_object_unref); + g_free (expectations_in); + g_object_unref (info); +} + +static void +check_library_missing_lib_result (SrtLibrary *library) +{ + const char * const *missing_symbols; + const char * const *misversioned_symbols; + const char * const *dependencies; + + g_assert_nonnull (library); + g_assert_cmpstr (srt_library_get_soname (library), ==, "libMISSING.so.62"); + g_assert_cmpstr (srt_library_get_absolute_path (library), ==, NULL); + + missing_symbols = srt_library_get_missing_symbols (library); + g_assert_nonnull (missing_symbols); + g_assert_cmpstr (missing_symbols[0], ==, NULL); + + misversioned_symbols = srt_library_get_misversioned_symbols (library); + g_assert_nonnull (misversioned_symbols); + g_assert_cmpstr (misversioned_symbols[0], ==, NULL); + + dependencies = srt_library_get_dependencies (library); + g_assert_nonnull (dependencies); + g_assert_cmpstr (dependencies[0], ==, NULL); +} + +/* + * Test `libz.so.1` expecting missing and misversioned symbols. + * Then test the missing library `libMISSING.so.62`. + */ +static void +library_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + gchar *expectations_in = NULL; + SrtLibrary *library = NULL; + SrtLibraryIssues issues; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + expectations_in = g_build_filename (f->srcdir, "expectations_with_missings", NULL); + info = srt_system_info_new (expectations_in); + + /* Check a present library that has a missing symbol */ + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libz.so.1", + &library); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); + check_library_libz_missing_sym_result (library); + + g_clear_pointer (&library, g_object_unref); + /* Do the check again, this time using the cache */ + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libz.so.1", + &library); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS, !=, 0); + g_assert_cmpint (issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS, !=, 0); + check_library_libz_missing_sym_result (library); + + g_clear_pointer (&library, g_object_unref); + /* Check for a library that isn't listed in any of the .symbols files */ + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libMISSING.so.62", + &library); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); + check_library_missing_lib_result (library); + + g_clear_pointer (&library, g_object_unref); + /* Do the check again, this time using the cache */ + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libMISSING.so.62", + &library); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_CANNOT_LOAD); + check_library_missing_lib_result (library); + + g_object_unref (library); + g_free (expectations_in); + g_object_unref (info); +} + +/* + * Test libraries with the expectations folder set to NULL. + */ +static void +wrong_expectations (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtLibraryIssues issues; + + if (strcmp (_SRT_MULTIARCH, "") == 0) + { + g_test_skip ("Unsupported architecture"); + return; + } + + /* Set the expectations folder to NULL. + * We expect the library checks to fail. */ + info = srt_system_info_new (NULL); + + issues = srt_system_info_check_libraries (info, + _SRT_MULTIARCH, + NULL); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + + issues = srt_system_info_check_library (info, + _SRT_MULTIARCH, + "libz.so.1", + NULL); + g_assert_cmpint (issues, ==, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); + + g_object_unref (info); +} + int main (int argc, char **argv) @@ -146,6 +645,16 @@ main (int argc, g_test_init (&argc, &argv, NULL); g_test_add ("/system-info/object", Fixture, NULL, setup, test_object, teardown); + g_test_add ("/system-info/libraries_presence", Fixture, NULL, + setup, libraries_presence, teardown); + g_test_add ("/system-info/library_presence", Fixture, NULL, + setup, library_presence, teardown); + g_test_add ("/system-info/libraries_missing", Fixture, NULL, + setup, libraries_missing, teardown); + g_test_add ("/system-info/library_missing", Fixture, NULL, + setup, library_missing, teardown); + g_test_add ("/system-info/wrong_expectations", Fixture, NULL, + setup, wrong_expectations, teardown); return g_test_run (); } -- GitLab