Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • steamrt/steam-runtime-tools
1 result
Show changes
Commits on Source (7)
......@@ -52,4 +52,8 @@ GFile *flatpak_get_data_dir (const char *app_id);
void flatpak_run_add_font_path_args (FlatpakBwrap *bwrap);
extern const char * const *flatpak_abs_usrmerged_dirs;
int open_namespace_fd_if_needed (const char *path,
const char *other_path);
#endif /* __FLATPAK_RUN_H__ */
......@@ -3418,8 +3418,9 @@ check_parental_controls (const char *app_ref,
return TRUE;
}
#endif
static int
int
open_namespace_fd_if_needed (const char *path,
const char *other_path) {
struct stat s, other_s;
......@@ -3438,6 +3439,7 @@ open_namespace_fd_if_needed (const char *path,
return -1;
}
#if 0
static gboolean
check_sudo (GError **error)
{
......
......@@ -60,9 +60,7 @@ as a subprocess of **pressure-vessel-launcher**.
owned by the **flatpak-session-helper** per-user service, and the
*COMMAND* is launched on the host system, similar to the
**--host** option of **flatpak-spawn**(1).
The **--terminate** option is not allowed in this mode, and most
options that would unset environment variables, such as **--unset-env**,
are ignored with a warning (but **--clear-env** is still possible).
The **--terminate** option is not allowed in this mode.
As another special case, if the *NAME* is
**org.freedesktop.portal.Flatpak**, then it is assumed to be
......@@ -72,8 +70,7 @@ as a subprocess of **pressure-vessel-launcher**.
Options that alter how the sub-sandbox is created, such as
**--sandbox-flag**, are not currently supported.
As with **org.freedesktop.Flatpak**, the
**--terminate** option is not allowed in this mode, and most
options that would unset environment variables are ignored with a warning.
**--terminate** option is not allowed in this mode.
**--clear-env**
: The *COMMAND* runs in an empty environment, apart from any environment
......
......@@ -534,6 +534,7 @@ main (int argc,
g_auto(GStrv) original_environ = NULL;
g_autoptr(GMainLoop) loop = NULL;
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GPtrArray) replacement_command_and_args = NULL;
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;
char **command_and_args;
......@@ -865,14 +866,14 @@ main (int argc,
if (g_hash_table_size (opt_unsetenv) > 0)
{
GVariantBuilder strv_builder;
g_variant_builder_init (&strv_builder, G_VARIANT_TYPE_STRING_ARRAY);
g_hash_table_iter_init (&iter, opt_unsetenv);
if (api == &launcher_api)
{
GVariantBuilder strv_builder;
g_variant_builder_init (&strv_builder, G_VARIANT_TYPE_STRING_ARRAY);
while (g_hash_table_iter_next (&iter, &key, NULL))
g_variant_builder_add (&strv_builder, "s", key);
......@@ -881,9 +882,29 @@ main (int argc,
}
else
{
replacement_command_and_args = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (replacement_command_and_args, g_strdup ("/usr/bin/env"));
while (g_hash_table_iter_next (&iter, &key, NULL))
g_warning ("Cannot unset %s when using Flatpak services",
(const char *) key);
{
g_ptr_array_add (replacement_command_and_args, g_strdup ("-u"));
g_ptr_array_add (replacement_command_and_args, g_strdup (key));
}
if (strchr (command_and_args[0], '=') != NULL)
{
g_ptr_array_add (replacement_command_and_args, g_strdup ("/bin/sh"));
g_ptr_array_add (replacement_command_and_args, g_strdup ("-euc"));
g_ptr_array_add (replacement_command_and_args, g_strdup ("exec \"$@\""));
g_ptr_array_add (replacement_command_and_args, g_strdup ("sh")); /* argv[0] */
}
for (i = 0; command_and_args[i] != NULL; i++)
g_ptr_array_add (replacement_command_and_args, g_strdup (command_and_args[i]));
g_ptr_array_add (replacement_command_and_args, NULL);
command_and_args = (char **) replacement_command_and_args->pdata;
}
}
......
......@@ -795,6 +795,50 @@ pv_terminate_all_child_processes (GTimeSpan wait_period,
return TRUE;
}
/*
* @str: A path
* @prefix: A possible prefix
*
* The same as flatpak_has_path_prefix(), but instead of a boolean,
* return the part of @str after @prefix (non-%NULL but possibly empty)
* if @str has prefix @prefix, or %NULL if it does not.
*
* Returns: (nullable) (transfer none): the part of @str after @prefix,
* or %NULL if @str is not below @prefix
*/
const char *
pv_get_path_after (const char *str,
const char *prefix)
{
while (TRUE)
{
/* Skip consecutive slashes to reach next path
element */
while (*str == '/')
str++;
while (*prefix == '/')
prefix++;
/* No more prefix path elements? Done! */
if (*prefix == 0)
return str;
/* Compare path element */
while (*prefix != 0 && *prefix != '/')
{
if (*str != *prefix)
return NULL;
str++;
prefix++;
}
/* Matched prefix path element,
must be entire str path element */
if (*str != '/' && *str != 0)
return NULL;
}
}
/**
* pv_current_namespace_path_to_host_path:
* @current_env_path: a path in the current environment
......@@ -807,40 +851,32 @@ gchar *
pv_current_namespace_path_to_host_path (const gchar *current_env_path)
{
gchar *path_on_host = NULL;
g_autofree gchar *home_env_guarded = NULL;
const gchar *home_env = g_getenv ("HOME");
g_return_val_if_fail (g_path_is_absolute (current_env_path),
g_strdup (current_env_path));
if (home_env == NULL)
home_env = g_get_home_dir ();
if (home_env != NULL)
{
/* Avoid the edge case where e.g. current_env_path is
* '/home/melanie/Games' and home_env is '/home/me' */
if (g_str_has_suffix (home_env, "/"))
home_env_guarded = g_strdup (home_env);
else
home_env_guarded = g_strdup_printf ("%s/", home_env);
}
if (g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR))
{
struct stat via_current_env_stat;
struct stat via_persist_stat;
const gchar *home = g_getenv ("HOME");
const gchar *after = NULL;
if (home == NULL)
home = g_get_home_dir ();
if (home != NULL)
after = pv_get_path_after (current_env_path, home);
/* If we are inside a Flatpak container, usually, the home
* folder is '${HOME}/.var/app/${FLATPAK_ID}' on the host system */
if (home_env != NULL
&& g_str_has_prefix (current_env_path, home_env_guarded))
if (after != NULL)
{
path_on_host = g_build_filename (home_env,
path_on_host = g_build_filename (home,
".var",
"app",
g_getenv ("FLATPAK_ID"),
current_env_path + strlen (home_env),
after,
NULL);
if (lstat (path_on_host, &via_persist_stat) < 0)
......@@ -860,12 +896,12 @@ pv_current_namespace_path_to_host_path (const gchar *current_env_path)
}
}
after = pv_get_path_after (current_env_path, "/run/host");
/* In a Flatpak container, usually, '/run/host' is the root of the
* host system */
if (g_str_has_prefix (current_env_path, "/run/host/"))
path_on_host = g_strdup (current_env_path + strlen ("/run/host"));
else if (g_strcmp0 (current_env_path, "/run/host") == 0)
path_on_host = g_strdup ("/");
if (after != NULL && path_on_host == NULL)
path_on_host = g_build_filename ("/", after, NULL);
}
/* Either we are not in a Flatpak container or it's not obvious how the
* container to host translation should happen. Just keep the same path. */
......
......@@ -70,3 +70,6 @@ gboolean pv_terminate_all_child_processes (GTimeSpan wait_period,
GError **error);
gchar *pv_current_namespace_path_to_host_path (const gchar *current_env_path);
const char *pv_get_path_after (const char *str,
const char *prefix);
......@@ -58,12 +58,6 @@ pressure-vessel-wrap - run programs in a bubblewrap container
`--home` *DIR*
: Use *DIR* as the home directory. This implies `--unshare-home`.
`--host-fallback`
: If **bwrap**(1) cannot be run, attempt to run *COMMAND* in the
current execution environment using the `LD_LIBRARY_PATH`
Steam Runtime instead. In practice this will only work for
Steam Runtime version 1, "scout".
`--host-ld-preload` *MODULE*
: Add *MODULE* from the host system to `LD_PRELOAD` when executing
*COMMAND*. If *COMMAND* is run in a container, the path of the
......
......@@ -206,35 +206,26 @@ check_bwrap (const char *tools_dir,
return NULL;
}
static gchar *
check_flatpak_spawn (void)
static gboolean
check_launch_on_host (const char *launch_executable,
GError **error)
{
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;
g_autofree gchar *spawn_exec = NULL;
g_autofree gchar *child_stdout = NULL;
g_autofree gchar *child_stderr = NULL;
int wait_status;
const char *spawn_test_argv[] =
const char *test_argv[] =
{
NULL,
"--host",
"--directory=/",
"--bus-name=org.freedesktop.Flatpak",
"--",
"true",
NULL
};
/* All known Flatpak runtimes have flatpak-spawn in the PATH */
spawn_exec = g_find_program_in_path ("flatpak-spawn");
if (spawn_exec == NULL
|| !g_file_test (spawn_exec, G_FILE_TEST_IS_EXECUTABLE))
return NULL;
spawn_test_argv[0] = spawn_exec;
test_argv[0] = launch_executable;
if (!g_spawn_sync (NULL, /* cwd */
(gchar **) spawn_test_argv,
(gchar **) test_argv,
NULL, /* environ */
G_SPAWN_DEFAULT,
NULL, NULL, /* child setup */
......@@ -243,25 +234,24 @@ check_flatpak_spawn (void)
&wait_status,
error))
{
g_warning ("Cannot run flatpak-spawn: %s", local_error->message);
g_clear_error (&local_error);
return FALSE;
}
else if (wait_status != 0)
if (wait_status != 0)
{
g_warning ("Cannot run flatpak-spawn: wait status %d", wait_status);
g_warning ("Cannot run commands on host system: wait status %d",
wait_status);
if (child_stdout != NULL && child_stdout[0] != '\0')
g_warning ("Output:\n%s", child_stdout);
if (child_stderr != NULL && child_stderr[0] != '\0')
g_warning ("Diagnostic output:\n%s", child_stderr);
}
else
{
return g_steal_pointer (&spawn_exec);
return glnx_throw (error, "Unable to run a command on the host system");
}
return NULL;
return TRUE;
}
/*
......@@ -723,7 +713,6 @@ static char *opt_steam_app_id = NULL;
static gboolean opt_gc_runtimes = TRUE;
static gboolean opt_generate_locales = TRUE;
static char *opt_home = NULL;
static gboolean opt_host_fallback = FALSE;
static char *opt_graphics_provider = NULL;
static char *graphics_provider_mount_point = NULL;
static gboolean opt_launcher = FALSE;
......@@ -1021,9 +1010,6 @@ static GOptionEntry options[] =
G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &opt_home,
"Use HOME as home directory. Implies --unshare-home. "
"[Default: $PRESSURE_VESSEL_HOME if set]", "HOME" },
{ "host-fallback", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_host_fallback,
"Run COMMAND on the host system if we cannot run it in a container.", NULL },
{ "host-ld-preload", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, &opt_host_ld_preload_cb,
"Add MODULE from the host system to LD_PRELOAD when executing COMMAND.",
......@@ -1231,7 +1217,7 @@ main (int argc,
g_autoptr(FlatpakBwrap) adverb_args = NULL;
g_autofree gchar *adverb_in_container = NULL;
g_autoptr(FlatpakBwrap) wrapped_command = NULL;
g_autofree gchar *spawn_executable = NULL;
g_autofree gchar *launch_executable = NULL;
g_autofree gchar *bwrap_executable = NULL;
g_autoptr(GString) adjusted_ld_preload = g_string_new ("");
g_autofree gchar *cwd_p = NULL;
......@@ -1255,6 +1241,7 @@ main (int argc,
g_str_equal,
g_free, NULL);
g_autofree char *lock_env_fd = NULL;
g_autoptr(GArray) pass_fds_through_adverb = g_array_new (FALSE, FALSE, sizeof (int));
my_pid = getpid ();
......@@ -1625,76 +1612,33 @@ main (int argc,
/* If we are in a Flatpak environment we can't use bwrap directly */
if (is_flatpak_env)
{
g_debug ("Checking for flatpak-spawn...");
spawn_executable = check_flatpak_spawn ();
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");
/* If we can't launch a command on the host, just fail. */
if (!check_launch_on_host (launch_executable, error))
goto out;
}
else
{
g_debug ("Checking for bwrap...");
/* if this fails, it will warn */
bwrap_executable = check_bwrap (tools_dir, opt_only_prepare);
}
if (opt_test)
{
if ((is_flatpak_env && spawn_executable == NULL)
|| bwrap_executable == NULL)
{
ret = 1;
goto out;
}
else
{
if (spawn_executable != NULL)
g_debug ("OK (%s) (%s)", spawn_executable, bwrap_executable);
else
g_debug ("OK (%s)", bwrap_executable);
ret = 0;
goto out;
}
if (bwrap_executable == NULL)
goto out;
g_debug ("OK (%s)", bwrap_executable);
}
if ((is_flatpak_env && spawn_executable == NULL)
|| bwrap_executable == NULL)
if (opt_test)
{
/* TODO in a Flatpak environment, host is not what we expect it to be */
if (opt_host_fallback)
{
g_message ("Falling back to executing wrapped command directly");
if (opt_env_if_host != NULL)
{
for (i = 0; opt_env_if_host[i] != NULL; i++)
{
char *equals = strchr (opt_env_if_host[i], '=');
g_assert (equals != NULL);
*equals = '\0';
flatpak_bwrap_set_env (wrapped_command, opt_env_if_host[i],
equals + 1, TRUE);
}
}
flatpak_bwrap_finish (wrapped_command);
/* flatpak_bwrap_finish did this */
g_assert (g_ptr_array_index (wrapped_command->argv,
wrapped_command->argv->len - 1) == NULL);
execvpe (g_ptr_array_index (wrapped_command->argv, 0),
(char * const *) wrapped_command->argv->pdata,
wrapped_command->envp);
glnx_throw_errno_prefix (error, "execvpe %s",
(gchar *) g_ptr_array_index (wrapped_command->argv, 0));
goto out;
}
else
{
goto out;
}
ret = 0;
goto out;
}
if (!is_flatpak_env)
......@@ -2090,8 +2034,9 @@ main (int argc,
g_hash_table_add (extra_locked_vars_to_inherit, g_strdup ("XDG_RUNTIME_DIR"));
/* The bwrap envp will be completely ignored when calling
* `flatpak-spawn --host`. For this reason we convert them to
* `--setenv`. */
* pv-launch. For this reason we convert them to `--setenv`.
* (TODO: Now that we're using pv-launch instead of flatpak-spawn,
* we could use --pass-env) */
for (i = 0; bwrap->envp != NULL && bwrap->envp[i] != NULL; i++)
{
g_auto(GStrv) split = g_strsplit (bwrap->envp[i], "=", 2);
......@@ -2194,6 +2139,49 @@ main (int argc,
lock_env_fd = g_strdup_printf ("%d", lock_env_tmpf.fd);
flatpak_bwrap_add_fd (bwrap, glnx_steal_fd (&lock_env_tmpf.fd));
if (is_flatpak_env)
{
int userns_fd, userns2_fd, pidns_fd;
/* Tell the bwrap instance on the host to join the same user and pid
* namespaces as Steam in Flatpak. Otherwise, pid-based IPC between
* the Steam client and the game will fail.
*
* This is not expected to work if bwrap on the host is setuid,
* so it will not work for users of Debian, Arch linux-hardened, etc.,
* but it's better than nothing. */
userns_fd = open ("/run/.userns", O_RDONLY | O_CLOEXEC);
if (userns_fd >= 0)
{
g_array_append_val (pass_fds_through_adverb, userns_fd);
flatpak_bwrap_add_args_data_fd (bwrap, "--userns",
glnx_steal_fd (&userns_fd),
NULL);
userns2_fd = open_namespace_fd_if_needed ("/proc/self/ns/user",
"/run/.userns");
if (userns2_fd >= 0)
{
g_array_append_val (pass_fds_through_adverb, userns2_fd);
flatpak_bwrap_add_args_data_fd (bwrap, "--userns2",
glnx_steal_fd (&userns2_fd),
NULL);
}
}
pidns_fd = open ("/proc/self/ns/pid", O_RDONLY | O_CLOEXEC);
if (pidns_fd >= 0)
{
g_array_append_val (pass_fds_through_adverb, pidns_fd);
flatpak_bwrap_add_args_data_fd (bwrap, "--pidns",
glnx_steal_fd (&pidns_fd),
NULL);
}
}
if (opt_verbose)
{
g_message ("%s options before bundling:", bwrap_executable);
......@@ -2254,6 +2242,13 @@ main (int argc,
}
}
for (i = 0; i < pass_fds_through_adverb->len; i++)
{
int fd = g_array_index (pass_fds_through_adverb, int, i);
flatpak_bwrap_add_arg_printf (bwrap, "--pass-fd=%d", fd);
}
flatpak_bwrap_add_arg_printf (bwrap, "--pass-fd=%s", lock_env_fd);
switch (opt_shell)
......@@ -2325,24 +2320,26 @@ main (int argc,
if (is_flatpak_env)
{
/* Just use the envp from @bwrap */
g_autoptr(FlatpakBwrap) flatpak_spawn = flatpak_bwrap_new (flatpak_bwrap_empty_env);
flatpak_bwrap_add_arg (flatpak_spawn, spawn_executable);
flatpak_bwrap_add_arg (flatpak_spawn, "--host");
g_autoptr(FlatpakBwrap) launch_on_host = flatpak_bwrap_new (flatpak_bwrap_empty_env);
flatpak_bwrap_add_arg (launch_on_host, launch_executable);
flatpak_bwrap_add_arg (launch_on_host, "--bus-name=org.freedesktop.Flatpak");
for (i = 0; i < bwrap->fds->len; i++)
{
g_autofree char *fd_str = g_strdup_printf ("--forward-fd=%d",
g_array_index (bwrap->fds, int, i));
flatpak_bwrap_add_arg (flatpak_spawn, fd_str);
flatpak_bwrap_add_arg (launch_on_host, fd_str);
}
/* Change the current working directory where flatpak-spawn will run.
/* Change the current working directory where pv-launch will run bwrap.
* Bwrap will then set its directory by itself. For this reason here
* we just need a directory that it's known to exist. */
flatpak_bwrap_add_arg (flatpak_spawn, "--directory=/");
flatpak_bwrap_add_arg (launch_on_host, "--directory=/");
flatpak_bwrap_add_arg (launch_on_host, "--");
flatpak_bwrap_append_bwrap (flatpak_spawn, bwrap);
flatpak_bwrap_append_bwrap (launch_on_host, bwrap);
g_clear_pointer (&bwrap, flatpak_bwrap_free);
bwrap = g_steal_pointer (&flatpak_spawn);
bwrap = g_steal_pointer (&launch_on_host);
}
if (opt_verbose)
......
......@@ -182,6 +182,46 @@ test_envp_cmp (Fixture *f,
g_free (sort_this);
}
static void
test_get_path_after (Fixture *f,
gconstpointer context)
{
static const struct
{
const char *str;
const char *prefix;
const char *expected;
} tests[] =
{
{ "/run/host/usr", "/run/host", "usr" },
{ "/run/host/usr", "/run/host/", "usr" },
{ "/run/host", "/run/host", "" },
{ "////run///host////usr", "//run//host", "usr" },
{ "////run///host////usr", "//run//host////", "usr" },
{ "/run/hostage", "/run/host", NULL },
/* Any number of leading slashes is ignored, even zero */
{ "foo/bar", "/foo", "bar" },
{ "/foo/bar", "foo", "bar" },
};
gsize i;
for (i = 0; i < G_N_ELEMENTS (tests); i++)
{
const char *str = tests[i].str;
const char *prefix = tests[i].prefix;
const char *expected = tests[i].expected;
if (expected == NULL)
g_test_message ("%s should not have path prefix %s",
str, prefix);
else
g_test_message ("%s should have path prefix %s followed by %s",
str, prefix, expected);
g_assert_cmpstr (pv_get_path_after (str, prefix), ==, expected);
}
}
static void
test_search_path_append (Fixture *f,
gconstpointer context)
......@@ -223,6 +263,8 @@ main (int argc,
g_test_add ("/capture-output", Fixture, NULL,
setup, test_capture_output, teardown);
g_test_add ("/envp-cmp", Fixture, NULL, setup, test_envp_cmp, teardown);
g_test_add ("/get-path-after", Fixture, NULL,
setup, test_get_path_after, teardown);
g_test_add ("/search-path-append", Fixture, NULL,
setup, test_search_path_append, teardown);
......