From 4f3d2d4b9edf340a7f70afb798c379534f83f8c1 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Wed, 22 Nov 2023 14:14:22 +0000
Subject: [PATCH] pv-runtime: Delegate setup of VDPAU to pv-adverb

This lets us use a path based on ${LIB} rather than ${PLATFORM}, which
will work under FEX-Emu.

Helps: steamrt/tasks#349
Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/adverb.c        | 17 +++++++++
 pressure-vessel/per-arch-dirs.c | 35 +++++++++++++++++++
 pressure-vessel/per-arch-dirs.h |  6 ++++
 pressure-vessel/runtime.c       | 61 +++++++++------------------------
 4 files changed, 74 insertions(+), 45 deletions(-)

diff --git a/pressure-vessel/adverb.c b/pressure-vessel/adverb.c
index bc9773675..9e9037cfc 100644
--- a/pressure-vessel/adverb.c
+++ b/pressure-vessel/adverb.c
@@ -59,6 +59,7 @@ static gboolean opt_batch = FALSE;
 static gboolean opt_create = FALSE;
 static gboolean opt_exit_with_parent = FALSE;
 static gboolean opt_generate_locales = FALSE;
+static gchar *opt_overrides = NULL;
 static gchar *opt_regenerate_ld_so_cache = NULL;
 static gchar *opt_set_ld_library_path = NULL;
 static PvShell opt_shell = PV_SHELL_NONE;
@@ -927,6 +928,11 @@ static GOptionEntry options[] =
     "earlier on the command-line. May be repeated.",
     "PATH" },
 
+  { "overrides-path", '\0',
+    G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &opt_overrides,
+    "Libraries and drivers set up by pressure-vessel are in PATH.",
+    "PATH" },
+
   { "pass-fd", '\0',
     G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, opt_pass_fd_cb,
     "Let the launched process inherit the given fd.",
@@ -1198,6 +1204,16 @@ main (int argc,
       g_clear_error (error);
     }
 
+  if (opt_overrides != NULL
+      && !pv_adverb_set_up_overrides (wrapped_command,
+                                      lib_temp_dirs,
+                                      opt_overrides,
+                                      error))
+    {
+      g_warning ("%s", local_error->message);
+      g_clear_error (error);
+    }
+
   if (opt_preload_modules != NULL
       && !pv_adverb_set_up_preload_modules (wrapped_command,
                                             lib_temp_dirs,
@@ -1414,6 +1430,7 @@ out:
   global_ld_so_conf_entries = NULL;
   global_locks = NULL;
   global_pass_fds = NULL;
+  g_clear_pointer (&opt_overrides, g_free);
   g_clear_pointer (&opt_regenerate_ld_so_cache, g_free);
 
   if (locales_temp_dir != NULL)
diff --git a/pressure-vessel/per-arch-dirs.c b/pressure-vessel/per-arch-dirs.c
index b9a86a3a8..49ea25935 100644
--- a/pressure-vessel/per-arch-dirs.c
+++ b/pressure-vessel/per-arch-dirs.c
@@ -276,3 +276,38 @@ pv_per_arch_dirs_new (GError **error)
 
   return g_steal_pointer (&self);
 }
+
+gboolean
+pv_adverb_set_up_overrides (FlatpakBwrap *wrapped_command,
+                            PvPerArchDirs *lib_temp_dirs,
+                            const char *overrides,
+                            GError **error)
+{
+  g_autofree gchar *value = NULL;
+  gsize abi;
+
+  g_return_val_if_fail (wrapped_command != NULL, FALSE);
+
+  if (lib_temp_dirs == NULL)
+    return glnx_throw (error, "Unable to set up VDPAU driver search path");
+
+  for (abi = 0; abi < PV_N_SUPPORTED_ARCHITECTURES; abi++)
+    {
+      const char *multiarch_tuple = pv_multiarch_details[abi].tuple;
+      gchar *abi_path = g_build_filename (lib_temp_dirs->abi_paths[abi], "vdpau", NULL);
+      gchar *target = g_build_filename (overrides, multiarch_tuple, "vdpau", NULL);
+
+      if (!g_file_test (target, G_FILE_TEST_IS_DIR))
+        continue;
+
+      g_debug ("Creating \"%s\" -> \"%s\"", abi_path, target);
+
+      if (symlink (target, abi_path) != 0)
+        return glnx_throw_errno_prefix (error, "Cannot create symlink \"%s\"", abi_path);
+    }
+
+  value = g_build_filename (lib_temp_dirs->libdl_token_path, "vdpau", NULL);
+  g_debug ("Setting VDPAU_DRIVER_PATH=\"%s\"", value);
+  flatpak_bwrap_set_env (wrapped_command, "VDPAU_DRIVER_PATH", value, TRUE);
+  return TRUE;
+}
diff --git a/pressure-vessel/per-arch-dirs.h b/pressure-vessel/per-arch-dirs.h
index ab5f58a7a..67539b1eb 100644
--- a/pressure-vessel/per-arch-dirs.h
+++ b/pressure-vessel/per-arch-dirs.h
@@ -22,6 +22,7 @@
 #include "steam-runtime-tools/glib-backports-internal.h"
 #include "libglnx.h"
 
+#include "flatpak-bwrap-private.h"
 #include "supported-architectures.h"
 
 /*
@@ -42,3 +43,8 @@ PvPerArchDirs *pv_per_arch_dirs_new (GError **error);
 void pv_per_arch_dirs_free (PvPerArchDirs *self);
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PvPerArchDirs, pv_per_arch_dirs_free)
+
+gboolean pv_adverb_set_up_overrides (FlatpakBwrap *wrapped_command,
+                                     PvPerArchDirs *lib_temp_dirs,
+                                     const char *overrides,
+                                     GError **error);
diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c
index 0ff2ae027..e3e663e86 100644
--- a/pressure-vessel/runtime.c
+++ b/pressure-vessel/runtime.c
@@ -111,6 +111,7 @@ struct _PvRuntime
   unsigned is_steamrt : 1;
   unsigned is_scout : 1;
   unsigned is_flatpak_env : 1;
+  unsigned any_vdpau_drivers : 1;
 };
 
 struct _PvRuntimeClass
@@ -2265,6 +2266,11 @@ pv_runtime_get_adverb (PvRuntime *self,
 
   pv_runtime_adverb_regenerate_ld_so_cache (self, bwrap);
 
+  if (self->any_vdpau_drivers)
+    flatpak_bwrap_add_args (bwrap,
+                            "--overrides-path", self->overrides_in_container,
+                            NULL);
+
   return TRUE;
 }
 
@@ -6583,6 +6589,10 @@ collect_vdpau_drivers (PvRuntime *self,
                                                           SRT_DRIVER_FLAGS_NONE);
     }
 
+  if (vdpau_drivers == NULL)
+    return TRUE;
+
+  self->any_vdpau_drivers = TRUE;
   details_arr = g_ptr_array_new_full (g_list_length (vdpau_drivers),
                                       (GDestroyNotify) G_CALLBACK (icd_details_free));
 
@@ -7184,7 +7194,6 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self,
           g_autofree gchar *libdrm_amdgpu = NULL;
           g_autofree gchar *libglx_mesa = NULL;
           g_autofree gchar *libglx_nvidia = NULL;
-          g_autofree gchar *platform_token = NULL;
           g_autoptr(GPtrArray) patterns = NULL;
           SrtSystemInfo *arch_system_info;
           struct stat stat_buf;
@@ -7372,42 +7381,6 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self,
                 return FALSE;
             }
 
-          /* Unfortunately VDPAU_DRIVER_PATH can hold just a single path, so we can't
-           * easily list both x86_64 and i386 paths. As a workaround we set
-           * VDPAU_DRIVER_PATH based on ${PLATFORM} - but each of our
-           * supported ABIs can have multiple values for ${PLATFORM}, so we
-           * need to create symlinks. Try to avoid making use of this,
-           * because it's fragile (a new glibc version can introduce
-           * new platform strings), but for some things like VDPAU it's our
-           * only choice. */
-          for (j = 0; j < G_N_ELEMENTS (arch->details->platforms); j++)
-            {
-              g_autofree gchar *platform_link = NULL;
-
-              if (arch->details->platforms[j] == NULL)
-                break;
-
-              platform_link = g_strdup_printf ("%s/lib/platform-%s",
-                                               self->overrides,
-                                               arch->details->platforms[j]);
-
-              if (symlink (arch->details->tuple, platform_link) != 0)
-                return glnx_throw_errno_prefix (error,
-                                                "Unable to create symlink %s -> %s",
-                                                platform_link, arch->details->tuple);
-            }
-
-          platform_token = srt_system_info_dup_libdl_platform (arch_system_info,
-                                                               pv_multiarch_tuples[i],
-                                                               &local_error);
-          if (platform_token == NULL)
-            {
-              /* This is not a critical error, try to continue */
-              g_warning ("The dynamic linker expansion of \"$PLATFORM\" is not what we "
-                         "expected, VDPAU drivers might not work: %s", local_error->message);
-              g_clear_error (&local_error);
-            }
-
           if (!pv_runtime_create_aliases (self, arch, &local_error))
             {
               /* This is not a critical error, try to continue */
@@ -7659,14 +7632,12 @@ pv_runtime_use_provider_graphics_stack (PvRuntime *self,
 
   /* We binded the VDPAU drivers in "%{libdir}/vdpau".
    * Unfortunately VDPAU_DRIVER_PATH can hold just a single path, so we can't
-   * easily list both x86_64 and i386 drivers path.
-   * As a workaround we set VDPAU_DRIVER_PATH to
-   * "/overrides/lib/platform-${PLATFORM}/vdpau" (which is a symlink that we
-   * already created). */
-  g_autofree gchar *vdpau_val = g_strdup_printf ("%s/lib/platform-${PLATFORM}/vdpau",
-                                                 self->overrides_in_container);
-
-  pv_environ_setenv (container_env, "VDPAU_DRIVER_PATH", vdpau_val);
+   * easily list both x86_64 and i386 driver paths; instead, we delegate the
+   * setup of VDPAU drivers to pv-adverb, which is running with our final
+   * choice of glibc and therefore can do something more clever with
+   * dynamic string tokens. */
+  pv_environ_setenv (container_env, "VDPAU_DRIVER_PATH", NULL);
+
   return TRUE;
 }
 
-- 
GitLab