Use Environment Variables (#123)

Uses environment variables instead of hard coding the "deck" user/group.
This adds support for systems other than the steam deck that are using the DeckUI.

* Use Environment Variables

* Use method to get USER from a systemd root process

* Fix imports. Add get_user and get_user_group methods in helpers.py. Removed duplicated code

* Add separate setters/getters for user vars. Ensure sleep prevents race condition of user setter in while loop
This commit is contained in:
Derek J. Clark
2022-08-08 11:32:14 -07:00
committed by GitHub
parent 198591dbd7
commit 20094c5f75
7 changed files with 126 additions and 66 deletions
+20 -15
View File
@@ -1,20 +1,22 @@
from injector import get_tab # Full imports
import json
# Partial imports
from aiohttp import ClientSession, web
from asyncio import get_event_loop
from concurrent.futures import ProcessPoolExecutor
from hashlib import sha256
from io import BytesIO
from logging import getLogger from logging import getLogger
from os import path, rename, listdir from os import path, rename, listdir
from shutil import rmtree from shutil import rmtree
from aiohttp import ClientSession, web from subprocess import call
from io import BytesIO
from zipfile import ZipFile
from concurrent.futures import ProcessPoolExecutor
from asyncio import get_event_loop
from time import time from time import time
from hashlib import sha256 from zipfile import ZipFile
from subprocess import Popen
from injector import inject_to_tab
import json # Local modules
from helpers import get_ssl_context, get_user, get_user_group
import helpers from injector import get_tab, inject_to_tab
class PluginInstallContext: class PluginInstallContext:
def __init__(self, artifact, name, version, hash) -> None: def __init__(self, artifact, name, version, hash) -> None:
@@ -41,8 +43,11 @@ class PluginBrowser:
return False return False
zip_file = ZipFile(zip) zip_file = ZipFile(zip)
zip_file.extractall(self.plugin_path) zip_file.extractall(self.plugin_path)
Popen(["chown", "-R", "deck:deck", self.plugin_path]) code_chown = call(["chown", "-R", get_user()+":"+get_user_group(), self.plugin_path])
Popen(["chmod", "-R", "555", self.plugin_path]) code_chmod = call(["chmod", "-R", "555", self.plugin_path])
if code_chown != 0 or code_chmod != 0:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
return False
return True return True
def find_plugin_folder(self, name): def find_plugin_folder(self, name):
@@ -83,7 +88,7 @@ class PluginBrowser:
self.log.info(f"Installing {name} (Version: {version})") self.log.info(f"Installing {name} (Version: {version})")
async with ClientSession() as client: async with ClientSession() as client:
self.log.debug(f"Fetching {artifact}") self.log.debug(f"Fetching {artifact}")
res = await client.get(artifact, ssl=helpers.get_ssl_context()) res = await client.get(artifact, ssl=get_ssl_context())
if res.status == 200: if res.status == 200:
self.log.debug("Got 200. Reading...") self.log.debug("Got 200. Reading...")
data = await res.read() data = await res.read()
+45 -4
View File
@@ -1,11 +1,15 @@
from aiohttp.web import middleware, Response
import ssl
import certifi import certifi
import ssl
import uuid import uuid
ssl_ctx = ssl.create_default_context(cafile=certifi.where()) from subprocess import check_output
from time import sleep
# global vars
csrf_token = str(uuid.uuid4()) csrf_token = str(uuid.uuid4())
ssl_ctx = ssl.create_default_context(cafile=certifi.where())
user = None
group = None
def get_ssl_context(): def get_ssl_context():
return ssl_ctx return ssl_ctx
@@ -17,4 +21,41 @@ def get_csrf_token():
async def csrf_middleware(request, handler): async def csrf_middleware(request, handler):
if str(request.method) == "OPTIONS" or request.headers.get('Authentication') == csrf_token or str(request.rel_url) == "/auth/token" or str(request.rel_url).startswith("/plugins/load_main/") or str(request.rel_url).startswith("/static/") or str(request.rel_url).startswith("/legacy/") or str(request.rel_url).startswith("/steam_resource/"): if str(request.method) == "OPTIONS" or request.headers.get('Authentication') == csrf_token or str(request.rel_url) == "/auth/token" or str(request.rel_url).startswith("/plugins/load_main/") or str(request.rel_url).startswith("/static/") or str(request.rel_url).startswith("/legacy/") or str(request.rel_url).startswith("/steam_resource/"):
return await handler(request) return await handler(request)
return Response(text='Forbidden', status='403') return Response(text='Forbidden', status='403')
# Get the user by checking for the first logged in user. As this is run
# by systemd at startup the process is likely to start before the user
# logs in, so we will wait here until they are available. Note that
# other methods such as getenv wont work as there was no $SUDO_USER to
# start the systemd service.
def set_user():
global user
cmd = "who | awk '{print $1}' | sort | head -1"
while user == None:
name = check_output(cmd, shell=True).decode().strip()
if name not in [None, '']:
user = name
sleep(0.1)
# Get the global user. get_user must be called first.
def get_user() -> str:
global user
if user == None:
raise ValueError("helpers.get_user method called before user variable was set. Run helpers.set_user first.")
return user
# Set the global user group. get_user must be called first
def set_user_group() -> str:
global group
global user
if user == None:
raise ValueError("helpers.set_user_dir method called before user variable was set. Run helpers.set_user first.")
if group == None:
group = check_output(["id", "-g", "-n", user]).decode().strip()
# Get the group of the global user. set_user_group must be called first.
def get_user_group() -> str:
global group
if group == None:
raise ValueError("helpers.get_user_group method called before group variable was set. Run helpers.set_user_group first.")
return group
+29 -20
View File
@@ -1,10 +1,35 @@
from logging import DEBUG, INFO, basicConfig, getLogger # Full imports
from os import getenv import aiohttp_cors
# Partial imports
from aiohttp import ClientSession from aiohttp import ClientSession
from aiohttp.web import Application, run_app, static, get, Response
from aiohttp_jinja2 import setup as jinja_setup
from asyncio import get_event_loop, sleep
from json import dumps, loads
from logging import DEBUG, INFO, basicConfig, getLogger
from os import getenv, path
from subprocess import call
# local modules
from browser import PluginBrowser
from helpers import csrf_middleware, get_csrf_token, get_user, get_user_group, set_user, set_user_group
from injector import inject_to_tab, tab_has_global_var
from loader import Loader
from updater import Updater
from utilities import Utilities
# Ensure USER and GROUP vars are set first.
# TODO: This isn't the best way to do this but supports the current
# implementation. All the config load and environment setting eventually be
# moved into init or a config/loader method.
set_user()
set_user_group()
USER = get_user()
GROUP = get_user_group()
HOME_PATH = "/home/"+USER
CONFIG = { CONFIG = {
"plugin_path": getenv("PLUGIN_PATH", "/home/deck/homebrew/plugins"), "plugin_path": getenv("PLUGIN_PATH", HOME_PATH+"/homebrew/plugins"),
"chown_plugin_path": getenv("CHOWN_PLUGIN_PATH", "1") == "1", "chown_plugin_path": getenv("CHOWN_PLUGIN_PATH", "1") == "1",
"server_host": getenv("SERVER_HOST", "127.0.0.1"), "server_host": getenv("SERVER_HOST", "127.0.0.1"),
"server_port": int(getenv("SERVER_PORT", "1337")), "server_port": int(getenv("SERVER_PORT", "1337")),
@@ -14,26 +39,10 @@ CONFIG = {
basicConfig(level=CONFIG["log_level"], format="[%(module)s][%(levelname)s]: %(message)s") basicConfig(level=CONFIG["log_level"], format="[%(module)s][%(levelname)s]: %(message)s")
from asyncio import get_event_loop, sleep
from json import dumps, loads
from os import path
from subprocess import call
import aiohttp_cors
from aiohttp.web import Application, run_app, static, get, Response
from aiohttp_jinja2 import setup as jinja_setup
from browser import PluginBrowser
from injector import inject_to_tab, tab_has_global_var
from loader import Loader
from helpers import csrf_middleware, get_csrf_token
from utilities import Utilities
from updater import Updater
logger = getLogger("Main") logger = getLogger("Main")
async def chown_plugin_dir(_): async def chown_plugin_dir(_):
code_chown = call(["chown", "-R", "deck:deck", CONFIG["plugin_path"]]) code_chown = call(["chown", "-R", USER+":"+GROUP, CONFIG["plugin_path"]])
code_chmod = call(["chmod", "-R", "555", CONFIG["plugin_path"]]) code_chmod = call(["chmod", "-R", "555", CONFIG["plugin_path"]])
if code_chown != 0 or code_chmod != 0: if code_chown != 0 or code_chmod != 0:
logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})") logger.error(f"chown/chmod exited with a non-zero exit code (chown: {code_chown}, chmod: {code_chmod})")
+8 -8
View File
@@ -4,12 +4,13 @@
echo "Installing Steam Deck Plugin Loader nightly..." echo "Installing Steam Deck Plugin Loader nightly..."
HOMEBREW_FOLDER=/home/deck/homebrew USER_DIR="$(getent passwd $SUDO_USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
# Create folder structure # Create folder structure
rm -rf ${HOMEBREW_FOLDER}/services rm -rf ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest nightly build and install it # Download latest nightly build and install it
rm -rf /tmp/plugin_loader rm -rf /tmp/plugin_loader
@@ -22,7 +23,7 @@ chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader
systemctl --user stop plugin_loader 2> /dev/null systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null systemctl --user disable plugin_loader 2> /dev/null
rm -f /home/deck/.config/systemd/user/plugin_loader.service rm -f ${USER_DIR}/.config/systemd/user/plugin_loader.service
systemctl stop plugin_loader 2> /dev/null systemctl stop plugin_loader 2> /dev/null
systemctl disable plugin_loader 2> /dev/null systemctl disable plugin_loader 2> /dev/null
@@ -37,10 +38,9 @@ Type=simple
User=root User=root
Restart=always Restart=always
ExecStart=/home/deck/homebrew/services/PluginLoader ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=/home/deck/homebrew/services WorkingDirectory=${HOMEBREW_FOLDER}/services
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
Environment=PLUGIN_PATH=/home/deck/homebrew/plugins
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
Vendored Executable → Regular
+4 -3
View File
@@ -4,12 +4,13 @@
echo "Installing Steam Deck Plugin Loader pre-release..." echo "Installing Steam Deck Plugin Loader pre-release..."
HOMEBREW_FOLDER=/home/deck/homebrew USER_DIR="$(getent passwd $SUDO_USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
# # Create folder structure # # Create folder structure
rm -rf ${HOMEBREW_FOLDER}/services rm -rf ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest release and install it # Download latest release and install it
RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))") RELEASE=$(curl -s 'https://api.github.com/repos/SteamDeckHomebrew/decky-loader/releases' | jq -r "first(.[] | select(.prerelease == "true"))")
+12 -11
View File
@@ -4,33 +4,34 @@
echo "Installing Steam Deck Plugin Loader release..." echo "Installing Steam Deck Plugin Loader release..."
HOMEBREW_FOLDER=/home/deck/homebrew USER_DIR="$(getent passwd $SUDO_USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
# Create folder structure # Create folder structure
rm -rf ${HOMEBREW_FOLDER}/services rm -rf "${HOMEBREW_FOLDER}/services"
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest release and install it # Download latest release and install it
curl -L https://github.com/SteamDeckHomebrew/PluginLoader/releases/latest/download/PluginLoader --output ${HOMEBREW_FOLDER}/services/PluginLoader curl -L https://github.com/SteamDeckHomebrew/PluginLoader/releases/latest/download/PluginLoader --output "${HOMEBREW_FOLDER}/services/PluginLoader"
chmod +x ${HOMEBREW_FOLDER}/services/PluginLoader chmod +x "${HOMEBREW_FOLDER}/services/PluginLoader"
systemctl --user stop plugin_loader 2> /dev/null systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null systemctl --user disable plugin_loader 2> /dev/null
systemctl stop plugin_loader 2> /dev/null systemctl stop plugin_loader 2> /dev/null
systemctl disable plugin_loader 2> /dev/null systemctl disable plugin_loader 2> /dev/null
rm -f /etc/systemd/system/plugin_loader.service rm -f "/etc/systemd/system/plugin_loader.service"
cat > /etc/systemd/system/plugin_loader.service <<- EOM cat > "/etc/systemd/system/plugin_loader.service" <<- EOM
[Unit] [Unit]
Description=SteamDeck Plugin Loader Description=SteamDeck Plugin Loader
[Service] [Service]
Type=simple Type=simple
User=root User=root
Restart=always Restart=always
ExecStart=/home/deck/homebrew/services/PluginLoader ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=/home/deck/homebrew/services WorkingDirectory=${HOMEBREW_FOLDER}/services
Environment=PLUGIN_PATH=/home/deck/homebrew/plugins Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOM EOM
+8 -5
View File
@@ -1,17 +1,20 @@
#!/bin/sh #!/bin/sh
[ "$UID" -eq 0 ] || exec sudo "$0" "$@"
echo "Uninstalling Steam Deck Plugin Loader..." echo "Uninstalling Steam Deck Plugin Loader..."
HOMEBREW_FOLDER=/home/deck/homebrew USER_DIR="$(getent passwd $SUDO_USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
# Disable and remove services # Disable and remove services
sudo systemctl disable --now plugin_loader.service > /dev/null sudo systemctl disable --now plugin_loader.service > /dev/null
sudo rm -f /home/deck/.config/systemd/user/plugin_loader.service sudo rm -f "${USER_DIR}/.config/systemd/user/plugin_loader.service"
sudo rm -f /etc/systemd/system/plugin_loader.service sudo rm -f "/etc/systemd/system/plugin_loader.service"
# Remove temporary folder if it exists from the install process # Remove temporary folder if it exists from the install process
rm -rf /tmp/plugin_loader rm -rf "/tmp/plugin_loader"
# Cleanup services folder # Cleanup services folder
sudo rm ${HOMEBREW_FOLDER}/services/PluginLoader sudo rm "${HOMEBREW_FOLDER}/services/PluginLoader"