Newer
Older
g_get_prgname (), opt_filesystems[i]);
goto out;
}
}
}
/* Finished parsing arguments, so any subsequent failures will make
* us exit 1. */
ret = 1;
if (opt_terminal != PV_TERMINAL_TTY)
{
int fd;
if (!glnx_openat_rdonly (-1, "/dev/null", TRUE, &fd, error))
goto out;
if (dup2 (fd, STDIN_FILENO) < 0)
{
glnx_throw_errno_prefix (error,
"Cannot replace stdin with /dev/null");
goto out;
}
}
pv_get_current_dirs (&cwd_p, &cwd_l);
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
if (opt_verbose)
{
g_auto(GStrv) env = g_get_environ ();
g_log_set_handler (G_LOG_DOMAIN,
G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO,
cli_log_func, (void *) g_get_prgname ());
g_message ("Original argv:");
for (i = 0; i < original_argc; i++)
{
g_autofree gchar *quoted = g_shell_quote (original_argv[i]);
g_message ("\t%" G_GSIZE_FORMAT ": %s", i, quoted);
}
g_message ("Current working directory:");
g_message ("\tPhysical: %s", cwd_p);
g_message ("\tLogical: %s", cwd_l);
g_message ("Environment variables:");
qsort (env, g_strv_length (env), sizeof (char *), pv_envp_cmp);
for (i = 0; env[i] != NULL; i++)
{
g_autofree gchar *quoted = g_shell_quote (env[i]);
g_message ("\t%s", quoted);
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
}
g_message ("Wrapped command:");
for (i = 1; i < argc; i++)
{
g_autofree gchar *quoted = g_shell_quote (argv[i]);
g_message ("\t%" G_GSIZE_FORMAT ": %s", i, quoted);
}
}
tools_dir = find_executable_dir (error);
if (tools_dir == NULL)
goto out;
g_debug ("Found executable directory: %s", tools_dir);
wrapped_command = flatpak_bwrap_new (NULL);
switch (opt_terminal)
g_debug ("Wrapping command to use tty");
if (!pv_bwrap_wrap_tty (wrapped_command, error))
g_debug ("Wrapping command with xterm");
pv_bwrap_wrap_in_xterm (wrapped_command);
case PV_TERMINAL_AUTO:
case PV_TERMINAL_NONE:
default:
/* do nothing */
break;
if (opt_shell != PV_SHELL_NONE || opt_terminal == PV_TERMINAL_XTERM)
/* In the (PV_SHELL_NONE, PV_TERMINAL_XTERM) case, just don't let the
* xterm close before the user has had a chance to see the output */
pv_bwrap_wrap_interactive (wrapped_command, opt_shell);
if (argc > 1 && argv[1][0] == '-')
{
/* Make sure wrapped_command is something we can validly pass to env(1) */
if (strchr (argv[1], '=') != NULL)
flatpak_bwrap_add_args (wrapped_command,
"sh", "-euc", "exec \"$@\"", "sh",
NULL);
/* Make sure bwrap will interpret wrapped_command as the end of its
* options */
flatpak_bwrap_add_arg (wrapped_command, "env");
}
g_debug ("Setting arguments for wrapped command");
flatpak_bwrap_append_argsv (wrapped_command, &argv[1], argc - 1);
g_debug ("Checking for bwrap...");
bwrap_executable = check_bwrap (tools_dir, opt_only_prepare);
if (opt_test)
{
if (bwrap_executable == NULL)
{
ret = 1;
goto out;
}
else
{
g_debug ("OK (%s)", bwrap_executable);
ret = 0;
goto out;
}
}
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
if (bwrap_executable == NULL)
{
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;
}
}
g_debug ("Checking bwrap features...");
bwrap_help_argv[0] = bwrap_executable;
bwrap_help = pv_capture_output (bwrap_help_argv, error);
if (bwrap_help == NULL)
goto out;
bwrap = flatpak_bwrap_new (NULL);
flatpak_bwrap_add_arg (bwrap, bwrap_executable);
/* Protect the controlling terminal from the app/game, unless we are
* running an interactive shell in which case that would break its
* job control. */
if (opt_terminal != PV_TERMINAL_TTY)
flatpak_bwrap_add_arg (bwrap, "--new-session");
if (opt_runtime != NULL && opt_runtime[0] != '\0')
PvRuntimeFlags flags = PV_RUNTIME_FLAGS_NONE;
if (opt_gc_runtimes)
flags |= PV_RUNTIME_FLAGS_GC_RUNTIMES;
if (opt_generate_locales)
flags |= PV_RUNTIME_FLAGS_GENERATE_LOCALES;
if (opt_host_graphics)
flags |= PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK;
if (opt_verbose)
flags |= PV_RUNTIME_FLAGS_VERBOSE;
g_debug ("Configuring runtime %s...", opt_runtime);
runtime = pv_runtime_new (opt_runtime,
opt_copy_runtime_into,
bwrap_executable,
tools_dir,
goto out;
if (!pv_runtime_bind (runtime, bwrap, error))
goto out;
}
else
{
flatpak_bwrap_add_args (bwrap,
"--bind", "/", "/",
NULL);
/* /dev is already visible, because we mounted the entire root
* filesystem, but we need to remount parts of it without nodev */
pv_bwrap_add_api_filesystems (bwrap);
}
/* Protect other users' homes (but guard against the unlikely
* situation that they don't exist) */
if (g_file_test ("/home", G_FILE_TEST_EXISTS))
flatpak_bwrap_add_args (bwrap,
"--tmpfs", "/home",
NULL);
g_debug ("Making home directory available...");
if (opt_fake_home == NULL)
{
flatpak_bwrap_add_args (bwrap,
"--bind", home, home,
NULL);
}
else
{
if (!use_fake_home (bwrap, opt_fake_home, error))
goto out;
}
if (!opt_share_pid)
{
g_warning ("Unsharing process ID namespace. This is not expected "
"to work...");
flatpak_bwrap_add_arg (bwrap, "--unshare-pid");
}
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
g_debug ("Adjusting LD_PRELOAD...");
/* We need the LD_PRELOADs from Steam visible at the paths that were
* used for them, which might be their physical rather than logical
* locations. */
if (opt_ld_preload != NULL)
{
for (i = 0; i < opt_ld_preload->len; i++)
{
const char *preload = g_ptr_array_index (opt_ld_preload, i);
g_assert (preload != NULL);
if (*preload == '\0')
continue;
/* We have the beginnings of infrastructure to set a LD_PRELOAD
* from inside the container, but currently the only thing we
* support is it coming from the host. */
g_assert (g_str_has_prefix (preload, "host:"));
preload = preload + 5;
if (g_file_test (preload, G_FILE_TEST_EXISTS))
{
if (opt_remove_game_overlay
&& g_str_has_suffix (preload, "/gameoverlayrenderer.so"))
{
g_debug ("Disabling Steam Overlay: %s", preload);
continue;
}
&& (g_str_has_prefix (preload, "/usr/")
|| g_str_has_prefix (preload, "/lib")))
{
g_autofree gchar *in_run_host = g_build_filename ("/run/host",
preload,
NULL);
/* When using a runtime we can't write to /usr/ or /libQUAL/,
* so redirect this preloaded module to the corresponding
* location in /run/host. */
pv_search_path_append (adjusted_ld_preload, in_run_host);
}
else
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", preload, preload,
NULL);
pv_search_path_append (adjusted_ld_preload, preload);
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
}
}
else
{
g_debug ("LD_PRELOAD module '%s' does not exist", preload);
}
}
}
/* Put the caller's LD_PRELOAD back.
* This would be filtered out by a setuid bwrap, so we have to go
* via --setenv. */
if (adjusted_ld_preload->len != 0)
flatpak_bwrap_add_args (bwrap,
"--setenv", "LD_PRELOAD",
adjusted_ld_preload->str,
NULL);
else
flatpak_bwrap_add_args (bwrap,
"--unsetenv", "LD_PRELOAD",
NULL);
g_debug ("Making Steam compat tools available if required...");
bind_from_environ ("STEAM_COMPAT_CLIENT_INSTALL_PATH", bwrap);
bind_from_environ ("STEAM_COMPAT_DATA_PATH", bwrap);
bind_from_environ ("STEAM_COMPAT_TOOL_PATH", bwrap);
/* Make arbitrary filesystems available. This is not as complete as
* Flatpak yet. */
if (opt_filesystems != NULL)
{
g_debug ("Processing --filesystem arguments...");
for (i = 0; opt_filesystems[i] != NULL; i++)
{
/* We already checked this */
g_assert (g_path_is_absolute (opt_filesystems[i]));
g_debug ("Bind-mounting \"%s\"", opt_filesystems[i]);
flatpak_bwrap_add_args (bwrap,
"--bind", opt_filesystems[i],
opt_filesystems[i],
NULL);
}
}
/* Make sure the current working directory (the game we are going to
* run) is available. Some games write here. */
g_debug ("Making current working directory available...");
if (pv_is_same_file (home, cwd_p))
{
g_debug ("Not making physical working directory \"%s\" available to "
"container because it is the home directory",
cwd_p);
}
else
{
flatpak_bwrap_add_args (bwrap,
"--bind", cwd_p, cwd_p,
NULL);
}
/* 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 (bwrap);
flatpak_run_add_x11_args (bwrap, TRUE);
flatpak_run_add_pulseaudio_args (bwrap);
flatpak_run_add_session_dbus_args (bwrap);
flatpak_run_add_system_dbus_args (bwrap);
}
flatpak_bwrap_add_args (bwrap,
"--chdir", cwd_p,
"--unsetenv", "PWD",
NULL);
/* Put Steam Runtime environment variables back, if /usr is mounted
* from the host. */
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
{
g_debug ("Making Steam Runtime available...");
/* We need libraries from the Steam Runtime, so make sure that's
* visible (it should never need to be read/write though) */
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);
if (g_str_has_prefix (opt_env_if_host[i], "STEAM_RUNTIME=/"))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", equals + 1,
equals + 1,
NULL);
}
*equals = '\0';
/* We do this via --setenv instead of flatpak_bwrap_set_env()
* to make sure they aren't filtered out by a setuid bwrap. */
flatpak_bwrap_add_args (bwrap,
"--setenv", opt_env_if_host[i],
equals + 1,
NULL);
*equals = '=';
}
}
}
if (opt_verbose)
{
g_message ("%s options before bundling:", bwrap_executable);
for (i = 0; i < bwrap->argv->len; i++)
{
g_autofree gchar *quoted = NULL;
quoted = g_shell_quote (g_ptr_array_index (bwrap->argv, i));
g_message ("\t%s", quoted);
}
}
if (!flatpak_bwrap_bundle_args (bwrap, 1, -1, FALSE, error))
goto out;
adverb_args = flatpak_bwrap_new (NULL);
adverb_in_container = pv_runtime_get_adverb (runtime, adverb_args);
if (opt_terminate_timeout >= 0.0)
{
if (opt_terminate_idle_timeout > 0.0)
flatpak_bwrap_add_arg_printf (adverb_args,
"--terminate-idle-timeout=%f",
opt_terminate_idle_timeout);
flatpak_bwrap_add_arg_printf (adverb_args,
"--terminate-timeout=%f",
opt_terminate_timeout);
}
/* If not using a runtime, the adverb in the container has the
* same path as outside */
if (adverb_in_container == NULL)
adverb_in_container = g_build_filename (tools_dir,
"pressure-vessel-adverb",
NULL);
flatpak_bwrap_add_args (bwrap,
adverb_in_container,
"--exit-with-parent",
"--subreaper",
NULL);
if (opt_verbose)
flatpak_bwrap_add_arg (bwrap, "--verbose");
flatpak_bwrap_append_bwrap (bwrap, adverb_args);
flatpak_bwrap_add_arg (bwrap, "--");
g_debug ("Adding wrapped command...");
flatpak_bwrap_append_args (bwrap, wrapped_command->argv);
if (opt_verbose)
{
g_message ("Final %s options:", bwrap_executable);
for (i = 0; i < bwrap->argv->len; i++)
{
g_autofree gchar *quoted = NULL;
quoted = g_shell_quote (g_ptr_array_index (bwrap->argv, i));
g_message ("\t%s", quoted);
}
g_message ("%s environment:", bwrap_executable);
for (i = 0; bwrap->envp != NULL && bwrap->envp[i] != NULL; i++)
{
g_autofree gchar *quoted = NULL;
quoted = g_shell_quote (bwrap->envp[i]);
g_message ("\t%s", quoted);
}
/* Clean up temporary directory before running our long-running process */
if (runtime != NULL)
pv_runtime_cleanup (runtime);
flatpak_bwrap_finish (bwrap);
if (opt_write_bwrap != NULL)
{
FILE *file = fopen (opt_write_bwrap, "w");
if (file == NULL)
{
g_warning ("An error occurred trying to write the bwrap arguments: %s",
g_strerror (errno));
/* This is not a fatal error, try to continue */
}
else
{
for (i = 0; i < bwrap->argv->len; i++)
fprintf (file, "%s%c", (gchar *)g_ptr_array_index (bwrap->argv, i), '\0');
fclose (file);
}
}
if (opt_only_prepare)
ret = 0;
else
pv_bwrap_execve (bwrap, fileno (original_stdout), error);
out:
if (local_error != NULL)
g_warning ("%s", local_error->message);
g_clear_pointer (&opt_ld_preload, g_ptr_array_unref);
g_clear_pointer (&opt_env_if_host, g_strfreev);
g_clear_pointer (&opt_freedesktop_app_id, g_free);
g_clear_pointer (&opt_steam_app_id, g_free);
g_clear_pointer (&opt_home, g_free);
g_clear_pointer (&opt_fake_home, g_free);
g_clear_pointer (&opt_runtime_base, g_free);
g_clear_pointer (&opt_runtime, g_free);
return ret;
}