Run Sunshine as a service

Goodbye Flatpak Sunshine. Hello AppImage.
Until a deb package is available for Debian Bookworm, we can just run sunshine as an AppImage.
It is faster to startup and is a little less bloated.

The goal here is to run Sunshine as a service managed by supervisord so that it is kept running and is restarted automatically if stopped.
This commit is contained in:
Josh.5
2023-08-29 17:21:31 +12:00
committed by Josh Sunnex
parent 39d4ce0a0c
commit 97613b227f
10 changed files with 195 additions and 139 deletions

View File

@@ -483,6 +483,17 @@ RUN \
&& \
echo
# Install Sunshine
ARG SUNSHINE_VERSION=0.20.0
RUN \
echo "**** Install Sunshine ****" \
&& wget -O /usr/bin/sunshine \
https://github.com/LizardByte/Sunshine/releases/download/v${SUNSHINE_VERSION}/sunshine.AppImage \
&& chmod +x /usr/bin/sunshine \
&& setcap cap_sys_admin+p /usr/bin/sunshine \
&& \
echo
# Install Steam
RUN \
echo "**** Update apt database ****" \
@@ -506,7 +517,9 @@ RUN \
ARG DUMB_INIT_VERSION=1.2.5
RUN \
echo "**** Install dumb-init ****" \
&& wget -O /usr/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64 \
&& wget --no-check-certificate --no-cookies --quiet \
-O /usr/bin/dumb-init \
https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64 \
&& chmod +x /usr/bin/dumb-init \
&& \
echo
@@ -535,7 +548,7 @@ ENV \
NEKO_PASSWORD_ADMIN=admin \
ENABLE_STEAM="true" \
STEAM_ARGS="-silent" \
ENABLE_SUNSHINE="false" \
ENABLE_SUNSHINE="true" \
ENABLE_EVDEV_INPUTS="false"
# Configure required ports

View File

@@ -4,11 +4,22 @@
},
"apps": [
{
"name": "Steam BigPicture",
"output": "steam.txt",
"cmd": "",
"name": "Desktop",
"image-path": "desktop.png",
"exclude-global-prep-cmd": "true"
},
{
"name": "Steam Big Picture",
"image-path": "steam.png",
"exclude-global-prep-cmd": "false",
"detached": [
"\/usr\/games\/steam steam:\/\/open\/bigpicture"
],
"prep-cmd": [
{
"do": "",
"undo": "\/usr\/bin\/xfce4-close-all-windows"
}
]
}
]

View File

@@ -28,6 +28,10 @@
#
min_log_level = info
# A list of commands to be run before/after all applications.
# If any of the prep-commands fail, starting the application is aborted.
global_prep_cmd = [{"do":"/usr/bin/xfce4-minimise-all-windows","undo":"/usr/bin/sunshine-stop"}]
# The origin of the remote endpoint address that is not denied for HTTP method /pin
# Could be any of the following values:
# pc|lan|wan
@@ -95,7 +99,7 @@ min_log_level = info
# ping_timeout = 10000
# The file where configuration for the different applications that Sunshine can run during a stream
file_apps = /home/default/sunshine/apps.json
file_apps = apps.json
# Percentage of error correcting packets per data packet in each video frame
# Higher values can correct for more network packet loss, but at the cost of increasing bandwidth usage
@@ -111,7 +115,7 @@ file_apps = /home/default/sunshine/apps.json
#
# Unlike simply broadcasting to multiple Client, this will generate distinct video streams.
# Note, CPU usage increases for each distinct video stream generated
# channels = 1
channels = 2
# The back/select button on the controller
# On the Shield, the home and powerbutton are not passed to Moonlight

View File

@@ -28,7 +28,7 @@ wait_for_x() {
wait_for_udev() {
MAX=10
CT=0
while [ ! -f /tmp/.udev-started ]; do
while [ ! -f /tmp/.started-udev ]; do
sleep 1
CT=$(( CT + 1 ))
if [ "$CT" -ge "$MAX" ]; then
@@ -53,6 +53,20 @@ wait_for_docker() {
echo "DOCKERD RUNNING!"
}
# Wait for desktop to start
wait_for_desktop() {
MAX=30
CT=0
while [ ! -f /tmp/.started-desktop ]; do
sleep 1
CT=$(( CT + 1 ))
if [ "$CT" -ge "$MAX" ]; then
echo "FATAL: $0: Gave up waiting for Desktop to start"
exit 11
fi
done
}
# Fech NVIDIA GPU device (if one exists)
get_nvidia_gpu_id() {
if [ "${NVIDIA_VISIBLE_DEVICES:-}" == "all" ]; then
@@ -67,3 +81,24 @@ get_nvidia_gpu_id() {
fi
echo ${gpu_select}
}
export_desktop_dbus_session() {
if [ ! -f /tmp/.dbus-desktop-session.env ]; then
echo "$(dbus-launch)" > /tmp/.dbus-desktop-session.env
fi
export $(cat /tmp/.dbus-desktop-session.env)
}
# Wait for desktop dbus session to start
wait_for_desktop_dbus_session() {
MAX=10
CT=0
while [ ! -f /tmp/.dbus-desktop-session.env ]; do
sleep 1
CT=$(( CT + 1 ))
if [ "$CT" -ge "$MAX" ]; then
echo "FATAL: $0: Gave up waiting for Desktop dbus-launch session to be created"
exit 11
fi
done
}

View File

@@ -1,91 +0,0 @@
#!/usr/bin/env bash
echo "**** Installing/upgrading Sunshine via flatpak ****"
# Install Sunshine
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak --user install --assumeyes --or-update flathub dev.lizardbyte.app.Sunshine
# Configure any required overrides
flatpak --user override --talk-name=org.freedesktop.Flatpak dev.lizardbyte.app.Sunshine
# Configure Sunshine as the default browser
echo "Configure Sunshine..."
sunshine_autostart_desktop="$(cat <<EOF
[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=Sunshine
Comment=Launch sunshine on login
Exec=/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=dev.lizardbyte.app.Sunshine.sh dev.lizardbyte.app.Sunshine
Icon=sunshine
OnlyShowIn=XFCE;
RunHook=0
StartupNotify=false
Terminal=false
Hidden=false
EOF
)"
mkdir -p "${USER_HOME:?}/.config/autostart"
if [[ ! -f "${USER_HOME:?}/.config/autostart/Sunshine.desktop" ]]; then
echo "${sunshine_autostart_desktop:?}" > "${USER_HOME:?}/.config/autostart/Sunshine.desktop"
fi
# Generate default launchers template:
sunshine_apps_data="$(cat <<EOF
{
"env": {
"PATH": "\$(PATH):\$(HOME)\/.local\/bin"
},
"apps": [
{
"name": "Desktop",
"image-path": "desktop.png"
},
{
"name": "Low Res Desktop",
"image-path": "desktop.png",
"prep-cmd": [
{
"do": "xrandr --output HDMI-1 --mode 1920x1080",
"undo": "xrandr --output HDMI-1 --mode 1920x1200"
}
]
},
{
"name": "Steam Big Picture",
"image-path": "steam.png",
"exclude-global-prep-cmd": "false",
"detached": [
"flatpak-spawn --host \/usr\/games\/steam steam:\/\/open\/bigpicture"
]
}
]
}
EOF
)"
# Generate default sunshine configuration template:
# REF: https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/advanced_usage.html
sunshine_config_data="$(cat <<EOF
# global_prep_cmd
# A list of commands to be run before/after all applications.
# If any of the prep-commands fail, starting the application is aborted.
global_prep_cmd = [{"do":"flatpak-spawn --host /usr/bin/xfce4-minimise-all-windows","undo":"flatpak-spawn --host /usr/bin/sunshine-stop"}]
# channels
# This will generate distinct video streams, unlike simply broadcasting to multiple Clients.
# When multicasting, it could be useful to have different configurations for each connected Client.
# For instance:
# - Clients connected through WAN and LAN have different bitrate constraints.
# - Decoders may require different settings for color.
channels = 2
EOF
)"
mkdir -p "${USER_HOME:?}/.config/sunshine"
if [[ ! -f "${USER_HOME:?}/.config/sunshine/apps.json" ]]; then
echo "${sunshine_apps_data:?}" > "${USER_HOME:?}/.config/sunshine/apps.json"
fi
if [[ ! -f "${USER_HOME:?}/.config/sunshine/sunshine.conf " ]]; then
echo "${sunshine_config_data:?}" > "${USER_HOME:?}/.config/sunshine/sunshine.conf"
fi
echo "DONE"

View File

@@ -19,7 +19,12 @@ trap _term SIGTERM SIGINT
# CONFIGURE:
export $(dbus-launch)
# Remove lockfile
rm -f /tmp/.started-desktop
# Start a session bus instance of dbus-daemon
# Note: This script should be the only one that waits for X after exporting this dbus session
rm -fv /tmp/.dbus-desktop-session.env
export_desktop_dbus_session
# EXECUTE PROCESS:
@@ -30,7 +35,6 @@ if [[ ! -f /tmp/.desktop-apps-updated.lock ]]; then
xterm -geometry 200x50+0+0 -ls -e /bin/bash -c "
source /usr/bin/install_firefox.sh;
source /usr/bin/install_protonup.sh;
source /usr/bin/install_sunshine.sh;
sleep 1;
"
touch /tmp/.desktop-apps-updated.lock
@@ -40,6 +44,7 @@ fi
echo "**** Starting Xfce4 ****"
/usr/bin/startxfce4 &
desktop_pid=$!
touch /tmp/.started-desktop
# WAIT FOR CHILD PROCESS:
wait "$desktop_pid"

View File

@@ -13,42 +13,58 @@ source /usr/bin/common-functions.sh
# CATCH TERM SIGNAL:
_term() {
kill -TERM "$sunshine_pid" 2>/dev/null
kill -INT "$sunshine_pid" 2>/dev/null
sleep 0.5
counter=0
while kill -0 "$sunshine_pid"; do
kill -TERM "$sunshine_pid" 2>/dev/null
counter=$((counter + 1))
[ "$counter" -gt 2 ] && break
sleep 2
done
counter=0
while kill -0 "$sunshine_pid"; do
kill -KILL "$sunshine_pid" 2>/dev/null
counter=$((counter + 1))
[ "$counter" -gt 2 ] && break
sleep 1
done
}
trap _term SIGTERM SIGINT
# CONFIGURE:
# Install default configurations
mkdir -p /home/${USER}/sunshine
if [[ ! -f /home/${USER}/sunshine/sunshine.conf ]]; then
cp -vf /templates/sunshine/* /home/${USER}/sunshine/
# TODO: Set the default encoder '# encoder = nvenc'
# nvidia_gpu_id=$(get_nvidia_gpu_id)
# if [[ "X${nvidia_gpu_id:-}" != "X" ]]; then
# if [[ "all video" == *"${NVIDIA_DRIVER_CAPABILITIES}"* ]]; then
# # Check if we have a nvidia GPU available
# sed -i 's|^# encoder.*=.*$|encoder = nvenc|' /home/${USER}/sunshine/sunshine.conf
# fi
# else
# # TODO: Enable the vaapi device if not using nvenc
# # vainfo --display drm --device /dev/dri/renderD128 2> /dev/null | grep -E "((VAProfileH264High|VAProfileHEVCMain|VAProfileHEVCMain10).*VAEntrypointEncSlice)"
# # Loop over any render devices
# echo
# fi
mkdir -p "${USER_HOME:?}/.config/sunshine"
if [ ! -f "${USER_HOME:?}/.config/sunshine/sunshine.conf" ]; then
cp -vf /templates/sunshine/sunshine.conf "${USER_HOME:?}/.config/sunshine/sunshine.conf"
fi
if [ ! -f "${USER_HOME:?}/.config/sunshine/apps.json" ]; then
cp -vf /templates/sunshine/apps.json "${USER_HOME:?}/.config/sunshine/apps.json"
fi
# Reset the default username/password
if ([ "X${SUNSHINE_USER:-}" != "X" ] && [ "X${SUNSHINE_PASS:-}" != "X" ]); then
sunshine /home/${USER}/sunshine/sunshine.conf --creds ${SUNSHINE_USER:-} ${SUNSHINE_PASS:-}
sunshine "${USER_HOME:?}/.config/sunshine/sunshine.conf" --creds "${SUNSHINE_USER:?}" "${SUNSHINE_PASS:?}"
fi
# Remove any auto-start scripts from user's .local dir
if [ -f "${USER_HOME:?}/.config/autostart/Sunshine.desktop" ]; then
rm -fv "${USER_HOME:?}/.config/autostart/Sunshine.desktop"
fi
# EXECUTE PROCESS:
# Wait for the X server to start
wait_for_x
# Start a session bus instance of dbus-daemon
wait_for_desktop_dbus_session
export_desktop_dbus_session
# Wait for the desktop to start
wait_for_desktop
# Start the sunshine server
sunshine /home/${USER}/sunshine/sunshine.conf &
/usr/bin/dumb-init /usr/bin/sunshine "${USER_HOME:?}/.config/sunshine/sunshine.conf" &
sunshine_pid=$!

View File

@@ -17,9 +17,12 @@ _term() {
trap _term SIGTERM SIGINT
# EXECUTE PROCESS:
# CONFIGURE:
# Remove lockfile
rm -f /tmp/.udev-started
rm -f /tmp/.started-udev
# EXECUTE PROCESS:
# Start udev
if command -v udevd &>/dev/null; then
unshare --net udevd --daemon &>/dev/null
@@ -31,7 +34,7 @@ udevadm monitor &
monitor_pid=$!
# Touch lockfile
sleep 1
touch /tmp/.udev-started
touch /tmp/.started-udev
# Wait for 10 seconds, then request device events from the kernel
sleep 10
udevadm trigger

View File

@@ -10,23 +10,40 @@
###
set -e
# CATCH TERM SIGNAL:
exec >> >(tee -a /tmp/sunshine-exec-run.log) 2>&1
echo
echo "-------------------------------"
echo
date
echo
echo "-------------------------------"
echo
echo "**** Execute sunshine-run ****"
echo
# Trap SIGINT, SIGQUIT, SIGHUP, SIGTERM to forward signals to the child process
_term() {
pkill -P $$
if [ -n "${proc_pid}" ]; then
echo " - Forwarding signal $1 to process ${proc_pid}"
kill -s "${1}" "${proc_pid}"
fi
}
for sig in INT QUIT HUP TERM; do
trap "
_term
trap - $sig EXIT
kill -s $sig "'"$$"' "$sig"
done
trap _term EXIT
trap '_term INT' SIGINT
trap '_term QUIT' SIGQUIT
trap '_term HUP' SIGHUP
trap '_term TERM' SIGTERM
# RUN CHILD PROCESS
# Run child process
/usr/bin/dumb-init "${@}" &
proc_pid=$!
echo " - Recording sunshine-run PID '${proc_pid}' in /tmp/sunshine-exec-run.pid"
echo "${proc_pid}" > /tmp/sunshine-exec-run.pid
# WAIT FOR CHILD PROCESS:
# Wait for child process to exit:
echo " - Waiting for PID '${proc_pid}' to exit"
wait "$proc_pid"
# Clean up PID file
rm -f /tmp/sunshine-exec-run.pid
echo "DONE"

View File

@@ -10,5 +10,48 @@
###
set -e
# Terminate any running sunshine-run processes:
kill $(ps aux | grep -v grep | grep sunshine-run | awk '{print $2}')
exec >> >(tee -a /tmp/sunshine-exec-stop.log) 2>&1
echo
echo "-------------------------------"
echo
date
echo
echo "-------------------------------"
echo
echo "**** Execute sunshine-stop ****"
echo
process_pid="$(cat /tmp/sunshine-exec-run.pid)"
echo " - Found initial sunshine-run PID '${process_pid}'"
echo " - Sending SIGINT to PID '${process_pid}'"
kill -INT "${process_pid}"
echo " - Checking for other sunshine-run processes '${process_pid}'"
for process_pid in $(ps aux | grep -v grep | grep sunshine-run | awk '{print $2}'); do
echo " - Sending SIGINT to PID '${process_pid}'"
kill -INT "${process_pid}" 2>/dev/null
sleep 0.5
counter=0
while kill -0 "$process_pid" 2>/dev/null; do
echo " - Sending SIGTERM to PID '${process_pid}'"
kill -TERM "$process_pid" 2>/dev/null
counter=$((counter + 1))
[ "$counter" -gt 2 ] && break
sleep 2
done
counter=0
while kill -0 "$process_pid" 2>/dev/null; do
echo " - Sending SIGKILL to PID '${process_pid}'"
kill -KILL "$process_pid" 2>/dev/null
counter=$((counter + 1))
[ "$counter" -gt 2 ] && break
sleep 1
done
done
# Clean up PID file
rm -f /tmp/sunshine-exec-run.pid
echo "DONE"