Skip to content
Snippets Groups Projects
Commit d5c46fac authored by Simon McVittie's avatar Simon McVittie
Browse files

Initial, basic wrapper script

parents
No related branches found
No related tags found
No related merge requests found
#!/bin/sh
# pressure-vessel — use containers for Steam
#
# Copyright © 2017 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 -u
me="$0"
me="${me##*/}"
# Undo any weird environment before we start running external
# executables. We put it back before running the actual app/game.
caller_ld_preload="$LD_PRELOAD"
unset LD_PRELOAD
caller_ld_library_path="$LD_LIBRARY_PATH"
if [ -n "$SYSTEM_LD_LIBRARY_PATH" ]; then
export LD_LIBRARY_PATH="$SYSTEM_LD_LIBRARY_PATH"
else
unset LD_LIBRARY_PATH
fi
caller_path="$PATH"
if [ -n "$SYSTEM_PATH" ]; then
export PATH="$SYSTEM_PATH"
else
PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
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
fi
fi
if [ -z "$BWRAP" ]; then
echo "$me: cannot find bwrap, falling back to executing '$*' directly" >&2
exec "$@"
fi
fake_home=
interactive=
# Pop the pressure-vessel-wrap options from $@, leaving the command
# and arguments.
getopt_temp="$(getopt -o '' --long \
'freedesktop-app-id:,home:,interactive,steam-app-id:' \
-n "$me" -- "$@")"
if [ $? -ne 0 ]; then
exit $?
fi
eval set -- "$getopt_temp"
unset getopt_temp
while [ "$#" -gt 0 ]; do
case "$1" in
(--freedesktop-app-id)
fake_home="$HOME/.var/app/$2"
shift 2
;;
(--steam-app-id)
fake_home="$HOME/.var/app/com.steampowered.App$2"
shift 2
;;
(--home)
fake_home="$2"
shift 2
;;
(--interactive)
interactive=yes
shift
;;
(--)
shift
break
;;
(--*)
echo "$me: unknown option: $1" >&2
exit 2
;;
(*)
break
;;
esac
done
if [ "$#" -eq 0 ]; then
echo "$me: an executable to run is required" >&2
exit 2
fi
# bwrap doesn't support the -- syntax
case "$1" in
(-*)
command="$1"
shift
set -- "./$command" "$@"
;;
esac
if [ -n "$interactive" ]; then
exec </dev/tty
exec >/dev/tty
exec 2>/dev/tty
echo "$me: Starting interactive shell (original command is in \"\$@\")">&2
set -- bash -i -s "$@"
fi
if [ -z "$fake_home" ]; then
if [ -n "$SteamAppId" ]; then
fake_home="$HOME/.var/app/com.steampowered.App$SteamAppId"
else
echo "$me: either --home, --freedesktop-app-id, --steam-app-id" \
"or \$SteamAppId is required" >&2
exit 2
fi
fi
# Because this is a shell script, we only have access to one array, which
# is $@; so we need to build up the bwrap command line backwards, with
# "wider" mount points like / coming after "narrower" mount points
# like $HOME.
# 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
set -- --new-session "$@"
fi
# Put the caller's LD_PRELOAD and search paths back.
if [ -n "$caller_ld_preload" ]; then
set -- --setenv LD_PRELOAD "$caller_ld_preload" "$@"
else
set -- --unsetenv LD_PRELOAD "$@"
fi
if [ -n "$caller_path" ]; then
set -- --setenv PATH "$caller_path" "$@"
fi
if [ -n "$caller_ld_library_path" ]; then
set -- --setenv LD_LIBRARY_PATH "$caller_ld_library_path" "$@"
else
set -- --unsetenv LD_LIBRARY_PATH "$@"
fi
# Make sure the current working directory (the game we are going to
# run) is available. Some games write here.
PWD="$(pwd)"
set -- --bind "$PWD" "$PWD" --chdir "$PWD" "$@"
# We need libraries from the Steam Runtime, so make sure that's visible
# (it should never need to be read/write though)
if [ -n "${STEAM_RUNTIME:-}" ]; then
set -- --ro-bind "$STEAM_RUNTIME" "$STEAM_RUNTIME" "$@"
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
old_ifs="$IFS"
IFS=:
for preload in $caller_ld_preload; do
if [ -e "$preload" ]; then
set -- --ro-bind "$preload" "$preload" "$@"
fi
done
IFS="$old_ifs"
# 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
set -- --ro-bind "$dir" "$dir" "$@"
fi
done
# steamclient.so relies on this for communication with Steam
if [ -e "$HOME/.steam/steam.pid" ]; then
set -- --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
set -- --bind "$HOME/.steam/steam.pipe" "$HOME/.steam/steam.pipe" "$@"
fi
# Set up the home directory
mkdir -m700 -p "$fake_home"
set -- --bind "$fake_home" "$HOME" --bind "$fake_home" "$fake_home" "$@"
# Set up /var/tmp, XDG_*_HOME like Flatpak does
mkdir -m700 -p "$fake_home/cache/tmp"
set -- --bind "$fake_home/cache/tmp" /var/tmp "$@"
set -- --setenv XDG_CACHE_HOME "$fake_home/cache" "$@"
mkdir -m700 -p "$fake_home/config"
set -- --setenv XDG_CONFIG_HOME "$fake_home/config" "$@"
mkdir -m700 -p "$fake_home/data"
set -- --setenv XDG_DATA_HOME "$fake_home/data" "$@"
# Protect other users' homes (but guard against the unlikely situation
# that they don't exist)
if [ -d /home ]; then
set -- --tmpfs /home "$@"
fi
# Make /dev available
if [ -d /dev/pts ]; then
set -- --bind /dev/pts /dev/pts "$@"
fi
if [ -d /dev/shm ]; then
set -- --bind /dev/shm /dev/shm "$@"
fi
set -- --dev-bind /dev /dev "$@"
# Everything else comes from the root filesystem for now
set -- --bind / / "$@"
if "$BWRAP" --help | grep unshare-uts >/dev/null; then
# Set a standard hostname for the container to make it easier
# to see which shell is which
set -- --hostname pressure-vessel --unshare-uts "$@"
fi
# Potential future expansion: use --unshare-pid for more isolation
# Replace this process with bwrap, which replaces itself with the
# desired command (unless exec fails)
exec "$BWRAP" "$@" || e=$?
echo "$me: failed to execute '$BWRAP $*': exec status $?" >&2
exit $?
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment