Newer
Older
/* pressure-vessel-wrap — run a program in a container that protects $HOME,
* optionally using a Flatpak-style runtime.
*
* Contains code taken from Flatpak.
*
* Copyright © 2014-2019 Red Hat, Inc
* Copyright © 2017-2020 Collabora Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "steam-runtime-tools/glib-backports-internal.h"
#include "steam-runtime-tools/utils-internal.h"
#include "bwrap.h"
#include "bwrap-lock.h"
#include "flatpak-bwrap-private.h"
#include "flatpak-run-private.h"
#include "flatpak-utils-base-private.h"
#include "flatpak-utils-private.h"
/* List of variables that are stripped down from the environment when
* using the secure-execution mode.
* List taken from glibc sysdeps/generic/unsecvars.h */
static const char* unsecure_environment_variables[] = {
"GCONV_PATH",
"GETCONF_DIR",
"GLIBC_TUNABLES",
"HOSTALIASES",
"LD_AUDIT",
"LD_DEBUG",
"LD_DEBUG_OUTPUT",
"LD_DYNAMIC_WEAK",
"LD_HWCAP_MASK",
"LD_LIBRARY_PATH",
"LD_ORIGIN_PATH",
"LD_PRELOAD",
"LD_PROFILE",
"LD_SHOW_AUXV",
"LD_USE_LOAD_BIAS",
"LOCALDOMAIN",
"LOCPATH",
"MALLOC_TRACE",
"NIS_PATH",
"NLSPATH",
"RESOLV_HOST_CONF",
"RES_OPTIONS",
"TMPDIR",
"TZDIR",
NULL,
};
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
static gchar *
find_executable_dir (GError **error)
{
g_autofree gchar *target = glnx_readlinkat_malloc (-1, "/proc/self/exe",
NULL, error);
if (target == NULL)
return glnx_prefix_error_null (error, "Unable to resolve /proc/self/exe");
return g_path_get_dirname (target);
}
static gchar *
find_bwrap (const char *tools_dir)
{
static const char * const flatpak_libexecdirs[] =
{
"/usr/local/libexec",
"/usr/libexec",
"/usr/lib/flatpak"
};
const char *tmp;
g_autofree gchar *candidate = NULL;
gsize i;
g_return_val_if_fail (tools_dir != NULL, NULL);
tmp = g_getenv ("BWRAP");
if (tmp != NULL)
return g_strdup (tmp);
candidate = g_find_program_in_path ("bwrap");
if (candidate != NULL)
return g_steal_pointer (&candidate);
for (i = 0; i < G_N_ELEMENTS (flatpak_libexecdirs); i++)
{
candidate = g_build_filename (flatpak_libexecdirs[i],
"flatpak-bwrap", NULL);
if (g_file_test (candidate, G_FILE_TEST_IS_EXECUTABLE))
return g_steal_pointer (&candidate);
else
g_clear_pointer (&candidate, g_free);
}
candidate = g_build_filename (tools_dir, "bwrap", NULL);
if (g_file_test (candidate, G_FILE_TEST_IS_EXECUTABLE))
return g_steal_pointer (&candidate);
else
g_clear_pointer (&candidate, g_free);
return NULL;
}
static gchar *
check_bwrap (const char *tools_dir,
gboolean only_prepare)
{
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;

Ludovico de Nittis
committed
g_autofree gchar *bwrap_executable = NULL;
const char *bwrap_test_argv[] =
{
NULL,
"--bind", "/", "/",
"true",
NULL
};
g_return_val_if_fail (tools_dir != NULL, NULL);

Ludovico de Nittis
committed
bwrap_executable = find_bwrap (tools_dir);
if (bwrap_executable == NULL)
{
g_warning ("Cannot find bwrap");
}
else if (only_prepare)
{
/* With --only-prepare we don't necessarily expect to be able to run
* it anyway (we are probably in a Docker container that doesn't allow
* creation of nested user namespaces), so just assume that it's the
* right one. */
return g_steal_pointer (&bwrap_executable);
}
int wait_status;
g_autofree gchar *child_stdout = NULL;
g_autofree gchar *child_stderr = NULL;
bwrap_test_argv[0] = bwrap_executable;
/* We use LEAVE_DESCRIPTORS_OPEN to work around a deadlock in older GLib,
* see flatpak_close_fds_workaround */
if (!g_spawn_sync (NULL, /* cwd */
(gchar **) bwrap_test_argv,
NULL, /* environ */
G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
flatpak_bwrap_child_setup_cb, NULL,
&child_stdout,
&child_stderr,
error))
{
g_warning ("Cannot run bwrap: %s", local_error->message);
g_clear_error (&local_error);
}
else if (wait_status != 0)
g_warning ("Cannot run bwrap: 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 (&bwrap_executable);
}
}
return NULL;
}
static gboolean
check_launch_on_host (const char *launch_executable,
GError **error)

Ludovico de Nittis
committed
{
g_autofree gchar *child_stdout = NULL;
g_autofree gchar *child_stderr = NULL;
int wait_status;
const char *test_argv[] =

Ludovico de Nittis
committed
{
NULL,
"--bus-name=org.freedesktop.Flatpak",
"--",

Ludovico de Nittis
committed
"true",
NULL
};
test_argv[0] = launch_executable;

Ludovico de Nittis
committed
if (!g_spawn_sync (NULL, /* cwd */
(gchar **) test_argv,

Ludovico de Nittis
committed
NULL, /* environ */
G_SPAWN_LEAVE_DESCRIPTORS_OPEN,

Ludovico de Nittis
committed
NULL, NULL, /* child setup */
&child_stdout,
&child_stderr,
&wait_status,
error))
{
return FALSE;

Ludovico de Nittis
committed
}
if (wait_status != 0)

Ludovico de Nittis
committed
{
g_warning ("Cannot run commands on host system: wait status %d",
wait_status);

Ludovico de Nittis
committed
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);
return glnx_throw (error, "Unable to run a command on the host system");

Ludovico de Nittis
committed
}
return TRUE;

Ludovico de Nittis
committed
}
/*
* Export most root directories, but not the ones that
* "flatpak run --filesystem=host" would skip.
* (See flatpak_context_export(), which might replace this function
* later on.)
*
* If we are running inside Flatpak, we assume that any directory
* that is made available in the root, and is not in dont_mount_in_root,
* came in via --filesystem=host or similar and matches its equivalent
* on the real root filesystem.
*/
static gboolean
export_root_dirs_like_filesystem_host (FlatpakExports *exports,
FlatpakFilesystemMode mode,
GError **error)
{
g_autoptr(GDir) dir = NULL;
const char *member = NULL;
g_return_val_if_fail (exports != NULL, FALSE);
g_return_val_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
dir = g_dir_open ("/", 0, error);
if (dir == NULL)
return FALSE;
for (member = g_dir_read_name (dir);
member != NULL;
member = g_dir_read_name (dir))
{
g_autofree gchar *path = NULL;
if (g_strv_contains (dont_mount_in_root, member))
continue;
path = g_build_filename ("/", member, NULL);
flatpak_exports_add_path_expose (exports, mode, path);
}
/* For parity with Flatpak's handling of --filesystem=host */
flatpak_exports_add_path_expose (exports, mode, "/run/media");
return TRUE;
}
/*
* This function assumes that /run on the host is the same as in the
* current namespace, so it won't work in Flatpak.
*/
static gboolean
export_contents_of_run (FlatpakBwrap *bwrap,
GError **error)
{
static const char *ignore[] =
{
"gfx", /* can be created by pressure-vessel */
"host", /* created by pressure-vessel */
"media", /* see export_root_dirs_like_filesystem_host() */
"pressure-vessel", /* created by pressure-vessel */
NULL
};
g_autoptr(GDir) dir = NULL;
const char *member = NULL;
g_return_val_if_fail (bwrap != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (!g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR),
FALSE);
dir = g_dir_open ("/run", 0, error);
if (dir == NULL)
return FALSE;
for (member = g_dir_read_name (dir);
member != NULL;
member = g_dir_read_name (dir))
{
g_autofree gchar *path = NULL;
if (g_strv_contains (ignore, member))
continue;
path = g_build_filename ("/run", member, NULL);
flatpak_bwrap_add_args (bwrap,
"--bind", path, path,
NULL);
}
return TRUE;
}
typedef enum
{
ENV_MOUNT_FLAGS_COLON_DELIMITED = (1 << 0),
ENV_MOUNT_FLAGS_DEPRECATED = (1 << 1),
ENV_MOUNT_FLAGS_READ_ONLY = (1 << 2),
ENV_MOUNT_FLAGS_NONE = 0
} EnvMountFlags;
typedef struct
{
const char *name;
EnvMountFlags flags;
} EnvMount;
static const EnvMount known_required_env[] =
{
{ "PRESSURE_VESSEL_FILESYSTEMS_RO",
ENV_MOUNT_FLAGS_READ_ONLY | ENV_MOUNT_FLAGS_COLON_DELIMITED },
{ "PRESSURE_VESSEL_FILESYSTEMS_RW", ENV_MOUNT_FLAGS_COLON_DELIMITED },
{ "STEAM_COMPAT_APP_LIBRARY_PATH", ENV_MOUNT_FLAGS_DEPRECATED },
{ "STEAM_COMPAT_APP_LIBRARY_PATHS",
ENV_MOUNT_FLAGS_COLON_DELIMITED | ENV_MOUNT_FLAGS_DEPRECATED },
{ "STEAM_COMPAT_CLIENT_INSTALL_PATH", ENV_MOUNT_FLAGS_NONE },
{ "STEAM_COMPAT_DATA_PATH", ENV_MOUNT_FLAGS_NONE },
{ "STEAM_COMPAT_INSTALL_PATH", ENV_MOUNT_FLAGS_NONE },
{ "STEAM_COMPAT_LIBRARY_PATHS", ENV_MOUNT_FLAGS_COLON_DELIMITED },
{ "STEAM_COMPAT_MOUNT_PATHS",
ENV_MOUNT_FLAGS_COLON_DELIMITED | ENV_MOUNT_FLAGS_DEPRECATED },
{ "STEAM_COMPAT_MOUNTS", ENV_MOUNT_FLAGS_COLON_DELIMITED },
{ "STEAM_COMPAT_SHADER_PATH", ENV_MOUNT_FLAGS_NONE },
{ "STEAM_COMPAT_TOOL_PATH", ENV_MOUNT_FLAGS_DEPRECATED },
{ "STEAM_COMPAT_TOOL_PATHS", ENV_MOUNT_FLAGS_COLON_DELIMITED },
{ "STEAM_EXTRA_COMPAT_TOOLS_PATHS", ENV_MOUNT_FLAGS_COLON_DELIMITED },
bind_and_propagate_from_environ (FlatpakExports *exports,
FlatpakBwrap *bwrap,
const char *variable,
EnvMountFlags flags)
g_auto(GStrv) values = NULL;
FlatpakFilesystemMode mode = FLATPAK_FILESYSTEM_MODE_READ_WRITE;
const char *value;
const char *before;
const char *after;
gboolean changed = FALSE;
gsize i;
g_return_if_fail (exports != NULL);
g_return_if_fail (variable != NULL);
value = g_getenv (variable);
if (value == NULL)
return;
if (flags & ENV_MOUNT_FLAGS_DEPRECATED)
g_message ("Setting $%s is deprecated", variable);
if (flags & ENV_MOUNT_FLAGS_READ_ONLY)
mode = FLATPAK_FILESYSTEM_MODE_READ_ONLY;
if (flags & ENV_MOUNT_FLAGS_COLON_DELIMITED)
{
values = g_strsplit (value, ":", -1);
before = "...:";
after = ":...";
}
else
values = g_new0 (gchar *, 2);
values[0] = g_strdup (value);
values[1] = NULL;
before = "";
after = "";
for (i = 0; values[i] != NULL; i++)
{
g_autofree gchar *value_host = NULL;
g_autofree gchar *canon = NULL;

Ludovico de Nittis
committed
if (values[i][0] == '\0')
continue;
if (!g_file_test (values[i], G_FILE_TEST_EXISTS))
{
g_debug ("Not bind-mounting %s=\"%s%s%s\" because it does not exist",
variable, before, values[i], after);
continue;
}
canon = g_canonicalize_filename (values[i], NULL);
value_host = pv_current_namespace_path_to_host_path (canon);
g_debug ("Bind-mounting %s=\"%s%s%s\" from the current env as %s=\"%s%s%s\" in the host",
variable, before, values[i], after,
variable, before, value_host, after);
flatpak_exports_add_path_expose (exports, mode, canon);
if (strcmp (values[i], value_host) != 0)
{
g_clear_pointer (&values[i], g_free);
values[i] = g_steal_pointer (&value_host);
changed = TRUE;
}
}

Ludovico de Nittis
committed
if (changed || g_file_test ("/.flatpak-info", G_FILE_TEST_IS_REGULAR))
{
g_autofree gchar *joined = g_strjoinv (":", values);
flatpak_bwrap_set_env (bwrap, variable, joined, TRUE);
/* Order matters here: root, steam and steambeta are or might be symlinks
* to the root of the Steam installation, so we want to bind-mount their
* targets before we deal with the rest. */
static const char * const steam_api_subdirs[] =
{
"root", "steam", "steambeta", "bin", "bin32", "bin64", "sdk32", "sdk64",
static gboolean expose_steam (FlatpakExports *exports,
FlatpakFilesystemMode mode,
const char *real_home,
const char *fake_home,
GError **error);
use_fake_home (FlatpakExports *exports,
FlatpakBwrap *bwrap,
const gchar *fake_home,
GError **error)
{
const gchar *real_home = g_get_home_dir ();
g_autofree gchar *cache = g_build_filename (fake_home, ".cache", NULL);
g_autofree gchar *cache2 = g_build_filename (fake_home, "cache", NULL);
g_autofree gchar *tmp = g_build_filename (cache, "tmp", NULL);
g_autofree gchar *config = g_build_filename (fake_home, ".config", NULL);
g_autofree gchar *config2 = g_build_filename (fake_home, "config", NULL);
g_autofree gchar *local = g_build_filename (fake_home, ".local", NULL);
g_autofree gchar *data = g_build_filename (local, "share", NULL);
g_autofree gchar *data2 = g_build_filename (fake_home, "data", NULL);
g_return_val_if_fail (bwrap != NULL, FALSE);
g_return_val_if_fail (exports != NULL, FALSE);
g_return_val_if_fail (fake_home != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_mkdir_with_parents (fake_home, 0700);
g_mkdir_with_parents (cache, 0700);
g_mkdir_with_parents (tmp, 0700);
g_mkdir_with_parents (config, 0700);
g_mkdir_with_parents (local, 0700);
g_mkdir_with_parents (data, 0700);
if (!g_file_test (cache2, G_FILE_TEST_EXISTS))
{
g_unlink (cache2);
if (symlink (".cache", cache2) != 0)
return glnx_throw_errno_prefix (error,
"Unable to create symlink %s -> .cache",
cache2);
}
if (!g_file_test (config2, G_FILE_TEST_EXISTS))
{
g_unlink (config2);
if (symlink (".config", config2) != 0)
return glnx_throw_errno_prefix (error,
"Unable to create symlink %s -> .config",
config2);
}
if (!g_file_test (data2, G_FILE_TEST_EXISTS))
{
g_unlink (data2);
if (symlink (".local/share", data2) != 0)
return glnx_throw_errno_prefix (error,
"Unable to create symlink %s -> .local/share",
data2);
}
flatpak_bwrap_add_args (bwrap,
"--bind", fake_home, real_home,
"--bind", tmp, "/var/tmp",
NULL);
flatpak_bwrap_set_env (bwrap, "XDG_CACHE_HOME", cache, TRUE);
flatpak_bwrap_set_env (bwrap, "XDG_CONFIG_HOME", config, TRUE);
flatpak_bwrap_set_env (bwrap, "XDG_DATA_HOME", data, TRUE);
flatpak_exports_add_path_expose (exports,
FLATPAK_FILESYSTEM_MODE_READ_WRITE,
fake_home);
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
return expose_steam (exports, FLATPAK_FILESYSTEM_MODE_READ_ONLY,
real_home, fake_home, error);
}
static gboolean
expose_steam (FlatpakExports *exports,
FlatpakFilesystemMode mode,
const char *real_home,
const char *fake_home,
GError **error)
{
g_autofree gchar *dot_steam = g_build_filename (real_home, ".steam", NULL);
gsize i;
g_return_val_if_fail (exports != NULL, FALSE);
g_return_val_if_fail (real_home != NULL, FALSE);
g_return_val_if_fail ((unsigned) mode <= FLATPAK_FILESYSTEM_MODE_LAST, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* We need ~/.steam to be visible in the container, even if it's a
* symlink to somewhere outside $HOME. (It's better not to do this; use
* a separate Steam library instead, or use bind-mounts.) */
if (fake_home != NULL)
{
flatpak_exports_add_path_expose (exports, mode, dot_steam);
}
else
{
/* Expose the target, but don't try to create the symlink itself:
* that will fail, because we are already sharing the home directory
* with the container, and there's already a symlink where we want
* to put it. */
g_autofree gchar *target = flatpak_resolve_link (dot_steam, NULL);
if (target != NULL)
flatpak_exports_add_path_expose (exports, mode, target);
}
/*
* These might be API entry points, according to Steam/steam.sh.
* They're usually symlinks into the Steam root, except for in
* older steam Debian packages that had Debian bug #916303.
* Even though the symlinks themselves are exposed as part of ~/.steam,
* we need to tell FlatpakExports to also expose the directory to which
* they point, typically (but not necessarily!) ~/.local/share/Steam.
*
* TODO: We probably want to hide part or all of root, steam,
* steambeta?
*/
for (i = 0; i < G_N_ELEMENTS (steam_api_subdirs); i++)
{
g_autofree gchar *dir = g_build_filename (dot_steam,
steam_api_subdirs[i], NULL);
if (fake_home != NULL)
{
g_autofree gchar *mount_point = g_build_filename (fake_home, ".steam",
steam_api_subdirs[i],
NULL);
g_autofree gchar *target = NULL;
target = glnx_readlinkat_malloc (-1, dir, NULL, NULL);
if (target != NULL)
{
/* We used to bind-mount these directories, so transition them
* to symbolic links if we can. */
if (rmdir (mount_point) != 0 && errno != ENOENT && errno != ENOTDIR)
g_debug ("rmdir %s: %s", mount_point, g_strerror (errno));
/* Remove any symlinks that might have already been there. */
if (unlink (mount_point) != 0 && errno != ENOENT)
g_debug ("unlink %s: %s", mount_point, g_strerror (errno));
}
}
flatpak_exports_add_path_expose (exports, mode, dir);
}
return TRUE;
}
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
/*
* @bwrap: Arguments produced by flatpak_exports_append_bwrap_args(),
* not including an executable name (the 0'th argument must be
* `--bind` or similar)
* @home: The home directory
*
* Adjust arguments in @bwrap to cope with potentially running in a
* container.
*/
static void
adjust_exports (FlatpakBwrap *bwrap,
const char *home)
{
gsize i = 0;
while (i < bwrap->argv->len)
{
const char *opt = bwrap->argv->pdata[i];
g_assert (opt != NULL);
if (g_str_equal (opt, "--symlink"))
{
g_assert (i + 3 <= bwrap->argv->len);
/* pdata[i + 1] is the target: unchanged. */
/* pdata[i + 2] is a path in the final container: unchanged. */
i += 3;
}
else if (g_str_equal (opt, "--dir") ||
g_str_equal (opt, "--tmpfs"))
{
g_assert (i + 2 <= bwrap->argv->len);
/* pdata[i + 1] is a path in the final container: unchanged. */
i += 2;
}
else if (g_str_equal (opt, "--ro-bind") ||
g_str_equal (opt, "--bind"))
{
g_autofree gchar *src = NULL;
g_assert (i + 3 <= bwrap->argv->len);
src = g_steal_pointer (&bwrap->argv->pdata[i + 1]);
/* pdata[i + 2] is a path in the final container: unchanged. */
/* Paths in the home directory might need adjusting.
* Paths outside the home directory do not: if they're part of
* /run/host, they've been adjusted already by
* flatpak_exports_take_host_fd(), and if not, they appear in
* the container with the same path as on the host. */
if (flatpak_has_path_prefix (src, home))
bwrap->argv->pdata[i + 1] = pv_current_namespace_path_to_host_path (src);
else
bwrap->argv->pdata[i + 1] = g_steal_pointer (&src);
i += 3;
}
else
{
g_return_if_reached ();
}
}
}
typedef enum
{
TRISTATE_NO = 0,
TRISTATE_YES,
TRISTATE_MAYBE
} Tristate;
static gboolean opt_batch = FALSE;
static char *opt_copy_runtime_into = NULL;
static char **opt_env_if_host = NULL;
static char *opt_fake_home = NULL;
static char **opt_filesystems = NULL;
static char *opt_freedesktop_app_id = NULL;
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 char *opt_graphics_provider = NULL;
static char *graphics_provider_mount_point = NULL;
static gboolean opt_only_prepare = FALSE;
static gboolean opt_remove_game_overlay = FALSE;
static gboolean opt_import_vulkan_layers = TRUE;
static PvShell opt_shell = PV_SHELL_NONE;
static GPtrArray *opt_ld_preload = NULL;
static GArray *opt_pass_fds = NULL;
static char *opt_runtime_base = NULL;
static char *opt_runtime = NULL;
static Tristate opt_share_home = TRISTATE_MAYBE;
static gboolean opt_share_pid = TRUE;
static double opt_terminate_idle_timeout = 0.0;
static double opt_terminate_timeout = -1.0;
static gboolean opt_verbose = FALSE;
static gboolean opt_version = FALSE;
static gboolean opt_version_only = FALSE;
static gboolean opt_test = FALSE;
static PvTerminal opt_terminal = PV_TERMINAL_AUTO;
static char *opt_write_bwrap = NULL;
static gboolean
opt_host_ld_preload_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
gchar *preload = g_strdup_printf ("host:%s", value);
if (opt_ld_preload == NULL)
opt_ld_preload = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (opt_ld_preload, g_steal_pointer (&preload));
return TRUE;
}
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
static gboolean
opt_pass_fd_cb (const char *name,
const char *value,
gpointer data,
GError **error)
{
char *endptr;
gint64 i64 = g_ascii_strtoll (value, &endptr, 10);
int fd;
int fd_flags;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
if (i64 < 0 || i64 > G_MAXINT || endptr == value || *endptr != '\0')
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Integer out of range or invalid: %s", value);
return FALSE;
}
fd = (int) i64;
fd_flags = fcntl (fd, F_GETFD);
if (fd_flags < 0)
return glnx_throw_errno_prefix (error, "Unable to receive --fd %d", fd);
if (opt_pass_fds == NULL)
opt_pass_fds = g_array_new (FALSE, FALSE, sizeof (int));
g_array_append_val (opt_pass_fds, fd);
return TRUE;
}
static gboolean
opt_shell_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
if (g_strcmp0 (option_name, "--shell-after") == 0)
value = "after";
else if (g_strcmp0 (option_name, "--shell-fail") == 0)
value = "fail";
else if (g_strcmp0 (option_name, "--shell-instead") == 0)
value = "instead";
if (value == NULL || *value == '\0')
{
return TRUE;
}
switch (value[0])
{
case 'a':
if (g_strcmp0 (value, "after") == 0)
{
return TRUE;
}
break;
case 'f':
if (g_strcmp0 (value, "fail") == 0)
{
return TRUE;
}
break;
case 'i':
if (g_strcmp0 (value, "instead") == 0)
{
return TRUE;
}
break;
case 'n':
if (g_strcmp0 (value, "none") == 0 || g_strcmp0 (value, "no") == 0)
{
return TRUE;
}
break;
default:
/* fall through to error */
break;
}
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Unknown choice \"%s\" for %s", value, option_name);
return FALSE;
}
static gboolean
opt_terminal_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
if (g_strcmp0 (option_name, "--tty") == 0)
value = "tty";
else if (g_strcmp0 (option_name, "--xterm") == 0)
value = "xterm";
if (value == NULL || *value == '\0')
{
return TRUE;
}
switch (value[0])
{
case 'a':
if (g_strcmp0 (value, "auto") == 0)
{
return TRUE;
}
break;
case 'n':
if (g_strcmp0 (value, "none") == 0 || g_strcmp0 (value, "no") == 0)
{
return TRUE;
}
break;
case 't':
if (g_strcmp0 (value, "tty") == 0)
{
return TRUE;
}
break;
case 'x':
if (g_strcmp0 (value, "xterm") == 0)
{
return TRUE;
}
break;
default:
/* fall through to error */
break;
}
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
"Unknown choice \"%s\" for %s", value, option_name);
return FALSE;
}
static gboolean
opt_share_home_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
if (g_strcmp0 (option_name, "--share-home") == 0)
opt_share_home = TRISTATE_YES;
else if (g_strcmp0 (option_name, "--unshare-home") == 0)
opt_share_home = TRISTATE_NO;
else
g_return_val_if_reached (FALSE);
return TRUE;
}
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
static gboolean
opt_with_host_graphics_cb (const gchar *option_name,
const gchar *value,
gpointer data,
GError **error)
{
/* This is the old way to get the graphics from the host system */
if (g_strcmp0 (option_name, "--with-host-graphics") == 0)
{
if (g_file_test ("/run/host", G_FILE_TEST_IS_DIR))
opt_graphics_provider = g_strdup ("/run/host");
else
opt_graphics_provider = g_strdup ("/");
}
/* This is the old way to avoid using graphics from the host */
else if (g_strcmp0 (option_name, "--without-host-graphics") == 0)
{
opt_graphics_provider = g_strdup ("");
}
else
{
g_return_val_if_reached (FALSE);
}
g_warning ("\"--with-host-graphics\" and \"--without-host-graphics\" have "
"been deprecated and could be removed in future releases. Please use "
"use \"--graphics-provider=/\", \"--graphics-provider=/run/host\" or "
"\"--graphics-provider=\" instead.");
return TRUE;
}
static GOptionEntry options[] =
{
{ "batch", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_batch,
"Disable all interactivity and redirection: ignore --shell*, "
"--terminal, --xterm, --tty. [Default: if $PRESSURE_VESSEL_BATCH]", NULL },
{ "copy-runtime-into", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &opt_copy_runtime_into,
"If a --runtime is used, copy it into DIR and edit the copy in-place. "
"[Default: $PRESSURE_VESSEL_COPY_RUNTIME_INTO or empty]",
"DIR" },
{ "env-if-host", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING_ARRAY, &opt_env_if_host,
"Set VAR=VAL if COMMAND is run with /usr from the host system, "
"but not if it is run with /usr from RUNTIME.", "VAR=VAL" },
{ "filesystem", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY, &opt_filesystems,
"Share filesystem directories with the container. "
"They must currently be given as absolute paths.",
"PATH" },
{ "freedesktop-app-id", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_freedesktop_app_id,
"Make --unshare-home use ~/.var/app/ID as home directory, where ID "
"is com.example.MyApp or similar. This interoperates with Flatpak. "
"[Default: $PRESSURE_VESSEL_FDO_APP_ID if set]",
{ "steam-app-id", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &opt_steam_app_id,
"Make --unshare-home use ~/.var/app/com.steampowered.AppN "
"as home directory. [Default: $STEAM_COMPAT_APP_ID or $SteamAppId]",
"N" },
{ "gc-runtimes", '\0',
G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_gc_runtimes,
"If using --copy-runtime-into, garbage-collect old temporary "
"runtimes. [Default, unless $PRESSURE_VESSEL_GC_RUNTIMES is 0]",
NULL },
{ "no-gc-runtimes", '\0',
G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_gc_runtimes,
"If using --copy-runtime-into, don't garbage-collect old "
"temporary runtimes.", NULL },