diff --git a/pressure-vessel/meson.build b/pressure-vessel/meson.build index 2534a40c948ec2c322bb163a92ecc7f48a4a53ef..f45622cf9be0022938a7edbd3fae91905eebc5d6 100644 --- a/pressure-vessel/meson.build +++ b/pressure-vessel/meson.build @@ -205,6 +205,8 @@ executable( 'runtime.c', 'runtime.h', 'wrap.c', + 'wrap-setup.c', + 'wrap-setup.h', ] + enums, c_args : pv_c_args, dependencies : [ diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c new file mode 100644 index 0000000000000000000000000000000000000000..5fb54a13f5c86179396195425fac3c94f9df447a --- /dev/null +++ b/pressure-vessel/wrap-setup.c @@ -0,0 +1,287 @@ +/* + * Copyright © 2014-2019 Red Hat, Inc + * Copyright © 2017-2021 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "wrap-setup.h" + +#include "steam-runtime-tools/glib-backports-internal.h" +#include "steam-runtime-tools/utils-internal.h" +#include "libglnx/libglnx.h" + +#include <string.h> + +#include "bwrap.h" +#include "flatpak-run-private.h" + +/* + * Use code borrowed from Flatpak to share various bits of the + * execution environment with the host system, in particular Wayland, + * X11 and PulseAudio sockets. + */ +void +pv_wrap_share_sockets (FlatpakBwrap *bwrap, + PvEnviron *container_env, + gboolean using_a_runtime, + gboolean is_flatpak_env) +{ + g_autoptr(FlatpakBwrap) sharing_bwrap = + flatpak_bwrap_new (flatpak_bwrap_empty_env); + g_auto(GStrv) envp = NULL; + gsize i; + + g_return_if_fail (bwrap != NULL); + g_return_if_fail (container_env != NULL); + + /* If these are set by flatpak_run_add_x11_args(), etc., we'll + * change them from locked-and-unset to locked-and-set later. + * Every variable that is unset with flatpak_bwrap_unset_env() in + * the functions we borrow from Flatpak (below) should be listed + * here. */ + pv_environ_lock_env (container_env, "DISPLAY", NULL); + pv_environ_lock_env (container_env, "PULSE_SERVER", NULL); + pv_environ_lock_env (container_env, "XAUTHORITY", NULL); + + flatpak_run_add_font_path_args (sharing_bwrap); + + /* We need to set up IPC rendezvous points relatively late, so that + * even if we are sharing /tmp via --filesystem=/tmp, we'll still + * mount our own /tmp/.X11-unix over the top of the OS's. */ + if (using_a_runtime) + { + flatpak_run_add_wayland_args (sharing_bwrap); + + /* When in a Flatpak container the "DISPLAY" env is equal to ":99.0", + * but it might be different on the host system. As a workaround we simply + * bind the whole "/tmp/.X11-unix" directory and later unset the container + * "DISPLAY" env. + */ + if (is_flatpak_env) + { + flatpak_bwrap_add_args (sharing_bwrap, + "--ro-bind", "/tmp/.X11-unix", "/tmp/.X11-unix", + NULL); + } + else + { + flatpak_run_add_x11_args (sharing_bwrap, TRUE); + } + + flatpak_run_add_pulseaudio_args (sharing_bwrap); + flatpak_run_add_session_dbus_args (sharing_bwrap); + flatpak_run_add_system_dbus_args (sharing_bwrap); + } + + envp = pv_bwrap_steal_envp (sharing_bwrap); + + for (i = 0; envp[i] != NULL; i++) + { + static const char * const known_vars[] = + { + "DBUS_SESSION_BUS_ADDRESS", + "DBUS_SYSTEM_BUS_ADDRESS", + "DISPLAY", + "PULSE_CLIENTCONFIG", + "PULSE_SERVER", + "XAUTHORITY", + }; + char *equals = strchr (envp[i], '='); + const char *var = envp[i]; + const char *val = NULL; + gsize j; + + if (equals != NULL) + { + *equals = '\0'; + val = equals + 1; + } + + for (j = 0; j < G_N_ELEMENTS (known_vars); j++) + { + if (strcmp (var, known_vars[j]) == 0) + break; + } + + /* If this warning is reached, we might need to add this + * variable to the block of + * pv_environ_lock_env (container_env, ., NULL) calls above */ + if (j >= G_N_ELEMENTS (known_vars)) + g_warning ("Extra environment variable %s set during container " + "setup but not in known_vars; check logic", + var); + + pv_environ_lock_env (container_env, var, val); + } + + g_warn_if_fail (g_strv_length (sharing_bwrap->envp) == 0); + flatpak_bwrap_append_bwrap (bwrap, sharing_bwrap); +} + +/* + * Export most root directories, but not the ones that + * "flatpak run --filesystem=host" would skip. + * (See flatpak_context_export(), which might replace this function + * later on.) + * + * If we are running inside Flatpak, we assume that any directory + * that is made available in the root, and is not in dont_mount_in_root, + * came in via --filesystem=host or similar and matches its equivalent + * on the real root filesystem. + */ +static gboolean +export_root_dirs_like_filesystem_host (FlatpakExports *exports, + FlatpakFilesystemMode mode, + GError **error) +{ + g_autoptr(GDir) dir = NULL; + const char *member = NULL; + + g_return_val_if_fail (exports != NULL, FALSE); + g_return_val_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + dir = g_dir_open ("/", 0, error); + + if (dir == NULL) + return FALSE; + + for (member = g_dir_read_name (dir); + member != NULL; + member = g_dir_read_name (dir)) + { + g_autofree gchar *path = NULL; + + if (g_strv_contains (dont_mount_in_root, member)) + continue; + + path = g_build_filename ("/", member, NULL); + flatpak_exports_add_path_expose (exports, mode, path); + } + + /* For parity with Flatpak's handling of --filesystem=host */ + flatpak_exports_add_path_expose (exports, mode, "/run/media"); + + return TRUE; +} + +/* + * This function assumes that /run on the host is the same as in the + * current namespace, so it won't work in Flatpak. + */ +static gboolean +export_contents_of_run (FlatpakBwrap *bwrap, + GError **error) +{ + static const char *ignore[] = + { + "gfx", /* can be created by pressure-vessel */ + "host", /* created by pressure-vessel */ + "media", /* see export_root_dirs_like_filesystem_host() */ + "pressure-vessel", /* created by pressure-vessel */ + NULL + }; + g_autoptr(GDir) dir = NULL; + const char *member = NULL; + + g_return_val_if_fail (bwrap != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (!g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR), + FALSE); + + dir = g_dir_open ("/run", 0, error); + + if (dir == NULL) + return FALSE; + + for (member = g_dir_read_name (dir); + member != NULL; + member = g_dir_read_name (dir)) + { + g_autofree gchar *path = NULL; + + if (g_strv_contains (ignore, member)) + continue; + + path = g_build_filename ("/run", member, NULL); + flatpak_bwrap_add_args (bwrap, + "--bind", path, path, + NULL); + } + + return TRUE; +} + +/* + * Configure @exports and @bwrap to use the host operating system to + * provide basically all directories. + * + * /app and /boot are excluded, but are assumed to be unnecessary. + * + * /dev, /proc and /sys are assumed to have been handled by + * pv_bwrap_add_api_filesystems() already. + */ +gboolean +pv_wrap_use_host_os (FlatpakExports *exports, + FlatpakBwrap *bwrap, + GError **error) +{ + static const char * const export_os_mutable[] = { "/etc", "/tmp", "/var" }; + gsize i; + + g_return_val_if_fail (exports != NULL, FALSE); + g_return_val_if_fail (bwrap != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!pv_bwrap_bind_usr (bwrap, "/", "/", "/", error)) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS (export_os_mutable); i++) + { + const char *dir = export_os_mutable[i]; + + if (g_file_test (dir, G_FILE_TEST_EXISTS)) + flatpak_bwrap_add_args (bwrap, "--bind", dir, dir, NULL); + } + + /* We do each subdirectory of /run separately, so that we can + * always create /run/host and /run/pressure-vessel. */ + if (!export_contents_of_run (bwrap, error)) + return FALSE; + + /* This handles everything except: + * + * /app (should be unnecessary) + * /boot (should be unnecessary) + * /dev (handled by pv_bwrap_add_api_filesystems()) + * /etc (handled by export_os_mutable above) + * /proc (handled by pv_bwrap_add_api_filesystems()) + * /root (should be unnecessary) + * /run (handled by export_contents_of_run() above) + * /sys (handled by pv_bwrap_add_api_filesystems()) + * /tmp (handled by export_os_mutable above) + * /usr, /lib, /lib32, /lib64, /bin, /sbin + * (all handled by pv_bwrap_bind_usr() above) + * /var (handled by export_os_mutable above) + */ + if (!export_root_dirs_like_filesystem_host (exports, + FLATPAK_FILESYSTEM_MODE_READ_WRITE, + error)) + return FALSE; + + return TRUE; +} diff --git a/pressure-vessel/wrap-setup.h b/pressure-vessel/wrap-setup.h new file mode 100644 index 0000000000000000000000000000000000000000..9dc1f5f7fb43948bd0ede19a33e4714ff7679886 --- /dev/null +++ b/pressure-vessel/wrap-setup.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2014-2019 Red Hat, Inc + * Copyright © 2017-2021 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <glib.h> + +#include "environ.h" + +#include "flatpak-bwrap-private.h" +#include "flatpak-exports-private.h" + +void pv_wrap_share_sockets (FlatpakBwrap *bwrap, + PvEnviron *container_env, + gboolean using_a_runtime, + gboolean is_flatpak_env); + +gboolean pv_wrap_use_host_os (FlatpakExports *exports, + FlatpakBwrap *bwrap, + GError **error); diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c index e6a55b1e06c637d31b3432acb20bfb67d8a71f80..0f5bd8091a5b3693f92f14f256781760fcf7e7aa 100644 --- a/pressure-vessel/wrap.c +++ b/pressure-vessel/wrap.c @@ -44,6 +44,7 @@ #include "runtime.h" #include "utils.h" #include "wrap-interactive.h" +#include "wrap-setup.h" /* List of variables that are stripped down from the environment when * using the secure-execution mode. @@ -255,100 +256,6 @@ check_launch_on_host (const char *launch_executable, return TRUE; } -/* - * Export most root directories, but not the ones that - * "flatpak run --filesystem=host" would skip. - * (See flatpak_context_export(), which might replace this function - * later on.) - * - * If we are running inside Flatpak, we assume that any directory - * that is made available in the root, and is not in dont_mount_in_root, - * came in via --filesystem=host or similar and matches its equivalent - * on the real root filesystem. - */ -static gboolean -export_root_dirs_like_filesystem_host (FlatpakExports *exports, - FlatpakFilesystemMode mode, - GError **error) -{ - g_autoptr(GDir) dir = NULL; - const char *member = NULL; - - g_return_val_if_fail (exports != NULL, FALSE); - g_return_val_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - dir = g_dir_open ("/", 0, error); - - if (dir == NULL) - return FALSE; - - for (member = g_dir_read_name (dir); - member != NULL; - member = g_dir_read_name (dir)) - { - g_autofree gchar *path = NULL; - - if (g_strv_contains (dont_mount_in_root, member)) - continue; - - path = g_build_filename ("/", member, NULL); - flatpak_exports_add_path_expose (exports, mode, path); - } - - /* For parity with Flatpak's handling of --filesystem=host */ - flatpak_exports_add_path_expose (exports, mode, "/run/media"); - - return TRUE; -} - -/* - * This function assumes that /run on the host is the same as in the - * current namespace, so it won't work in Flatpak. - */ -static gboolean -export_contents_of_run (FlatpakBwrap *bwrap, - GError **error) -{ - static const char *ignore[] = - { - "gfx", /* can be created by pressure-vessel */ - "host", /* created by pressure-vessel */ - "media", /* see export_root_dirs_like_filesystem_host() */ - "pressure-vessel", /* created by pressure-vessel */ - NULL - }; - g_autoptr(GDir) dir = NULL; - const char *member = NULL; - - g_return_val_if_fail (bwrap != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_return_val_if_fail (!g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR), - FALSE); - - dir = g_dir_open ("/run", 0, error); - - if (dir == NULL) - return FALSE; - - for (member = g_dir_read_name (dir); - member != NULL; - member = g_dir_read_name (dir)) - { - g_autofree gchar *path = NULL; - - if (g_strv_contains (ignore, member)) - continue; - - path = g_build_filename ("/run", member, NULL); - flatpak_bwrap_add_args (bwrap, - "--bind", path, path, - NULL); - } - - return TRUE; -} - /* Nvidia Vulkan ray-tracing requires to load the `nvidia_uvm.ko` kernel * module, and this is usually done in `libcuda.so.1` by running the setuid * binary `nvidia-modprobe`. But when we are inside a container we don't bind @@ -1959,44 +1866,9 @@ main (int argc, } else { - static const char * const export_os_mutable[] = { "/etc", "/tmp", "/var" }; - g_assert (!is_flatpak_env); - if (!pv_bwrap_bind_usr (bwrap, "/", "/", "/", error)) - goto out; - - for (i = 0; i < G_N_ELEMENTS (export_os_mutable); i++) - { - const char *dir = export_os_mutable[i]; - - if (g_file_test (dir, G_FILE_TEST_EXISTS)) - flatpak_bwrap_add_args (bwrap, "--bind", dir, dir, NULL); - } - - /* We do each subdirectory of /run separately, so that we can - * always create /run/host and /run/pressure-vessel. */ - if (!export_contents_of_run (bwrap, error)) - goto out; - - /* This handles everything except: - * - * /app (should be unnecessary) - * /boot (should be unnecessary) - * /dev (handled by pv_bwrap_add_api_filesystems() above) - * /etc (handled by export_os_mutable) - * /proc (handled by pv_bwrap_add_api_filesystems() above) - * /root (should be unnecessary) - * /run (handled by export_contents_of_run()) - * /sys (handled by pv_bwrap_add_api_filesystems() above) - * /tmp (handled by export_os_mutable) - * /usr, /lib, /lib32, /lib64, /bin, /sbin - * (all handled by pv_bwrap_bind_usr() above) - * /var (handled by export_os_mutable) - */ - if (!export_root_dirs_like_filesystem_host (exports, - FLATPAK_FILESYSTEM_MODE_READ_WRITE, - error)) + if (!pv_wrap_use_host_os (exports, bwrap, error)) goto out; } @@ -2254,96 +2126,10 @@ main (int argc, flatpak_bwrap_append_bwrap (bwrap, exports_bwrap); } - /* Use code borrowed from Flatpak to share various other bits of the - * execution environment with the host system */ - { - g_autoptr(FlatpakBwrap) sharing_bwrap = - flatpak_bwrap_new (flatpak_bwrap_empty_env); - g_auto(GStrv) envp = NULL; - - /* If these are set by flatpak_run_add_x11_args(), etc., we'll - * change them from locked-and-unset to locked-and-set later. - * Every variable that is unset with flatpak_bwrap_unset_env() in - * the functions we borrow from Flatpak (below) should be listed - * here. */ - pv_environ_lock_env (container_env, "DISPLAY", NULL); - pv_environ_lock_env (container_env, "PULSE_SERVER", NULL); - pv_environ_lock_env (container_env, "XAUTHORITY", NULL); - - flatpak_run_add_font_path_args (sharing_bwrap); - - /* We need to set up IPC rendezvous points relatively late, so that - * even if we are sharing /tmp via --filesystem=/tmp, we'll still - * mount our own /tmp/.X11-unix over the top of the OS's. */ - if (runtime != NULL) - { - flatpak_run_add_wayland_args (sharing_bwrap); - - /* When in a Flatpak container the "DISPLAY" env is equal to ":99.0", - * but it might be different on the host system. As a workaround we simply - * bind the whole "/tmp/.X11-unix" directory and later unset the container - * "DISPLAY" env. - */ - if (is_flatpak_env) - { - flatpak_bwrap_add_args (sharing_bwrap, - "--ro-bind", "/tmp/.X11-unix", "/tmp/.X11-unix", - NULL); - } - else - { - flatpak_run_add_x11_args (sharing_bwrap, TRUE); - } - - flatpak_run_add_pulseaudio_args (sharing_bwrap); - flatpak_run_add_session_dbus_args (sharing_bwrap); - flatpak_run_add_system_dbus_args (sharing_bwrap); - } - - envp = pv_bwrap_steal_envp (sharing_bwrap); - - for (i = 0; envp[i] != NULL; i++) - { - static const char * const known_vars[] = - { - "DBUS_SESSION_BUS_ADDRESS", - "DBUS_SYSTEM_BUS_ADDRESS", - "DISPLAY", - "PULSE_CLIENTCONFIG", - "PULSE_SERVER", - "XAUTHORITY", - }; - char *equals = strchr (envp[i], '='); - const char *var = envp[i]; - const char *val = NULL; - gsize j; - - if (equals != NULL) - { - *equals = '\0'; - val = equals + 1; - } - - for (j = 0; j < G_N_ELEMENTS (known_vars); j++) - { - if (strcmp (var, known_vars[j]) == 0) - break; - } - - /* If this warning is reached, we might need to add this - * variable to the block of - * pv_environ_lock_env (container_env, ., NULL) calls above */ - if (j >= G_N_ELEMENTS (known_vars)) - g_warning ("Extra environment variable %s set during container " - "setup but not in known_vars; check logic", - var); - - pv_environ_lock_env (container_env, var, val); - } - - g_warn_if_fail (g_strv_length (sharing_bwrap->envp) == 0); - flatpak_bwrap_append_bwrap (bwrap, sharing_bwrap); - } + if (bwrap != NULL) + pv_wrap_share_sockets (bwrap, container_env, + (runtime != NULL), + is_flatpak_env); if (is_flatpak_env) {