Newer
Older
g_printerr ("%s: --graphics-provider path must be absolute, not \"%s\"\n",
g_get_prgname (), opt_graphics_provider);
goto out;
}
if (g_strcmp0 (opt_graphics_provider, "/") == 0)
graphics_provider_mount_point = g_strdup ("/run/host");
else
graphics_provider_mount_point = g_strdup ("/run/gfx");
if (opt_version_only)
{
g_print ("%s\n", VERSION);
ret = 0;
goto out;
}
if (opt_version)
{
g_print ("%s:\n"
" Package: pressure-vessel\n"
" Version: %s\n",
argv[0], VERSION);
ret = 0;
goto out;
}
original_stdout = pv_divert_stdout_to_stderr (error);
if (original_stdout == NULL)
{
ret = 1;
goto out;
}
pv_avoid_gvfs ();
if (argc < 2 && !opt_test && !opt_only_prepare)
{
g_printerr ("%s: An executable to run is required\n",
g_get_prgname ());
goto out;
}
if (opt_terminal == PV_TERMINAL_AUTO)
if (opt_shell != PV_SHELL_NONE)
opt_terminal = PV_TERMINAL_XTERM;
if (opt_terminal == PV_TERMINAL_NONE && opt_shell != PV_SHELL_NONE)
g_printerr ("%s: --terminal=none is incompatible with --shell\n",
g_get_prgname ());
goto out;
if (opt_batch)
{
/* --batch or PRESSURE_VESSEL_BATCH=1 overrides these */
opt_shell = PV_SHELL_NONE;
opt_terminal = PV_TERMINAL_NONE;
}
if (argc > 1 && strcmp (argv[1], "--") == 0)
{
argv++;
argc--;
}
home = g_get_home_dir ();
if (opt_share_home == TRISTATE_YES)
{
opt_fake_home = NULL;
}
else if (opt_home)
{
opt_fake_home = g_strdup (opt_home);
}
else if (opt_share_home == TRISTATE_MAYBE)
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
{
opt_fake_home = NULL;
}
else if (opt_freedesktop_app_id)
{
opt_fake_home = g_build_filename (home, ".var", "app",
opt_freedesktop_app_id, NULL);
}
else if (opt_steam_app_id)
{
opt_freedesktop_app_id = g_strdup_printf ("com.steampowered.App%s",
opt_steam_app_id);
opt_fake_home = g_build_filename (home, ".var", "app",
opt_freedesktop_app_id, NULL);
}
else if (g_getenv ("SteamAppId") != NULL)
{
opt_freedesktop_app_id = g_strdup_printf ("com.steampowered.App%s",
g_getenv ("SteamAppId"));
opt_fake_home = g_build_filename (home, ".var", "app",
opt_freedesktop_app_id, NULL);
}
else
{
g_printerr ("%s: Either --home, --freedesktop-app-id, --steam-app-id "
"or $SteamAppId is required\n",
g_get_prgname ());
goto out;
}
if (opt_env_if_host != NULL)
{
for (i = 0; opt_env_if_host[i] != NULL; i++)
{
const char *equals = strchr (opt_env_if_host[i], '=');
if (equals == NULL)
{
g_printerr ("%s: --env-if-host argument must be of the form "
"NAME=VALUE, not \"%s\"\n",
g_get_prgname (), opt_env_if_host[i]);
goto out;
}
if (opt_only_prepare && opt_test)
{
g_printerr ("%s: --only-prepare and --test are mutually exclusive",
g_get_prgname ());
goto out;
}
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
if (opt_filesystems != NULL)
{
for (i = 0; opt_filesystems[i] != NULL; i++)
{
if (strchr (opt_filesystems[i], ':') != NULL ||
strchr (opt_filesystems[i], '\\') != NULL)
{
g_printerr ("%s: ':' and '\\' in --filesystem argument "
"not handled yet\n",
g_get_prgname ());
goto out;
}
else if (!g_path_is_absolute (opt_filesystems[i]))
{
g_printerr ("%s: --filesystem argument must be an absolute "
"path, not \"%s\"\n",
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);
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
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);
}
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 (flatpak_bwrap_empty_env);
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);

Ludovico de Nittis
committed
/* 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 ();
/* Assume "bwrap" to exist in the host system and to be in its PATH */
bwrap_executable = g_strdup ("bwrap");
}
else
{
g_debug ("Checking for bwrap...");
bwrap_executable = check_bwrap (tools_dir, opt_only_prepare);
}
if (opt_test)
{

Ludovico de Nittis
committed
if ((is_flatpak_env && spawn_executable == NULL)
|| bwrap_executable == NULL)
{
ret = 1;
goto out;
}
else
{

Ludovico de Nittis
committed
if (spawn_executable != NULL)
g_debug ("OK (%s) (%s)", spawn_executable, bwrap_executable);
else
g_debug ("OK (%s)", bwrap_executable);
ret = 0;
goto out;
}
}

Ludovico de Nittis
committed
if ((is_flatpak_env && spawn_executable == NULL)
|| bwrap_executable == NULL)

Ludovico de Nittis
committed
/* TODO in a Flatpak environment, host is not what we expect it to be */
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
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;
}
}

Ludovico de Nittis
committed
if (!is_flatpak_env)
{
g_debug ("Checking bwrap features...");
bwrap_help_argv[0] = bwrap_executable;
bwrap_help = pv_capture_output (bwrap_help_argv, error);

Ludovico de Nittis
committed
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_graphics_provider != NULL && opt_graphics_provider[0] != '\0')
flags |= PV_RUNTIME_FLAGS_PROVIDER_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,
opt_graphics_provider,
graphics_provider_mount_point,
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");
}
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
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);
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
}
}
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);

Ludovico de Nittis
committed
g_debug ("Making Steam environment variables available if required...");
for (i = 0; i < G_N_ELEMENTS (known_required_env); i++)
bind_and_propagate_from_environ (known_required_env[i], 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...");
cwd_p_host = pv_current_namespace_path_to_host_path (cwd_p);
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_host, cwd_p_host,
flatpak_bwrap_add_args (bwrap,
"--chdir", cwd_p_host,
"--unsetenv", "PWD",
NULL);
/* Put Steam Runtime environment variables back, if /usr is mounted
* from the host. */
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
{
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 = '=';
}
}
}
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
/* 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);
/* When in a Flatpak container the "DISPLAY" env is equal to ":99.0",
* but it might be different on the host system. As a workaround we simply
* bind the whole "/tmp/.X11-unix" directory and unset the container
* "DISPLAY" env.
*/
if (g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR))
{
flatpak_bwrap_add_args (bwrap,
"--ro-bind", "/tmp/.X11-unix", "/tmp/.X11-unix",
NULL);
flatpak_bwrap_unset_env (bwrap, "DISPLAY");
}
else
{
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);
}
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 (flatpak_bwrap_empty_env);
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);

Ludovico de Nittis
committed
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
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");
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);
}
/* Change the current working directory where flatpak-spawn will run.
* 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_append_bwrap (flatpak_spawn, bwrap);
g_clear_pointer (&bwrap, flatpak_bwrap_free);
bwrap = g_steal_pointer (&flatpak_spawn);
}
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);
g_debug ("Exiting with status %d", ret);