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

runtime: Move most runtime setup into PvRuntime


There's very little refactoring so far: bind_runtime() is still
basically monolithic, and takes individual bits of PvRuntime's state
rather than a PvRuntime object.

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent dae6ea0e
No related branches found
No related tags found
No related merge requests found
...@@ -19,10 +19,21 @@ ...@@ -19,10 +19,21 @@
#include "src/runtime.h" #include "src/runtime.h"
#include <sysexits.h>
#include <gio/gio.h> #include <gio/gio.h>
/* Include these before steam-runtime-tools.h so that their backport of
* G_DEFINE_AUTOPTR_CLEANUP_FUNC will be visible to it */
#include "glib-backports.h"
#include "libglnx/libglnx.h"
#include <steam-runtime-tools/steam-runtime-tools.h>
#include "bwrap.h" #include "bwrap.h"
#include "bwrap-lock.h" #include "bwrap-lock.h"
#include "flatpak-run-private.h"
#include "utils.h"
/* /*
* PvRuntime: * PvRuntime:
...@@ -38,6 +49,11 @@ struct _PvRuntime ...@@ -38,6 +49,11 @@ struct _PvRuntime
gchar *source_files; gchar *source_files;
gchar *tools_dir; gchar *tools_dir;
PvBwrapLock *runtime_lock; PvBwrapLock *runtime_lock;
gchar *tmpdir;
gchar *scratch;
gchar *overrides;
gchar *overrides_bin;
}; };
struct _PvRuntimeClass struct _PvRuntimeClass
...@@ -62,6 +78,26 @@ G_DEFINE_TYPE_WITH_CODE (PvRuntime, pv_runtime, G_TYPE_OBJECT, ...@@ -62,6 +78,26 @@ G_DEFINE_TYPE_WITH_CODE (PvRuntime, pv_runtime, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
pv_runtime_initable_iface_init)) pv_runtime_initable_iface_init))
/*
* Supported Debian-style multiarch tuples
*/
static const char * const multiarch_tuples[] =
{
"x86_64-linux-gnu",
"i386-linux-gnu",
NULL
};
/*
* Directories other than /usr/lib that we must search for loadable
* modules, in the same order as multiarch_tuples
*/
static const char * const libquals[] =
{
"lib64",
"lib32"
};
static void static void
pv_runtime_init (PvRuntime *self) pv_runtime_init (PvRuntime *self)
{ {
...@@ -179,14 +215,50 @@ pv_runtime_initable_init (GInitable *initable, ...@@ -179,14 +215,50 @@ pv_runtime_initable_init (GInitable *initable,
if (self->runtime_lock == NULL) if (self->runtime_lock == NULL)
return FALSE; return FALSE;
g_debug ("Creating temporary directories...");
/* Using a runtime requires a temporary directory */
self->tmpdir = g_dir_make_tmp ("pressure-vessel-wrap.XXXXXX", error);
if (self->tmpdir == NULL)
return FALSE;
self->scratch = g_build_filename (self->tmpdir, "scratch", NULL);
g_mkdir (self->scratch, 0700);
self->overrides = g_build_filename (self->tmpdir, "overrides", NULL);
g_mkdir (self->overrides, 0700);
self->overrides_bin = g_build_filename (self->overrides, "bin", NULL);
g_mkdir (self->overrides_bin, 0700);
return TRUE; return TRUE;
} }
void
pv_runtime_cleanup (PvRuntime *self)
{
g_autoptr(GError) local_error = NULL;
g_return_if_fail (PV_IS_RUNTIME (self));
if (self->tmpdir != NULL &&
!glnx_shutil_rm_rf_at (-1, self->tmpdir, NULL, &local_error))
{
g_warning ("Unable to delete temporary directory: %s",
local_error->message);
}
g_clear_pointer (&self->overrides_bin, g_free);
g_clear_pointer (&self->overrides, g_free);
g_clear_pointer (&self->scratch, g_free);
g_clear_pointer (&self->tmpdir, g_free);
}
static void static void
pv_runtime_finalize (GObject *object) pv_runtime_finalize (GObject *object)
{ {
PvRuntime *self = PV_RUNTIME (object); PvRuntime *self = PV_RUNTIME (object);
pv_runtime_cleanup (self);
g_free (self->bubblewrap); g_free (self->bubblewrap);
g_free (self->source_files); g_free (self->source_files);
g_free (self->tools_dir); g_free (self->tools_dir);
...@@ -306,6 +378,1407 @@ pv_runtime_append_lock_adverb (PvRuntime *self, ...@@ -306,6 +378,1407 @@ pv_runtime_append_lock_adverb (PvRuntime *self,
NULL); NULL);
} }
static gboolean
try_bind_dri (FlatpakBwrap *bwrap,
FlatpakBwrap *mount_runtime_on_scratch,
const char *overrides,
const char *scratch,
const char *tool_path,
const char *libdir,
const char *libdir_on_host,
GError **error)
{
g_autofree gchar *dri = g_build_filename (libdir, "dri", NULL);
g_autofree gchar *s2tc = g_build_filename (libdir, "libtxc_dxtn.so", NULL);
/* mount_runtime_on_scratch can't own any fds, because if it did,
* flatpak_bwrap_append_bwrap() would steal them. */
g_return_val_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0,
FALSE);
if (g_file_test (dri, G_FILE_TEST_IS_DIR))
{
g_autoptr(FlatpakBwrap) temp_bwrap = NULL;
g_autofree gchar *expr = NULL;
g_autofree gchar *host_dri = NULL;
g_autofree gchar *dest_dri = NULL;
expr = g_strdup_printf ("only-dependencies:if-exists:path-match:%s/dri/*.so",
libdir);
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
expr,
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
host_dri = g_build_filename ("/run/host", libdir, "dri", NULL);
dest_dri = g_build_filename (libdir_on_host, "dri", NULL);
temp_bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_args (temp_bwrap,
bwrap->argv->pdata[0],
"--ro-bind", "/", "/",
"--tmpfs", "/run",
"--ro-bind", "/", "/run/host",
"--bind", overrides, overrides,
"sh", "-c",
"ln -fns \"$1\"/* \"$2\"",
"sh", /* $0 */
host_dri,
dest_dri,
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
}
if (g_file_test (s2tc, G_FILE_TEST_EXISTS))
{
g_autoptr(FlatpakBwrap) temp_bwrap = NULL;
g_autofree gchar *expr = NULL;
expr = g_strdup_printf ("path-match:%s", s2tc);
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
expr,
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
}
return TRUE;
}
/*
* Try to make sure we have all the locales we need, by running
* the helper from steam-runtime-tools in the container. If this
* fails, it isn't fatal - carry on anyway.
*
* @bwrap must be set up to have the same libc that we will be using
* for the container.
*/
static void
ensure_locales (gboolean on_host,
const char *tools_dir,
FlatpakBwrap *bwrap,
const char *overrides)
{
g_autoptr(GError) local_error = NULL;
g_autoptr(FlatpakBwrap) run_locale_gen = NULL;
g_autofree gchar *locale_gen = NULL;
g_autofree gchar *locales = g_build_filename (overrides, "locales", NULL);
g_autoptr(GDir) dir = NULL;
int exit_status;
/* bwrap can't own any fds yet, because if it did,
* flatpak_bwrap_append_bwrap() would steal them. */
g_return_if_fail (bwrap->fds == NULL || bwrap->fds->len == 0);
g_mkdir (locales, 0700);
run_locale_gen = flatpak_bwrap_new (NULL);
if (on_host)
{
locale_gen = g_build_filename (tools_dir,
"pressure-vessel-locale-gen",
NULL);
flatpak_bwrap_add_args (run_locale_gen,
bwrap->argv->pdata[0],
"--ro-bind", "/", "/",
NULL);
pv_bwrap_add_api_filesystems (run_locale_gen);
flatpak_bwrap_add_args (run_locale_gen,
"--bind", locales, locales,
"--chdir", locales,
locale_gen,
"--verbose",
NULL);
}
else
{
locale_gen = g_build_filename ("/run/host/tools",
"pressure-vessel-locale-gen",
NULL);
flatpak_bwrap_append_bwrap (run_locale_gen, bwrap);
pv_bwrap_copy_tree (run_locale_gen, overrides, "/overrides");
if (!flatpak_bwrap_bundle_args (run_locale_gen, 1, -1, FALSE,
&local_error))
{
g_warning ("Unable to set up locale-gen command: %s",
local_error->message);
g_clear_error (&local_error);
}
flatpak_bwrap_add_args (run_locale_gen,
"--ro-bind", tools_dir, "/run/host/tools",
"--bind", locales, "/overrides/locales",
"--chdir", "/overrides/locales",
locale_gen,
"--verbose",
NULL);
}
flatpak_bwrap_finish (run_locale_gen);
/* locale-gen exits 72 (EX_OSFILE) if it had to correct for
* missing locales at OS level. This is not an error. */
if (!pv_bwrap_run_sync (run_locale_gen, &exit_status, &local_error))
{
if (exit_status == EX_OSFILE)
g_debug ("pressure-vessel-locale-gen created missing locales");
else
g_warning ("Unable to generate locales: %s", local_error->message);
g_clear_error (&local_error);
}
else
{
g_debug ("No locales generated");
}
dir = g_dir_open (locales, 0, NULL);
/* If the directory is not empty, make it the container's LOCPATH */
if (dir != NULL && g_dir_read_name (dir) != NULL)
{
g_autoptr(GString) locpath = NULL;
g_debug ("%s is non-empty", locales);
locpath = g_string_new ("/overrides/locales");
pv_search_path_append (locpath, g_getenv ("LOCPATH"));
flatpak_bwrap_add_args (bwrap,
"--setenv", "LOCPATH", locpath->str,
NULL);
}
else
{
g_debug ("%s is empty", locales);
}
}
typedef enum
{
ICD_KIND_NONEXISTENT,
ICD_KIND_ABSOLUTE,
ICD_KIND_SONAME
} IcdKind;
typedef struct
{
/* (type SrtEglIcd) or (type SrtVulkanIcd) */
gpointer icd;
gchar *resolved_library;
/* Last entry is always NONEXISTENT */
IcdKind kinds[G_N_ELEMENTS (multiarch_tuples)];
/* Last entry is always NULL */
gchar *paths_in_container[G_N_ELEMENTS (multiarch_tuples)];
} IcdDetails;
static IcdDetails *
icd_details_new (gpointer icd)
{
IcdDetails *self;
gsize i;
g_return_val_if_fail (G_IS_OBJECT (icd), NULL);
g_return_val_if_fail (SRT_IS_EGL_ICD (icd) || SRT_IS_VULKAN_ICD (icd),
NULL);
self = g_slice_new0 (IcdDetails);
self->icd = g_object_ref (icd);
self->resolved_library = NULL;
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++)
{
self->kinds[i] = ICD_KIND_NONEXISTENT;
self->paths_in_container[i] = NULL;
}
return self;
}
static void
icd_details_free (IcdDetails *self)
{
gsize i;
g_object_unref (self->icd);
g_free (self->resolved_library);
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++)
g_free (self->paths_in_container[i]);
g_slice_free (IcdDetails, self);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (IcdDetails, icd_details_free)
static gboolean
bind_icd (gsize multiarch_index,
gsize sequence_number,
const char *tool_path,
FlatpakBwrap *mount_runtime_on_scratch,
const char *scratch,
const char *libdir_on_host,
const char *libdir_in_container,
const char *subdir,
IcdDetails *details,
GError **error)
{
static const char options[] = "if-exists:if-same-abi";
g_autofree gchar *on_host = NULL;
g_autofree gchar *pattern = NULL;
g_autofree gchar *dependency_pattern = NULL;
g_autofree gchar *seq_str = NULL;
const char *mode;
g_autoptr(FlatpakBwrap) temp_bwrap = NULL;
g_return_val_if_fail (tool_path != NULL, FALSE);
g_return_val_if_fail (mount_runtime_on_scratch != NULL, FALSE);
g_return_val_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0,
FALSE);
g_return_val_if_fail (scratch != NULL, FALSE);
g_return_val_if_fail (libdir_on_host != NULL, FALSE);
g_return_val_if_fail (subdir != NULL, FALSE);
g_return_val_if_fail (multiarch_index < G_N_ELEMENTS (multiarch_tuples) - 1,
FALSE);
g_return_val_if_fail (details != NULL, FALSE);
g_return_val_if_fail (details->resolved_library != NULL, FALSE);
g_return_val_if_fail (details->kinds[multiarch_index] == ICD_KIND_NONEXISTENT,
FALSE);
g_return_val_if_fail (details->paths_in_container[multiarch_index] == NULL,
FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (g_path_is_absolute (details->resolved_library))
{
details->kinds[multiarch_index] = ICD_KIND_ABSOLUTE;
mode = "path";
/* Because the ICDs might have collisions among their
* basenames (might differ only by directory), we put each
* in its own numbered directory. */
seq_str = g_strdup_printf ("%" G_GSIZE_FORMAT, sequence_number);
on_host = g_build_filename (libdir_on_host, subdir, seq_str, NULL);
g_debug ("Ensuring %s exists", on_host);
if (g_mkdir_with_parents (on_host, 0700) != 0)
return glnx_throw_errno_prefix (error, "Unable to create %s", on_host);
}
else
{
/* ICDs in the default search path by definition can't collide:
* one of them is the first one we find, and we use that one. */
details->kinds[multiarch_index] = ICD_KIND_SONAME;
mode = "soname";
}
pattern = g_strdup_printf ("no-dependencies:even-if-older:%s:%s:%s",
options, mode, details->resolved_library);
dependency_pattern = g_strdup_printf ("only-dependencies:%s:%s:%s",
options, mode, details->resolved_library);
temp_bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", on_host == NULL ? libdir_on_host : on_host,
"--provider", "/",
pattern,
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
if (on_host != NULL)
{
/* Try to remove the directory we created. If it succeeds, then we
* can optimize slightly by not capturing the dependencies: there's
* no point, because we know we didn't create a symlink to the ICD
* itself. (It must have been nonexistent or for a different ABI.) */
if (g_rmdir (on_host) == 0)
{
details->kinds[multiarch_index] = ICD_KIND_NONEXISTENT;
return TRUE;
}
}
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
dependency_pattern,
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
if (details->kinds[multiarch_index] == ICD_KIND_ABSOLUTE)
{
g_assert (seq_str != NULL);
g_assert (on_host != NULL);
details->paths_in_container[multiarch_index] = g_build_filename (libdir_in_container,
subdir,
seq_str,
glnx_basename (details->resolved_library),
NULL);
}
return TRUE;
}
static gboolean
bind_runtime (FlatpakBwrap *bwrap,
const char *tools_dir,
const char *runtime,
const char *overrides,
const char *scratch,
GError **error)
{
static const char * const bind_mutable[] =
{
"etc",
"var/cache",
"var/lib"
};
static const char * const dont_bind[] =
{
"/etc/group",
"/etc/passwd",
"/etc/host.conf",
"/etc/hosts",
"/etc/localtime",
"/etc/machine-id",
"/etc/resolv.conf",
"/var/lib/dbus",
"/var/lib/dhcp",
"/var/lib/sudo",
"/var/lib/urandom",
NULL
};
g_autofree gchar *xrd = g_strdup_printf ("/run/user/%ld", (long) geteuid ());
gsize i, j;
const gchar *member;
g_autoptr(GString) dri_path = g_string_new ("");
g_autoptr(GString) egl_path = g_string_new ("");
g_autoptr(GString) vulkan_path = g_string_new ("");
gboolean any_architecture_works = FALSE;
gboolean any_libc_from_host = FALSE;
gboolean all_libc_from_host = TRUE;
gboolean all_libdrm_from_host = TRUE;
g_autoptr(GHashTable) libdrm_data_from_host = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
g_autofree gchar *libdrm_data_in_runtime = NULL;
g_autofree gchar *best_libdrm_data_from_host = NULL;
g_autofree gchar *localedef = NULL;
g_autofree gchar *dir_on_host = NULL;
g_autoptr(SrtSystemInfo) system_info = srt_system_info_new (NULL);
g_autoptr(SrtObjectList) egl_icds = NULL;
g_autoptr(SrtObjectList) vulkan_icds = NULL;
g_autoptr(GPtrArray) egl_icd_details = NULL; /* (element-type IcdDetails) */
g_autoptr(GPtrArray) vulkan_icd_details = NULL; /* (element-type IcdDetails) */
guint n_egl_icds;
guint n_vulkan_icds;
const GList *icd_iter;
g_return_val_if_fail (tools_dir != NULL, FALSE);
g_return_val_if_fail (runtime != NULL, FALSE);
g_return_val_if_fail (overrides != NULL, FALSE);
g_return_val_if_fail (scratch != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!pv_bwrap_bind_usr (bwrap, runtime, "/", error))
return FALSE;
flatpak_bwrap_add_args (bwrap,
"--setenv", "XDG_RUNTIME_DIR", xrd,
"--tmpfs", "/run",
"--tmpfs", "/tmp",
"--tmpfs", "/var",
"--symlink", "../run", "/var/run",
NULL);
for (i = 0; i < G_N_ELEMENTS (bind_mutable); i++)
{
g_autofree gchar *path = g_build_filename (runtime, bind_mutable[i],
NULL);
g_autoptr(GDir) dir = NULL;
dir = g_dir_open (path, 0, NULL);
if (dir == NULL)
continue;
for (member = g_dir_read_name (dir);
member != NULL;
member = g_dir_read_name (dir))
{
g_autofree gchar *dest = g_build_filename ("/", bind_mutable[i],
member, NULL);
g_autofree gchar *full = NULL;
g_autofree gchar *target = NULL;
if (g_strv_contains (dont_bind, dest))
continue;
full = g_build_filename (runtime, bind_mutable[i], member, NULL);
target = glnx_readlinkat_malloc (-1, full, NULL, NULL);
if (target != NULL)
flatpak_bwrap_add_args (bwrap, "--symlink", target, dest, NULL);
else
flatpak_bwrap_add_args (bwrap, "--ro-bind", full, dest, NULL);
}
}
if (g_file_test ("/etc/machine-id", G_FILE_TEST_EXISTS))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/machine-id", "/etc/machine-id",
"--symlink", "/etc/machine-id",
"/var/lib/dbus/machine-id",
NULL);
}
else if (g_file_test ("/var/lib/dbus/machine-id", G_FILE_TEST_EXISTS))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/var/lib/dbus/machine-id",
"/etc/machine-id",
"--symlink", "/etc/machine-id",
"/var/lib/dbus/machine-id",
NULL);
}
if (g_file_test ("/etc/resolv.conf", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/resolv.conf", "/etc/resolv.conf",
NULL);
if (g_file_test ("/etc/host.conf", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/host.conf", "/etc/host.conf",
NULL);
if (g_file_test ("/etc/hosts", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/hosts", "/etc/hosts",
NULL);
/* TODO: Synthesize a passwd with only the user and nobody,
* like Flatpak does? */
if (g_file_test ("/etc/passwd", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/passwd", "/etc/passwd",
NULL);
if (g_file_test ("/etc/group", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/group", "/etc/group",
NULL);
g_debug ("Enumerating EGL ICDs on host system...");
egl_icds = srt_system_info_list_egl_icds (system_info, multiarch_tuples);
n_egl_icds = g_list_length (egl_icds);
egl_icd_details = g_ptr_array_new_full (n_egl_icds,
(GDestroyNotify) G_CALLBACK (icd_details_free));
for (icd_iter = egl_icds, j = 0;
icd_iter != NULL;
icd_iter = icd_iter->next, j++)
{
SrtEglIcd *icd = icd_iter->data;
const gchar *path = srt_egl_icd_get_json_path (icd);
GError *local_error = NULL;
if (!srt_egl_icd_check_error (icd, &local_error))
{
g_debug ("Failed to load EGL ICD #%" G_GSIZE_FORMAT " from %s: %s",
j, path, local_error->message);
g_clear_error (&local_error);
continue;
}
g_debug ("EGL ICD #%" G_GSIZE_FORMAT " at %s: %s",
j, path, srt_egl_icd_get_library_path (icd));
g_ptr_array_add (egl_icd_details, icd_details_new (icd));
}
g_debug ("Enumerating Vulkan ICDs on host system...");
vulkan_icds = srt_system_info_list_vulkan_icds (system_info,
multiarch_tuples);
n_vulkan_icds = g_list_length (vulkan_icds);
vulkan_icd_details = g_ptr_array_new_full (n_vulkan_icds,
(GDestroyNotify) G_CALLBACK (icd_details_free));
for (icd_iter = vulkan_icds, j = 0;
icd_iter != NULL;
icd_iter = icd_iter->next, j++)
{
SrtVulkanIcd *icd = icd_iter->data;
const gchar *path = srt_vulkan_icd_get_json_path (icd);
GError *local_error = NULL;
if (!srt_vulkan_icd_check_error (icd, &local_error))
{
g_debug ("Failed to load Vulkan ICD #%" G_GSIZE_FORMAT " from %s: %s",
j, path, local_error->message);
g_clear_error (&local_error);
continue;
}
g_debug ("Vulkan ICD #%" G_GSIZE_FORMAT " at %s: %s",
j, path, srt_vulkan_icd_get_library_path (icd));
g_ptr_array_add (vulkan_icd_details, icd_details_new (icd));
}
g_assert (multiarch_tuples[G_N_ELEMENTS (multiarch_tuples) - 1] == NULL);
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
g_autofree gchar *tool = g_strdup_printf ("%s-capsule-capture-libs",
multiarch_tuples[i]);
g_autofree gchar *tool_path = NULL;
g_autofree gchar *ld_so = NULL;
const gchar *argv[] = { NULL, "--print-ld.so", NULL };
g_debug ("Checking for %s libraries...", multiarch_tuples[i]);
tool_path = g_build_filename (tools_dir, tool, NULL);
argv[0] = tool_path;
/* This has the side-effect of testing whether we can run binaries
* for this architecture on the host system. */
ld_so = pv_capture_output (argv, NULL);
if (ld_so != NULL)
{
g_auto(GStrv) dirs = NULL;
g_autoptr(FlatpakBwrap) mount_runtime_on_scratch = NULL;
g_autoptr(FlatpakBwrap) temp_bwrap = NULL;
g_autofree gchar *libdir_in_container = g_build_filename ("/overrides",
"lib",
multiarch_tuples[i],
NULL);
g_autofree gchar *libdir_on_host = g_build_filename (overrides, "lib",
multiarch_tuples[i],
NULL);
g_autofree gchar *this_dri_path_on_host = g_build_filename (libdir_on_host,
"dri", NULL);
g_autofree gchar *this_dri_path_in_container = g_build_filename (libdir_in_container,
"dri", NULL);
g_autofree gchar *libc = NULL;
g_autofree gchar *ld_so_in_runtime = NULL;
g_autofree gchar *libdrm = NULL;
const gchar *libqual = NULL;
temp_bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_args (temp_bwrap,
bwrap->argv->pdata[0],
NULL);
if (!pv_bwrap_bind_usr (temp_bwrap, runtime, "/", error))
return FALSE;
flatpak_bwrap_add_args (temp_bwrap,
"readlink", "-e", ld_so,
NULL);
flatpak_bwrap_finish (temp_bwrap);
ld_so_in_runtime = pv_capture_output ((const char * const *) temp_bwrap->argv->pdata,
NULL);
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
if (ld_so_in_runtime == NULL)
{
g_debug ("Container does not have %s so it cannot run "
"%s binaries", ld_so, multiarch_tuples[i]);
continue;
}
any_architecture_works = TRUE;
g_debug ("Container path: %s -> %s", ld_so, ld_so_in_runtime);
pv_search_path_append (dri_path, this_dri_path_in_container);
g_mkdir_with_parents (libdir_on_host, 0755);
g_mkdir_with_parents (this_dri_path_on_host, 0755);
mount_runtime_on_scratch = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_args (mount_runtime_on_scratch,
bwrap->argv->pdata[0],
"--ro-bind", "/", "/",
"--bind", overrides, overrides,
"--tmpfs", scratch,
NULL);
if (!pv_bwrap_bind_usr (mount_runtime_on_scratch, runtime, scratch,
error))
return FALSE;
g_debug ("Collecting GLX drivers from host system...");
temp_bwrap = flatpak_bwrap_new (NULL);
/* mount_runtime_on_scratch can't own any fds, because if it did,
* flatpak_bwrap_append_bwrap() would steal them. */
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
"gl:",
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
"if-exists:even-if-older:soname-match:libEGL.so.*",
"if-exists:even-if-older:soname-match:libEGL_nvidia.so.*",
"if-exists:even-if-older:soname-match:libGL.so.*",
"if-exists:even-if-older:soname-match:libGLESv1_CM.so.*",
"if-exists:even-if-older:soname-match:libGLESv1_CM_nvidia.so.*",
"if-exists:even-if-older:soname-match:libGLESv2.so.*",
"if-exists:even-if-older:soname-match:libGLESv2_nvidia.so.*",
"if-exists:even-if-older:soname-match:libGLX.so.*",
"if-exists:even-if-older:soname-match:libGLX_nvidia.so.*",
"if-exists:even-if-older:soname-match:libGLX_indirect.so.*",
"if-exists:even-if-older:soname-match:libGLdispatch.so.*",
"if-exists:even-if-older:soname-match:libOpenGL.so.*",
"if-exists:even-if-older:soname-match:libcuda.so.*",
"if-exists:even-if-older:soname-match:libglx.so.*",
"if-exists:even-if-older:soname-match:libnvidia-cbl.so.*",
"if-exists:even-if-older:soname-match:libnvidia-cfg.so.*",
"if-exists:even-if-older:soname-match:libnvidia-compiler.so.*",
"if-exists:even-if-older:soname-match:libnvidia-egl-wayland.so.*",
"if-exists:even-if-older:soname-match:libnvidia-eglcore.so.*",
"if-exists:even-if-older:soname-match:libnvidia-encode.so.*",
"if-exists:even-if-older:soname-match:libnvidia-fatbinaryloader.so.*",
"if-exists:even-if-older:soname-match:libnvidia-fbc.so.*",
"if-exists:even-if-older:soname-match:libnvidia-glcore.so.*",
"if-exists:even-if-older:soname-match:libnvidia-glsi.so.*",
"if-exists:even-if-older:soname-match:libnvidia-glvkspirv.so.*",
"if-exists:even-if-older:soname-match:libnvidia-ifr.so.*",
"if-exists:even-if-older:soname-match:libnvidia-ml.so.*",
"if-exists:even-if-older:soname-match:libnvidia-opencl.so.*",
"if-exists:even-if-older:soname-match:libnvidia-opticalflow.so.*",
"if-exists:even-if-older:soname-match:libnvidia-ptxjitcompiler.so.*",
"if-exists:even-if-older:soname-match:libnvidia-rtcore.so.*",
"if-exists:even-if-older:soname-match:libnvidia-tls.so.*",
"if-exists:even-if-older:soname-match:libOpenCL.so.*",
"if-exists:even-if-older:soname-match:libvdpau_nvidia.so.*",
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
g_debug ("Collecting %s EGL drivers from host system...",
multiarch_tuples[i]);
for (j = 0; j < egl_icd_details->len; j++)
{
IcdDetails *details = g_ptr_array_index (egl_icd_details, j);
SrtEglIcd *icd = SRT_EGL_ICD (details->icd);
if (!srt_egl_icd_check_error (icd, NULL))
continue;
details->resolved_library = srt_egl_icd_resolve_library_path (icd);
g_assert (details->resolved_library != NULL);
if (!bind_icd (i,
j,
tool_path,
mount_runtime_on_scratch,
scratch,
libdir_on_host,
libdir_in_container,
"glvnd",
details,
error))
return FALSE;
}
g_debug ("Collecting %s Vulkan drivers from host system...",
multiarch_tuples[i]);
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
"if-exists:if-same-abi:soname:libvulkan.so.1",
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
for (j = 0; j < vulkan_icd_details->len; j++)
{
IcdDetails *details = g_ptr_array_index (vulkan_icd_details, j);
SrtVulkanIcd *icd = SRT_VULKAN_ICD (details->icd);
if (!srt_vulkan_icd_check_error (icd, NULL))
continue;
details->resolved_library = srt_vulkan_icd_resolve_library_path (icd);
g_assert (details->resolved_library != NULL);
if (!bind_icd (i,
j,
tool_path,
mount_runtime_on_scratch,
scratch,
libdir_on_host,
libdir_in_container,
"vulkan",
details,
error))
return FALSE;
}
libc = g_build_filename (libdir_on_host, "libc.so.6", NULL);
/* If we are going to use the host system's libc6 (likely)
* then we have to use its ld.so too. */
if (g_file_test (libc, G_FILE_TEST_IS_SYMLINK))
{
g_autofree gchar *ld_so_in_host = NULL;
g_debug ("Making host ld.so visible in container");
ld_so_in_host = flatpak_canonicalize_filename (ld_so);
g_debug ("Host path: %s -> %s", ld_so, ld_so_in_host);
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
temp_bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_args (temp_bwrap,
bwrap->argv->pdata[0],
NULL);
if (!pv_bwrap_bind_usr (temp_bwrap, runtime, "/", error))
return FALSE;
flatpak_bwrap_add_args (temp_bwrap,
"readlink", "-f", ld_so,
NULL);
flatpak_bwrap_finish (temp_bwrap);
g_debug ("Container path: %s -> %s", ld_so, ld_so_in_runtime);
flatpak_bwrap_add_args (bwrap,
"--ro-bind", ld_so_in_host,
ld_so_in_runtime,
NULL);
/* Collect miscellaneous libraries that libc might dlopen.
* At the moment this is just libidn2. */
temp_bwrap = flatpak_bwrap_new (NULL);
g_warn_if_fail (mount_runtime_on_scratch->fds == NULL
|| mount_runtime_on_scratch->fds->len == 0);
flatpak_bwrap_append_bwrap (temp_bwrap, mount_runtime_on_scratch);
flatpak_bwrap_add_args (temp_bwrap,
tool_path,
"--container", scratch,
"--link-target", "/run/host",
"--dest", libdir_on_host,
"--provider", "/",
"if-exists:libidn2.so.0",
NULL);
flatpak_bwrap_finish (temp_bwrap);
if (!pv_bwrap_run_sync (temp_bwrap, NULL, error))
return FALSE;
g_clear_pointer (&temp_bwrap, flatpak_bwrap_free);
any_libc_from_host = TRUE;
}
else
{
all_libc_from_host = FALSE;
}
libdrm = g_build_filename (libdir_on_host, "libdrm.so.2", NULL);
/* If we have libdrm.so.2 in overrides we also want to mount
* ${prefix}/share/libdrm from the host. ${prefix} is derived from
* the absolute path of libdrm.so.2 */
if (g_file_test (libdrm, G_FILE_TEST_IS_SYMLINK))
{
g_autofree char *target = NULL;
target = glnx_readlinkat_malloc (-1, libdrm, NULL, NULL);
if (target != NULL)
{
g_autofree gchar *dir = NULL;
g_autofree gchar *lib_multiarch = NULL;
g_autofree gchar *libdrm_dir_in_host = NULL;
dir = g_path_get_dirname (target);
lib_multiarch = g_build_filename ("/lib", multiarch_tuples[i], NULL);
if (g_str_has_suffix (dir, lib_multiarch))
dir[strlen (dir) - strlen (lib_multiarch)] = '\0';
else if (g_str_has_suffix (dir, "/lib64"))
dir[strlen (dir) - strlen ("/lib64")] = '\0';
else if (g_str_has_suffix (dir, "/lib32"))
dir[strlen (dir) - strlen ("/lib32")] = '\0';
else if (g_str_has_suffix (dir, "/lib"))
dir[strlen (dir) - strlen ("/lib")] = '\0';
if (g_str_has_prefix (dir, "/run/host"))
memmove (dir, dir + strlen ("/run/host"), strlen (dir) - strlen ("/run/host") + 1);
libdrm_dir_in_host = g_build_filename (dir, "share", "libdrm", NULL);
if (g_file_test (libdrm_dir_in_host, G_FILE_TEST_IS_DIR))
{
g_hash_table_add (libdrm_data_from_host, g_steal_pointer (&libdrm_dir_in_host));
}
else
{
g_debug ("We were expecting to have the libdrm directory in the host "
"to be located in \"%s\", but instead it is missing",
libdrm_dir_in_host);
}
}
}
else
{
/* For at least a single architecture, libdrm is newer in the container */
all_libdrm_from_host = FALSE;
}
/* /lib32 or /lib64 */
g_assert (i < G_N_ELEMENTS (libquals));
libqual = libquals[i];
dirs = g_new0 (gchar *, 7);
dirs[0] = g_build_filename ("/lib", multiarch_tuples[i], NULL);
dirs[1] = g_build_filename ("/usr", "lib", multiarch_tuples[i], NULL);
dirs[2] = g_strdup ("/lib");
dirs[3] = g_strdup ("/usr/lib");
dirs[4] = g_build_filename ("/", libqual, NULL);
dirs[5] = g_build_filename ("/usr", libqual, NULL);
for (j = 0; j < 6; j++)
{
if (!try_bind_dri (bwrap, mount_runtime_on_scratch,
overrides, scratch, tool_path, dirs[j],
libdir_on_host, error))
return FALSE;
}
}
else
{
g_debug ("Cannot determine ld.so for %s", multiarch_tuples[i]);
}
}
if (!any_architecture_works)
{
GString *archs = g_string_new ("");
g_assert (multiarch_tuples[G_N_ELEMENTS (multiarch_tuples) - 1] == NULL);
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
if (archs->len > 0)
g_string_append (archs, ", ");
g_string_append (archs, multiarch_tuples[i]);
}
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"None of the supported CPU architectures are common to "
"the host system and the container (tried: %s)",
archs->str);
g_string_free (archs, TRUE);
return FALSE;
}
if (any_libc_from_host && !all_libc_from_host)
{
/*
* This shouldn't happen. It would mean that there exist at least
* two architectures (let's say aaa and bbb) for which we have:
* host libc6:aaa < container libc6 < host libc6:bbb
* (we know that the container's libc6:aaa and libc6:bbb are
* constrained to be the same version because that's how multiarch
* works).
*
* If the host system locales work OK with both the aaa and bbb
* versions, let's assume they will also work with the intermediate
* version from the container...
*/
g_warning ("Using glibc from host system for some but not all "
"architectures! Arbitrarily using host locales.");
}
if (any_libc_from_host)
{
g_debug ("Making host locale data visible in container");
if (g_file_test ("/usr/lib/locale", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/usr/lib/locale",
"/usr/lib/locale",
NULL);
if (g_file_test ("/usr/share/i18n", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/usr/share/i18n",
"/usr/share/i18n",
NULL);
localedef = g_find_program_in_path ("localedef");
if (localedef == NULL)
{
g_warning ("Cannot find localedef in PATH");
}
else
{
g_autofree gchar *target = g_build_filename ("/run/host",
localedef, NULL);
flatpak_bwrap_add_args (bwrap,
"--symlink", target,
"/overrides/bin/localedef",
NULL);
}
}
else
{
g_debug ("Using included locale data from container");
}
if (g_hash_table_size (libdrm_data_from_host) > 0 && !all_libdrm_from_host)
{
/* See the explanation in the similar "any_libc_from_host && !all_libc_from_host"
* case, above */
g_warning ("Using libdrm.so.2 from host system for some but not all "
"architectures! Will take /usr/share/libdrm from host.");
}
if (g_hash_table_size (libdrm_data_from_host) == 1)
{
best_libdrm_data_from_host = g_strdup (pv_hash_table_get_arbitrary_key (libdrm_data_from_host));
}
else if (g_hash_table_size (libdrm_data_from_host) > 1)
{
g_warning ("Found more than one possible libdrm data directory from host");
/* Prioritize "/usr/share/libdrm" if available. Otherwise randomly pick
* the first directory in the hash table */
if (g_hash_table_contains (libdrm_data_from_host, "/usr/share/libdrm"))
best_libdrm_data_from_host = g_strdup ("/usr/share/libdrm");
else
best_libdrm_data_from_host = g_strdup (pv_hash_table_get_arbitrary_key (libdrm_data_from_host));
}
g_autofree gchar *runtime_usr = g_build_filename (runtime, "usr", NULL);
if (g_file_test (runtime_usr, G_FILE_TEST_IS_DIR))
libdrm_data_in_runtime = g_build_filename (runtime_usr, "share", "libdrm", NULL);
else
libdrm_data_in_runtime = g_build_filename (runtime, "share", "libdrm", NULL);
if (best_libdrm_data_from_host != NULL &&
g_file_test (libdrm_data_in_runtime, G_FILE_TEST_IS_DIR))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", best_libdrm_data_from_host, "/usr/share/libdrm",
NULL);
}
g_debug ("Setting up EGL ICD JSON...");
dir_on_host = g_build_filename (overrides,
"share", "glvnd", "egl_vendor.d", NULL);
if (g_mkdir_with_parents (dir_on_host, 0700) != 0)
{
glnx_throw_errno_prefix (error, "Unable to create %s", dir_on_host);
return FALSE;
}
for (j = 0; j < egl_icd_details->len; j++)
{
IcdDetails *details = g_ptr_array_index (egl_icd_details, j);
SrtEglIcd *icd = SRT_EGL_ICD (details->icd);
gboolean need_host_json = FALSE;
if (!srt_egl_icd_check_error (icd, NULL))
continue;
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
g_assert (i < G_N_ELEMENTS (details->kinds));
g_assert (i < G_N_ELEMENTS (details->paths_in_container));
if (details->kinds[i] == ICD_KIND_ABSOLUTE)
{
g_autoptr(SrtEglIcd) replacement = NULL;
g_autofree gchar *json_on_host = NULL;
g_autofree gchar *json_in_container = NULL;
g_autofree gchar *json_base = NULL;
g_assert (details->paths_in_container[i] != NULL);
json_base = g_strdup_printf ("%" G_GSIZE_FORMAT "-%s.json",
j, multiarch_tuples[i]);
json_on_host = g_build_filename (dir_on_host, json_base, NULL);
json_in_container = g_build_filename ("/overrides", "share",
"glvnd", "egl_vendor.d",
json_base, NULL);
replacement = srt_egl_icd_new_replace_library_path (icd,
details->paths_in_container[i]);
if (!srt_egl_icd_write_to_file (replacement, json_on_host,
error))
return FALSE;
pv_search_path_append (egl_path, json_in_container);
}
else if (details->kinds[i] == ICD_KIND_SONAME)
{
need_host_json = TRUE;
}
}
if (need_host_json)
{
g_autofree gchar *json_in_container = NULL;
g_autofree gchar *json_base = NULL;
const char *json_on_host = srt_egl_icd_get_json_path (icd);
json_base = g_strdup_printf ("%" G_GSIZE_FORMAT ".json", j);
json_in_container = g_build_filename ("/overrides", "share",
"glvnd", "egl_vendor.d",
json_base, NULL);
flatpak_bwrap_add_args (bwrap,
"--ro-bind", json_on_host, json_in_container,
NULL);
pv_search_path_append (egl_path, json_in_container);
}
}
g_debug ("Setting up Vulkan ICD JSON...");
dir_on_host = g_build_filename (overrides,
"share", "vulkan", "icd.d", NULL);
if (g_mkdir_with_parents (dir_on_host, 0700) != 0)
{
glnx_throw_errno_prefix (error, "Unable to create %s", dir_on_host);
return FALSE;
}
for (j = 0; j < vulkan_icd_details->len; j++)
{
IcdDetails *details = g_ptr_array_index (vulkan_icd_details, j);
SrtVulkanIcd *icd = SRT_VULKAN_ICD (details->icd);
gboolean need_host_json = FALSE;
if (!srt_vulkan_icd_check_error (icd, NULL))
continue;
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
g_assert (i < G_N_ELEMENTS (details->kinds));
g_assert (i < G_N_ELEMENTS (details->paths_in_container));
if (details->kinds[i] == ICD_KIND_ABSOLUTE)
{
g_autoptr(SrtVulkanIcd) replacement = NULL;
g_autofree gchar *json_on_host = NULL;
g_autofree gchar *json_in_container = NULL;
g_autofree gchar *json_base = NULL;
g_assert (details->paths_in_container[i] != NULL);
json_base = g_strdup_printf ("%" G_GSIZE_FORMAT "-%s.json",
j, multiarch_tuples[i]);
json_on_host = g_build_filename (dir_on_host, json_base, NULL);
json_in_container = g_build_filename ("/overrides", "share",
"vulkan", "icd.d",
json_base, NULL);
replacement = srt_vulkan_icd_new_replace_library_path (icd,
details->paths_in_container[i]);
if (!srt_vulkan_icd_write_to_file (replacement, json_on_host,
error))
return FALSE;
pv_search_path_append (vulkan_path, json_in_container);
}
else if (details->kinds[i] == ICD_KIND_SONAME)
{
need_host_json = TRUE;
}
}
if (need_host_json)
{
g_autofree gchar *json_in_container = NULL;
g_autofree gchar *json_base = NULL;
const char *json_on_host = srt_vulkan_icd_get_json_path (icd);
json_base = g_strdup_printf ("%" G_GSIZE_FORMAT ".json", j);
json_in_container = g_build_filename ("/overrides", "share",
"vulkan", "icd.d",
json_base, NULL);
flatpak_bwrap_add_args (bwrap,
"--ro-bind", json_on_host, json_in_container,
NULL);
pv_search_path_append (vulkan_path, json_in_container);
}
}
if (!pv_bwrap_bind_usr (bwrap, "/", "/run/host", error))
return FALSE;
ensure_locales (any_libc_from_host, tools_dir, bwrap, overrides);
if (dri_path->len != 0)
flatpak_bwrap_add_args (bwrap,
"--setenv", "LIBGL_DRIVERS_PATH", dri_path->str,
NULL);
else
flatpak_bwrap_add_args (bwrap,
"--unsetenv", "LIBGL_DRIVERS_PATH",
NULL);
if (egl_path->len != 0)
flatpak_bwrap_add_args (bwrap,
"--setenv", "__EGL_VENDOR_LIBRARY_FILENAMES",
egl_path->str, NULL);
else
flatpak_bwrap_add_args (bwrap,
"--unsetenv", "__EGL_VENDOR_LIBRARY_FILENAMES",
NULL);
flatpak_bwrap_add_args (bwrap,
"--unsetenv", "__EGL_VENDOR_LIBRARY_DIRS",
NULL);
if (vulkan_path->len != 0)
flatpak_bwrap_add_args (bwrap,
"--setenv", "VK_ICD_FILENAMES",
vulkan_path->str, NULL);
else
flatpak_bwrap_add_args (bwrap,
"--unsetenv", "VK_ICD_FILENAMES",
NULL);
/* These can add data fds to @bwrap, so they must come last - after
* other functions stop using @bwrap as a basis for their own bwrap
* invocations with flatpak_bwrap_append_bwrap().
* Otherwise, when flatpak_bwrap_append_bwrap() calls
* flatpak_bwrap_steal_fds(), it will make the original FlatpakBwrap
* unusable. */
flatpak_run_add_wayland_args (bwrap);
flatpak_run_add_x11_args (bwrap, TRUE);
flatpak_run_add_pulseaudio_args (bwrap);
flatpak_run_add_session_dbus_args (bwrap);
flatpak_run_add_system_dbus_args (bwrap);
pv_bwrap_copy_tree (bwrap, overrides, "/overrides");
/* /etc/localtime and /etc/resolv.conf can not exist (or be symlinks to
* non-existing targets), in which case we don't want to attempt to create
* bogus symlinks or bind mounts, as that will cause flatpak run to fail.
*/
if (g_file_test ("/etc/localtime", G_FILE_TEST_EXISTS))
{
g_autofree char *target = NULL;
gboolean is_reachable = FALSE;
g_autofree char *tz = flatpak_get_timezone ();
g_autofree char *timezone_content = g_strdup_printf ("%s\n", tz);
target = glnx_readlinkat_malloc (-1, "/etc/localtime", NULL, NULL);
if (target != NULL)
{
g_autoptr(GFile) base_file = NULL;
g_autoptr(GFile) target_file = NULL;
g_autofree char *target_canonical = NULL;
base_file = g_file_new_for_path ("/etc");
target_file = g_file_resolve_relative_path (base_file, target);
target_canonical = g_file_get_path (target_file);
is_reachable = g_str_has_prefix (target_canonical, "/usr/");
}
if (is_reachable)
{
flatpak_bwrap_add_args (bwrap,
"--symlink", target, "/etc/localtime",
NULL);
}
else
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/etc/localtime", "/etc/localtime",
NULL);
}
flatpak_bwrap_add_args_data (bwrap, "timezone",
timezone_content, -1, "/etc/timezone",
NULL);
}
return TRUE;
}
gboolean
pv_runtime_bind (PvRuntime *self,
FlatpakBwrap *bwrap,
GError **error)
{
g_autofree gchar *pressure_vessel_prefix = NULL;
g_return_val_if_fail (PV_IS_RUNTIME (self), FALSE);
g_return_val_if_fail (!pv_bwrap_was_finished (bwrap), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (!bind_runtime (bwrap,
self->tools_dir,
self->source_files,
self->overrides,
self->scratch,
error))
return FALSE;
pressure_vessel_prefix = g_path_get_dirname (self->tools_dir);
/* Make sure pressure-vessel itself is visible there. */
flatpak_bwrap_add_args (bwrap,
"--ro-bind",
pressure_vessel_prefix,
"/run/pressure-vessel",
NULL);
return TRUE;
}
void
pv_runtime_set_search_paths (PvRuntime *self,
FlatpakBwrap *bwrap)
{
g_autoptr(GString) ld_library_path = g_string_new ("");
g_autoptr(GString) bin_path = g_string_new ("");
gsize i;
pv_search_path_append (bin_path, "/overrides/bin");
pv_search_path_append (bin_path, g_getenv ("PATH"));
flatpak_bwrap_add_args (bwrap,
"--setenv", "PATH",
bin_path->str,
NULL);
/* TODO: Adapt the use_ld_so_cache code from Flatpak instead
* of setting LD_LIBRARY_PATH, for better robustness against
* games that set their own LD_LIBRARY_PATH ignoring what they
* got from the environment */
g_assert (multiarch_tuples[G_N_ELEMENTS (multiarch_tuples) - 1] == NULL);
for (i = 0; i < G_N_ELEMENTS (multiarch_tuples) - 1; i++)
{
g_autofree gchar *ld_path = NULL;
ld_path = g_build_filename ("/overrides", "lib",
multiarch_tuples[i], NULL);
pv_search_path_append (ld_library_path, ld_path);
}
/* This would be filtered out by a setuid bwrap, so we have to go
* via --setenv. */
flatpak_bwrap_add_args (bwrap,
"--setenv", "LD_LIBRARY_PATH",
ld_library_path->str,
NULL);
}
static void static void
pv_runtime_initable_iface_init (GInitableIface *iface, pv_runtime_initable_iface_init (GInitableIface *iface,
gpointer unused G_GNUC_UNUSED) gpointer unused G_GNUC_UNUSED)
......
...@@ -20,12 +20,14 @@ ...@@ -20,12 +20,14 @@
#pragma once #pragma once
#include <glib.h> #include <glib.h>
#include <glib/gstdio.h>
#include <glib-object.h> #include <glib-object.h>
#include "glib-backports.h" #include "glib-backports.h"
#include "libglnx/libglnx.h" #include "libglnx/libglnx.h"
#include "flatpak-bwrap-private.h" #include "flatpak-bwrap-private.h"
#include "flatpak-utils-base-private.h"
typedef struct _PvRuntime PvRuntime; typedef struct _PvRuntime PvRuntime;
typedef struct _PvRuntimeClass PvRuntimeClass; typedef struct _PvRuntimeClass PvRuntimeClass;
...@@ -45,5 +47,11 @@ PvRuntime *pv_runtime_new (const char *source_files, ...@@ -45,5 +47,11 @@ PvRuntime *pv_runtime_new (const char *source_files,
void pv_runtime_append_lock_adverb (PvRuntime *self, void pv_runtime_append_lock_adverb (PvRuntime *self,
FlatpakBwrap *bwrap); FlatpakBwrap *bwrap);
void pv_runtime_set_search_paths (PvRuntime *self,
FlatpakBwrap *bwrap);
gboolean pv_runtime_bind (PvRuntime *self,
FlatpakBwrap *bwrap,
GError **error);
void pv_runtime_cleanup (PvRuntime *self);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PvRuntime, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (PvRuntime, g_object_unref)
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment