-
Ludovico de Nittis authored
LD_PRELOAD usually contains libraries such as gameoverlayrenderer.so that can throw a warning if we try to preload them without adjusting them first. For this reason we unset LD_PRELOAD and pass its values as arguments for pressure-vessel-wrap, that will adjust them before launching the requested program/game. A similar thing was already done in pressure-vessel-unruntime, but we should do it at the very first opportunity to avoid preloading warnings every time we launch an external command. Signed-off-by:
Ludovico de Nittis <ludovico.denittis@collabora.com>
Ludovico de Nittis authoredLD_PRELOAD usually contains libraries such as gameoverlayrenderer.so that can throw a warning if we try to preload them without adjusting them first. For this reason we unset LD_PRELOAD and pass its values as arguments for pressure-vessel-wrap, that will adjust them before launching the requested program/game. A similar thing was already done in pressure-vessel-unruntime, but we should do it at the very first opportunity to avoid preloading warnings every time we launch an external command. Signed-off-by:
Ludovico de Nittis <ludovico.denittis@collabora.com>
_v2-entry-point 11.80 KiB
#!/usr/bin/env bash
set -eu
me="$(readlink -f "$0")"
here="${me%/*}"
me="${me##*/}"
# This is a prototype and will probably not survive in its current form.
# Don't rely on it.
is_main=yes
suite=
verbose=
log_to_file=
log_dir=${STEAM_LINUX_RUNTIME_LOG_DIR-"${PRESSURE_VESSEL_VARIABLE_DIR-"${here}/var"}"}
keep_logs=
use_timestamp=
ld_preload=
# Arguments for pressure-vessel-wrap
declare -a container_args=()
if [ -n "${LD_PRELOAD-}" ]; then
container_args+=("--env-if-host=LD_PRELOAD=$LD_PRELOAD")
ld_preload="$LD_PRELOAD"
fi
unset LD_PRELOAD
if [ "${STEAM_LINUX_RUNTIME_VERBOSE-}" = 1 ]; then
verbose=yes
# Propagate it to pressure-vessel too
export PRESSURE_VESSEL_VERBOSE=1
fi
if [ "${STEAM_LINUX_RUNTIME_LOG-}" = 1 ]; then
log_to_file=yes
use_timestamp=yes
fi
if [ "${STEAM_LINUX_RUNTIME_KEEP_LOGS-}" = 1 ]; then
keep_logs=yes
fi
log () {
if [ -n "$use_timestamp" ]; then
timestamp=$(date +'%H:%M:%S.%6N')
printf '%s\n' "${timestamp}: ${me}[$$]: $*" >&2
else
printf '%s\n' "${me}[$$]: $*" >&2
fi
}
info () {
if [ -n "${log_to_file}${verbose}" ]; then
log "$@"
fi
}
verbose () {
if [ -n "$verbose" ]; then
log "$@"
fi
}
usage () {
local code="$1"
shift
if [ "$code" -ne 0 ]; then
exec >&2
fi
echo "Usage:"
echo "$me [OPTIONS] COMMAND [ARGS...]"
echo
echo "Run a command in a container that is shared with other"
echo "invocations of the same Steam game."
echo
echo "Required arguments"
echo "COMMAND [ARGS...] Run this."
echo
echo "Options"
echo "--deploy=DIR Ignored for backwards compatibility."
echo "--keep-logs Do not remove the older logs of this same app id."
echo "--log-to-file Log to a file instead of the default stderr."
echo "--suite=SUITE Run in this runtime [default=choose automatically]."
echo "--use-timestamp Prepend the timestamp to the log entries [default with --log-to-file]."
echo "--verb=%verb% Mode to operate in [default=waitforexitandrun]."
echo "--verbose Be more verbose."
exit "${code}"
}
getopt_temp="help"
getopt_temp="${getopt_temp},deploy:"
getopt_temp="${getopt_temp},log-to-file:"
getopt_temp="${getopt_temp},keep-logs:"
getopt_temp="${getopt_temp},suite:"
getopt_temp="${getopt_temp},use-timestamp:"
getopt_temp="${getopt_temp},verb:"
getopt_temp="${getopt_temp},verbose"
getopt_temp="$(getopt -o '' --long "$getopt_temp" -n "$me" -- "$@")"
eval "set -- $getopt_temp"
unset getopt_temp
while [ "$#" -gt 0 ]; do
case "$1" in
(--help)
usage 0
# not reached
;;
(--deploy)
shift 2
;;
(--log-to-file)
log_to_file=yes
use_timestamp=yes
shift
;;
(--keep-logs)
keep_logs=yes
shift
;;
(--suite)
suite="$2"
shift 2
;;
(--verb)
case "$2" in
(waitforexitandrun)
is_main=yes
;;
(run)
export PRESSURE_VESSEL_BATCH=1
is_main=
;;
(*)
is_main=
;;
esac
shift 2
;;
(--use-timestamp)
use_timestamp=yes
shift
;;
(--verbose)
verbose=yes
# Propagate it to pressure-vessel too
export PRESSURE_VESSEL_VERBOSE=1
shift
;;
(--)
shift
break
;;
(-*)
log "Unknown option: $1"
usage 125 # EX_USAGE from sysexits.h
# not reached
;;
(*)
break
;;
esac
done
if [ -n "$log_to_file" ]; then
app=
log_filename=
if [[ -z "${STEAM_COMPAT_APP_ID-}" && -z "${SteamAppId-}" ]]; then
app="non-steam-game"
else
app="app${STEAM_COMPAT_APP_ID-${SteamAppId}}"
fi
if [ -z "${STEAM_COMPAT_SESSION_ID-}" ]; then
log_filename="slr-${app}-t$(date +'%Y%m%dT%H%M%S').log"
else
log_filename="slr-${app}-s${STEAM_COMPAT_SESSION_ID}.log"
fi
mkdir -p "${log_dir}"
# Remove older logs of the same app
if [ -z "${keep_logs}" ]; then
find "${log_dir}" -maxdepth 1 -name "slr-${app}-*.log" '!' -name "${log_filename}" -type f -delete
fi
ln -fns "${log_filename}" "${log_dir}/slr-latest.log" || :
if [[ (-z "$is_main" && -n "${STEAM_COMPAT_SESSION_ID-}")
|| -n "${PRESSURE_VESSEL_BATCH-}" ]]; then
# If we are running setup commands, or if we are gathering a system
# information report, we only redirect stderr to the log, on the
# assumption that stderr is for human-readable diagnostics but
# stdout could be for machine-readable output.
exec 2>> "${log_dir}/${log_filename}"
else
exec >> "${log_dir}/${log_filename}" 2>&1
fi
export PRESSURE_VESSEL_LOG_INFO=1
export PRESSURE_VESSEL_LOG_WITH_TIMESTAMP=1
fi
info "argv: $(printf '%q ' "$@")"
if [ "$#" -eq 0 ] || [ "x$1" = x-- ]; then
log "Error: A command to run is required"
usage 125
fi
# Steam App ID of this app or game
verbose "STEAM_COMPAT_APP_ID=${STEAM_COMPAT_APP_ID-}"
# Session ID used for coordination between processes involving the same game.
# If unset/empty, only execute a single command.
verbose "STEAM_COMPAT_SESSION_ID=${STEAM_COMPAT_SESSION_ID-}"
# Used to create sockets.
verbose "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR-}"
relaunch=
if [ -z "${STEAM_COMPAT_SESSION_ID-}" ]; then
info "Non-session mode, behaving like the main program"
is_main=yes
elif [ -n "$is_main" ]; then
info "Main program in session mode"
# Use the relaunch mode by default
case "${PRESSURE_VESSEL_RELAUNCH_CONTAINER-1}" in
(1)
relaunch=yes
;;
(0)
;;
(*)
echo "\$PRESSURE_VESSEL_RELAUNCH_CONTAINER should be either 0 or 1" >&2
;;
esac
else
verbose "Setup program in session mode"
fi
# At this point $LD_LIBRARY_PATH might
# 1. be equal to $STEAM_RUNTIME_LIBRARY_PATH (e.g. a game without
# any special launch options).
# 2. contain all the entries of $STEAM_RUNTIME_LIBRARY_PATH, plus eventually
# any additional paths from the game launch options (e.g. a game launched
# with "LD_LIBRARY_PATH=/my_game_path:${LD_LIBRARY_PATH} %command%")
# 3. contain just new entries from the game launch options (e.g. a game
# launched with "LD_LIBRARY_PATH=/my_game_path %command%")
# 4. being unset or empty (e.g. a game launched with
# "LD_LIBRARY_PATH="" %command%")
#
# We extract all the entries from $LD_LIBRARY_PATH that are not under the
# $STEAM_RUNTIME paths. In this way we should end up with a list of paths
# that are from the system $LD_LIBRARY_PATH, the system "ldconfig" and the
# manually set $LD_LIBRARY_PATH paths from the game launch options.
case "${STEAM_RUNTIME-}" in
(/*)
oldIFS="$IFS"
IFS=:
paths=
for path in ${LD_LIBRARY_PATH-}; do
if [ "${path}" == "${STEAM_RUNTIME}" ]; then
# path is exactly the ${STEAM_RUNTIME}; ignore
continue
elif [ "${path#${STEAM_RUNTIME}/}" != "${path}" ]; then
# path is ${STEAM_RUNTIME}/...; ignore
continue
else
# keep it (note that we discard the extra leading ":" later)
paths="${paths}:${path}"
fi
done
IFS="$oldIFS"
export PRESSURE_VESSEL_APP_LD_LIBRARY_PATH="${paths#:}"
;;
(*)
# use LD_LIBRARY_PATH as-is
export PRESSURE_VESSEL_APP_LD_LIBRARY_PATH="${LD_LIBRARY_PATH-}"
;;
esac
old_IFS="$IFS"
IFS=":"
for word in $ld_preload; do
container_args+=("--ld-preload=$word")
done
IFS="$old_IFS"
unset LD_LIBRARY_PATH
unset STEAM_RUNTIME
pressure_vessel="${PRESSURE_VESSEL_PREFIX:-"${here}/pressure-vessel"}"
if [ -z "${suite}" ]; then
run=run
else
run="run-in-${suite}"
fi
exec_container () {
# Run as a single command
exec "${here}/${run}" \
${container_args[0]+"${container_args[@]}"} \
-- \
"$@" \
${NULL+}
exit 125
}
if [ -z "${STEAM_COMPAT_SESSION_ID-}" ]; then
verbose "Non-session mode, just running the container"
exec_container "$@"
# not reached
fi
# Session mode. Either PRESSURE_VESSEL_SOCKET_DIR or XDG_RUNTIME_DIR are required.
verbose "Session mode"
if [[ -v PRESSURE_VESSEL_SOCKET_DIR ]]; then
if ! [ -O "$PRESSURE_VESSEL_SOCKET_DIR" ]; then
log "PRESSURE_VESSEL_SOCKET_DIR is owned by someone else"
exit 125
fi
rendezvous="${PRESSURE_VESSEL_SOCKET_DIR}/SteamLinuxRuntime.${STEAM_COMPAT_SESSION_ID:?}"
else
if ! [ -O "$XDG_RUNTIME_DIR" ]; then
log "XDG_RUNTIME_DIR is owned by someone else"
exit 125
fi
rendezvous="${XDG_RUNTIME_DIR}/SteamLinuxRuntime.${STEAM_COMPAT_SESSION_ID:?}"
fi
if mkdir -m700 "$rendezvous" 2>/dev/null; then
:
elif [ -d "$rendezvous" ] && [ -O "$rendezvous" ]; then
:
else
log "Unable to create or adopt $rendezvous"
exit 125
fi
terminate_container () {
if ! [ -S "$rendezvous/socket" ]; then
# nothing to do
return
fi
"$pressure_vessel/bin/pressure-vessel-adverb" \
--create \
--wait \
--write \
--lock-file "${rendezvous}/.ref" \
-- \
"$pressure_vessel/bin/pressure-vessel-launch" \
--socket="$rendezvous/socket" \
--terminate
}
if [ -n "$relaunch" ]; then
info "Terminating and re-launching container for main game"
terminate_container || :
exec_container "$@"
# not reached
fi
if [ -n "$is_main" ]; then
trap terminate_container EXIT
fi
# Launch the pressure-vessel-launcher(1). This is used to run the
# setup commands (--verb=run), if any. If not requested via
# PRESSURE_VESSEL_RELAUNCH_CONTAINER=1, it is also used to run the
# actual game.
#
# We have to launch pressure-vessel-launcher(1) *without* setting
# SteamAppId, SteamAppUser, etc., otherwise Steam will think the game
# is already running and refuse to run it again. (Tested with SteamAppId,
# just guessing about the others)
"$pressure_vessel/bin/pressure-vessel-adverb" \
--create \
--wait \
--write \
--lock-file "${rendezvous}/.ref" \
-- \
env \
-u FONTCONFIG_PATH \
-u SteamLauncherUI \
-u SteamAppId \
-u SteamAppUser \
-u SteamClientLaunch \
-u SteamEnv \
-u SteamGameId \
-u SteamOverlayGameId \
"${here}/_start-container-in-background" \
${is_main:+--force} \
--session="$rendezvous" \
--suite="$suite" \
-- \
${container_args[0]+"${container_args[@]}"} \
--terminate-timeout=2 \
${NULL+}
launch_args=( \
)
if [ -n "$is_main" ]; then
if [ "${SteamAppId-}" != "${STEAM_COMPAT_APP_ID-}" ]; then
log "Mismatch: SteamAppId=${SteamAppId-}, STEAM_COMPAT_APP_ID=${STEAM_COMPAT_APP_ID-}"
exit 125
fi
launch_args=( \
${launch_args[0]+"${launch_args[@]}"} \
--pass-env-matching '*' \
--terminate \
)
# For the main game we wait for all processes to terminate before
# shutting down the container.
set -- \
"$pressure_vessel/bin/pressure-vessel-adverb" \
${PRESSURE_VESSEL_SHELL+--shell="$PRESSURE_VESSEL_SHELL"} \
${PRESSURE_VESSEL_TERMINAL+--terminal="$PRESSURE_VESSEL_TERMINAL"} \
--subreaper \
-- \
"$@"
else
launch_args=( \
${launch_args[0]+"${launch_args[@]}"} \
)
fi
exec "$pressure_vessel/bin/pressure-vessel-launch" \
--directory="$(pwd)" \
--socket="$rendezvous/socket" \
${launch_args[0]+"${launch_args[@]}"} \
-- \
"$@"
exit 125
# not reached
# vim:set sw=4 sts=4 et: