diff --git a/pressure-vessel/bwrap.c b/pressure-vessel/bwrap.c index b6931446ffc6869f9262caa7b84997608a6e3104..dae5a3126f1becab03b9d39e0022100796accc5a 100644 --- a/pressure-vessel/bwrap.c +++ b/pressure-vessel/bwrap.c @@ -22,6 +22,7 @@ #include <gio/gio.h> #include "bwrap.h" +#include "utils.h" #include "steam-runtime-tools/resolve-in-sysroot-internal.h" @@ -42,65 +43,14 @@ pv_bwrap_run_sync (FlatpakBwrap *bwrap, int *exit_status_out, GError **error) { - gint exit_status; - g_autofree gchar *output = NULL; - g_autofree gchar *errors = NULL; - guint i; - g_autoptr(GString) command = g_string_new (""); - g_return_val_if_fail (bwrap != NULL, FALSE); g_return_val_if_fail (bwrap->argv->len >= 2, FALSE); g_return_val_if_fail (pv_bwrap_was_finished (bwrap), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if (exit_status_out != NULL) - *exit_status_out = -1; - - for (i = 0; i < bwrap->argv->len; i++) - { - g_autofree gchar *quoted = NULL; - char *unquoted = g_ptr_array_index (bwrap->argv, i); - - if (unquoted == NULL) - break; - - quoted = g_shell_quote (unquoted); - g_string_append_printf (command, " %s", quoted); - } - - g_debug ("run:%s", command->str); - - /* We use LEAVE_DESCRIPTORS_OPEN to work around a deadlock in older GLib, - * and to avoid wasting a lot of time closing fds if the rlimit for - * maximum open file descriptors is high. Because we're waiting for the - * subprocess to finish anyway, it doesn't really matter that any fds - * that are not close-on-execute will get leaked into the child. */ - if (!g_spawn_sync (NULL, /* cwd */ - (char **) bwrap->argv->pdata, - bwrap->envp, - (G_SPAWN_SEARCH_PATH | - G_SPAWN_LEAVE_DESCRIPTORS_OPEN), - NULL, - NULL, - &output, - &errors, - &exit_status, - error)) - return FALSE; - - g_print ("%s", output); - g_printerr ("%s", errors); - - if (exit_status_out != NULL) - { - if (WIFEXITED (exit_status)) - *exit_status_out = WEXITSTATUS (exit_status); - } - - if (!g_spawn_check_exit_status (exit_status, error)) - return FALSE; - - return TRUE; + return pv_run_sync ((const char * const *) bwrap->argv->pdata, + (const char * const *) bwrap->envp, + exit_status_out, NULL, error); } /** diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c index 449214f6afe152f1ddda0b4c0c93f5b3c0173124..80e66a014f63ab168bb27b922be6c4c25d430499 100644 --- a/pressure-vessel/runtime.c +++ b/pressure-vessel/runtime.c @@ -325,7 +325,7 @@ runtime_architecture_init (RuntimeArchitecture *self, * assume that this is the same as whether we can run them * on the host, if different. */ argv[0] = self->capsule_capture_libs; - self->ld_so = pv_capture_output (argv, NULL, NULL); + pv_run_sync (argv, NULL, NULL, &self->ld_so, NULL); if (self->ld_so == NULL) { @@ -3432,9 +3432,9 @@ pv_runtime_get_ld_so (PvRuntime *self, NULL); flatpak_bwrap_finish (temp_bwrap); - *ld_so_in_runtime = pv_capture_output ( - (const char * const *) temp_bwrap->argv->pdata, - (const char * const *) temp_bwrap->envp, NULL); + pv_run_sync ((const char * const *) temp_bwrap->argv->pdata, + (const char * const *) temp_bwrap->envp, NULL, + ld_so_in_runtime, NULL); } return TRUE; diff --git a/pressure-vessel/utils.c b/pressure-vessel/utils.c index af84f80484f2e2b5aa94bb2dda78a773d50ddf95..57a2fb37b34f51bb7ec1315fe49dab4dd05db286 100644 --- a/pressure-vessel/utils.c +++ b/pressure-vessel/utils.c @@ -168,10 +168,12 @@ pv_search_path_append (GString *search_path, g_string_append (search_path, item); } -gchar * -pv_capture_output (const char * const * argv, - const char * const * envp, - GError **error) +gboolean +pv_run_sync (const char * const * argv, + const char * const * envp, + int *exit_status_out, + char **output_out, + GError **error) { gsize len; gint wait_status; @@ -180,10 +182,14 @@ pv_capture_output (const char * const * argv, gsize i; g_autoptr(GString) command = g_string_new (""); - g_return_val_if_fail (argv != NULL, NULL); - g_return_val_if_fail (argv[0] != NULL, NULL); + g_return_val_if_fail (argv != NULL, FALSE); + g_return_val_if_fail (argv[0] != NULL, FALSE); + g_return_val_if_fail (output_out == NULL || *output_out == NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + if (exit_status_out != NULL) + *exit_status_out = -1; + for (i = 0; argv[i] != NULL; i++) { g_autofree gchar *quoted = g_shell_quote (argv[i]); @@ -208,12 +214,15 @@ pv_capture_output (const char * const * argv, &errors, &wait_status, error)) - return NULL; + return FALSE; g_printerr ("%s", errors); - if (!g_spawn_check_exit_status (wait_status, error)) - return NULL; + if (exit_status_out != NULL) + { + if (WIFEXITED (wait_status)) + *exit_status_out = WEXITSTATUS (wait_status); + } len = strlen (output); @@ -223,7 +232,13 @@ pv_capture_output (const char * const * argv, g_debug ("-> %s", output); - return g_steal_pointer (&output); + if (!g_spawn_check_exit_status (wait_status, error)) + return FALSE; + + if (output_out != NULL) + *output_out = g_steal_pointer (&output); + + return TRUE; } /* diff --git a/pressure-vessel/utils.h b/pressure-vessel/utils.h index 5920b7b6ee4a30b1e2163f1e753fa0cf14795e23..cf08ccf76757cce8c33f5279857332354fa1552f 100644 --- a/pressure-vessel/utils.h +++ b/pressure-vessel/utils.h @@ -48,9 +48,11 @@ void pv_get_current_dirs (gchar **cwd_p, void pv_search_path_append (GString *search_path, const gchar *item); -gchar *pv_capture_output (const char * const * argv, - const char * const * envp, - GError **error); +gboolean pv_run_sync (const char * const * argv, + const char * const * envp, + int *exit_status_out, + char **output_out, + GError **error); gpointer pv_hash_table_get_arbitrary_key (GHashTable *table); diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c index 1c86297deb184706b686d2a84c06baa328f29d92..bafd4e2f005133e5a763e0db1a3da84d7be589bc 100644 --- a/pressure-vessel/wrap.c +++ b/pressure-vessel/wrap.c @@ -349,6 +349,34 @@ export_contents_of_run (FlatpakBwrap *bwrap, 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 + * `nvidia-modprobe` and, even if we did, its setuid would not be effective + * because we have `PR_SET_NO_NEW_PRIVS` and we don't have `CAP_SYS_MODULE` in + * our capability bounding set. + * For this reason if the current system is using the proprietary Nvidia + * drivers, and `nvidia_uvm.ko` has not been already loaded, we should execute + * `nvidia-modprobe` before entering in the container environment. */ +static gboolean +maybe_load_nvidia_modules (GError **error) +{ + const char *nvidia_modprobe_argv[] = + { + "nvidia-modprobe", + "-u", + NULL + }; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_file_test ("/sys/module/nvidia/version", G_FILE_TEST_IS_REGULAR) + && !g_file_test ("/sys/module/nvidia_uvm", G_FILE_TEST_IS_DIR)) + return pv_run_sync (nvidia_modprobe_argv, NULL, NULL, NULL, error); + + return TRUE; +} + typedef enum { ENV_MOUNT_FLAGS_COLON_DELIMITED = (1 << 0), @@ -2697,6 +2725,15 @@ main (int argc, } } + if (!is_flatpak_env) + { + if (!maybe_load_nvidia_modules (error)) + { + g_debug ("Cannot load nvidia modules: %s", local_error->message); + g_clear_error (&local_error); + } + } + if (opt_only_prepare) ret = 0; else diff --git a/tests/pressure-vessel/utils.c b/tests/pressure-vessel/utils.c index 9fa84bb99e0e26d7134acaf3b2d6b9e00c014c9c..202431ff899bd3dabf0c12bc397d158483c76986 100644 --- a/tests/pressure-vessel/utils.c +++ b/tests/pressure-vessel/utils.c @@ -86,8 +86,8 @@ test_arbitrary_key (Fixture *f, } static void -test_capture_output (Fixture *f, - gconstpointer context) +test_run_sync (Fixture *f, + gconstpointer context) { const gchar * argv[] = { @@ -96,20 +96,27 @@ test_capture_output (Fixture *f, g_autofree gchar *output = NULL; g_autoptr(GError) error = NULL; g_auto(GStrv) envp = g_get_environ (); + int exit_status = -1; - output = pv_capture_output (argv, NULL, &error); + pv_run_sync (argv, NULL, NULL, &output, &error); g_assert_cmpstr (output, ==, "hello"); g_assert_no_error (error); g_clear_pointer (&output, g_free); + pv_run_sync (argv, NULL, &exit_status, NULL, &error); + g_assert_cmpint (exit_status, ==, 0); + g_assert_no_error (error); + argv[1] = "hello\\nworld"; /* deliberately no trailing newline */ - output = pv_capture_output (argv, NULL, &error); + pv_run_sync (argv, NULL, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, 0); g_assert_no_error (error); g_assert_cmpstr (output, ==, "hello\nworld"); g_clear_pointer (&output, g_free); argv[0] = "/nonexistent/doesnotexist"; - output = pv_capture_output (argv, NULL, &error); + pv_run_sync (argv, NULL, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, -1); g_assert_nonnull (error); if (error->domain == G_SPAWN_ERROR @@ -134,7 +141,8 @@ test_capture_output (Fixture *f, argv[0] = "false"; argv[1] = NULL; - output = pv_capture_output (argv, NULL, &error); + pv_run_sync (argv, NULL, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, 1); g_assert_error (error, G_SPAWN_EXIT_ERROR, 1); g_assert_cmpstr (output, ==, NULL); g_clear_error (&error); @@ -142,7 +150,8 @@ test_capture_output (Fixture *f, argv[0] = "sh"; argv[1] = "-euc"; argv[2] = "echo \"$PATH\""; - output = pv_capture_output (argv, NULL, &error); + pv_run_sync (argv, NULL, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, 0); g_assert_no_error (error); g_assert_cmpstr (output, ==, g_getenv ("PATH")); g_clear_pointer (&output, g_free); @@ -151,7 +160,8 @@ test_capture_output (Fixture *f, argv[0] = "sh"; argv[1] = "-euc"; argv[2] = "echo \"${FOO-unset}\""; - output = pv_capture_output (argv, (const char * const *) envp, &error); + pv_run_sync (argv, (const char * const *) envp, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, 0); g_assert_no_error (error); g_assert_cmpstr (output, ==, "bar"); g_clear_pointer (&output, g_free); @@ -160,7 +170,8 @@ test_capture_output (Fixture *f, argv[0] = "sh"; argv[1] = "-euc"; argv[2] = "echo \"${FOO-unset}\""; - output = pv_capture_output (argv, (const char * const *) envp, &error); + pv_run_sync (argv, (const char * const *) envp, &exit_status, &output, &error); + g_assert_cmpint (exit_status, ==, 0); g_assert_no_error (error); g_assert_cmpstr (output, ==, "unset"); g_clear_pointer (&output, g_free); @@ -371,8 +382,8 @@ main (int argc, g_test_init (&argc, &argv, NULL); g_test_add ("/arbitrary-key", Fixture, NULL, setup, test_arbitrary_key, teardown); - g_test_add ("/capture-output", Fixture, NULL, - setup, test_capture_output, teardown); + g_test_add ("/run-sync", Fixture, NULL, + setup, test_run_sync, teardown); g_test_add ("/delete-dangling-symlink", Fixture, NULL, setup, test_delete_dangling_symlink, teardown); g_test_add ("/envp-cmp", Fixture, NULL, setup, test_envp_cmp, teardown);