diff --git a/debian/rules b/debian/rules index 402da7a7612a9ea6b01611a7457e00f93759016c..f419379e51aa90f684cc8a05ff45ca1885276b0f 100755 --- a/debian/rules +++ b/debian/rules @@ -40,6 +40,7 @@ override_dh_auto_configure: --libexecdir=/usr/libexec \ -Dgtk_doc=$(gtk_doc_has_cflags) \ -Dintrospection=false \ + -Dmultiarch_tuple=$(DEB_HOST_MULTIARCH) \ $(NULL) override_dh_auto_build: @@ -55,6 +56,10 @@ endif override_dh_auto_install: DESTDIR=$(CURDIR)/debian/tmp ninja -C builddir install + ln -s ../../bin/$(DEB_HOST_MULTIARCH)-vulkaninfo \ + debian/tmp/usr/libexec/steam-runtime-tools-0/ + ln -s ../../bin/$(DEB_HOST_MULTIARCH)-wflinfo \ + debian/tmp/usr/libexec/steam-runtime-tools-0/ for x in debian/tmp/usr/libexec/installed-tests/steam-runtime-tools-0/*; do \ if [ -f "$$x" ] \ && cmp -s "$$x" \ diff --git a/helpers/meson.build b/helpers/meson.build index bda568148f81491e91a78b84ac96be838b53b404..3c37a53a09cd5589c26c7e31bc4ac65f7fdf557c 100644 --- a/helpers/meson.build +++ b/helpers/meson.build @@ -25,10 +25,7 @@ executable( multiarch + '-true', 'true.c', install : true, - install_dir : join_paths( - get_option('libexecdir'), - 'steam-runtime-tools-' + api_major, - ) + install_dir : pkglibexecdir, ) executable( @@ -37,10 +34,7 @@ executable( include_directories : project_include_dirs, dependencies : [json_glib, glib], install : true, - install_dir : join_paths( - get_option('libexecdir'), - 'steam-runtime-tools-' + api_major, - ) + install_dir : pkglibexecdir, ) executable( @@ -48,10 +42,7 @@ executable( 'inspect-library.c', dependencies : [libdl], install : true, - install_dir : join_paths( - get_option('libexecdir'), - 'steam-runtime-tools-' + api_major, - ) + install_dir : pkglibexecdir, ) executable( diff --git a/meson.build b/meson.build index 8f64fa14b85f25192e4b6f82daae9294ae74b70b..046d480fc378a232b9a778e115053032ae19d6df 100644 --- a/meson.build +++ b/meson.build @@ -150,7 +150,14 @@ libdl = c_compiler.find_library('dl', required : false) project_include_dirs = include_directories('.') -if host_machine.cpu_family() == 'x86_64' +pkglibexecdir = join_paths( + get_option('libexecdir'), + 'steam-runtime-tools-' + api_major, +) + +if get_option('multiarch_tuple') != '' + multiarch = get_option('multiarch_tuple') +elif host_machine.cpu_family() == 'x86_64' multiarch = 'x86_64-linux-gnu' elif host_machine.cpu_family() == 'x86' multiarch = 'i386-linux-gnu' diff --git a/meson_options.txt b/meson_options.txt index 9e52f2064503d274bab92928bfccfaaf2005341f..e4e20172277ad0b8b17a92d9bb4e307fa2cdf348 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -25,3 +25,10 @@ option( value : true, description : 'enable man pages', ) + +option( + 'multiarch_tuple', + type : 'string', + value : '', + description : 'Debian-style multiarch tuple', +) diff --git a/steam-runtime-tools/architecture.c b/steam-runtime-tools/architecture.c index 00f2d6720cff49d76b293aa8accabad3108b870d..72bdda2d1224567228f512dcbc8317e0d6a8bb41 100644 --- a/steam-runtime-tools/architecture.c +++ b/steam-runtime-tools/architecture.c @@ -26,6 +26,7 @@ #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.h" #include "steam-runtime-tools/utils-internal.h" @@ -47,7 +48,7 @@ _srt_architecture_can_run (const char *helpers_path, const char *multiarch) { gchar *helper = NULL; - const gchar *argv[] = { "true", NULL }; + GPtrArray *argv = NULL; int exit_status = -1; GError *error = NULL; gboolean ret = FALSE; @@ -58,12 +59,18 @@ _srt_architecture_can_run (const char *helpers_path, g_return_val_if_fail (multiarch != NULL, FALSE); g_return_val_if_fail (_srt_check_not_setuid (), FALSE); - if (helpers_path == NULL) - helpers_path = _srt_get_helpers_path (); + argv = _srt_get_helper (helpers_path, multiarch, "true", + SRT_HELPER_FLAGS_NONE, &error); + if (argv == NULL) + { + g_debug ("%s", error->message); + goto out; + } + + g_ptr_array_add (argv, NULL); - helper = g_strdup_printf ("%s/%s-true", helpers_path, multiarch); - argv[0] = helper; - g_debug ("Testing architecture %s with %s", multiarch, helper); + g_debug ("Testing architecture %s with %s", + multiarch, (const char *) g_ptr_array_index (argv, 0)); my_environ = g_get_environ (); ld_preload = g_environ_getenv (my_environ, "LD_PRELOAD"); @@ -74,7 +81,7 @@ _srt_architecture_can_run (const char *helpers_path, } if (!g_spawn_sync (NULL, /* working directory */ - (gchar **) argv, + (gchar **) argv->pdata, my_environ, /* envp */ 0, /* flags */ NULL, /* child setup */ @@ -101,6 +108,7 @@ out: g_free (helper); g_free (filtered_preload); g_clear_error (&error); + g_clear_pointer (&argv, g_ptr_array_unref); return ret; } diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c index 558c53e38a4ea41914ad8ff2b6ab556854cc33eb..e861db43c78b2427bf066baef88aa14ff140a3a7 100644 --- a/steam-runtime-tools/graphics.c +++ b/steam-runtime-tools/graphics.c @@ -401,13 +401,16 @@ _argv_for_graphics_test (const char *helpers_path, SrtTestFlags test_flags, const char *multiarch_tuple, SrtWindowSystem window_system, - SrtRenderingInterface rendering_interface) + SrtRenderingInterface rendering_interface, + GError **error) { GPtrArray *argv = NULL; - gchar *platformstring = NULL; + SrtHelperFlags flags = (SRT_HELPER_FLAGS_TIME_OUT + | SRT_HELPER_FLAGS_SEARCH_PATH); - argv = g_ptr_array_new_with_free_func (g_free); + if (test_flags & SRT_TEST_FLAGS_TIME_OUT_SOONER) + flags |= SRT_HELPER_FLAGS_TIME_OUT_SOONER; if (window_system == SRT_WINDOW_SYSTEM_GLX) { @@ -469,62 +472,34 @@ _argv_for_graphics_test (const char *helpers_path, g_return_val_if_reached (NULL); } - // Use timeout command to limit how long the helper can run - g_ptr_array_add (argv, g_strdup ("timeout")); - g_ptr_array_add (argv, g_strdup ("--signal=TERM")); - - if (test_flags & SRT_TEST_FLAGS_TIME_OUT_SOONER) - { - // Speed up the failing case in automated testing - g_ptr_array_add (argv, g_strdup ("--kill-after=1")); - g_ptr_array_add (argv, g_strdup ("1")); - } - else + if (rendering_interface == SRT_RENDERING_INTERFACE_GL + || rendering_interface == SRT_RENDERING_INTERFACE_GLESV2) { - // Kill the helper (if still running) 3 seconds after the TERM signal - g_ptr_array_add (argv, g_strdup ("--kill-after=3")); - // Send TERM signal after 10 seconds - g_ptr_array_add (argv, g_strdup ("10")); - } + const char *api; - if (rendering_interface == SRT_RENDERING_INTERFACE_GL) - { - if (helpers_path != NULL) - { - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-wflinfo", helpers_path, multiarch_tuple)); - } - else - { - g_ptr_array_add (argv, g_strdup_printf ("%s-wflinfo", multiarch_tuple)); - } - g_ptr_array_add (argv, g_strdup_printf ("--platform=%s", platformstring)); - g_ptr_array_add (argv, g_strdup ("--api=gl")); - g_ptr_array_add (argv, g_strdup ("--format=json")); - } - else if (rendering_interface == SRT_RENDERING_INTERFACE_GLESV2) - { - if (helpers_path != NULL) - { - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-wflinfo", helpers_path, multiarch_tuple)); - } + argv = _srt_get_helper (helpers_path, multiarch_tuple, "wflinfo", flags, + error); + + if (argv == NULL) + goto out; + + if (rendering_interface == SRT_RENDERING_INTERFACE_GLESV2) + api = "gles2"; else - { - g_ptr_array_add (argv, g_strdup_printf ("%s-wflinfo", multiarch_tuple)); - } + api = "gl"; + g_ptr_array_add (argv, g_strdup_printf ("--platform=%s", platformstring)); - g_ptr_array_add (argv, g_strdup ("--api=gles2")); + g_ptr_array_add (argv, g_strdup_printf ("--api=%s", api)); g_ptr_array_add (argv, g_strdup ("--format=json")); } else if (rendering_interface == SRT_RENDERING_INTERFACE_VULKAN) { - if (helpers_path != NULL) - { - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-vulkaninfo", helpers_path, multiarch_tuple)); - } - else - { - g_ptr_array_add (argv, g_strdup_printf ("%s-vulkaninfo", multiarch_tuple)); - } + argv = _srt_get_helper (helpers_path, multiarch_tuple, "vulkaninfo", + flags, error); + + if (argv == NULL) + goto out; + g_ptr_array_add (argv, g_strdup ("-j")); } else @@ -536,8 +511,8 @@ _argv_for_graphics_test (const char *helpers_path, g_ptr_array_add (argv, NULL); +out: g_free (platformstring); - return argv; } @@ -545,34 +520,22 @@ _argv_for_graphics_test (const char *helpers_path, static GPtrArray * _argv_for_check_vulkan (const char *helpers_path, SrtTestFlags test_flags, - const char *multiarch_tuple) + const char *multiarch_tuple, + GError **error) { - GPtrArray *argv = g_ptr_array_new_with_free_func (g_free); - - // Use timeout command to limit how long the helper can run - g_ptr_array_add (argv, g_strdup ("timeout")); - g_ptr_array_add (argv, g_strdup ("--signal=TERM")); + GPtrArray *argv; + SrtHelperFlags flags = SRT_HELPER_FLAGS_TIME_OUT; if (test_flags & SRT_TEST_FLAGS_TIME_OUT_SOONER) - { - // Speed up the failing case in automated testing - g_ptr_array_add (argv, g_strdup ("--kill-after=1")); - g_ptr_array_add (argv, g_strdup ("1")); - } - else - { - // Kill the helper (if still running) 3 seconds after the TERM signal - g_ptr_array_add (argv, g_strdup ("--kill-after=3")); - // Send TERM signal after 10 seconds - g_ptr_array_add (argv, g_strdup ("10")); - } + flags |= SRT_HELPER_FLAGS_TIME_OUT_SOONER; - if (helpers_path == NULL) - helpers_path = _srt_get_helpers_path (); + argv = _srt_get_helper (helpers_path, multiarch_tuple, "check-vulkan", + flags, error); - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-check-vulkan", helpers_path, multiarch_tuple)); - g_ptr_array_add (argv, NULL); + if (argv == NULL) + return NULL; + g_ptr_array_add (argv, NULL); return argv; } @@ -624,10 +587,16 @@ _srt_check_graphics (const char *helpers_path, test_flags, multiarch_tuple, window_system, - rendering_interface); + rendering_interface, + &error); if (argv == NULL) - return SRT_GRAPHICS_ISSUES_INTERNAL_ERROR; + { + issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD; + /* Put the error message in the 'messages' */ + child_stderr = g_strdup (error->message); + goto out; + } my_environ = g_get_environ (); ld_preload = g_environ_getenv (my_environ, "LD_PRELOAD"); @@ -698,9 +667,16 @@ _srt_check_graphics (const char *helpers_path, argv = _argv_for_check_vulkan (helpers_path, test_flags, - multiarch_tuple); + multiarch_tuple, + &error); - g_return_val_if_fail (argv != NULL, SRT_GRAPHICS_ISSUES_INTERNAL_ERROR); + if (argv == NULL) + { + issues |= SRT_GRAPHICS_ISSUES_CANNOT_DRAW; + /* Put the error message in the 'messages' */ + child_stderr2 = g_strdup (error->message); + goto out; + } /* Now run and report exit code/messages if failure */ if (!g_spawn_sync (NULL, /* working directory */ @@ -715,19 +691,10 @@ _srt_check_graphics (const char *helpers_path, &error)) { g_debug ("An error occurred calling the helper: %s", error->message); - issues |= SRT_GRAPHICS_ISSUES_CANNOT_LOAD; + issues |= SRT_GRAPHICS_ISSUES_CANNOT_DRAW; goto out; } - if (child_stderr2 != NULL && child_stderr2[0] != '\0') - { - gchar *tmp = g_strconcat (child_stderr, child_stderr2, NULL); - - g_free (child_stderr); - child_stderr = tmp; - } - g_free (child_stderr2); - if (exit_status != 0) { g_debug ("... wait status %d", exit_status); @@ -743,6 +710,16 @@ _srt_check_graphics (const char *helpers_path, } out: + /* If we have stderr (or error messages) from both vulkaninfo and + * check-vulkan, combine them */ + if (child_stderr2 != NULL && child_stderr2[0] != '\0') + { + gchar *tmp = g_strconcat (child_stderr, child_stderr2, NULL); + + g_free (child_stderr); + child_stderr = tmp; + } + if (details_out != NULL) *details_out = _srt_graphics_new (multiarch_tuple, window_system, @@ -756,9 +733,10 @@ out: g_object_unref (parser); g_free (new_version_string); - g_ptr_array_unref (argv); + g_clear_pointer (&argv, g_ptr_array_unref); g_free (output); g_free (child_stderr); + g_free (child_stderr2); g_clear_error (&error); g_free (filtered_preload); g_strfreev (my_environ); diff --git a/steam-runtime-tools/library.c b/steam-runtime-tools/library.c index 19789f77c8e43631bcbd25cd0837305022c11a7b..681d4bb98980d5d035d12aa7dfc6f69cf564ebf1 100644 --- a/steam-runtime-tools/library.c +++ b/steam-runtime-tools/library.c @@ -27,6 +27,7 @@ #include "steam-runtime-tools/architecture.h" #include "steam-runtime-tools/enums.h" +#include "steam-runtime-tools/glib-compat.h" #include "steam-runtime-tools/library-internal.h" #include "steam-runtime-tools/utils.h" #include "steam-runtime-tools/utils-internal.h" @@ -506,7 +507,6 @@ _srt_check_library_presence (const char *helpers_path, GStrv my_environ = NULL; const gchar *ld_preload; gchar *filtered_preload = NULL; - argv = g_ptr_array_new_with_free_func (g_free); g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); @@ -514,10 +514,21 @@ _srt_check_library_presence (const char *helpers_path, SRT_LIBRARY_ISSUES_INTERNAL_ERROR); g_return_val_if_fail (_srt_check_not_setuid (), SRT_LIBRARY_ISSUES_INTERNAL_ERROR); - if (helpers_path == NULL) - helpers_path = _srt_get_helpers_path (); + if (symbols_path == NULL) + issues |= SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS; + + argv = _srt_get_helper (helpers_path, multiarch, "inspect-library", + SRT_HELPER_FLAGS_NONE, &error); + + if (argv == NULL) + { + issues |= SRT_LIBRARY_ISSUES_CANNOT_LOAD; + /* Use the error message as though the child had printed it on stderr - + * either way, it's a useful diagnostic */ + child_stderr = g_strdup (error->message); + goto out; + } - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-inspect-library", helpers_path, multiarch)); g_debug ("Checking library %s integrity with %s", soname, (gchar *)g_ptr_array_index (argv, 0)); switch (symbols_format) @@ -534,7 +545,7 @@ _srt_check_library_presence (const char *helpers_path, break; default: - g_return_val_if_reached (SRT_LIBRARY_ISSUES_CANNOT_LOAD); + g_return_val_if_reached (SRT_LIBRARY_ISSUES_INTERNAL_ERROR); } for (gsize i = 0; hidden_deps != NULL && hidden_deps[i] != NULL; i++) @@ -546,9 +557,6 @@ _srt_check_library_presence (const char *helpers_path, /* NULL terminate the array */ g_ptr_array_add (argv, NULL); - if (symbols_path == NULL) - issues |= SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS; - my_environ = g_get_environ (); ld_preload = g_environ_getenv (my_environ, "LD_PRELOAD"); if (ld_preload != NULL) @@ -665,7 +673,7 @@ out: g_strfreev (missing_symbols); g_strfreev (misversioned_symbols); g_strfreev (dependencies); - g_ptr_array_free (argv, TRUE); + g_clear_pointer (&argv, g_ptr_array_unref); g_free (absolute_path); g_free (child_stderr); g_free (output); diff --git a/steam-runtime-tools/locale.c b/steam-runtime-tools/locale.c index e04c14a4163bee8e0fac13b31a460f6881583b7f..a59e08d8568b675892f4bd9f18bbb071373fde3c 100644 --- a/steam-runtime-tools/locale.c +++ b/steam-runtime-tools/locale.c @@ -225,6 +225,15 @@ _srt_check_locale (const char *helpers_path, g_return_val_if_fail (requested_name != NULL, NULL); g_return_val_if_fail (_srt_check_not_setuid (), NULL); + if (multiarch_tuple == NULL) + multiarch_tuple = _SRT_MULTIARCH; + + argv = _srt_get_helper (helpers_path, multiarch_tuple, "check-locale", + SRT_HELPER_FLAGS_NONE, error); + + if (argv == NULL) + goto out; + my_environ = g_get_environ (); ld_preload = g_environ_getenv (my_environ, "LD_PRELOAD"); if (ld_preload != NULL) @@ -234,16 +243,6 @@ _srt_check_locale (const char *helpers_path, filtered_preload, TRUE); } - argv = g_ptr_array_new_with_free_func (g_free); - - if (helpers_path == NULL) - helpers_path = _srt_get_helpers_path (); - - if (multiarch_tuple == NULL) - multiarch_tuple = _SRT_MULTIARCH; - - g_ptr_array_add (argv, g_strdup_printf ("%s/%s-check-locale", - helpers_path, multiarch_tuple)); g_ptr_array_add (argv, g_strdup (requested_name)); g_ptr_array_add (argv, NULL); @@ -350,7 +349,7 @@ out: } g_clear_object (&parser); - g_ptr_array_unref (argv); + g_clear_pointer (&argv, g_ptr_array_unref); g_free (output); g_free (filtered_preload); g_strfreev (my_environ); diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h index 8fc5b0ecaed50282164de870a22a29e1674ae463..8f6b15e2d08cdb263a1686781c82e4e5de797142 100644 --- a/steam-runtime-tools/utils-internal.h +++ b/steam-runtime-tools/utils-internal.h @@ -28,5 +28,17 @@ #include <glib.h> -G_GNUC_INTERNAL const char *_srt_get_helpers_path (void); +typedef enum +{ + SRT_HELPER_FLAGS_SEARCH_PATH = (1 << 0), + SRT_HELPER_FLAGS_TIME_OUT = (1 << 1), + SRT_HELPER_FLAGS_TIME_OUT_SOONER = (1 << 2), + SRT_HELPER_FLAGS_NONE = 0 +} SrtHelperFlags; + +G_GNUC_INTERNAL GPtrArray *_srt_get_helper (const char *helpers_path, + const char *multiarch, + const char *base, + SrtHelperFlags flags, + GError **error); gchar *_srt_filter_gameoverlayrenderer (const gchar *input); diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c index c8a06acdd708bba569ddf5a9128d09d49a27fb0a..00d7346326f0f5d9045b778bccc3e612ebf91d25 100644 --- a/steam-runtime-tools/utils.c +++ b/steam-runtime-tools/utils.c @@ -39,6 +39,8 @@ #endif #include <glib-object.h> +#include <gio/gio.h> +#include "steam-runtime-tools/glib-compat.h" #ifdef HAVE_GETAUXVAL #define getauxval_AT_SECURE() getauxval (AT_SECURE) @@ -144,19 +146,20 @@ _srt_check_not_setuid (void) return !is_setuid; } -static gchar *helpers_path = NULL; +static gchar *global_helpers_path = NULL; -G_GNUC_INTERNAL const char * -_srt_get_helpers_path (void) +static const char * +_srt_get_helpers_path (GError **error) { const char *path; Dl_info ignored; struct link_map *map = NULL; gchar *dir; - g_return_val_if_fail (_srt_check_not_setuid (), "/"); + g_return_val_if_fail (_srt_check_not_setuid (), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); - path = helpers_path; + path = global_helpers_path; if (path != NULL) goto out; @@ -170,8 +173,9 @@ _srt_get_helpers_path (void) RTLD_DL_LINKMAP) == 0 || map == NULL) { - g_warning ("Unable to locate shared library containing " - "_srt_get_helpers_path()"); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unable to locate shared library containing " + "_srt_get_helpers_path()"); goto out; } @@ -195,22 +199,120 @@ _srt_get_helpers_path (void) } /* deliberate one-per-process leak */ - helpers_path = g_build_filename ( + global_helpers_path = g_build_filename ( dir, "libexec", "steam-runtime-tools-" _SRT_API_MAJOR, NULL); - path = helpers_path; + path = global_helpers_path; g_free (dir); out: - /* We have to return *something* non-NULL */ - if (path == NULL) + return path; +} + +/* + * _srt_get_helper: + * @helpers_path: (nullable): Directory to search for helper executables, + * or %NULL for default behaviour + * @multiarch: (nullable): A multiarch tuple like %SRT_ABI_I386 to prefix + * to the executable name, or %NULL + * @base: (not nullable): Base name of the executable + * @flags: Flags affecting how we set up the helper + * @error: Used to raise an error if %NULL is returned + * + * Find a helper executable. We return an array of arguments so that the + * helper can be wrapped by an "adverb" like `env`, `timeout` or a + * specific `ld.so` implementation if required. + * + * Returns: (nullable) (element-type filename) (transfer container): The + * initial `argv` for the helper, with g_free() set as the free-function, and + * no %NULL terminator. Free with g_ptr_array_unref() or g_ptr_array_free(). + */ +G_GNUC_INTERNAL GPtrArray * +_srt_get_helper (const char *helpers_path, + const char *multiarch, + const char *base, + SrtHelperFlags flags, + GError **error) +{ + GPtrArray *argv = NULL; + gchar *path; + gchar *prefixed; + + g_return_val_if_fail (base != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + argv = g_ptr_array_new_with_free_func (g_free); + + if (flags & SRT_HELPER_FLAGS_TIME_OUT) + { + g_ptr_array_add (argv, g_strdup ("timeout")); + g_ptr_array_add (argv, g_strdup ("--signal=TERM")); + + if (flags & SRT_HELPER_FLAGS_TIME_OUT_SOONER) + { + /* Speed up the failing case in automated testing */ + g_ptr_array_add (argv, g_strdup ("--kill-after=1")); + g_ptr_array_add (argv, g_strdup ("1")); + } + else + { + /* Kill the helper (if still running) 3 seconds after the TERM + * signal */ + g_ptr_array_add (argv, g_strdup ("--kill-after=3")); + /* Send TERM signal after 10 seconds */ + g_ptr_array_add (argv, g_strdup ("10")); + } + } + + if (helpers_path == NULL) { - g_warning ("Unable to determine path to helpers"); - path = "/"; + helpers_path = _srt_get_helpers_path (error); + + if (helpers_path == NULL) + { + g_ptr_array_unref (argv); + return NULL; + } } - return path; + /* Prefer a helper from ${SRT_HELPERS_PATH} or + * ${libexecdir}/steam-runtime-tools-${_SRT_API_MAJOR} + * if it exists */ + path = g_strdup_printf ("%s/%s%s%s", + helpers_path, + multiarch == NULL ? "" : multiarch, + multiarch == NULL ? "" : "-", + base); + + g_debug ("Looking for %s", path); + + if (g_file_test (path, G_FILE_TEST_IS_EXECUTABLE)) + { + g_ptr_array_add (argv, g_steal_pointer (&path)); + return argv; + } + + if ((flags & SRT_HELPER_FLAGS_SEARCH_PATH) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "%s not found", path); + g_free (path); + g_ptr_array_unref (argv); + return NULL; + } + + /* For helpers that are not part of steam-runtime-tools, such as + * *-wflinfo and *-vulkaninfo, we fall back to searching $PATH */ + g_free (path); + + if (multiarch == NULL) + prefixed = g_strdup (base); + else + prefixed = g_strdup_printf ("%s-%s", multiarch, base); + + g_ptr_array_add (argv, g_steal_pointer (&prefixed)); + return argv; } /**