diff --git a/build-relocatable-install.py b/build-relocatable-install.py index d0bc4ab253464c9ea8b58af87c9d05fa64b68049..c87f026929e8eea0ba969f8fbbf0707a09826265 100755 --- a/build-relocatable-install.py +++ b/build-relocatable-install.py @@ -108,8 +108,8 @@ SCRIPTS = [ 'pressure-vessel-unruntime-test-ui', ] EXECUTABLES = [ + 'pressure-vessel-adverb', 'pressure-vessel-try-setlocale', - 'pressure-vessel-with-lock', 'pressure-vessel-wrap', ] LIBCAPSULE_TOOLS = [ diff --git a/src/with-lock.c b/src/adverb.c similarity index 71% rename from src/with-lock.c rename to src/adverb.c index 2ac1b4331155ccdb5a8ee4fe5eedcd5b76d855bb..5fe9f098cdaef12d3b51acc08dbe6f551e540179 100644 --- a/src/with-lock.c +++ b/src/adverb.c @@ -1,8 +1,9 @@ -/* pressure-vessel-with-lock — run a command with a lock held. - * Basically flock(1), but using fcntl locks compatible with those used - * by bubblewrap and Flatpak. +/* pressure-vessel-adverb — run a command with an altered execution environment, + * e.g. holding a lock. + * The lock is basically flock(1), but using fcntl locks compatible with + * those used by bubblewrap and Flatpak. * - * Copyright © 2019 Collabora Ltd. + * Copyright © 2019-2020 Collabora Ltd. * * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -46,6 +47,7 @@ static GPtrArray *global_locks = NULL; static gboolean opt_create = FALSE; +static gboolean opt_generate_locales = FALSE; static gboolean opt_subreaper = FALSE; static gboolean opt_verbose = FALSE; static gboolean opt_version = FALSE; @@ -126,6 +128,93 @@ opt_lock_file_cb (const char *name, return TRUE; } +static gboolean +generate_locales (gchar **locpath_out, + GError **error) +{ + g_autofree gchar *temp_dir = NULL; + g_autoptr(GDir) dir = NULL; + int wait_status; + g_autofree gchar *child_stdout = NULL; + g_autofree gchar *child_stderr = NULL; + g_autofree gchar *pvlg = NULL; + g_autofree gchar *this_path = NULL; + g_autofree gchar *this_dir = NULL; + + g_return_val_if_fail (locpath_out == NULL || *locpath_out == NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + temp_dir = g_dir_make_tmp ("pressure-vessel-locales-XXXXXX", error); + + if (temp_dir == NULL) + { + if (error != NULL) + glnx_prefix_error (error, + "Cannot create temporary directory for locales"); + return FALSE; + } + + this_path = g_file_read_link ("/proc/self/exe", NULL); + this_dir = g_dirname (this_path); + pvlg = g_build_filename (this_dir, "pressure-vessel-locale-gen", NULL); + + const char *locale_gen_argv[] = + { + pvlg, + "--output-dir", temp_dir, + "--verbose", + NULL + }; + + if (!g_spawn_sync (NULL, /* cwd */ + (gchar **) locale_gen_argv, + NULL, /* environ */ + G_SPAWN_DEFAULT, + NULL, NULL, /* child setup */ + &child_stdout, + &child_stderr, + &wait_status, + error)) + { + if (error != NULL) + glnx_prefix_error (error, "Cannot run pressure-vessel-locale-gen"); + return FALSE; + } + + if (child_stdout != NULL && child_stdout[0] != '\0') + g_debug ("Output:\n%s", child_stdout); + + if (child_stderr != NULL && child_stderr[0] != '\0') + g_debug ("Diagnostic output:\n%s", child_stderr); + + if (WIFEXITED (wait_status) && WEXITSTATUS (wait_status) == EX_OSFILE) + { + /* locale-gen exits 72 (EX_OSFILE) if it had to correct for + * missing locales at OS level. This is not an error. */ + g_debug ("pressure-vessel-locale-gen created missing locales"); + } + else if (!g_spawn_check_exit_status (wait_status, error)) + { + if (error != NULL) + glnx_prefix_error (error, "Unable to generate locales"); + return FALSE; + } + /* else all locales were already present (exit status 0) */ + + dir = g_dir_open (temp_dir, 0, error); + + if (dir == NULL || g_dir_read_name (dir) == NULL) + { + g_debug ("No locales have been generated"); + return TRUE; + } + + if (locpath_out != NULL) + *locpath_out = g_steal_pointer (&temp_dir); + + return TRUE; +} + static GOptionEntry options[] = { { "fd", '\0', @@ -143,6 +232,13 @@ static GOptionEntry options[] = "Don't create subsequent nonexistent lock files [default].", NULL }, + { "generate-locales", '\0', + G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_generate_locales, + "Attempt to generate any missing locales.", NULL }, + { "no-generate-locales", '\0', + G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_generate_locales, + "Don't generate any missing locales [default].", NULL }, + { "write", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_write, "Lock each subsequent lock file for write access.", @@ -206,6 +302,8 @@ main (int argc, char **command_and_args; GPid child_pid; int wait_status = -1; + g_autofree gchar *locales_temp_dir = NULL; + g_auto(GStrv) my_environ = NULL; setlocale (LC_ALL, ""); pv_avoid_gvfs (); @@ -213,7 +311,7 @@ main (int argc, locks = g_ptr_array_new_with_free_func ((GDestroyNotify) pv_bwrap_lock_free); global_locks = locks; - g_set_prgname ("pressure-vessel-with-lock"); + g_set_prgname ("pressure-vessel-adverb"); g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING | G_LOG_LEVEL_MESSAGE, @@ -278,11 +376,27 @@ main (int argc, goto out; } + my_environ = g_get_environ (); + + if (opt_generate_locales) + { + /* If this fails, it is not fatal - carry on anyway */ + if (!generate_locales (&locales_temp_dir, error)) + { + g_warning ("%s", local_error->message); + g_clear_error (error); + } + else if (locales_temp_dir != NULL) + { + my_environ = g_environ_setenv (my_environ, "LOCPATH", locales_temp_dir, TRUE); + } + } + g_debug ("Launching child process..."); if (!g_spawn_async (NULL, /* working directory */ command_and_args, - NULL, /* environment */ + my_environ, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, /* child setup + user_data */ &child_pid, @@ -343,6 +457,9 @@ main (int argc, out: global_locks = NULL; + if (locales_temp_dir != NULL) + pv_rm_rf (locales_temp_dir); + if (local_error != NULL) g_warning ("%s", local_error->message); diff --git a/src/locking.md b/src/locking.md index adbd4caa8f3d9b608d4571b01aa65fe439a345a8..e0034334187c62a5e22b72e8a018d82ef32e574b 100644 --- a/src/locking.md +++ b/src/locking.md @@ -7,7 +7,7 @@ process-oriented `F_SETLK` locks on older kernels. See `src/bwrap-lock.c` for full details. Do not use `flock(2)`, `flock(1)` or `lockf(3)` to interact with -pressure-vessel. `bwrap --lock-file` or `pressure-vessel-with-lock` +pressure-vessel. `bwrap --lock-file` or `pressure-vessel-adverb` can be used. Runtimes diff --git a/src/meson.build b/src/meson.build index 220d1d30be08deec869384d4fab3bf2573979338..4e348724629e7f3a2fd1038ad6fd369f631570a6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -60,9 +60,9 @@ pressure_vessel_utils = static_library( ) executable( - 'pressure-vessel-with-lock', + 'pressure-vessel-adverb', sources : [ - 'with-lock.c', + 'adverb.c', ], dependencies : [ gio_unix, diff --git a/src/runtime.c b/src/runtime.c index 07366f00f1853141c4413ce6dd2f0f8d4f834876..00589c7957af848372f5b12c3ef6b0ca6c105306 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -63,7 +63,7 @@ struct _PvRuntime FlatpakBwrap *container_access_adverb; const gchar *runtime_files; /* either source_files or mutable_sysroot */ gchar *runtime_usr; /* either runtime_files or that + "/usr" */ - const gchar *with_lock_in_container; + const gchar *adverb_in_container; PvRuntimeFlags flags; int mutable_parent_fd; @@ -880,32 +880,37 @@ pv_runtime_new (const char *source_files, NULL); } -/* If we are using a runtime, pass the lock fd to the executed process, +/* If we are using a runtime, ensure the locales to be generated, + * pass the lock fd to the executed process, * and make it act as a subreaper for the game itself. * * If we were using --unshare-pid then we could use bwrap --sync-fd * and rely on bubblewrap's init process for this, but we currently - * can't do that without breaking gameoverlayrender.so's assumptions. */ + * can't do that without breaking gameoverlayrender.so's assumptions, + * and we want -adverb for its locale functionality anyway. */ void -pv_runtime_append_lock_adverb (PvRuntime *self, - FlatpakBwrap *bwrap) +pv_runtime_append_adverbs (PvRuntime *self, + FlatpakBwrap *bwrap) { g_return_if_fail (PV_IS_RUNTIME (self)); g_return_if_fail (!pv_bwrap_was_finished (bwrap)); /* This will be true if pv_runtime_bind() was successfully called. */ - g_return_if_fail (self->with_lock_in_container != NULL); + g_return_if_fail (self->adverb_in_container != NULL); flatpak_bwrap_add_args (bwrap, - self->with_lock_in_container, + self->adverb_in_container, "--subreaper", NULL); + if (self->flags & PV_RUNTIME_FLAGS_GENERATE_LOCALES) + flatpak_bwrap_add_args (bwrap, "--generate-locales", NULL); + if (pv_bwrap_lock_is_ofd (self->runtime_lock)) { int fd = pv_bwrap_lock_steal_fd (self->runtime_lock); g_autofree gchar *fd_str = NULL; - g_debug ("Passing lock fd %d down to with-lock", fd); + g_debug ("Passing lock fd %d down to adverb", fd); flatpak_bwrap_add_fd (bwrap, fd); fd_str = g_strdup_printf ("%d", fd); flatpak_bwrap_add_args (bwrap, @@ -916,10 +921,10 @@ pv_runtime_append_lock_adverb (PvRuntime *self, { /* * We were unable to take out an open file descriptor lock, - * so it will be released on fork(). Tell the with-lock process + * so it will be released on fork(). Tell the adverb process * to take out its own compatible lock instead. There will be * a short window during which we have lost our lock but the - * with-lock process has not taken its lock - that's unavoidable + * adverb process has not taken its lock - that's unavoidable * if we want to use exec() to replace ourselves with the * container. * @@ -1142,117 +1147,6 @@ try_bind_dri (PvRuntime *self, 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 (PvRuntime *self, - gboolean on_host, - FlatpakBwrap *bwrap) -{ - 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 (self->overrides, "locales", NULL); - g_autofree gchar *locales_in_container = g_build_filename (self->overrides_in_container, - "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 (self->tools_dir, - "pressure-vessel-locale-gen", - NULL); - /* We don't actually need to use bwrap when we're just running on - * the host system. */ - flatpak_bwrap_add_args (run_locale_gen, - locale_gen, - "--output-dir", locales, - "--verbose", - NULL); - } - else - { - locale_gen = g_build_filename ("/run/host/tools", - "pressure-vessel-locale-gen", - NULL); - - flatpak_bwrap_append_bwrap (run_locale_gen, bwrap); - flatpak_bwrap_add_args (run_locale_gen, - "--ro-bind", self->overrides, - self->overrides_in_container, - NULL); - - 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", self->tools_dir, "/run/host/tools", - "--bind", locales, locales_in_container, - locale_gen, - "--output-dir", locales_in_container, - "--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 (locales_in_container); - 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, @@ -1597,11 +1491,6 @@ bind_runtime (PvRuntime *self, return FALSE; } - /* This needs to be done after pv_runtime_use_host_graphics_stack() - * has decided whether to bring in the host system's libc. */ - if (self->flags & PV_RUNTIME_FLAGS_GENERATE_LOCALES) - ensure_locales (self, self->any_libc_from_host, bwrap); - /* 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(). @@ -3083,7 +2972,7 @@ pv_runtime_bind (PvRuntime *self, if (!pv_cheap_tree_copy (pressure_vessel_prefix, dest, error)) return FALSE; - self->with_lock_in_container = "/usr/lib/pressure-vessel/from-host/bin/pressure-vessel-with-lock"; + self->adverb_in_container = "/usr/lib/pressure-vessel/from-host/bin/pressure-vessel-adverb"; } else { @@ -3092,7 +2981,7 @@ pv_runtime_bind (PvRuntime *self, pressure_vessel_prefix, "/run/pressure-vessel/pv-from-host", NULL); - self->with_lock_in_container = "/run/pressure-vessel/pv-from-host/bin/pressure-vessel-with-lock"; + self->adverb_in_container = "/run/pressure-vessel/pv-from-host/bin/pressure-vessel-adverb"; } pv_runtime_set_search_paths (self, bwrap); diff --git a/src/runtime.h b/src/runtime.h index 3c004c72254b4782baff9fc056e327efc110cc6f..e9a7bf8424aa738940ae4b532214ab43721e4c6a 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -70,8 +70,8 @@ PvRuntime *pv_runtime_new (const char *source_files, PvRuntimeFlags flags, GError **error); -void pv_runtime_append_lock_adverb (PvRuntime *self, - FlatpakBwrap *bwrap); +void pv_runtime_append_adverbs (PvRuntime *self, + FlatpakBwrap *bwrap); gboolean pv_runtime_bind (PvRuntime *self, FlatpakBwrap *bwrap, GError **error); diff --git a/src/utils.c b/src/utils.c index f5063cdcd3459d54cc806a896c4e98219ff65c98..b299b3c0c43f8db113a596e29f3495f404fd0c1b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -408,3 +408,34 @@ pv_cheap_tree_copy (const char *source_root, g_assert (nftw_data.error == NULL); return (res == 0); } + +static gint +ftw_remove (const gchar *path, + const struct stat *sb, + gint typeflags, + struct FTW *ftwbuf) +{ + if (remove (path) < 0) + return -1; + + return 0; +} + +/* + * @directory: (type filename): The directory to remove. + * + * Recursively delete @directory within the same file system and + * without following symbolic links. + * + * Returns: %TRUE if the removal was successful + */ +gboolean +pv_rm_rf (const char *directory) +{ + g_return_val_if_fail (directory != NULL, FALSE); + + if (nftw (directory, ftw_remove, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + return FALSE; + + return TRUE; +} diff --git a/src/utils.h b/src/utils.h index 11172fd542cb9797b909b69f221b4a7fc32e828f..47ea98d29784fcd6f38792415d6d4493062e5d0f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -46,3 +46,5 @@ gpointer pv_hash_table_get_arbitrary_key (GHashTable *table); gboolean pv_cheap_tree_copy (const char *source_root, const char *dest_root, GError **error); + +gboolean pv_rm_rf (const char *directory); diff --git a/src/wrap.c b/src/wrap.c index b31162e1102086acb28dad30909a97037ad04887..99bfd790431265bd15c625429fd3f57733807e21 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -1369,7 +1369,7 @@ main (int argc, goto out; if (runtime != NULL) - pv_runtime_append_lock_adverb (runtime, bwrap); + pv_runtime_append_adverbs (runtime, bwrap); g_debug ("Adding wrapped command..."); flatpak_bwrap_append_args (bwrap, wrapped_command->argv); diff --git a/tests/containers.py b/tests/containers.py index b033fddf8d111d279c5db1192f8707d08a8e3c31..3a54a51049a674cc3f370a5a197cb09505ed90b7 100755 --- a/tests/containers.py +++ b/tests/containers.py @@ -162,8 +162,8 @@ class TestContainers(BaseTest): ) for exe in ( + 'pressure-vessel-adverb', 'pressure-vessel-try-setlocale', - 'pressure-vessel-with-lock', ): in_containers_dir = os.path.join( cls.containers_dir, @@ -583,7 +583,7 @@ class TestContainers(BaseTest): os.path.isfile( os.path.join( tree, 'usr', 'lib', 'pressure-vessel', 'from-host', - 'bin', 'pressure-vessel-with-lock', + 'bin', 'pressure-vessel-adverb', ) ) ) diff --git a/tests/relocatable-install.py b/tests/relocatable-install.py index 2829b243da38dfd85112920ad89c7d9df8b60345..cc8270b8566ca2babfa7516b719d61e63164c077 100755 --- a/tests/relocatable-install.py +++ b/tests/relocatable-install.py @@ -78,7 +78,7 @@ class TapTest: EXES = [ - 'pressure-vessel-with-lock', + 'pressure-vessel-adverb', 'pressure-vessel-wrap', ] WRAPPED = [