mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-20 08:40:10 +00:00
Restore TIO always - the firejail doesn't always let UG exit gracefully (seems like there is some timeout), so restore this with the wrapping script.
382 lines
16 KiB
Bash
Executable File
382 lines
16 KiB
Bash
Executable File
#!/bin/sh -eu
|
|
# shellcheck disable=SC2059 # use of $bold et al. in printf format str
|
|
|
|
# defaults
|
|
use_bundled_libva_default=0
|
|
|
|
# if not run from AppImage (eg. extracted), use default values
|
|
export APPIMAGE="${APPIMAGE-none}"
|
|
ARGV0=${ARGV0-$0}
|
|
|
|
bold=$(tput bold || true)
|
|
red=$(tput setaf 1 || true)
|
|
reset=$(tput sgr0 || true)
|
|
|
|
DIR=$(dirname "$0")
|
|
readonly AI_LIB_PATH="$DIR"/usr/lib
|
|
n_ld_library_path="$AI_LIB_PATH"${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"}
|
|
LD_PRELOAD=${LD_PRELOAD-}
|
|
# there is an issue with running_from_path() which evaluates this executable
|
|
# as being system-installed
|
|
#export PATH=$DIR/usr/bin:$PATH
|
|
export QT_QPA_FONTDIR="$DIR/usr/share/fonts"
|
|
export UG_FONT_DIR="$DIR/usr/share/fonts"
|
|
QT_PLUGIN_PATH=$(set -- "$DIR"/usr/lib/qt?/plugins; echo "$1")
|
|
export QT_PLUGIN_PATH
|
|
export QT_QPA_PLATFORM_PLUGIN_PATH="$QT_PLUGIN_PATH/platforms"
|
|
|
|
get_tools() {(
|
|
find "$DIR/usr/bin" -mindepth 1 -exec basename {} \; | tr '\n' ' '
|
|
)}
|
|
|
|
usage() {
|
|
printf "usage:\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}[--gui [args]]${reset}\n" "$ARGV0"
|
|
printf "\t\tinvokes GUI\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}--appimage-help${reset}\n" "$ARGV0"
|
|
printf "\t\tprints AppImage related options\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}-h | --help | --fullhelp${reset}\n" "$ARGV0"
|
|
printf "\t\tprints this help (extended version with ${bold}--fullhelp${reset})\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}-m|--man [uv|hd-rum-transcode]${reset}\n" "$ARGV0"
|
|
printf "\t\tprints manual page\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}-u|--update [args]${reset}\n" "$ARGV0"
|
|
printf "\t\tupdates AppImage (args will be passed to appimageupdatetool)\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}-o|--tool uv --help${reset}\n" "$ARGV0"
|
|
printf "\t\tprints command-line UltraGrid help\n"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}-o|--tool <t> [args]${reset}\n" "$ARGV0"
|
|
printf "\t\tinvokes specified tool\n"
|
|
printf "\t\ttool may be one of: ${bold}%s${reset}\n" "$(get_tools)"
|
|
printf "\n"
|
|
printf "\t${bold}${red}%s${reset} ${bold}args${reset}\n" "$ARGV0"
|
|
printf "\t\tinvokes command-line UltraGrid\n"
|
|
printf "\n"
|
|
}
|
|
|
|
usage_aux() {
|
|
printf "environment variables:\n"
|
|
printf "\tULTRAGRID_AUTOUPDATE: autoupdate interval in days (0 - check always); -1 - disable update advice\n"
|
|
printf "\tULTRAGRID_BUNDLED_LIBVA: 1 - use bundled libva; 0 - use system libva (if available), default %d\n" $use_bundled_libva_default
|
|
printf "\tULTRAGRID_USE_FIREJAIL: run the UltraGrid executable with firejail. If\n"
|
|
printf "\t the variable contains a profile path (ends with\n"
|
|
printf "\t '.profile'), it will be used.\n"
|
|
printf "\tFIREJAIL_DEBUG: print used Firejail options\n"
|
|
printf "\tFIREJAIL_OPTS: custom options to be passed to Firejail\n"
|
|
printf "\n"
|
|
}
|
|
|
|
update_check_days=90
|
|
## Trigger update if $ULTRAGRID_AUTOUPDATE days passed.
|
|
## If $ULTRAGRID_AUTOUPDATE unset or empty, print update hint if UG binary is older than $update_check_days days.
|
|
handle_updates() {(
|
|
if [ "${ULTRAGRID_AUTOUPDATE-0}" -eq -1 ]; then
|
|
return
|
|
fi
|
|
if [ "$APPIMAGE" = none ]; then
|
|
return
|
|
fi
|
|
if expr "$APPIMAGE" : '.*continuous' > /dev/null; then
|
|
update_check_days=30
|
|
fi
|
|
if [ "${ULTRAGRID_AUTOUPDATE-}" ] && [ "$ULTRAGRID_AUTOUPDATE" -ge 0 ]; then
|
|
update_check_days=$ULTRAGRID_AUTOUPDATE
|
|
fi
|
|
appimage_mtime=$(stat -c %Y "$APPIMAGE")
|
|
curr_timestamp=$(date +%s)
|
|
if [ "$curr_timestamp" -lt $((appimage_mtime + update_check_days * 24 * 60 * 60)) ]; then
|
|
return
|
|
fi
|
|
if [ "${ULTRAGRID_AUTOUPDATE-}" ] && [ "$ULTRAGRID_AUTOUPDATE" -ge 0 ]; then
|
|
if ! "$DIR/appimageupdatetool" -j "$APPIMAGE" && "$DIR/appimageupdatetool" "$APPIMAGE"; then
|
|
ULTRAGRID_AUTOUPDATE=-1 # prevent update loops if the new version not stored for some reason
|
|
exec "$APPIMAGE" "$@" # launch the updated version
|
|
fi
|
|
touch "$APPIMAGE" # update AppImage mtime otherwise if there are no updates after elapsing the interval, the check would run always
|
|
return
|
|
fi
|
|
printf "UltraGrid binary older than %d days, consider checking updates:\n" "$update_check_days"
|
|
printf "\n"
|
|
printf "%s -u\n" "$ARGV0"
|
|
printf "\t- updates AppImage\n"
|
|
printf "%s -u -j; [ \$? -eq 1 ] && echo Update available || echo No update available\n" "$ARGV0"
|
|
printf "\t- check for update without actually updating\n"
|
|
printf "%s -u -h\n" "$ARGV0"
|
|
printf "\t- prints update options\n"
|
|
printf "\n"
|
|
printf "Hint: you can set environment variable ULTRAGRID_AUTOUPDATE to 1 for daily automatic update or -1 to suppress the above message.\n"
|
|
printf "\n"
|
|
)}
|
|
|
|
## Tries to find system libva. If found it is preloaded (+libva-drm+libva-x11)
|
|
## @retval 0 if preloaded; 1 otherwise
|
|
set_libva_ld_preload() {
|
|
[ -f "$AI_LIB_PATH/ultragrid/ultragrid_vcompress_libavcodec.so" ] ||
|
|
return 0
|
|
LOADER=$(get_loader)
|
|
[ -x "$LOADER" ] || return 1
|
|
get_ai_lib_path() {(
|
|
LD_LIBRARY_PATH=$n_ld_library_path LD_TRACE_LOADED_OBJECTS=1 \
|
|
"$LOADER" "$AI_LIB_PATH/ultragrid/ultragrid_vcompress_\
|
|
libavcodec.so" | grep "$1" | grep -v 'not found' | awk '{print $3}'
|
|
)}
|
|
AI_LIBAVCODEC_LIB=$(get_ai_lib_path libavcodec.so)
|
|
check_libva_deps() { # check if all liva*.so satisfied from sys
|
|
[ -n "$1" ] || return 1
|
|
! LD_TRACE_LOADED_OBJECTS=1 "$LOADER" "$1" |
|
|
grep -q 'libva.*not found'
|
|
}
|
|
check_libva_deps "$AI_LIBAVCODEC_LIB" && check_libva_deps \
|
|
"$(get_ai_lib_path libavutil.so)" || return 1
|
|
LIBVA_LIB=$(LD_TRACE_LOADED_OBJECTS=1 "$LOADER" "$AI_LIBAVCODEC_LIB" | grep libva.so | grep -v 'not found' | awk '{print $3}')
|
|
[ -n "$LIBVA_LIB" ] || return 1
|
|
libva_libs=$LIBVA_LIB
|
|
# add also libva-drm, libva-x11 if present
|
|
for n in libva-drm libva-x11; do
|
|
NAME=$(echo "$LIBVA_LIB" | sed s/libva/$n/)
|
|
if [ -f "$NAME" ]; then
|
|
libva_libs=$libva_libs:$NAME
|
|
fi
|
|
done
|
|
# preload also VDPAU
|
|
libvdpau_lib=$(LD_TRACE_LOADED_OBJECTS=1 "$LOADER" "$AI_LIBAVCODEC_LIB"\
|
|
| grep libvdpau.so | grep -v 'not found' | awk '{print $3}')
|
|
if [ -n "$libvdpau_lib" ]; then
|
|
libva_libs=$libva_libs:$libvdpau_lib
|
|
fi
|
|
|
|
export LD_PRELOAD="$libva_libs${LD_PRELOAD:+:$LD_PRELOAD}"
|
|
}
|
|
|
|
## Tries to set LD_PRELOAD to system libva (see set_libva_ld_preload()). If failed, sets path to bundled libva drivers.
|
|
setup_vaapi() {
|
|
ULTRAGRID_BUNDLED_LIBVA=${ULTRAGRID_BUNDLED_LIBVA:-$use_bundled_libva_default}
|
|
if [ "$ULTRAGRID_BUNDLED_LIBVA" -ne 1 ]; then
|
|
if ! set_libva_ld_preload; then
|
|
echo "${bold}${red}Could not set system libva, using bundled libraries instead!${reset}" >&2
|
|
ULTRAGRID_BUNDLED_LIBVA=1
|
|
fi
|
|
fi
|
|
if [ "$ULTRAGRID_BUNDLED_LIBVA" -eq 1 ]; then
|
|
if [ -d "$DIR/usr/lib/va" ] && [ -z "${LIBVA_DRIVERS_PATH:-}" ]; then
|
|
export LIBVA_DRIVERS_PATH="$AI_LIB_PATH/va"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
setup_wayland() {
|
|
# use bundled Wayland libs only when not running on Wayland, otherwise system ones
|
|
if [ -z "${WAYLAND_DISPLAY-}" ]; then
|
|
n_ld_library_path="$n_ld_library_path:$AI_LIB_PATH/wayland"
|
|
fi
|
|
}
|
|
|
|
add_whitelist() {
|
|
if expr "$1" : /tmp >/dev/null; then
|
|
printf -- "$separator--read-write=\"%s\" --mkdir=\"%s\"" "$1" "$1"
|
|
else
|
|
printf -- "$separator--whitelist=\"%s\"" "$1"
|
|
fi
|
|
separator=' '
|
|
}
|
|
|
|
# realpath + expand tilde expansion (like ~/video.mp4; only single ~ now)
|
|
expand_path() {
|
|
# shellcheck disable=SC2088 # intentional
|
|
if ! expr "$1" : '~/' >/dev/null; then
|
|
realpath "$1"
|
|
else
|
|
realpath "$HOME${1#\~}"
|
|
fi
|
|
}
|
|
|
|
whitelist_file_path() {
|
|
abs_path=$(expand_path "$1")
|
|
add_whitelist "$abs_path"
|
|
if expr "$n" : 'dump:' >/dev/null || expr "$n" : '--record' >/dev/null
|
|
then
|
|
printf -- "$separator--mkdir=\"%s\"" "$abs_path" # may not exist
|
|
fi
|
|
if expr "$n" : '.*file[:=]' >/dev/null || expr "$n" : '.*filename' \
|
|
>/dev/null; then
|
|
parent_dir=$(dirname "$abs_path")
|
|
if [ "$parent_dir" = /dev ] || [ "$parent_dir" = /tmp ]; then
|
|
return
|
|
fi
|
|
# whitelist the parent folder to allow file creation
|
|
printf -- "$separator--whitelist=\"%s\"" "$parent_dir"
|
|
fi
|
|
}
|
|
|
|
## Parse params to get custom rules like whitelists that may be needed to add
|
|
## @todo spaces inside paths doesn't work
|
|
get_custom_firejail_rules() {
|
|
separator=''
|
|
playback_path=$(echo "$@" | sed -n 's/.*--playback \([^ :]*\).*/\1/p')
|
|
if [ -n "$playback_path" ]; then
|
|
add_whitelist "$(expand_path "$playback_path")"
|
|
fi
|
|
# print every argument of "filename=", "file[:=]", "dump:" or "--record=" pattern
|
|
for n in "$@"; do
|
|
file_path=$(echo "$n" | sed -n \
|
|
-e 's/.*filename=\([^:]*\).*/\1/p' \
|
|
-e 's/.*\(n\|name\)=\([^:]*\).*/\2/p' \
|
|
-e 's/.*file[:=]\([^:]*\).*/\1/p' \
|
|
-e 's/dump:\([^:]*\).*/\1/p' \
|
|
-e 's/^--record=\([^:]*\).*/\1/p')
|
|
if [ -n "$file_path" ]; then
|
|
whitelist_file_path "$file_path"
|
|
fi
|
|
done
|
|
# -d dump or -d dummy:dump_to_file
|
|
if expr "$*" : '.*dump' >/dev/null || expr "$*" : '.*record' >/dev/null; then
|
|
add_whitelist "$(pwd)"
|
|
fi
|
|
if ! expr "$DIR" : /tmp >/dev/null; then # add AppRun path if not in tmp (== extracted AppImage)
|
|
add_whitelist "$(expand_path "$DIR")"
|
|
fi
|
|
if [ -z "$separator" ]; then # no whitelist was set
|
|
printf -- "--private"
|
|
fi
|
|
# screen pw - requires root (could be disabled by default.profile) and dbus
|
|
if expr "$*" : '.*screen' >/dev/null; then
|
|
printf " --ignore=noroot"
|
|
elif firejail --version | grep -iq "d-\{0,1\}bus.*enabled"; then
|
|
printf " --dbus-system=filter \
|
|
--dbus-system.talk=org.freedesktop.Avahi"
|
|
fi
|
|
|
|
if ! expr "$*" : '.*/mnt' >/dev/null &&
|
|
! expr "$*" : '.*/media' >/dev/null &&
|
|
! expr "$*" : '.*/run/mount' >/dev/null &&
|
|
! expr "$*" : '.*/run/media' >/dev/null
|
|
then
|
|
printf " --disable-mnt"
|
|
fi
|
|
}
|
|
|
|
# shellcheck source=/dev/null
|
|
. "$DIR/scripts/preload.sh"
|
|
setup_vaapi
|
|
setup_wayland
|
|
# ultragrid_aplay_jack.so is not used because it loads JACK with dlopen,
|
|
# while portaudio is linked directly to JACK library
|
|
set_ld_preload ultragrid_aplay_portaudio.so libjack
|
|
set_ld_preload ultragrid_vidcap_pipewire.so libpipewire
|
|
|
|
setup_firejail() {
|
|
tool=$1
|
|
if [ "${ULTRAGRID_USE_FIREJAIL:-0}" = 0 ] ||
|
|
[ "$ULTRAGRID_USE_FIREJAIL" = no ]; then
|
|
return
|
|
fi
|
|
command -v firejail >/dev/null || { echo "Firejail not present in system!"; exit 1; }
|
|
fj_opt_file=$(mktemp)
|
|
if expr "$ULTRAGRID_USE_FIREJAIL" : '.*\.profile' >/dev/null; then
|
|
FIREJAIL_OPTS="${FIREJAIL_OPTS+$FIREJAIL_OPTS }--profile=$ULTRAGRID_USE_FIREJAIL"
|
|
else
|
|
FJ_TMPDIR=${TMPDIR-/tmp/ultragrid-$(id -u)}
|
|
FIREJAIL_OPTS="${FIREJAIL_OPTS+$FIREJAIL_OPTS }--caps.drop=all\
|
|
--ipc-namespace --nonewprivs --protocol=unix,inet,inet6,netlink --seccomp\
|
|
--private-bin=bash --private-opt=none --mkdir=$FJ_TMPDIR\
|
|
--read-write=$FJ_TMPDIR --writable-var"
|
|
FIREJAIL_OPTS="$FIREJAIL_OPTS $(get_custom_firejail_rules "$@") --private-etc=alsa,group,hostname,ld.so.conf,ld.so.cache,ld.so.conf.d,nsswitch.conf,passwd,resolv.conf --ignore=novideo"
|
|
if ! expr "$FIREJAIL_OPTS" : '.*--read-write=/tmp ' > /dev/null; then
|
|
FIREJAIL_OPTS="--read-only=/tmp $FIREJAIL_OPTS"
|
|
fi
|
|
fi
|
|
if firejail --help | grep -q -- --keep-var-tmp; then
|
|
FIREJAIL_OPTS="$FIREJAIL_OPTS --keep-var-tmp"
|
|
fi
|
|
if [ "$tool" = hd-rum-av ]; then
|
|
FIREJAIL_OPTS="$FIREJAIL_OPTS --private-bin=basename,dirname,\
|
|
expr,kill,ps,sed,seq,sh,tput,tr,tty,uname"
|
|
fi
|
|
echo "firejail --env=LD_PRELOAD=${LD_PRELOAD} --env=LD_LIBRARY_PATH=${n_ld_library_path}${FJ_TMPDIR+ --env=TMPDIR=${FJ_TMPDIR}} $FIREJAIL_OPTS " > "$fj_opt_file"
|
|
if [ "${FIREJAIL_DEBUG:-}" ]; then
|
|
opts=$(cat "$fj_opt_file")
|
|
printf "Firejail arguments:\n\t%s\n\n" "$opts"
|
|
fi
|
|
}
|
|
|
|
# run only if Firejail is used to workaround not restored TIO by UG if FJ kills
|
|
# the UG process prematurelly; otherwise not needed (exec is used)
|
|
fj_sigaction() {
|
|
trap '' TERM # inhibit following signal to ourselves
|
|
if ps -o cmd >/dev/null 2>&1; then
|
|
pgid=$(ps -o pgid= -p $$ | tr -d ' ')
|
|
else # MSW dowsn't have "ps -o"
|
|
pgid=$$
|
|
fi
|
|
if [ $$ -eq "$pgid" ]; then
|
|
kill -- -$$
|
|
else
|
|
echo "pgid $pgid not pid of the script ($$), not sending kill" \
|
|
>&2
|
|
fi
|
|
trap - INT TERM
|
|
wait
|
|
stty "$stty_orig"
|
|
echo "Restored TIO" >&2
|
|
}
|
|
|
|
run() {
|
|
tool=$1
|
|
shift
|
|
setup_firejail "$tool"
|
|
export LD_LIBRARY_PATH="$n_ld_library_path"
|
|
if [ -n "${fj_opt_file-}" ] && [ "$tool" != uv-qt ]; then
|
|
stty_orig=$(stty -g)
|
|
trap fj_sigaction INT TERM
|
|
# shellcheck disable=SC2046 # intentional
|
|
eval $(cat "$fj_opt_file"; rm "$fj_opt_file") "$DIR/usr/bin/$tool" '"$@"'
|
|
else
|
|
exec "$DIR/usr/bin/$tool" "$@"
|
|
fi
|
|
}
|
|
|
|
if [ $# -eq 0 ] || [ "${1-}" = "--gui" ]; then
|
|
handle_updates "$@"
|
|
if [ $# -eq 0 ]; then usage; else shift; fi
|
|
if [ -x "$DIR/usr/bin/uv-qt" ]; then
|
|
run uv-qt --with-uv "$DIR/usr/bin/uv" "$@"
|
|
else
|
|
echo "GUI was not compiled in!" >&2
|
|
exit 1
|
|
fi
|
|
elif [ "$1" = "-o" ] || [ "$1" = "--tool" ]; then
|
|
handle_updates "$@"
|
|
TOOL=$2
|
|
shift 2
|
|
if [ "$TOOL" = help ]; then
|
|
printf "available tools: ${bold}%s${reset}\n" "$(get_tools)"
|
|
exit 0
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
run "$TOOL" "$@"
|
|
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
|
usage
|
|
exit 0
|
|
elif [ "$1" = "--fullhelp" ]; then
|
|
usage
|
|
usage_aux
|
|
exit 0
|
|
elif { [ $# -eq 1 ] || [ $# -eq 2 ]; } && { [ "$1" = "-m" ] || [ "$1" = "--man" ]; }; then
|
|
PAGE=${2:-uv}
|
|
man -l "$DIR/usr/share/man/man1/$PAGE.1"
|
|
elif [ "$1" = "-u" ] || [ "$1" = "--update" ]; then
|
|
shift
|
|
if [ -d "$DIR/appimageupdatetool-lib" ]; then
|
|
export LD_LIBRARY_PATH="$DIR/appimageupdatetool-lib"
|
|
fi
|
|
touch "$APPIMAGE" # update AppImage mtime to avoid update notices if there are no updates avalable but were checked for
|
|
"$DIR/appimageupdatetool" ${1+"$@" }"$APPIMAGE"
|
|
else
|
|
handle_updates "$@"
|
|
run uv "$@"
|
|
fi
|