added dms greeter auto session switch

This commit is contained in:
2026-07-11 17:48:08 -04:00
parent 835a939a81
commit 4b7725849f
3 changed files with 761 additions and 71 deletions
+313 -58
View File
@@ -48,6 +48,9 @@ CONFIG_FILE="/etc/gaming-mode.conf"
[[ -f "$HOME/.gaming-mode.conf" ]] && CONFIG_FILE="$HOME/.gaming-mode.conf"
source "$CONFIG_FILE" 2>/dev/null || true
: "${PERFORMANCE_MODE:=enabled}"
: "${DECKSHIFT_SESSION_BACKEND:=auto}"
: "${DECKSHIFT_DESKTOP_SESSION_ID:=hyprland-uwsm.desktop}"
: "${DECKSHIFT_GAMING_SESSION_ID:=gamescope-session-steam-nm.desktop}"
# Flags that track whether the user needs to reboot or re-login after setup.
# Various steps set these to 1 when they make changes that only take effect
@@ -1321,10 +1324,10 @@ setup_settings_tui() {
# to seamlessly switch between Desktop Mode (Hyprland) and Gaming Mode
# (Gamescope + Steam Big Picture).
#
# The switching mechanism works through SDDM (the display/login manager):
# The switching mechanism works through a session backend:
# 1. User presses Super+Shift+S in Hyprland
# 2. switch-to-gaming script updates SDDM config to point to Gaming session
# 3. SDDM restarts and auto-logs into the Gaming Mode session
# 2. switch-to-gaming selects the Gaming Mode session
# 3. The backend transitions through SDDM or the configured greeter
# 4. gamescope-session-nm-wrapper starts performance tuning, NetworkManager,
# drive mounting, keybind monitor, then launches Gamescope + Steam
# 5. When done (Super+Shift+R or Steam > Exit to Desktop), the reverse happens
@@ -1335,7 +1338,7 @@ setup_settings_tui() {
# - Keybind monitor (Python daemon using evdev for Super+Shift+R)
# - NetworkManager start/stop scripts (iwd <-> NM handoff)
# - Steam library auto-mount daemon
# - SDDM session entry and config
# - Session entry, backend config, and display-manager helpers
# - Polkit and sudoers rules for passwordless operation
# - Hyprland keybind for Super+Shift+S
#
@@ -1431,6 +1434,50 @@ PY
info "Patched $gsp — CUSTOM_REFRESH_RATES now reaches gamescope via --nested-refresh"
}
detect_session_backend() {
case "${DECKSHIFT_SESSION_BACKEND}" in
sddm|dms-greeter-manual)
printf '%s\n' "$DECKSHIFT_SESSION_BACKEND"
return 0
;;
auto)
;;
*)
die "Unsupported DECKSHIFT_SESSION_BACKEND: $DECKSHIFT_SESSION_BACKEND"
;;
esac
local display_manager=""
if [[ -L /etc/systemd/system/display-manager.service ]]; then
display_manager=$(basename "$(readlink -f /etc/systemd/system/display-manager.service)" 2>/dev/null || true)
fi
if [[ "$display_manager" == "greetd.service" ]] &&
[[ -r /etc/greetd/config.toml ]] &&
grep -q 'dms-greeter' /etc/greetd/config.toml; then
printf '%s\n' "dms-greeter-manual"
return 0
fi
printf '%s\n' "sddm"
}
read_installed_session_backend() {
local conf="/etc/deckshift/session-backend.conf"
if [[ -r "$conf" ]]; then
local installed_backend
installed_backend=$(sed -n 's/^DECKSHIFT_SESSION_BACKEND=//p' "$conf" 2>/dev/null | head -1)
case "$installed_backend" in
sddm|dms-greeter-manual)
printf '%s\n' "$installed_backend"
return 0
;;
esac
fi
detect_session_backend
}
setup_session_switching() {
echo ""
echo "================================================================"
@@ -1471,6 +1518,10 @@ setup_session_switching() {
local current_user="${SUDO_USER:-$USER}"
local user_home
user_home=$(eval echo "~$current_user")
local session_backend
session_backend=$(detect_session_backend)
local gaming_session_path="/usr/share/wayland-sessions/${DECKSHIFT_GAMING_SESSION_ID}"
info "Using session backend: $session_backend"
# GPU detection only — the installer no longer chooses a monitor, resolution
# or refresh rate. Those are user choices, made later via Walker → "DeckShift
@@ -2285,14 +2336,13 @@ NM_WRAPPER
sudo chmod +x "$nm_wrapper"
info "Created $nm_wrapper"
# SDDM Session Entry
# Gaming Session Entry
#
# SDDM (the login/display manager) needs a .desktop file to know about
# Gaming Mode as a session option. This is what tells SDDM "when you
# auto-login to the gaming session, run this script." It's placed in
# /usr/share/wayland-sessions/ alongside the normal Hyprland session.
info "Creating SDDM session entry..."
local session_desktop="/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"
# Display managers and greeters need a .desktop file to know about Gaming
# Mode as a session option. The active session backend selects this entry
# when switching into Gaming Mode.
info "Creating Gaming Mode session entry..."
local session_desktop="$gaming_session_path"
sudo tee "$session_desktop" > /dev/null << 'SESSION_DESKTOP'
[Desktop Entry]
@@ -2310,19 +2360,19 @@ SESSION_DESKTOP
# When you click Steam > Power > "Exit to Desktop" inside Gaming Mode,
# Steam calls /usr/lib/os-session-select. On a real Steam Deck this
# switches to Desktop Mode. Our version does the same thing — it updates
# SDDM to boot back into Hyprland and restarts the display manager.
# selects the desktop session and asks the active backend to transition.
info "Creating session-select script..."
local os_session_select="/usr/lib/os-session-select"
sudo tee "$os_session_select" > /dev/null << 'OS_SESSION_SELECT'
#!/bin/bash
rm -f /tmp/.gaming-session-active
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || {
sudo -n /usr/local/bin/deckshift-session-switch desktop 2>/dev/null || {
echo "Warning: Failed to update session config"
}
timeout 5 steam -shutdown 2>/dev/null || true
sleep 1
nohup sudo -n systemctl restart sddm &>/dev/null &
nohup /usr/local/bin/deckshift-session-transition desktop &>/dev/null &
disown
exit 0
OS_SESSION_SELECT
@@ -2335,10 +2385,10 @@ OS_SESSION_SELECT
# This script handles the transition from Desktop to Gaming Mode:
# 1. Masks suspend targets — prevents the system from sleeping when the
# monitor briefly disconnects during the display manager restart
# 2. Updates SDDM config to auto-login to the gaming session
# 2. Selects the gaming session through DeckShift's active backend
# 3. Kills any leftover gamescope processes from a previous session
# 4. Switches to VT2 (virtual terminal) to avoid display conflicts
# 5. Restarts SDDM, which auto-logs into Gaming Mode
# 5. Transitions through SDDM or the configured greeter
info "Creating switch-to-gaming script..."
local switch_script="/usr/local/bin/switch-to-gaming"
@@ -2346,7 +2396,7 @@ OS_SESSION_SELECT
#!/bin/bash
# Inhibit suspend FIRST - prevents suspend when monitor detaches during switch
sudo -n systemctl mask --runtime sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null
sudo -n /usr/local/bin/gaming-session-switch gaming 2>/dev/null || {
sudo -n /usr/local/bin/deckshift-session-switch gaming 2>/dev/null || {
notify-send -u critical -t 3000 "Gaming Mode" "Failed to update session config" 2>/dev/null || true
}
notify-send -u normal -t 2000 "Gaming Mode" "Switching to Gaming Mode..." 2>/dev/null || true
@@ -2377,7 +2427,7 @@ fi
sudo -n chvt 2 2>/dev/null || true
sleep 0.3
sudo -n systemctl restart sddm
/usr/local/bin/deckshift-session-transition gaming
SWITCH_SCRIPT
sudo chmod +x "$switch_script"
@@ -2390,8 +2440,8 @@ SWITCH_SCRIPT
# 2. Restores Bluetooth (disabled during gaming to reduce interference)
# 3. Gracefully shuts down Steam (timeout 5s, then force kill)
# 4. Kills gamescope with SIGTERM first, then SIGKILL if it won't die
# 5. Updates SDDM config back to Hyprland session
# 6. Restarts SDDM, which auto-logs into Desktop Mode
# 5. Selects the desktop session through DeckShift's active backend
# 6. Transitions through SDDM or the configured greeter
info "Creating switch-to-desktop script..."
local switch_desktop_script="/usr/local/bin/switch-to-desktop"
@@ -2429,7 +2479,7 @@ fi
sudo -n systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null
sudo -n systemctl unmask --runtime sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null
sudo -n systemctl daemon-reload 2>/dev/null
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || true
sudo -n /usr/local/bin/deckshift-session-switch desktop 2>/dev/null || true
# Re-enable Bluetooth
sudo -n /usr/bin/rfkill unblock bluetooth 2>/dev/null || true
@@ -2465,9 +2515,10 @@ sudo -n chvt 2 2>/dev/null || true
sleep 0.5
# Atomic restart — stop+start (with stop and start as separate sudo calls)
# was unreliable: stop/start aren't NOPASSWD-allowed individually (only
# `restart` is), and the disowned `start` could be killed by session teardown
# before SDDM actually came back up, leaving the user on a black screen.
sudo -n systemctl restart sddm
# `restart` is for SDDM), and the disowned `start` could be killed by session
# teardown before the display manager actually came back up, leaving the user
# on a black screen.
/usr/local/bin/deckshift-session-transition desktop
exit 0
SWITCH_DESKTOP
@@ -2661,21 +2712,14 @@ HYPR_PORTAL
warn "Add manually: exec-once = /usr/local/bin/deckshift-portal-recovery"
fi
# SDDM Session Switching Config
# Session Backend Config
#
# SDDM supports auto-login — it can automatically log in a user to a
# specific session without showing the login screen. This config file
# controls WHICH session SDDM auto-logs into.
#
# The switching mechanism works by editing this file:
# - "Session=hyprland-uwsm" → boots into Desktop Mode
# - "Session=gamescope-session-steam-nm" → boots into Gaming Mode
#
# The gaming-session-switch helper script toggles this value, then SDDM
# is restarted to pick up the change. The "zz-" prefix ensures this
# config loads LAST and overrides any other SDDM autologin settings.
info "Creating SDDM session switching config..."
local sddm_gaming_conf="/etc/sddm.conf.d/zz-gaming-session.conf"
# Keep display-manager-specific behavior behind small helpers so DeckShift's
# Gamescope, Steam, GPU, and performance logic can stay backend-agnostic.
info "Creating session backend config..."
local deckshift_config_dir="/etc/deckshift"
local deckshift_backend_conf="${deckshift_config_dir}/session-backend.conf"
sudo install -d -m 0755 "$deckshift_config_dir"
local autologin_user="$current_user"
if [[ -f /etc/sddm.conf.d/autologin.conf ]]; then
@@ -2683,49 +2727,198 @@ HYPR_PORTAL
[[ -z "$autologin_user" ]] && autologin_user="$current_user"
fi
sudo tee "$sddm_gaming_conf" > /dev/null << SDDM_GAMING
sudo tee "$deckshift_backend_conf" > /dev/null << BACKEND_CONF
DECKSHIFT_SESSION_BACKEND=${session_backend}
DECKSHIFT_DESKTOP_SESSION_ID=${DECKSHIFT_DESKTOP_SESSION_ID}
DECKSHIFT_GAMING_SESSION_ID=${DECKSHIFT_GAMING_SESSION_ID}
DECKSHIFT_GAMING_SESSION_PATH=${gaming_session_path}
DECKSHIFT_SDDM_CONF=/etc/sddm.conf.d/zz-gaming-session.conf
DECKSHIFT_SDDM_USER=${autologin_user}
DECKSHIFT_DMS_MEMORY_FILE=/var/cache/dms-greeter/.local/state/memory.json
DECKSHIFT_DMS_GREETER_GROUP=greeter
BACKEND_CONF
sudo chmod 0644 "$deckshift_backend_conf"
info "Created $deckshift_backend_conf"
if [[ "$session_backend" == "sddm" ]]; then
info "Creating SDDM session switching config..."
local sddm_gaming_conf="/etc/sddm.conf.d/zz-gaming-session.conf"
sudo install -d -m 0755 /etc/sddm.conf.d
sudo tee "$sddm_gaming_conf" > /dev/null << SDDM_GAMING
[Autologin]
User=${autologin_user}
Session=hyprland-uwsm
Session=${DECKSHIFT_DESKTOP_SESSION_ID%.desktop}
Relogin=true
SDDM_GAMING
info "Created $sddm_gaming_conf"
else
info "DMS greeter backend selected; SDDM autologin config will not be created"
fi
info "Created $sddm_gaming_conf"
info "Creating session switching helper script..."
local session_helper="/usr/local/bin/gaming-session-switch"
info "Creating session switching helper scripts..."
local session_helper="/usr/local/bin/deckshift-session-switch"
sudo tee "$session_helper" > /dev/null << 'SESSION_HELPER'
#!/bin/bash
CONF="/etc/sddm.conf.d/zz-gaming-session.conf"
set -euo pipefail
CONF="/etc/deckshift/session-backend.conf"
if [[ ! -f "$CONF" ]]; then
echo "Error: Config file not found: $CONF" >&2
exit 1
fi
case "$1" in
gaming)
sed -i 's/^Session=.*/Session=gamescope-session-steam-nm/' "$CONF"
echo "Session set to: gaming mode"
;;
desktop)
sed -i 's/^Session=.*/Session=hyprland-uwsm/' "$CONF"
echo "Session set to: desktop mode"
;;
# shellcheck disable=SC1090
source "$CONF"
target="${1:-}"
case "$target" in
gaming|desktop) ;;
*)
echo "Usage: $0 {gaming|desktop}" >&2
exit 1
;;
esac
session_id_for_target() {
case "$1" in
gaming) printf '%s\n' "$DECKSHIFT_GAMING_SESSION_ID" ;;
desktop) printf '%s\n' "$DECKSHIFT_DESKTOP_SESSION_ID" ;;
esac
}
resolve_session_path() {
local session_id="$1"
local explicit=""
if [[ "$session_id" == "$DECKSHIFT_GAMING_SESSION_ID" ]]; then
explicit="${DECKSHIFT_GAMING_SESSION_PATH:-}"
fi
if [[ -n "$explicit" && -f "$explicit" ]]; then
printf '%s\n' "$explicit"
return 0
fi
local dir candidate
for dir in /usr/local/share/wayland-sessions /usr/share/wayland-sessions; do
candidate="${dir}/${session_id}"
if [[ -f "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
done
echo "Error: session file not found for ${session_id}" >&2
exit 1
}
write_dms_memory() {
local session_id="$1"
local session_path="$2"
local memory_file="${DECKSHIFT_DMS_MEMORY_FILE:-/var/cache/dms-greeter/.local/state/memory.json}"
local tmp
tmp=$(mktemp)
python3 - "$memory_file" "$session_path" "$session_id" > "$tmp" <<'PY'
import json
import os
import sys
path, session_path, session_id = sys.argv[1:4]
data = {}
try:
if os.path.exists(path):
with open(path) as fh:
loaded = json.load(fh)
if isinstance(loaded, dict):
data = loaded
except Exception:
data = {}
data["lastSessionId"] = session_path
data["lastSessionDesktopId"] = session_id
json.dump(data, sys.stdout, indent=2)
sys.stdout.write("\n")
PY
install -d -m 0775 "$(dirname "$memory_file")"
install -m 0664 "$tmp" "$memory_file"
rm -f "$tmp"
if getent group "${DECKSHIFT_DMS_GREETER_GROUP:-greeter}" >/dev/null 2>&1; then
chgrp "${DECKSHIFT_DMS_GREETER_GROUP:-greeter}" "$(dirname "$memory_file")" "$memory_file" || true
fi
}
session_id="$(session_id_for_target "$target")"
case "${DECKSHIFT_SESSION_BACKEND:-sddm}" in
sddm)
sddm_conf="${DECKSHIFT_SDDM_CONF:-/etc/sddm.conf.d/zz-gaming-session.conf}"
if [[ ! -f "$sddm_conf" ]]; then
echo "Error: SDDM config file not found: $sddm_conf" >&2
exit 1
fi
sed -i "s/^Session=.*/Session=${session_id%.desktop}/" "$sddm_conf"
;;
dms-greeter-manual)
session_path="$(resolve_session_path "$session_id")"
write_dms_memory "$session_id" "$session_path"
;;
*)
echo "Error: unsupported DeckShift session backend: ${DECKSHIFT_SESSION_BACKEND:-}" >&2
exit 1
;;
esac
echo "Session set to: ${target} mode"
SESSION_HELPER
sudo chmod +x "$session_helper"
info "Created $session_helper"
local legacy_session_helper="/usr/local/bin/gaming-session-switch"
sudo tee "$legacy_session_helper" > /dev/null << 'LEGACY_SESSION_HELPER'
#!/bin/bash
exec /usr/local/bin/deckshift-session-switch "$@"
LEGACY_SESSION_HELPER
sudo chmod +x "$legacy_session_helper"
info "Created compatibility wrapper $legacy_session_helper"
local transition_helper="/usr/local/bin/deckshift-session-transition"
sudo tee "$transition_helper" > /dev/null << 'TRANSITION_HELPER'
#!/bin/bash
set -euo pipefail
CONF="/etc/deckshift/session-backend.conf"
[[ -f "$CONF" ]] || { echo "Error: Config file not found: $CONF" >&2; exit 1; }
# shellcheck disable=SC1090
source "$CONF"
case "${DECKSHIFT_SESSION_BACKEND:-sddm}" in
sddm)
exec sudo -n systemctl restart sddm
;;
dms-greeter-manual)
if [[ -n "${XDG_SESSION_ID:-}" ]]; then
exec loginctl terminate-session "$XDG_SESSION_ID"
fi
exec sudo -n systemctl restart greetd.service
;;
*)
echo "Error: unsupported DeckShift session backend: ${DECKSHIFT_SESSION_BACKEND:-}" >&2
exit 1
;;
esac
TRANSITION_HELPER
sudo chmod +x "$transition_helper"
info "Created $transition_helper"
# Sudoers Rules for Session Switching
#
# The session switching scripts need to run several commands as root
# (restart SDDM, start/stop NetworkManager, control Bluetooth, etc.).
# (restart display managers, start/stop NetworkManager, control Bluetooth, etc.).
# These sudoers rules allow members of the "video" and "wheel" groups
# to run ONLY these specific commands without a password.
#
@@ -2744,7 +2937,10 @@ SESSION_HELPER
local switch_output
switch_output=$(sudo tee "$sudoers_session" << 'SUDOERS_SWITCH' 2>&1
%video ALL=(ALL) NOPASSWD: /usr/local/bin/gaming-session-switch
%video ALL=(ALL) NOPASSWD: /usr/local/bin/deckshift-session-switch
%video ALL=(ALL) NOPASSWD: /usr/local/bin/deckshift-session-transition
%video ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart sddm
%video ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart greetd.service
%video ALL=(ALL) NOPASSWD: /usr/bin/chvt
%video ALL=(ALL) NOPASSWD: /usr/bin/systemctl mask --runtime sleep.target suspend.target hibernate.target hybrid-sleep.target
%video ALL=(ALL) NOPASSWD: /usr/bin/systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target
@@ -2846,10 +3042,13 @@ HYPR_GAMING
echo " Files created/modified:"
echo " - ~/.config/environment.d/gamescope-session-plus.conf"
echo " - /usr/local/bin/gamescope-session-nm-wrapper"
echo " - /usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"
echo " - ${gaming_session_path}"
echo " - /usr/lib/os-session-select"
echo " - /usr/local/bin/switch-to-gaming"
echo " - /usr/local/bin/switch-to-desktop"
echo " - /usr/local/bin/deckshift-session-switch"
echo " - /usr/local/bin/deckshift-session-transition"
echo " - /etc/deckshift/session-backend.conf (${session_backend})"
echo " - /usr/local/bin/gaming-keybind-monitor (Super+Shift+R)"
echo " - ~/.config/hypr/bindings.conf (keybind added)"
echo ""
@@ -2910,10 +3109,14 @@ verify_installation() {
local all_ok=true
local missing_files=()
local permission_issues=()
local verify_session_backend
verify_session_backend=$(read_installed_session_backend)
declare -A expected_files=(
["/usr/local/bin/gamescope-session-nm-wrapper"]="755:ChimeraOS session with NM wrapper"
["/usr/local/lib/gamescope-nvidia/gamescope"]="755:NVIDIA gamescope wrapper (--force-composition)"
["/usr/local/bin/deckshift-session-switch"]="755:Backend-neutral session selector"
["/usr/local/bin/deckshift-session-transition"]="755:Backend-neutral display-manager transition helper"
["/usr/local/bin/gaming-session-switch"]="755:Session switching helper (gaming/desktop)"
["/usr/lib/os-session-select"]="755:Steam Exit to Desktop handler"
["/usr/local/bin/switch-to-gaming"]="755:Hyprland to Gaming Mode switcher"
@@ -2927,8 +3130,9 @@ verify_installation() {
["/usr/bin/steamos-update"]="755:Steam compatibility (from AUR package)"
["/usr/bin/jupiter-biosupdate"]="755:Steam compatibility (from AUR package)"
["/usr/bin/steamos-select-branch"]="755:Steam compatibility (from AUR package)"
["/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"]="644:SDDM session entry"
["/usr/share/wayland-sessions/gamescope-session-steam-nm.desktop"]="644:Gaming Mode Wayland session entry"
["/usr/share/gamescope-session-plus/gamescope-session-plus"]="755:ChimeraOS session launcher (from AUR)"
["/etc/deckshift/session-backend.conf"]="644:Session backend config"
["/etc/sddm.conf.d/zz-gaming-session.conf"]="644:SDDM session switching config"
["/etc/polkit-1/rules.d/50-gamescope-networkmanager.rules"]="644:Polkit NM rules"
["/etc/polkit-1/rules.d/50-udisks-gaming.rules"]="644:Polkit udisks2 rules (external drive mount)"
@@ -2953,7 +3157,16 @@ verify_installation() {
local description="${expected_files[$file]#*:}"
local is_optional=false
[[ "$description" == *"(optional)"* ]] && is_optional=true
case "$file" in
/etc/sddm.conf.d/zz-gaming-session.conf)
[[ "$verify_session_backend" == "dms-greeter-manual" ]] && is_optional=true
;;
/etc/NetworkManager/conf.d/10-iwd-backend.conf|\
/etc/NetworkManager/conf.d/20-unmanaged-systemd.conf|\
/usr/share/libalpm/hooks/deckshift-gamescope-cap.hook)
is_optional=true
;;
esac
if sudo test -f "$file" 2>/dev/null; then
local actual_perm
@@ -3112,6 +3325,48 @@ verify_installation() {
echo " systemd-networkd: $(systemctl is-active systemd-networkd.service 2>/dev/null || echo 'inactive')"
echo " polkit: $(systemctl is-active polkit.service 2>/dev/null || echo 'inactive')"
if [[ "$verify_session_backend" == "dms-greeter-manual" ]]; then
echo ""
echo " DMS GREETER BACKEND:"
echo " --------------------"
local dms_memory_file="/var/cache/dms-greeter/.local/state/memory.json"
if [[ -r /etc/deckshift/session-backend.conf ]]; then
local configured_memory_file
configured_memory_file=$(sed -n 's/^DECKSHIFT_DMS_MEMORY_FILE=//p' /etc/deckshift/session-backend.conf 2>/dev/null | head -1)
[[ -n "$configured_memory_file" ]] && dms_memory_file="$configured_memory_file"
fi
echo " Backend: $verify_session_backend"
echo " Memory file: $dms_memory_file"
if [[ -r "$dms_memory_file" ]]; then
local memory_stat
memory_stat=$(stat -c "%U:%G %a" "$dms_memory_file" 2>/dev/null || echo "unknown")
echo " ✓ DMS memory readable ($memory_stat)"
if python3 - "$dms_memory_file" <<'PY'
import json
import sys
with open(sys.argv[1]) as fh:
data = json.load(fh)
if not isinstance(data, dict):
raise SystemExit(1)
for key in ("lastSessionId", "lastSessionDesktopId"):
value = data.get(key)
if not isinstance(value, str) or not value:
raise SystemExit(1)
PY
then
echo " ✓ DMS memory has lastSessionId and lastSessionDesktopId"
else
echo " ✗ DMS memory JSON is missing expected session keys"
all_ok=false
fi
else
echo " ✗ DMS memory file is not readable"
all_ok=false
fi
fi
echo ""
echo " SUDO PERMISSIONS TEST:"
echo " ----------------------"