diff --git a/ansible/files/game_station/bin/system/gamescope-session b/ansible/files/game_station/bin/system/gamescope-session new file mode 100644 index 0000000..3bd0cf3 --- /dev/null +++ b/ansible/files/game_station/bin/system/gamescope-session @@ -0,0 +1,149 @@ +#!/bin/bash +set -euo pipefail +LOG_FILE="$HOME/.local/logs/session.log" + +log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOG_FILE"; } + +CONF_FILE="$HOME/.config/gamescope/gamescope.conf" +[ -f "$CONF_FILE" ] && source "$CONF_FILE" + +FRAMERATE=${FRAMERATE:-60} +RESOLUTION=${RESOLUTION:-1920x1080} +WIDTH="${RESOLUTION%x*}" +HEIGHT="${RESOLUTION#*x}" + +export __GL_GSYNC_ALLOWED=1 +export __GL_VRR_ALLOWED=1 +export __GL_SHADER_DISK_CACHE=1 +export __GL_SYNC_TO_VBLANK=0 +export LIBVA_DRIVER_NAME=nvidia +export __GLX_VENDOR_LIBRARY_NAME=nvidia +export NVD_BACKEND=direct +# export __GL_THREADED_OPTIMIZATIONS=1 + +GAMESCOPE_BIN="$(command -v gamescope || true)" +if [ -z "$GAMESCOPE_BIN" ]; then + log "ERROR: gamescope not found in PATH" + exit 127 +fi + +HDR_FLAG="" +if [[ "$HDR_ENABLED" == "1" || "$HDR_ENABLED" == "true" || "$HDR_ENABLED" == "yes" ]]; then + HDR_FLAG="--hdr-enabled" + log "HDR flag added: --hdr-enabled" +else + log "HDR disabled, no HDR flag added" +fi + +# Create run directory file for startup and stats sockets +# shellcheck disable=SC2030 # (broken warning) +tmpdir="$([[ -n ${XDG_RUNTIME_DIR+x} ]] && mktemp -p "$XDG_RUNTIME_DIR" -d -t gamescope.XXXXXXX)" +socket="${tmpdir:+$tmpdir/startup.socket}" +stats="${tmpdir:+$tmpdir/stats.pipe}" +# Fail early if we don't have a proper runtime directory setup +# shellcheck disable=SC2031 # (broken warning) +if [[ -z $tmpdir || -z ${XDG_RUNTIME_DIR+x} ]]; then + echo >&2 "!! Failed to find run directory in which to create stats session sockets (is \$XDG_RUNTIME_DIR set?)" + exit 0 +fi + +# Chromium (and therefore Steam) ignore XCursor and use on the GTK config +kwriteconfig6 --file gtk-3.0/settings.ini --group Settings --key gtk-cursor-theme-name steam + +# Workaround for Steam login issue while Steam client change propagates out of Beta +touch ~/.steam/root/config/SteamAppData.vdf || true + +# Increase open file limit for gamescope session +ulimit -n 524288 + +# 1048576 = 1M - passing it like that omits the 'M' suffix - xargs removes whitespace +free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs) +minimum_free_disk_space_needed_megs=500 + +if [[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]]; then + echo >&2 "gamescope-session: not enough disk space to proceed, trying to find game to delete" + + find ~/.local/share/Steam/steamapps/common/ -mindepth 1 -maxdepth 1 -type d -printf "%T@ %p\0" | sort -n -z | while IFS= read -r -d $'\0' line; do + timestamp=${line%% *} + game_folder=${line#* } + + [[ -d $game_folder ]] || continue + + acf=$(grep -F -- "$(basename -- "$game_folder")" ~/.local/share/Steam/steamapps/*.acf | grep \"installdir\" | cut -d: -f1) + [[ -e "$acf" ]] || continue + + echo >&2 "gamescope-session: deleting $(basename "$game_folder")" + appid=$(basename "$acf" | cut -d_ -f2 | cut -d. -f1) + + # TODO leave a note for Steam to display UI to explain what happened, if this logic stays + # intentionally leave compatdata; could be unclouded save files there + rm -rf --one-file-system -- "$game_folder" "$acf" ~/.local/share/Steam/steamapps/shadercache/"$appid" + + free_disk_space_megs=$(df ~/ --output=avail -B1048576 | sed -n '2 p' | xargs) + [[ "$free_disk_space_megs" -lt "$minimum_free_disk_space_needed_megs" ]] || break + done +fi + +export GAMESCOPE_STATS="$stats" +mkfifo -- "$stats" +mkfifo -- "$socket" + +# Attempt to claim global session if we're the first one running (e.g. /run/1000/gamescope) +linkname="gamescope-stats" +# shellcheck disable=SC2031 # (broken warning) +sessionlink="${XDG_RUNTIME_DIR:+$XDG_RUNTIME_DIR/}${linkname}" # Account for XDG_RUNTIME_DIR="" (notfragileatall) +lockfile="$sessionlink".lck +exec 9>"$lockfile" # Keep as an fd such that the lock lasts as long as the session if it is taken +if flock -n 9 && rm -f "$sessionlink" && ln -sf "$tmpdir" "$sessionlink"; then + # Took the lock. Don't blow up if those commands fail, though. + echo >&2 "Claimed global gamescope stats session at \"$sessionlink\"" +else + echo >&2 "!! Failed to claim global gamescope stats session" +fi + +read_gamescope_env() { + log "Waiting for gamescope to provide DISPLAY and GAMESCOPE_WAYLAND_DISPLAY via socket..." + if read -r -t 3 response_x_display response_wl_display <> "$socket"; then + log "Received gamescope environment:" + log " DISPLAY=$response_x_display" + log " GAMESCOPE_WAYLAND_DISPLAY=$response_wl_display" + export DISPLAY="$response_x_display" + export GAMESCOPE_WAYLAND_DISPLAY="$response_wl_display" + + # Sync environment variables to systemd, then notify we are ready to launch subsequent services + log "Storing gamescope environment for systemd..." + env > $XDG_RUNTIME_DIR/gamescope-environment + systemd-notify --ready + fi +} + +# Spawned in parallel to read values from gamescope +(read_gamescope_env &) + +log "Starting Gamescope session..." + +# Build gamescope argument array for safe quoting and readability +GAMESCOPE_ARGS=( + --generate-drm-mode fixed + --fade-out-duration 200 + --cursor-scale-height 720 + -f + -W "$WIDTH" -H "$HEIGHT" + -w "$WIDTH" -h "$HEIGHT" + --rt --mangoapp -e -R "$socket" -T "$stats" +) + +# Append HDR flag if enabled +if [ -n "${HDR_FLAG:-}" ]; then + GAMESCOPE_ARGS+=("$HDR_FLAG") +fi + +export XDG_CURRENT_DESKTOP=gamescope +export XDG_SESSION_TYPE=wayland +export DESKTOP_SESSION=gamescope + +log "Launching Gamescope: $GAMESCOPE_BIN ${GAMESCOPE_ARGS[*]} -- $*" +sudo setcap 'CAP_SYS_NICE=eip' "$GAMESCOPE_BIN" + +exec "$GAMESCOPE_BIN" "${GAMESCOPE_ARGS[@]}" >>"$LOG_FILE" 2>&1 + diff --git a/ansible/files/game_station/bin/system/launch-gamescope b/ansible/files/game_station/bin/system/launch-gamescope new file mode 100644 index 0000000..a9a02d1 --- /dev/null +++ b/ansible/files/game_station/bin/system/launch-gamescope @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail +LOGFILE="$HOME/.local/logs/gamescope.log" +export PATH="$HOME/.local/bin:$PATH" + +log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOGFILE"; } + +# SDDM sets this to wayland but apps using Gamescope must use x11 +export XDG_SESSION_TYPE=x11 + +# Update the enviroment with DESKTOP_SESSION and all XDG variables +log "Updating systemd user environment with XDG variables" +dbus-update-activation-environment --systemd DESKTOP_SESSION `env | grep ^XDG_ | cut -d = -f 1` + +# This makes it so that xdg-desktop-portal doesn't find any portal implementations and doesn't start them and makes +# them crash/exit because the dbus env has no DISPLAY. In turn this causes dbus calls to the portal which don't rely +# on implementations to hang (such as SDL talking to the real time portal) +# Plasma resets this variable when it starts +systemctl --user set-environment XDG_DESKTOP_PORTAL_DIR="" + +# Remove these as they prevent gamescope-session from starting correctly +systemctl --user unset-environment DISPLAY XAUTHORITY + +# If this shell script is killed then stop gamescope-session +trap 'systemctl --user stop gamescope-session.target' HUP INT TERM + +# Start gamescope-session and wait +log "Starting Gamescope Session" +systemctl --user --wait start gamescope-session.target & +wait +log "Gamescope Session Ended - Performing Final Cleanup" + +# The 'wait' above blocks until gamescope-session.target stops +# We want to wait until *everything* has finished. We know systemd will have +# queued a stop job on graphical-session-pre aleady +# by also queuing up a job we can block until that completes +systemctl --user stop graphical-session-pre.target +log "Gamescope Session Ended - Cleanup Complete" diff --git a/ansible/files/game_station/bin/system/launch_kde.sh b/ansible/files/game_station/bin/system/launch-kde similarity index 90% rename from ansible/files/game_station/bin/system/launch_kde.sh rename to ansible/files/game_station/bin/system/launch-kde index 3191111..f4a2fe2 100644 --- a/ansible/files/game_station/bin/system/launch_kde.sh +++ b/ansible/files/game_station/bin/system/launch-kde @@ -12,11 +12,7 @@ if [ -z "$DESKTOP_BIN" ]; then fi export XDG_CURRENT_DESKTOP=plasma -export XDG_SESSION_TYPE=wayland -export XDG_DATA_DIRS=/usr/local/share/:/usr/share/ -export XDG_CONFIG_DIRS=/usr/local/etc/xdg:/etc/xdg export XDG_CURRENT_DESKTOP=KDE -export XDG_RUNTIME_DIR=/run/user/$(id -u) export QT_QPA_PLATFORM=wayland export GDK_BACKEND=wayland diff --git a/ansible/files/game_station/bin/system/launch_desktop.sh b/ansible/files/game_station/bin/system/launch_desktop.sh deleted file mode 100644 index 875f404..0000000 --- a/ansible/files/game_station/bin/system/launch_desktop.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -set -euo pipefail -LOGFILE="$HOME/.local/logs/desktop.log" - -log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOGFILE"; } - -log "Starting Desktop session..." - -DESKTOP_SESSION_BIN="$(command -v startplasma-wayland || true)" -if [ -z "$DESKTOP_SESSION_BIN" ]; then - log "ERROR: startplasma-wayland not found in PATH" - exit 127 -fi - -log "Launching Desktop: $DESKTOP_SESSION_BIN" -exec "$DESKTOP_SESSION_BIN" >>"$LOGFILE" 2>&1 & - -sleep 5 -systemd-run --user --slice=sunshine.slice --scope sunshine >>"$LOGFILE" 2>&1 || { - log "ERROR: Failed to start Sunshine" - exit 1 -} & - -tail -f /dev/null diff --git a/ansible/files/game_station/bin/system/launch_session.sh b/ansible/files/game_station/bin/system/launch_session.sh deleted file mode 100644 index 1131d12..0000000 --- a/ansible/files/game_station/bin/system/launch_session.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash -set -euo pipefail -LOG_FILE="$HOME/.local/logs/session.log" - -log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOG_FILE"; } - -start_gamescope() { - log "Starting Gamescope session..." - - CONF_FILE="$HOME/.config/gamescope/gamescope.conf" - [ -f "$CONF_FILE" ] && source "$CONF_FILE" - - FRAMERATE=${FRAMERATE:-60} - RESOLUTION=${RESOLUTION:-1920x1080} - WIDTH="${RESOLUTION%x*}" - HEIGHT="${RESOLUTION#*x}" - - export __GL_GSYNC_ALLOWED=1 - export __GL_VRR_ALLOWED=1 - export __GL_SHADER_DISK_CACHE=1 - export __GL_SYNC_TO_VBLANK=0 - export LIBVA_DRIVER_NAME=nvidia - export __GLX_VENDOR_LIBRARY_NAME=nvidia - export NVD_BACKEND=direct - # export __GL_THREADED_OPTIMIZATIONS=1 - - GAMESCOPE_BIN="$(command -v gamescope || true)" - if [ -z "$GAMESCOPE_BIN" ]; then - log "ERROR: gamescope not found in PATH" - exit 127 - fi - - HDR_FLAG="" - if [[ "$HDR_ENABLED" == "1" || "$HDR_ENABLED" == "true" || "$HDR_ENABLED" == "yes" ]]; then - HDR_FLAG="--hdr-enabled" - log "HDR flag added: --hdr-enabled" - else - log "HDR disabled, no HDR flag added" - fi - - # Build gamescope argument array for safe quoting and readability - GAMESCOPE_ARGS=( - --generate-drm-mode fixed - --fade-out-duration 200 - --cursor-scale-height 720 - -f - -W "$WIDTH" -H "$HEIGHT" - -w "$WIDTH" -h "$HEIGHT" - --rt --mangoapp -e - -r "$FRAMERATE" - ) - - # Append HDR flag if enabled - if [ -n "${HDR_FLAG:-}" ]; then - GAMESCOPE_ARGS+=("$HDR_FLAG") - fi - - export XDG_CURRENT_DESKTOP=gamescope - export XDG_SESSION_TYPE=wayland - export DESKTOP_SESSION=gamescope - - log "Launching Gamescope: $GAMESCOPE_BIN ${GAMESCOPE_ARGS[*]} -- $*" - sudo setcap 'CAP_SYS_NICE=eip' "$GAMESCOPE_BIN" - # Execute gamescope inside a DBus session, preserving proper argument quoting - exec dbus-run-session -- "$GAMESCOPE_BIN" "${GAMESCOPE_ARGS[@]}" -- "$@" >>"$LOG_FILE" 2>&1 -} - -start_kwin() { - log "Starting KWin session..." - KWIN_BIN="$(command -v kwin_wayland || true)" - if [ -z "$KWIN_BIN" ]; then - log "ERROR: kwin_wayland not found in PATH" - exit 127 - fi - - export XDG_CURRENT_DESKTOP=plasma - export XDG_SESSION_TYPE=wayland - export DESKTOP_SESSION=plasma-wayland - - # Ensure we don't run nested; width/height/fullscreen are for the windowed backend. - unset DISPLAY WAYLAND_DISPLAY - KWIN_ARGS="--drm --exit-with-session" - log "Launching KWin (DRM): $KWIN_BIN ${KWIN_ARGS} $*" - exec dbus-run-session -- "$KWIN_BIN" ${KWIN_ARGS} "$@" >>"$LOG_FILE" 2>&1 -} - -export XDG_SEAT=seat0 -export XDG_RUNTIME_DIR=/run/user/$(id -u) -export XDG_DATA_DIRS=/usr/local/share/:/usr/share/ -export XDG_CONFIG_DIRS=/usr/local/etc/xdg:/etc/xdg - -if [[ -f "$HOME/.desktop-session-plasma" ]]; then - log "Plasma session selected." - rm "$HOME/.desktop-session-plasma" - start_kwin /usr/local/bin/launch_kde.sh -else - log "Gamescope session selected." - start_gamescope /usr/local/bin/launch_steam.sh -fi - diff --git a/ansible/files/game_station/bin/system/launch_steam.sh b/ansible/files/game_station/bin/system/steam-launcher similarity index 99% rename from ansible/files/game_station/bin/system/launch_steam.sh rename to ansible/files/game_station/bin/system/steam-launcher index 16d83bf..36666b5 100644 --- a/ansible/files/game_station/bin/system/launch_steam.sh +++ b/ansible/files/game_station/bin/system/steam-launcher @@ -145,4 +145,4 @@ if systemctl --user is-active --quiet sunshine.scope; then fi log "Steam session ended." -exit "$status" \ No newline at end of file +exit "$status" diff --git a/ansible/files/game_station/bin/system/steam-short-session-tracker b/ansible/files/game_station/bin/system/steam-short-session-tracker new file mode 100644 index 0000000..410635d --- /dev/null +++ b/ansible/files/game_station/bin/system/steam-short-session-tracker @@ -0,0 +1,77 @@ +#!/bin/bash + +short_session_tracker_file="/tmp/steamos-short-session-tracker" +short_session_start_file="/tmp/steamos-short-session-start" +short_session_duration=60 +short_session_count_before_reset=3 + +do_repair() { + # This should really be part of the steam launcher as `steam --repair` or similar, but we presently only have + # `steam --reset` which is too heavy of a hammer. + + echo >&2 "steam-short-session-tracker: Re-bootstrapping steam from OS copy." + # at this point, might as well make sure we won't relaunch on a sideloaded build + rm -f -- "$HOME/devkit-game/devkit-steam" + mkdir -p ~/.local/share/Steam + # remove some caches and stateful things known to cause Steam to fail to start if corrupt + rm -rf --one-file-system ~/.local/share/Steam/config/widevine + # cleanup the steam config dir, i.e. ~/.steam. We should try to preserve registry.vdf if possible + steam_config_dir="$HOME/.steam" + steam_config_backup_dir="$HOME/dot-steam.bak.$(date +%s)" + registry_vdf="$steam_config_dir/registry.vdf" + registry_backup_vdf="$steam_config_backup_dir/.steam/registry.vdf" + mv "$steam_config_dir" "$steam_config_backup_dir" + mkdir -p "$steam_config_dir" + cp -f "$registry_backup_vdf" "$registry_vdf" || true + # restore clean copy of binaries from RO partition + tar xf /usr/lib/steam/bootstraplinux_ubuntu12_32.tar.xz -C ~/.local/share/Steam + # rearm + rm "$short_session_tracker_file" +} + +handle_started() { + short_session_count=$(< "$short_session_tracker_file" wc -l) + touch $short_session_start_file + + if [[ "$short_session_count" -ge "$short_session_count_before_reset" ]]; then + echo >&2 "steam-short-session-tracker: Steam failed to start $short_session_count_before_reset times within $short_session_duration seconds" + + if [ -f "$HOME/.config/inhibit-short-session-tracker" ]; then + echo >&2 "steam-short-session-tracker: Auto-repair disabled by config. Remove ~/.config/inhibit-short-session-tracker to re-enable" + return + fi + + do_repair + fi +} + +handle_stopped() { + seconds_since_started=$(($(date +%s) - $(date +%s -r "$short_session_start_file"))) + + if [[ "seconds_since_started" -lt "$short_session_duration" ]]; then + echo "frog" >> "$short_session_tracker_file" + else + rm "$short_session_tracker_file" + fi +} + +case "$1" in + --track-started) + echo "steam-short-session-tracker: Tracking Steam start (unimplemented)" + # handle_started + ;; + --track-stopped) + echo "steam-short-session-tracker: Tracking Steam stop (unimplemented)" + # handle_stopped + ;; + --repair-now) + echo "steam-short-session-tracker: Performing immediate repair (unimplemented)" + # do_repair + ;; + *) + echo "Usage: $0 --track-started | --track-stopped | --repair-now" + exit 1 + ;; +esac + +exit 0 \ No newline at end of file diff --git a/ansible/files/game_station/bin/system/steamos b/ansible/files/game_station/bin/system/steamos new file mode 100644 index 0000000..bf769da --- /dev/null +++ b/ansible/files/game_station/bin/system/steamos @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail +LOG_FILE="$HOME/.local/logs/session.log" + +log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOG_FILE"; } + +if [[ -f "$HOME/.desktop-session-plasma" ]]; then + log "Plasma session selected." + rm "$HOME/.desktop-session-plasma" + exec /usr/bin/startplasma-wayland +else + log "Gamescope session selected." + exec /usr/local/bin/launch-gamescope +fi diff --git a/ansible/files/game_station/bin/system/steamos-session b/ansible/files/game_station/bin/system/steamos-session new file mode 100644 index 0000000..64d0fbc --- /dev/null +++ b/ansible/files/game_station/bin/system/steamos-session @@ -0,0 +1,26 @@ +# #!/bin/bash +set -euo pipefail +LOG_FILE="$HOME/.local/logs/session.log" + +log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOG_FILE"; } + +env > $XDG_RUNTIME_DIR/steamos-environment +log "Starting SteamOS Session" +while true; do + systemctl --user --wait start steamos.service + wait_status=$? + log "Graphical session service exited with status $wait_status" + if [ $wait_status -ne 0 ]; then + log "Graphical session service exited with error; ending loop." + break + fi + if [ -f "$HOME/.steamos-session-switch" ]; then + log "Graphical session service exited; switch file present -> restarting..." + rm "$HOME/.steamos-session-switch" + continue + fi + log "Graphical session service exited; switch file absent -> ending loop." + break +done +sleep 5 +log "SteamOS Session Ended" \ No newline at end of file diff --git a/ansible/files/game_station/bin/system/steamos-session-select b/ansible/files/game_station/bin/system/steamos-session-select index 845d297..552ef32 100644 --- a/ansible/files/game_station/bin/system/steamos-session-select +++ b/ansible/files/game_station/bin/system/steamos-session-select @@ -4,6 +4,11 @@ LOGFILE="$HOME/.local/logs/session.log" log() { echo "[$(date --iso-8601=seconds)] $*" | tee -a "$LOGFILE"; } +die() { echo >&2 "!! $*"; exit 1; } + +# File this script will modify +CONF_FILE="/etc/sddm.conf.d/zz-steamos-autologin.conf" + session="${1:-gamescope}" case "$session" in @@ -23,18 +28,12 @@ case "$session" in ;; esac -if systemctl --user is-active --quiet sunshine.scope; then - log "Stopping sunshine.scope" - systemctl --user stop sunshine.scope -fi -if systemctl --user is-active --quiet steam.scope; then - log "Stopping steam.scope" - systemctl --user stop steam.scope -fi -if systemctl --user is-active --quiet plasma.scope; then - log "Stopping plasma.scope" - systemctl --user stop plasma.scope -fi -sleep 5 -log "Restarting graphical session service..." -systemctl --user restart session.service \ No newline at end of file +log "Signaling session switch..." +touch "$HOME/.steamos-session-switch" + +# It is possible to get things remaining from the previous session if SDDM is sigkilled by root systemd +# and our units are still running in the user systemd +systemctl --user stop gamescope-session.target +systemctl --user stop plasma-workspace.target +systemctl --user reset-failed + diff --git a/ansible/files/game_station/bin/system/steamos.sh b/ansible/files/game_station/bin/system/steamos.sh deleted file mode 100644 index d74d51d..0000000 --- a/ansible/files/game_station/bin/system/steamos.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -set -euo pipefail - -systemctl --user start steamos.target \ No newline at end of file diff --git a/ansible/files/game_station/sddm.conf/steamos.conf b/ansible/files/game_station/sddm.conf/steamos.conf new file mode 100644 index 0000000..3823415 --- /dev/null +++ b/ansible/files/game_station/sddm.conf/steamos.conf @@ -0,0 +1,7 @@ +[General] +DisplayServer=wayland + +[Autologin] +Relogin=true +Session=steamos.desktop +User={{ user_name }} \ No newline at end of file diff --git a/ansible/files/game_station/shortcuts/alvr.desktop b/ansible/files/game_station/shortcuts/alvr.desktop new file mode 100644 index 0000000..552922c --- /dev/null +++ b/ansible/files/game_station/shortcuts/alvr.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=ALVR +Comment=Linux Virtual Reality +Exec=/opt/alvr/bin/alvr_dashboard +Icon=alvr.png +Terminal=false +Type=Application +Categories=Game; \ No newline at end of file diff --git a/ansible/files/game_station/shortcuts/icons/alvr.png b/ansible/files/game_station/shortcuts/icons/alvr.png new file mode 100644 index 0000000..aaa67da Binary files /dev/null and b/ansible/files/game_station/shortcuts/icons/alvr.png differ diff --git a/ansible/files/game_station/shortcuts/icons/steamos.png b/ansible/files/game_station/shortcuts/icons/steamos.png new file mode 100644 index 0000000..085d24c Binary files /dev/null and b/ansible/files/game_station/shortcuts/icons/steamos.png differ diff --git a/ansible/files/game_station/shortcuts/steamos.desktop b/ansible/files/game_station/shortcuts/steamos.desktop new file mode 100644 index 0000000..a5155f9 --- /dev/null +++ b/ansible/files/game_station/shortcuts/steamos.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=SteamOS +Comment=Switch to SteamOS +Exec=/usr/local/bin/steamos-session-select +Icon=steamos.png +Terminal=false +Type=Application +Categories=Game; \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/desktop.slice b/ansible/files/game_station/systemd/user/desktop.slice deleted file mode 100644 index 4618983..0000000 --- a/ansible/files/game_station/systemd/user/desktop.slice +++ /dev/null @@ -1,3 +0,0 @@ -[Slice] -CPUWeight=100 -AllowedCPUs={{ general_cores }} diff --git a/ansible/files/game_station/systemd/user/gamescope-mangoapp.service b/ansible/files/game_station/systemd/user/gamescope-mangoapp.service new file mode 100644 index 0000000..01d8667 --- /dev/null +++ b/ansible/files/game_station/systemd/user/gamescope-mangoapp.service @@ -0,0 +1,11 @@ +[Unit] +Description=mangoapp +After=graphical-session.target +PartOf=graphical-session.target + +[Service] +Type=exec +ExecStart=/usr/bin/mangoapp +Restart=always +EnvironmentFile=%t/gamescope-environment +TimeoutStopSec=5 \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/gamescope-session.service b/ansible/files/game_station/systemd/user/gamescope-session.service new file mode 100644 index 0000000..c7ee30a --- /dev/null +++ b/ansible/files/game_station/systemd/user/gamescope-session.service @@ -0,0 +1,17 @@ +[Unit] +Description=Gamescope Session +Before=graphical-session.target +PartOf=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target +RefuseManualStart=yes + +[Service] +TimeoutStartSec=5 +TimeoutStopSec=10 +ExecStart=gamescope-session +Type=notify +NotifyAccess=all +# Make Steam's srt-logger write to the journal with it's own prefixes +Environment=SRT_LOG_TO_JOURNAL=1 +Slice=session.slice \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/gamescope-session.target b/ansible/files/game_station/systemd/user/gamescope-session.target new file mode 100644 index 0000000..197a0e6 --- /dev/null +++ b/ansible/files/game_station/systemd/user/gamescope-session.target @@ -0,0 +1,13 @@ +[Unit] +Description=Gamescope Session +Requires=graphical-session.target +BindsTo=graphical-session.target +After=graphical-session.target + +Requires=gamescope-session.service +BindsTo=gamescope-session.service + +Upholds=steam-launcher.service + +Wants=ibus-gamescope.service +Wants=gamescope-mangoapp.service \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/ibus-gamescope.service b/ansible/files/game_station/systemd/user/ibus-gamescope.service new file mode 100644 index 0000000..bb6384b --- /dev/null +++ b/ansible/files/game_station/systemd/user/ibus-gamescope.service @@ -0,0 +1,12 @@ +[Unit] +Description=Wrapper for Ibus +PartOf=graphical-session.target +After=graphical-session.target + +[Service] +Type=dbus +ExecStart=/usr/bin/ibus-daemon -r --panel=disable --emoji-extension=disable +BusName=org.freedesktop.IBus +EnvironmentFile=%t/gamescope-environment +Slice=session.slice +TimeoutStopSec=5 \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/session.service b/ansible/files/game_station/systemd/user/session.service deleted file mode 100644 index 35cdb6a..0000000 --- a/ansible/files/game_station/systemd/user/session.service +++ /dev/null @@ -1,7 +0,0 @@ -[Unit] -Description=User graphical session service -After=steamos.target - -[Service] -ExecStart=/usr/bin/bash /usr/local/bin/launch_session.sh -Restart=on-failure diff --git a/ansible/files/game_station/systemd/user/session.slice b/ansible/files/game_station/systemd/user/session.slice new file mode 100644 index 0000000..6bb1f9b --- /dev/null +++ b/ansible/files/game_station/systemd/user/session.slice @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Core Session Slice +Documentation=man:systemd.special(7) + +[Slice] +CPUWeight=100 \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/steam-launcher.service b/ansible/files/game_station/systemd/user/steam-launcher.service new file mode 100644 index 0000000..974ce42 --- /dev/null +++ b/ansible/files/game_station/systemd/user/steam-launcher.service @@ -0,0 +1,22 @@ +[Unit] +Description=Steam Launcher +After=gamescope-session.target +PartOf=gamescope-session.target + +[Service] +ExecStart=steam-launcher + +# Track short sessions to trigger a repair after enough failures +ExecStartPre=steam-short-session-tracker --track-started +ExecStopPost=steam-short-session-tracker --track-stopped + +# To close properly we need to kill the child of the steam wrapper, as the wrapper itself does not forward signals +ExecStop=/bin/bash -c 'kill -TERM $(pgrep -P $MAINPID || echo $MAINPID)' +# Disable the term signal sent from systemd, as we we handle it manually above +# But still want systemd to kill the entire cgroup on timeout +KillSignal=SIGCONT +KillMode=mixed +TimeoutStopSec=60 + +Type=exec +EnvironmentFile=%t/gamescope-environment \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/steam.slice b/ansible/files/game_station/systemd/user/steam.slice index 4618983..6bb1f9b 100644 --- a/ansible/files/game_station/systemd/user/steam.slice +++ b/ansible/files/game_station/systemd/user/steam.slice @@ -1,3 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Core Session Slice +Documentation=man:systemd.special(7) + [Slice] -CPUWeight=100 -AllowedCPUs={{ general_cores }} +CPUWeight=100 \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/steamos.service b/ansible/files/game_station/systemd/user/steamos.service index 53514db..e447a94 100644 --- a/ansible/files/game_station/systemd/user/steamos.service +++ b/ansible/files/game_station/systemd/user/steamos.service @@ -1,10 +1,8 @@ [Unit] -Description=SteamOS user graphical session service -After=graphical-session.target systemd-user-sessions.service +Description=User graphical session service +After=graphical-session.target [Service] -ExecStart=/usr/bin/bash /usr/local/bin/steamos.sh +ExecStart=/usr/local/bin/steamos +EnvironmentFile=%t/steamos-environment Restart=on-failure - -[Install] -WantedBy=default.target \ No newline at end of file diff --git a/ansible/files/game_station/systemd/user/steamos.target b/ansible/files/game_station/systemd/user/steamos.target deleted file mode 100644 index 4ded1f3..0000000 --- a/ansible/files/game_station/systemd/user/steamos.target +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=SteamOS Target -Requires=graphical-session.target -BindsTo=graphical-session.target -After=graphical-session.target -PropagatesStopTo=graphical-session.target - -Requires=session.service -BindsTo=session.service - -# Wants=ibus-gamescope.service -# Wants=steam-notif-daemon.service -# Wants=gamescope-xbindkeys.service -# Wants=galileo-mura-setup.service -# Wants=gamescope-mangoapp.service diff --git a/ansible/files/game_station/systemd/user/sunshine.slice b/ansible/files/game_station/systemd/user/sunshine.slice index 571d093..6bb1f9b 100644 --- a/ansible/files/game_station/systemd/user/sunshine.slice +++ b/ansible/files/game_station/systemd/user/sunshine.slice @@ -1,3 +1,15 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=User Core Session Slice +Documentation=man:systemd.special(7) + [Slice] -CPUWeight=100 -AllowedCPUs={{ sunshine_cores }} +CPUWeight=100 \ No newline at end of file diff --git a/ansible/files/game_station/wayland-sessions/steamos.desktop b/ansible/files/game_station/wayland-sessions/steamos.desktop new file mode 100644 index 0000000..d3f72a3 --- /dev/null +++ b/ansible/files/game_station/wayland-sessions/steamos.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=SteamOS +Comment=SteamOS graphical session +Exec=/usr/local/bin/steamos-session +Icon=steamicon.png +Type=Application +DesktopNames=gamescope \ No newline at end of file diff --git a/ansible/host_vars/dev-01.lab.alexpires.me.yml b/ansible/host_vars/dev-01.lab.alexpires.me.yml index 4eec4b9..e26758f 100644 --- a/ansible/host_vars/dev-01.lab.alexpires.me.yml +++ b/ansible/host_vars/dev-01.lab.alexpires.me.yml @@ -66,6 +66,7 @@ ufw_outgoing_traffic: - 9090 # Prometheus ufw_default_forward_policy: "ACCEPT" +fw_loopback_traffic_deny: false ipv4_sysctl_settings: net.ipv4.ip_forward: 1 diff --git a/ansible/host_vars/gpu-01.lab.alexpires.me.yml b/ansible/host_vars/gpu-01.lab.alexpires.me.yml index c771ee0..6049132 100644 --- a/ansible/host_vars/gpu-01.lab.alexpires.me.yml +++ b/ansible/host_vars/gpu-01.lab.alexpires.me.yml @@ -51,8 +51,11 @@ ufw_outgoing_traffic: - 123 # NTP - 443 # HTTPS - 853 # DNS over TLS + - 139 # Samba + - 445 # Samba ufw_default_forward_policy: "ACCEPT" +fw_loopback_traffic_deny: false ipv4_sysctl_settings: net.ipv4.ip_forward: 1 diff --git a/ansible/playbook_game_station.yml b/ansible/playbook_game_station.yml index 39a53b8..addcd6c 100644 --- a/ansible/playbook_game_station.yml +++ b/ansible/playbook_game_station.yml @@ -9,9 +9,6 @@ systemd_user_dir: "/home/{{ user_name }}/.config/systemd/user" systemd_system_dir: "/etc/systemd/system" grub_cfg: "/etc/default/grub" - system_cores: "0,8" - general_cores: "2-7,10-15" - sunshine_cores: "1,9" screen_resolution: "{{ lookup('env', 'GAME_RESOLUTION') | d('1920x1080', true) }}" screen_width: "{{ screen_resolution.split('x')[0] }}" screen_height: "{{ screen_resolution.split('x')[1] }}" @@ -27,8 +24,6 @@ - fbdev=1 - mitigations=off - threadirqs - - nohz_full=1-7,9-15 - - rcu_nocbs=1-7,9-15 - drm.edid_firmware=DP-1:edid/aoc28e850.bin - video=DP-1:e - acpi_enforce_resources=lax @@ -63,140 +58,6 @@ - lizardbyte/stable - atim/heroic-games-launcher - # --- CachyOS Kernel (conditional) --- - - name: Detect supported x86_64 ISA levels - ansible.builtin.shell: - cmd: | - set -euo pipefail; - /lib64/ld-linux-x86-64.so.2 --help | grep -F "(supported, searched)" - register: glibc_loader_help - changed_when: false - failed_when: false - - - name: Determine CPU support for CachyOS kernel - ansible.builtin.set_fact: - has_x86_64_v3: "{{ ('x86-64-v3' in glibc_loader_help.stdout) or ('x86_64_v3' in glibc_loader_help.stdout) }}" - has_x86_64_v2: "{{ ('x86-64-v2' in glibc_loader_help.stdout) or ('x86_64_v2' in glibc_loader_help.stdout) }}" - - - name: Debug CPU ISA detection - ansible.builtin.debug: - msg: "CPU supports x86_64_v3: {{ has_x86_64_v3 }}, x86_64_v2: {{ has_x86_64_v2 }}" - - - name: Enable CachyOS COPR repository (kernel) - become: true - community.general.copr: - name: bieszczaders/kernel-cachyos - state: enabled - when: has_x86_64_v3 | bool or has_x86_64_v2 | bool - - - name: Enable CachyOS COPR repository (addons) - become: true - community.general.copr: - name: bieszczaders/kernel-cachyos-addons - state: enabled - when: has_x86_64_v3 | bool or has_x86_64_v2 | bool - - - name: Install CachyOS kernel for x86_64_v3 CPUs - become: true - ansible.builtin.dnf: - name: - - pesign - - openssl - - kernel-devel - - mokutil - - keyutils - - kernel-cachyos - - kernel-cachyos-devel - state: present - when: has_x86_64_v3 | bool - - - name: Ensures user is on /etc/pesign/users - become: true - ansible.builtin.lineinfile: - path: /etc/pesign/users - line: "{{ ansible_user }}" - state: present - when: has_x86_64_v3 | bool - notify: Run pesign - - - name: Run /usr/libexec/pesign/pesign-authorize - become: true - ansible.builtin.command: /usr/libexec/pesign/pesign-authorize - when: has_x86_64_v3 | bool - changed_when: false - - # - name: Generate certificate for CachyOS kernel signing - # become: true - # ansible.builtin.shell: | - # openssl req -new -x509 -newkey rsa:2048 -keyout "key.pem" \ - # -outform DER -out "cert.der" -nodes -days 36500 \ - # -subj "/CN=CachyOS Secure Boot/" && \ - # openssl pkcs12 -export -out key.p12 -inkey key.pem -in cert.der - # when: has_x86_64_v3 | bool - # changed_when: false - # args: - # executable: /bin/bash - # creates: - # - key.pem - # - cert.der - # - key.p12 - - # - name: Inform user to enroll MOK key - # ansible.builtin.debug: - # msg: | - # Please enroll the MOK key for kernel module signing on next reboot. - # Run: - # sudo certutil -A -i cert.der -n "CachyOS Secure Boot" -d /etc/pki/pesign/ -t "Pu,Pu,Pu" && \ - # sudo pk12util -i key.p12 -d /etc/pki/pesign - # sudo mokutil --import cert.der' if not prompted automatically. - # when: has_x86_64_v3 | bool - - - name: Create /etc/kernel/postinst.d directory - become: true - ansible.builtin.file: - path: /etc/kernel/postinst.d - state: directory - owner: root - group: root - mode: "0755" - - - name: Enable Automatically signing kernel updates - become: true - ansible.builtin.copy: - dest: /etc/kernel/postinst.d/00-signing - owner: root - group: root - mode: "0755" - content: | - #!/bin/sh - set -e - KERNEL_IMAGE="$2" - MOK_KEY_NICKNAME="CachyOS Secure Boot" - if [ "$#" -ne "2" ] ; then - echo "Wrong count of command line arguments. This is not meant to be called directly." >&2 - exit 1 - fi - if [ ! -x "$(command -v pesign)" ] ; then - echo "pesign not executable. Bailing." >&2 - exit 1 - fi - if [ ! -w "$KERNEL_IMAGE" ] ; then - echo "Kernel image $KERNEL_IMAGE is not writable." >&2 - exit 1 - fi - echo "Signing $KERNEL_IMAGE..." - pesign --certificate "$MOK_KEY_NICKNAME" --in "$KERNEL_IMAGE" --sign --out "$KERNEL_IMAGE.signed" - mv "$KERNEL_IMAGE.signed" "$KERNEL_IMAGE" - - - name: Install CachyOS LTS kernel for x86_64_v2-only CPUs - become: true - ansible.builtin.dnf: - name: - - kernel-cachyos-lts - - kernel-cachyos-lts-devel-matched - state: present - when: (not has_x86_64_v3 | bool) and (has_x86_64_v2 | bool) - - name: Ensure core gaming and streaming packages are installed become: true ansible.builtin.dnf: @@ -351,16 +212,11 @@ state: stopped loop: - gdm.service - - sddm.service - - bluetooth.service - ModemManager.service - cups.service - - avahi-daemon.service - chronyd.service - NetworkManager-wait-online.service - - geoclue.service - smartd.service - - upower.service # System actions - name: Install Systemd system files @@ -398,26 +254,26 @@ mode: "0755" loop: - /root/.local/logs - - "{{ systemd_system_dir }}/getty@tty1.service.d" - - name: Create override for getty on tty1 + - name: Copy wayland session files for SDDM become: true ansible.builtin.copy: - dest: "{{ systemd_system_dir }}/getty@tty1.service.d/override.conf" + src: "{{ item }}" + dest: "/usr/share/wayland-sessions/{{ item | basename }}" owner: root group: root mode: "0644" - content: | - [Service] - ExecStart= - ExecStart=-/usr/bin/agetty --autologin {{ user_name }} --noclear %I $TERM - Type=idle + loop: "{{ query('fileglob', 'game_station/wayland-sessions/*') }}" - - name: Enable user lingering for game user + - name: Install SDDM configuration for auto-login become: true - ansible.builtin.command: loginctl enable-linger {{ user_name }} - changed_when: false - failed_when: false + ansible.builtin.template: + src: "{{ item }}" + dest: "/etc/sddm.conf.d/{{ item | basename }}" + owner: root + group: root + mode: "0644" + loop: "{{ query('fileglob', 'game_station/sddm.conf/*') }}" - name: Create log file for Jupiter BIOS update (dummy file) become: true @@ -475,6 +331,8 @@ - "{{ user_config_dir }}/steam-rom-manager/userData" - "/home/{{ user_name }}/.local/logs" - "/home/{{ user_name }}/.local/run" + - "/home/{{ user_name }}/.local/share/applications" + - "/home/{{ user_name }}/.local/share/icons" - "/home/{{ user_name }}/Emulation" - "/home/{{ user_name }}/Emulation/snes" - "/home/{{ user_name }}/Emulation/psp" @@ -618,14 +476,35 @@ owner: "{{ user_name }}" group: "{{ user_name }}" - - name: Enable User session service login + - name: Copy shortcuts to remote host + become: true + ansible.builtin.copy: + src: "{{ item }}" + dest: "/home/{{ user_name }}/.local/share/applications/{{ item | basename }}" + owner: "{{ user_name }}" + group: "{{ user_name }}" + mode: "0644" + loop: "{{ query('fileglob', 'game_station/shortcuts/*.desktop') }}" + + - name: Copy icons to remote host + become: true + ansible.builtin.copy: + src: "{{ item }}" + dest: "/home/{{ user_name }}/.local/share/icons/{{ item | basename }}" + owner: "{{ user_name }}" + group: "{{ user_name }}" + mode: "0644" + loop: "{{ query('fileglob', 'game_station/shortcuts/icons/*.png') }}" + + + - name: Enable Required services become: true - become_user: "{{ user_name }}" ansible.builtin.systemd: - name: steamos.service + name: "{{ item }}" enabled: true state: started - scope: user + loop: + - sddm.service handlers: - name: Update grub diff --git a/terraform/apps/dev-01/config.tf b/terraform/apps/dev-01/config.tf index 701041f..cedb470 100644 --- a/terraform/apps/dev-01/config.tf +++ b/terraform/apps/dev-01/config.tf @@ -18,8 +18,9 @@ locals { # DNS dns_nameservers = [ - "193.110.81.0", - "185.253.5.0" + "192.168.0.252", + "1.1.1.1", + "8.8.8.8" ] override_hosts = [