#!/bin/bash # pressure-vessel-wrap — run a program in a container that protects $HOME, # optionally using a Flatpak-style runtime # # Copyright © 2017-2019 Collabora Ltd. # # SPDX-License-Identifier: MIT # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. set -e set -o pipefail set -u shopt -s nullglob me="$0" me="$(readlink -f "$0")" here="${me%/*}" me="${me##*/}" if [ -n "${PRESSURE_VESSEL_WRAP_NATIVE+set}" ]; then exec "${here}/pressure-vessel-wrap-c" "$@" fi if [ -n "${STEAM_RUNTIME+set}" ]; then echo "$me: This program should not be run in the Steam Runtime." >&2 echo "$me: Use pressure-vessel-unruntime instead." >&2 exit 2 fi # If bwrap isn't installed as a standalone binary on the PATH, see # whether we have a copy of Flatpak configured with the bundled # bubblewrap (--without-system-bubblewrap). If we do, use that one. if [ -z "${BWRAP:-}" ]; then if ! BWRAP="$(command -v bwrap)"; then for dir in /usr/local/libexec /usr/libexec /usr/lib/flatpak; do if [ -x "$dir/flatpak-bwrap" ]; then BWRAP="$dir/flatpak-bwrap" break fi done if [ -z "${BWRAP:-}" ] && [ -x "${here}/bwrap" ]; then BWRAP="${here}/bwrap" fi fi fi declare -a bwrap_options declare -a multiarch_tuples declare -a wrapped_command declare -a original_argv declare -a ld_preload declare -a env_if_host original_argv=("$@") env_if_host=() fake_home= freedesktop_app_id= host_fallback= ld_preload=() interactive= runtime= share_home=yes steam_app_id="${SteamAppId-}" tmpdir= verbose= xterm= multiarch_tuples=(x86_64-linux-gnu i386-linux-gnu) # Pop the pressure-vessel-wrap options from $@, leaving the command # and arguments. usage () { code="$1" shift if [ "$code" -ne 0 ]; then exec >&2 fi echo echo "Usage: $me [OPTIONS] [--] COMMAND [ARGS...]" echo echo "Run COMMAND [ARGS...] in a container that protects \$HOME." echo echo "The current working directory will be writeable for COMMAND," echo "unless it is the real home directory." echo echo "Options:" echo "--share-home Use the real home directory. [Default]" echo "--unshare-home Use a fake home directory, chosen" echo " automatically according to" echo " --freedesktop-app-id," echo " --steam-app-id, \$SteamAppId." echo "--home=HOME Use HOME as home directory. Implies" echo " --unshare-home." echo echo "--env-if-host=VAR=VAL Set VAR=VAL if COMMAND is run with" echo " /usr from the host system, but not if" echo " it is run with /usr from RUNTIME." echo "--freedesktop-app-id=ID Make --unshare-home default to" echo " ~/.var/app/ID as home directory." echo " ID is com.example.MyApp or similar." echo " This interoperates with Flatpak." echo "--host-fallback Run COMMAND on the host system if we" echo " cannot run it in a container." echo "--interactive Run an interactive shell instead of" echo " COMMAND. Executing \"\$@\" in that" echo " shell will run COMMAND [ARGS]." echo "--host-ld-preload=MODULE Add MODULE from host system to" echo " LD_PRELOAD when executing COMMAND." echo "--runtime=RUNTIME Mount the given sysroot or" echo " merged /usr in the container," echo " and augment it with the host" echo " system's graphics stack." echo "--steam-app-id=123 Make --unshare-home use" echo " ~/.var/app/com.steampowered.App123" echo " as home directory." echo " [Default: \$SteamAppId]" echo "--verbose Be more verbose." echo "--xterm Same as --interactive, but run an" echo " xterm in the container." echo exit "$code" } getopt_temp="help" getopt_temp="${getopt_temp},env-if-host:" getopt_temp="${getopt_temp},freedesktop-app-id:" getopt_temp="${getopt_temp},home:" getopt_temp="${getopt_temp},host-fallback" getopt_temp="${getopt_temp},host-ld-preload:" getopt_temp="${getopt_temp},interactive" getopt_temp="${getopt_temp},runtime:" getopt_temp="${getopt_temp},share-home" getopt_temp="${getopt_temp},steam-app-id:" getopt_temp="${getopt_temp},unshare-home" getopt_temp="${getopt_temp},verbose" getopt_temp="${getopt_temp},xterm" getopt_temp="$(getopt -o '' --long "$getopt_temp" -n "$me" -- "$@")" eval "set -- $getopt_temp" unset getopt_temp while [ "$#" -gt 0 ]; do case "$1" in (--env-if-host) case "$2" in (*=*) ;; (*) echo "$me: --env-if-host argument must be VAR=VALUE" >&2 exit 2 ;; esac env_if_host+=("$2") shift 2 ;; (--freedesktop-app-id) freedesktop_app_id="$2" shift 2 ;; (--steam-app-id) steam_app_id="$2" shift 2 ;; (--home) fake_home="$2" share_home= shift 2 ;; (--host-fallback) host_fallback=yes shift ;; (--host-ld-preload) ld_preload+=("host:$2") shift 2 ;; (--interactive) interactive=yes shift ;; (--runtime) runtime="$2" shift 2 ;; (--share-home) share_home=yes shift ;; (--unshare-home) share_home= shift ;; (--verbose) verbose=yes shift ;; (--xterm) xterm=yes shift ;; (--help) usage 0 # not reached ;; (--) shift break ;; (--*) echo "$me: unknown option: $1" >&2 usage 2 # not reached ;; (*) break ;; esac done if [ "$#" -eq 0 ]; then echo "$me: an executable to run is required" >&2 exit 2 fi if [ -n "$verbose" ]; then echo "Original argv:" >&2 # Deliberately not expanding $0 in format string # shellcheck disable=SC2016 printf '\t$0: %q\n' "$0" >&2 for arg in "${original_argv[@]}"; do printf '\t$@: %q\n' "$arg" >&2 done echo "Current working directory:" >&2 printf '\tLogical: %q\n' "$(pwd -L)" >&2 printf '\tPhysical: %q\n' "$(pwd -P)" >&2 echo "Environment variables:" >&2 env | sed -e 's/^/\t/' >&2 echo "Wrapped command:" >&2 for arg in "$@"; do printf '\t%q\n' "$arg" >&2 done fi if [ -n "$xterm" ]; then { # Yes, this weird quoting is really what I mean. Shells within # shells... # shellcheck disable=SC2016 wrapped_command=(xterm -e sh -c 'echo; echo "$1: Starting interactive shell (original command is in \"\$@\")"; echo; shift; exec "$@"' sh "$me" bash -i -s "$@") } elif [ -n "$interactive" ]; then exec </dev/tty exec >/dev/tty exec 2>/dev/tty echo "$me: Starting interactive shell (original command is in \"\$@\")">&2 wrapped_command=(bash -i -s "$@") else wrapped_command=("$@") fi # Make sure wrapped_command is something we can validly pass to env(1) case "${wrapped_command[0]}" in (*=*) wrapped_command=(sh -c 'exec "$@"' sh "${wrapped_command[@]}") ;; esac if [ -z "$BWRAP" ] || ! "$BWRAP" --bind / / true; then echo "$me: Cannot find bwrap or it doesn't work" >&2 if [ -z "$host_fallback" ]; then exit 1 fi echo "$me: Falling back to executing '${wrapped_command[*]}' directly" >&2 exec env -- ${env_if_host+"${env_if_host[@]}"} "${wrapped_command[@]}" fi if "$BWRAP" --help 2>&1 | grep -F '[--]' >/dev/null 2>/dev/null; then bwrap_end_of_options="--" else # bwrap doesn't support the -- syntax, but we ensured that # wrapped_command is not treated as an environment variable # expansion by env, so this is OK bwrap_end_of_options="env" fi cleanup () { if [ -n "$tmpdir" ]; then rm -fr "$tmpdir" fi } trap cleanup EXIT tmpdir="$(mktemp -d)" scratch="${tmpdir}/scratch" mkdir "$scratch" bwrap_options=() # 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 [ -z "$interactive" ]; then bwrap_options+=(--new-session) fi overrides= for t in "${multiarch_tuples[@]}"; do overrides="${overrides:+"$overrides:"}/overrides/lib/$t" done if [ -n "$runtime" ]; then bwrap_options+=(--setenv LD_LIBRARY_PATH "$overrides") fi declare -a bind_usr_result bind_usr () { local tree="$1" local dest="$2" local dir bind_usr_result=() if [ -d "$tree/usr" ]; then bind_usr_result+=(--ro-bind "$tree/usr" "$dest/usr") else bind_usr_result+=(--ro-bind "$tree" "$dest/usr") fi for dir in "$tree"/lib* "$tree"/bin "$tree"/sbin; do if [ -d "$dir" ]; then local basename basename="$(basename "$dir")" if [ -d "$tree/usr" ]; then bind_usr_result+=(--ro-bind "$dir" "$dest/$basename") else bind_usr_result+=(--symlink "usr/$basename" "$dest/$basename") fi fi done for etc in "/etc/ld.so.cache" "/etc/alternatives"; do if [ -e "$tree$etc" ]; then bind_usr_result+=(--ro-bind "$tree$etc" "$dest$etc") fi done } if [ -n "$runtime" ]; then uid="$(id -u)" bind_usr "$runtime" / bwrap_options+=("${bind_usr_result[@]}") bwrap_options+=(--setenv XDG_RUNTIME_DIR "/run/user/$uid") bwrap_options+=(--proc /proc) bwrap_options+=(--ro-bind /sys /sys) bwrap_options+=(--tmpfs /run) bwrap_options+=(--tmpfs /tmp) bwrap_options+=(--tmpfs /var) bwrap_options+=(--symlink ../run /var/run) for dir in /etc /var/cache /var/lib; do if [ -d "$runtime$dir" ]; then for member in "$runtime$dir"/*; do member="$(basename "$member")" # These paths from the container aren't made available case "$dir/$member" in (/etc/group|/etc/passwd) break ;; (/etc/host.conf|/etc/hosts|/etc/localtime) break ;; (/etc/machine-id|/etc/resolv.conf) break ;; (/var/lib/dbus|/var/lib/dhcp|/var/lib/sudo|/var/lib/urandom) break ;; esac if target="$(readlink "$runtime$dir/$member")"; then bwrap_options+=(--symlink "$target" "$dir/$member") else bwrap_options+=(--ro-bind "$runtime$dir/$member" \ "$dir/$member") fi done fi done # Take the machine ID from the host if [ -e /etc/machine-id ]; then bwrap_options+=(--ro-bind /etc/machine-id /etc/machine-id) bwrap_options+=(--symlink /etc/machine-id /var/lib/dbus/machine-id) elif [ -e /var/lib/dbus/machine-id ]; then bwrap_options+=(--ro-bind /var/lib/dbus/machine-id /etc/machine-id) bwrap_options+=(--symlink /etc/machine-id /var/lib/dbus/machine-id) fi # Take the timezone from the host if target="$(readlink /etc/localtime)" && \ [ "${target#/usr/}" != "$target" ]; then bwrap_options+=(--symlink "$target" /etc/localtime) elif [ -e /etc/localtime ]; then bwrap_options+=(--ro-bind /etc/localtime /etc/localtime) fi for file in resolv.conf host.conf hosts passwd group; do if [ -e "/etc/$file" ]; then bwrap_options+=(--ro-bind "/etc/$file" "/etc/$file") fi done # Simplified version of flatpak run --socket=X11 if [ -n "${DISPLAY-}" ]; then bwrap_options+=(--bind /tmp/.X11-unix /tmp/.X11-unix) bwrap_options+=(--setenv DISPLAY "$DISPLAY") fi for xauth in "${XAUTHORITY-}" "$HOME/.Xauthority"; do if [ -n "$xauth" ] && [ -e "$xauth" ]; then bwrap_options+=(--ro-bind "$xauth" "/run/user/$uid/Xauthority") bwrap_options+=(--setenv XAUTHORITY "/run/user/$uid/Xauthority") break fi done # Simplified version of flatpak run --socket=wayland if [ -n "${XDG_RUNTIME_DIR-}" ] && \ [ -S "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY:="wayland-0"}" ]; then bwrap_options+=(--bind "$XDG_RUNTIME_DIR/${WAYLAND_DISPLAY}" \ "/run/user/$uid/$WAYLAND_DISPLAY") fi # Simplified version of flatpak run --socket=pulseaudio, modified to # support the SteamOS system-wide PulseAudio instance if false; then # FIXME if [ -n "${XDG_RUNTIME_DIR-}" ] && \ [ -S "$XDG_RUNTIME_DIR/pulse/native" ]; then bwrap_options+=(--bind "$XDG_RUNTIME_DIR/pulse/native" \ "/run/user/$uid/pulse/native") bwrap_options+=(--setenv PULSE_SERVER "unix:/run/user/$uid/pulse/native") elif [ -S /var/run/pulse/native ]; then bwrap_options+=(--bind /var/run/pulse/native \ "/run/user/$uid/pulse/native") bwrap_options+=(--setenv PULSE_SERVER "unix:/run/user/$uid/pulse/native") fi fi # Simplified version of flatpak run --socket=system-bus if [ -n "${DBUS_SYSTEM_BUS_ADDRESS-}" ]; then path="$(perl -e '$_ = shift; print $1 if m/^unix:path=([^,;]*)/' \ "$DBUS_SYSTEM_BUS_ADDRESS")" if [ -n "$path" ] && [ -S "$path" ]; then bwrap_options+=(--bind "$path" /var/run/dbus/system_bus_socket) bwrap_options+=(--unsetenv DBUS_SYSTEM_BUS_ADDRESS) else echo "$me: warning: Unable to parse DBUS_SYSTEM_BUS_ADDRESS" \ "\"$DBUS_SYSTEM_BUS_ADDRESS\"" >&2 fi elif [ -S /var/run/dbus/system_bus_socket ]; then bwrap_options+=(--bind /var/run/dbus/system_bus_socket \ /run/dbus/system_bus_socket) fi # Simplified version of flatpak run --socket=system-bus if [ -n "${DBUS_SESSION_BUS_ADDRESS-}" ]; then path="$(perl -e '$_ = shift; print $1 if m/^unix:path=([^,;]*)/' \ "$DBUS_SESSION_BUS_ADDRESS")" if [ "${DBUS_SESSION_BUS_ADDRESS#*abstract=}" != "$DBUS_SESSION_BUS_ADDRESS" ]; then : # Do nothing: abstract Unix sockets can pass through elif [ -n "$path" ] && [ -S "$path" ]; then bwrap_options+=(--bind "$path" "/run/user/$uid/bus") bwrap_options+=(--setenv DBUS_SESSION_BUS_ADDRESS \ "unix:path=/run/user/$uid/bus") else echo "$me: warning: Unable to parse DBUS_SESSION_BUS_ADDRESS" \ "\"$DBUS_SESSION_BUS_ADDRESS\"" >&2 fi elif [ -n "${XDG_RUNTIME_DIR-}" ] && \ [ -S "$XDG_RUNTIME_DIR/bus" ]; then bwrap_options+=(--bind "$XDG_RUNTIME_DIR/bus" \ "/run/user/$uid/bus") bwrap_options+=(--setenv DBUS_SESSION_BUS_ADDRESS \ "unix:path=/run/user/$uid/bus") fi dri_path= for t in "${multiarch_tuples[@]}"; do # This has the side-effect of testing whether we can run # binaries for this architecture if ldso="$("$here/${t}-capsule-capture-libs" --print-ld.so)"; then dri_path="${dri_path:+"$dri_path:"}/overrides/lib/$t/dri" mkdir -p "$tmpdir/overrides/lib/$t" bind_usr "$runtime" "$scratch" "$BWRAP" \ --ro-bind / / \ --bind "$tmpdir/overrides" "$tmpdir/overrides" \ --tmpfs "$scratch" \ "${bind_usr_result[@]}" \ "$here/${t}-capsule-capture-libs" \ --container="$scratch" \ --link-target="/run/host" \ --dest="$tmpdir/overrides/lib/$t" \ --provider="/" \ gl: # If we are going to use the host system's libc6 (likely) # then we have to use its ld.so too. if [ -L "$tmpdir/overrides/lib/$t/libc.so.6" ]; then real_path_in_host="$(readlink -f "$ldso")" real_path_in_runtime="$("$BWRAP" \ --ro-bind / / \ --bind "$tmpdir/overrides" "$tmpdir/overrides" \ --tmpfs "$scratch" \ "${bind_usr_result[@]}" \ "$here/${t}-capsule-capture-libs" \ --resolve-ld.so="$scratch")" if [ -d "$runtime/usr" ] || \ [ "$real_path_in_runtime#/usr/" != "$real_path_in_runtime" ]; then bwrap_options+=(--ro-bind "$real_path_in_host" \ "$real_path_in_runtime") else # /lib, /lib64 are just going to be symlinks anyway bwrap_options+=(--ro-bind "$real_path_in_host" \ "/usr$real_path_in_runtime") fi # We should also pick up its locale archives. if [ -e /usr/lib/locale ]; then bwrap_options+=(--ro-bind /usr/lib/locale /usr/lib/locale) fi if [ -e /usr/share/i18n ]; then bwrap_options+=(--ro-bind /usr/share/i18n /usr/share/i18n) fi fi # /lib, /lib32 or /lib64 libQUAL="$(dirname "$ldso")" for dir in "/lib/$t" "/usr/lib/$t" "$libQUAL" "/usr$libQUAL"; do if [ -d "$dir/dri" ]; then mkdir -p "$tmpdir/overrides/lib/$t/dri" "$BWRAP" \ --ro-bind / / \ --bind "$tmpdir/overrides" "$tmpdir/overrides" \ --tmpfs "$scratch" \ "${bind_usr_result[@]}" \ "$here/${t}-capsule-capture-libs" \ --container="$scratch" \ --link-target="/run/host" \ --dest="$tmpdir/overrides/lib/$t" \ --provider="/" \ "only-dependencies:if-exists:path-match:$dir/dri/*.so" # Deliberately not expanding $1, $2 in sh -c # shellcheck disable=SC2016 "$BWRAP" \ --ro-bind / / \ --tmpfs /run \ --ro-bind / /run/host \ --bind "$tmpdir/overrides" "$tmpdir/overrides" \ sh -c 'ln -fns "$1"/* "$2"' \ sh "/run/host$dir/dri" "$tmpdir/overrides/lib/$t/dri" fi # S3TC or S2TC support for Mesa if [ -e "$dir/libtxc_dxtn.so" ]; then "$BWRAP" \ --ro-bind / / \ --bind "$tmpdir/overrides" "$tmpdir/overrides" \ --tmpfs "$scratch" \ "${bind_usr_result[@]}" \ "$here/${t}-capsule-capture-libs" \ --container="$scratch" \ --link-target="/run/host" \ --dest="$tmpdir/overrides/lib/$t" \ --provider="/" \ "path-match:$dir/libtxc_dxtn.so" fi done fi done bwrap_options+=(--setenv LIBGL_DRIVERS_PATH "$dri_path") bwrap_options+=(--ro-bind "$tmpdir/overrides" /overrides) bind_usr / /run/host bwrap_options+=("${bind_usr_result[@]}") else # Everything comes from the root filesystem, except where # modified below bwrap_options+=(--bind / /) fi # Make /dev available bwrap_options+=(--dev-bind /dev /dev) if [ -d /dev/pts ]; then bwrap_options+=(--dev-bind /dev/pts /dev/pts) fi if [ -d /dev/shm ]; then bwrap_options+=(--dev-bind /dev/shm /dev/shm) fi # Protect other users' homes (but guard against the unlikely situation # that they don't exist) if [ -d /home ]; then bwrap_options+=(--tmpfs /home) fi if [ -n "$share_home" ]; then # Share the home directory, and inherit HOME and XDG_*_HOME # from the parent process. bwrap_options+=(--bind "$HOME" "$HOME") else if [ -n "$fake_home" ]; then : # OK elif [ -n "$freedesktop_app_id" ]; then fake_home="$HOME/.var/app/$freedesktop_app_id" elif [ -n "$steam_app_id" ]; then fake_home="$HOME/.var/app/com.steampowered.App$steam_app_id" else echo "$me: Error: --unshare-home requires --fake-home," >&2 echo "$me: --freedesktop-app-id, --steam-app-id or \$SteamAppId" >&2 exit 2 fi # The shellcheck tool warns that -m is only applied to the deepest # directory and not any parents created via -p, but that's fine here: # we use a separate mkdir -p for each directory whose permissions we # care about. # shellcheck disable=SC2174 { # Set up the home directory mkdir -m700 -p "$fake_home" bwrap_options+=(--bind "$fake_home" "$HOME") bwrap_options+=(--bind "$fake_home" "$fake_home") # Set up /var/tmp, XDG_*_HOME like Flatpak does mkdir -m700 -p "$fake_home/.cache" mkdir -m700 -p "$fake_home/.cache/tmp" if [ ! -e "$fake_home/cache" ]; then ln -fns .cache "$fake_home/cache" fi bwrap_options+=(--bind "$fake_home/.cache/tmp" /var/tmp) bwrap_options+=(--setenv XDG_CACHE_HOME "$fake_home/.cache") mkdir -m700 -p "$fake_home/.config" if [ ! -e "$fake_home/config" ]; then ln -fns .config "$fake_home/config" fi bwrap_options+=(--setenv XDG_CONFIG_HOME "$fake_home/.config") mkdir -m700 -p "$fake_home/.local" mkdir -m700 -p "$fake_home/.local/share" if [ ! -e "$fake_home/data" ]; then ln -fns .local/share "$fake_home/data" fi bwrap_options+=(--setenv XDG_DATA_HOME "$fake_home/.local/share") } # These might be API entry points, according to .steam/steam/steam.sh. # On the host system they're symlinks to a real location, but in a # container it's easier to bind-mount them (this follows symlinks). # TODO: We probably want to hide part or all of root, steam, steambeta? for api in bin bin32 bin64 root sdk32 sdk64 steam steambeta; do dir="$HOME/.steam/$api" if [ -d "$dir" ]; then bwrap_options+=(--ro-bind "$dir" "$dir") fi done # steamclient.so relies on this for communication with Steam if [ -e "$HOME/.steam/steam.pid" ]; then bwrap_options+=(--ro-bind "$HOME/.steam/steam.pid" "$HOME/.steam/steam.pid") fi # Make sure Steam IPC is available (TODO: do we need this? do we need more?) if [ -e "$HOME/.steam/steam.pipe" ]; then bwrap_options+=(--bind "$HOME/.steam/steam.pipe" "$HOME/.steam/steam.pipe") fi fi # 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 adjusted_ld_preload= for preload in ${ld_preload+"${ld_preload[@]}"}; do # Currently the only preloaded modules we support are ones that come # from the host. preload="${preload#host:}" if [ -e "$preload" ]; then case "$preload" in (/usr/*|/lib*) if [ -n "$runtime" ]; then # We can't write here, so redirect them to the # corresponding locations in /run/host bwrap_options+=(--ro-bind "$preload" "/run/host$preload") adjusted_ld_preload="${adjusted_ld_preload:+"$adjusted_ld_preload:"}/run/host$preload" else bwrap_options+=(--ro-bind "$preload" "$preload") adjusted_ld_preload="${adjusted_ld_preload:+"$adjusted_ld_preload:"}$preload" fi ;; (*) bwrap_options+=(--ro-bind "$preload" "$preload") adjusted_ld_preload="${adjusted_ld_preload:+"$adjusted_ld_preload:"}$preload" ;; esac elif [ -n "$preload" ]; then echo "$me: LD_PRELOAD module '$preload' does not exist" >&2 fi done # Make sure the current working directory (the game we are going to # run) is available. Some games write here. PWD_L="$(pwd -L)" if test "$HOME" -ef "$PWD_L"; then # Keep quiet about this if we're sharing $HOME anyway if [ -z "$share_home" ]; then echo "$me: Not making real $PWD_L available to container because it is" \ "the home directory" >&2 fi else bwrap_options+=(--bind "$PWD_L" "$PWD_L") fi bwrap_options+=(--chdir "$PWD_L") PWD_P="$(pwd -P)" if test "$PWD_P" = "$PWD_L"; then : # Nothing special to do elif test "$HOME" -ef "$PWD_P"; then # Keep quiet about this if we're sharing $HOME anyway if [ -z "$share_home" ]; then echo "$me: Not making real $PWD_P available to container because it is" \ "the home directory" >&2 fi else bwrap_options+=(--bind "$PWD_P" "$PWD_P") fi # Put the caller's LD_PRELOAD and search paths back. if [ -n "$adjusted_ld_preload" ]; then bwrap_options+=(--setenv LD_PRELOAD "$adjusted_ld_preload") else bwrap_options+=(--unsetenv LD_PRELOAD) fi if "$BWRAP" --help 2>&1 | grep unshare-uts >/dev/null; then # Set a standard hostname for the container to make it easier # to see which shell is which bwrap_options+=(--hostname pressure-vessel --unshare-uts) fi # Potential future expansion: use --unshare-pid for more isolation # Put Steam Runtime environment variables back, if /usr is mounted # from the host. if [ -z "$runtime" ]; then # We need libraries from the Steam Runtime, so make sure that's visible # (it should never need to be read/write though) for env in ${env_if_host+"${env_if_host[@]}"}; do case "$env" in (STEAM_RUNTIME=/*) value="${env#STEAM_RUNTIME=}" bwrap_options+=(--ro-bind "$value" "$value") ;; esac done wrapped_command=(env -- ${env_if_host+"${env_if_host[@]}"} "${wrapped_command[@]}") fi # Replace this process with bwrap, which replaces itself with the # desired command (unless exec fails) if [ -n "$verbose" ]; then echo "$BWRAP options:">&2 for x in "${bwrap_options[@]}"; do printf '\t%q\n' "$x" >&2 done echo "$me: '$BWRAP ${bwrap_options[*]} $bwrap_end_of_options ${wrapped_command[*]}'" >&2 fi e=0 "$BWRAP" "${bwrap_options[@]}" $bwrap_end_of_options "${wrapped_command[@]}" || e=$? if [ "$e" -eq 0 ]; then if [ -n "$verbose" ]; then echo "$me: '$BWRAP ${bwrap_options[*]} $bwrap_end_of_options ${wrapped_command[*]}': successful" >&2 fi else echo "$me: failed to execute '$BWRAP ${bwrap_options[*]} $bwrap_end_of_options ${wrapped_command[*]}': exec status $e" >&2 fi exit "$e" # vim:set sw=4 sts=4 et: