diff --git a/pressure-vessel/meson.build b/pressure-vessel/meson.build index f45622cf9be0022938a7edbd3fae91905eebc5d6..757e68af49eb98471d9eaf1926a73c2114eb6cd1 100644 --- a/pressure-vessel/meson.build +++ b/pressure-vessel/meson.build @@ -205,6 +205,8 @@ executable( 'runtime.c', 'runtime.h', 'wrap.c', + 'wrap-flatpak.c', + 'wrap-flatpak.h', 'wrap-setup.c', 'wrap-setup.h', ] + enums, diff --git a/pressure-vessel/wrap-flatpak.c b/pressure-vessel/wrap-flatpak.c new file mode 100644 index 0000000000000000000000000000000000000000..f8dbb49e3b8a7a77c7f669cdb4b4ad5b4fec4135 --- /dev/null +++ b/pressure-vessel/wrap-flatpak.c @@ -0,0 +1,161 @@ +/* + * Copyright © 2020-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-flatpak.h" + +#include "libglnx/libglnx.h" + +#include "utils.h" + +static gboolean +check_launch_on_host (const char *launch_executable, + GError **error) +{ + g_autofree gchar *child_stdout = NULL; + g_autofree gchar *child_stderr = NULL; + int wait_status; + const char *test_argv[] = + { + NULL, + "--bus-name=org.freedesktop.Flatpak", + "--", + "true", + NULL + }; + + test_argv[0] = launch_executable; + + if (!g_spawn_sync (NULL, /* cwd */ + (gchar **) test_argv, + NULL, /* environ */ + G_SPAWN_LEAVE_DESCRIPTORS_OPEN, + NULL, NULL, /* child setup */ + &child_stdout, + &child_stderr, + &wait_status, + error)) + { + return FALSE; + } + + if (wait_status != 0) + { + pv_log_failure ("Cannot run commands on host system: wait status %d", + wait_status); + + if (child_stdout != NULL && child_stdout[0] != '\0') + pv_log_failure ("Output:\n%s", child_stdout); + + if (child_stderr != NULL && child_stderr[0] != '\0') + pv_log_failure ("Diagnostic output:\n%s", child_stderr); + + return glnx_throw (error, "Unable to run a command on the host system"); + } + + return TRUE; +} + +/* + * pv_wrap_check_flatpak: + * @tools_dir: Path to .../pressure-vessel/bin/ + * @subsandbox_out: (out) (not optional) (nullable): Used to return + * an adverb command that will launch its arguments in a sub-sandbox, + * or %NULL if not using sub-sandboxing + * @run_on_host_out: (out) (not optional) (nullable): Used to return + * an adverb command that will launch its arguments on the host system, + * or %NULL if not using sandbox escape + * + * Check that we are running under Flatpak and can launch the game somehow. + * On success, exactly one of the `(out)` parameters will be set to non-%NULL. + * + * Returns: %TRUE on success + */ +gboolean +pv_wrap_check_flatpak (const char *tools_dir, + FlatpakBwrap **subsandbox_out, + FlatpakBwrap **run_on_host_out, + GError **error) +{ + g_autoptr(FlatpakBwrap) subsandbox = NULL; + g_autoptr(FlatpakBwrap) run_on_host = NULL; + g_autofree gchar *launch_executable = NULL; + + g_return_val_if_fail (tools_dir != NULL, FALSE); + g_return_val_if_fail (subsandbox_out != NULL, FALSE); + g_return_val_if_fail (*subsandbox_out == NULL, FALSE); + g_return_val_if_fail (run_on_host_out != NULL, FALSE); + g_return_val_if_fail (*run_on_host_out == NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + launch_executable = g_build_filename (tools_dir, + "pressure-vessel-launch", + NULL); + + /* Deliberately not documented: only people who are in a position + * to run their own modified versions of Flatpak and pressure-vessel + * should be using this, and those people can find this in the + * source code */ + if (g_getenv ("PRESSURE_VESSEL_FLATPAK_PR4018") != NULL) + { + g_warning ("Assuming your version of Flatpak contains unmerged " + "changes (#4018, #4125, #4126, #4093)"); + /* Use a sub-sandbox */ + subsandbox = flatpak_bwrap_new (flatpak_bwrap_empty_env); + flatpak_bwrap_add_arg (subsandbox, launch_executable); + /* Tell pressure-vessel-launch to send its whole environment + * to the subsandbox, except for the parts that we edit later. + * This effectively matches bwrap's behaviour. */ + flatpak_bwrap_add_arg (subsandbox, "--pass-env-matching=*"); + flatpak_bwrap_add_arg (subsandbox, + "--bus-name=org.freedesktop.portal.Flatpak"); + } + /* Also deliberately not documented */ + else if (g_getenv ("PRESSURE_VESSEL_FLATPAK_SANDBOX_ESCAPE") != NULL) + { + g_warning ("Assuming permissions have been set to allow Steam " + "to escape from the Flatpak sandbox"); + + /* If we have permission to escape from the sandbox, we'll do that, + * and launch bwrap that way */ + run_on_host = flatpak_bwrap_new (flatpak_bwrap_empty_env); + flatpak_bwrap_add_arg (run_on_host, + launch_executable); + flatpak_bwrap_add_arg (run_on_host, + "--bus-name=org.freedesktop.Flatpak"); + + /* If we can't launch a command on the host, just fail. */ + if (!check_launch_on_host (launch_executable, error)) + return FALSE; + } + else + { + return glnx_throw (error, + "pressure-vessel (SteamLinuxRuntime) cannot be run " + "in a Flatpak environment. For Proton 5.13+, " + "unofficial community builds that do not use " + "pressure-vessel are available."); + } + + /* Exactly one of them is non-NULL on success */ + g_assert ((subsandbox != NULL) + (run_on_host != NULL) == 1); + + *subsandbox_out = g_steal_pointer (&subsandbox); + *run_on_host_out = g_steal_pointer (&run_on_host); + return TRUE; +} diff --git a/pressure-vessel/wrap-flatpak.h b/pressure-vessel/wrap-flatpak.h new file mode 100644 index 0000000000000000000000000000000000000000..f513b493ed82c962c941195e20c2aca4de500d14 --- /dev/null +++ b/pressure-vessel/wrap-flatpak.h @@ -0,0 +1,31 @@ +/* + * Copyright © 2020-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 "libglnx/libglnx.h" + +#include "flatpak-bwrap-private.h" + +gboolean pv_wrap_check_flatpak (const char *tools_dir, + FlatpakBwrap **flatpak_subsandbox, + FlatpakBwrap **flatpak_run_on_host, + GError **error); diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c index 6f938585825b401bdd6d157c27655d695ad3c182..38034ed22dfaf76be404b14819383ca9f2d0042f 100644 --- a/pressure-vessel/wrap.c +++ b/pressure-vessel/wrap.c @@ -43,6 +43,7 @@ #include "flatpak-utils-private.h" #include "runtime.h" #include "utils.h" +#include "wrap-flatpak.h" #include "wrap-interactive.h" #include "wrap-setup.h" @@ -208,54 +209,6 @@ check_bwrap (const char *tools_dir, return NULL; } -static gboolean -check_launch_on_host (const char *launch_executable, - GError **error) -{ - g_autofree gchar *child_stdout = NULL; - g_autofree gchar *child_stderr = NULL; - int wait_status; - const char *test_argv[] = - { - NULL, - "--bus-name=org.freedesktop.Flatpak", - "--", - "true", - NULL - }; - - test_argv[0] = launch_executable; - - if (!g_spawn_sync (NULL, /* cwd */ - (gchar **) test_argv, - NULL, /* environ */ - G_SPAWN_LEAVE_DESCRIPTORS_OPEN, - NULL, NULL, /* child setup */ - &child_stdout, - &child_stderr, - &wait_status, - error)) - { - return FALSE; - } - - if (wait_status != 0) - { - pv_log_failure ("Cannot run commands on host system: wait status %d", - wait_status); - - if (child_stdout != NULL && child_stdout[0] != '\0') - pv_log_failure ("Output:\n%s", child_stdout); - - if (child_stderr != NULL && child_stderr[0] != '\0') - pv_log_failure ("Diagnostic output:\n%s", child_stderr); - - return glnx_throw (error, "Unable to run a command on the host system"); - } - - 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 @@ -1368,7 +1321,6 @@ main (int argc, g_autoptr(FlatpakBwrap) argv_in_container = NULL; g_autoptr(FlatpakBwrap) final_argv = NULL; g_autoptr(FlatpakExports) exports = NULL; - g_autofree gchar *launch_executable = NULL; g_autofree gchar *bwrap_executable = NULL; g_autofree gchar *cwd_p = NULL; g_autofree gchar *cwd_l = NULL; @@ -1797,60 +1749,14 @@ main (int argc, /* If we are in a Flatpak environment we can't use bwrap directly */ if (is_flatpak_env) { + if (!pv_wrap_check_flatpak (tools_dir, + &flatpak_subsandbox, + &flatpak_run_on_host, + error)) + goto out; - launch_executable = g_build_filename (tools_dir, - "pressure-vessel-launch", - NULL); /* Assume "bwrap" to exist in the host system and to be in its PATH */ bwrap_executable = g_strdup ("bwrap"); - - /* Deliberately not documented: only people who are in a position - * to run their own modified versions of Flatpak and pressure-vessel - * should be using this, and those people can find this in the - * source code */ - if (g_getenv ("PRESSURE_VESSEL_FLATPAK_PR4018") != NULL) - { - g_warning ("Assuming your version of Flatpak contains unmerged " - "changes (#4018, #4125, #4126, #4093)"); - - /* Use a sub-sandbox */ - flatpak_subsandbox = flatpak_bwrap_new (flatpak_bwrap_empty_env); - flatpak_bwrap_add_arg (flatpak_subsandbox, - launch_executable); - /* Tell pressure-vessel-launch to send its whole environment - * to the subsandbox, except for the parts that we edit later. - * This effectively matches bwrap's behaviour. */ - flatpak_bwrap_add_arg (flatpak_subsandbox, "--pass-env-matching=*"); - flatpak_bwrap_add_arg (flatpak_subsandbox, - "--bus-name=org.freedesktop.portal.Flatpak"); - } - /* Also deliberately not documented */ - else if (g_getenv ("PRESSURE_VESSEL_FLATPAK_SANDBOX_ESCAPE") != NULL) - { - g_warning ("Assuming permissions have been set to allow Steam " - "to escape from the Flatpak sandbox"); - - /* If we have permission to escape from the sandbox, we'll do that, - * and launch bwrap that way */ - flatpak_run_on_host = flatpak_bwrap_new (flatpak_bwrap_empty_env); - flatpak_bwrap_add_arg (flatpak_run_on_host, - launch_executable); - flatpak_bwrap_add_arg (flatpak_run_on_host, - "--bus-name=org.freedesktop.Flatpak"); - - /* If we can't launch a command on the host, just fail. */ - if (!check_launch_on_host (launch_executable, error)) - goto out; - } - else - { - glnx_throw (error, - "pressure-vessel (SteamLinuxRuntime) cannot be run " - "in a Flatpak environment. For Proton 5.13+, " - "unofficial community builds that do not use " - "pressure-vessel are available."); - goto out; - } } else {