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 os import path, rename, listdir
from shutil import rmtree
from aiohttp import ClientSession, web
from io import BytesIO
from zipfile import ZipFile
from concurrent.futures import ProcessPoolExecutor
from asyncio import get_event_loop
from subprocess import call
from time import time
from hashlib import sha256
from subprocess import Popen
from injector import inject_to_tab
from zipfile import ZipFile
import json
import helpers
# Local modules
from helpers import get_ssl_context, get_user, get_user_group
from injector import get_tab, inject_to_tab
class PluginInstallContext:
def __init__(self, artifact, name, version, hash) -> None:
@@ -41,8 +43,11 @@ class PluginBrowser:
return False
zip_file = ZipFile(zip)
zip_file.extractall(self.plugin_path)
Popen(["chown", "-R", "deck:deck", self.plugin_path])
Popen(["chmod", "-R", "555", self.plugin_path])
code_chown = call(["chown", "-R", get_user()+":"+get_user_group(), 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
def find_plugin_folder(self, name):
@@ -83,7 +88,7 @@ class PluginBrowser:
self.log.info(f"Installing {name} (Version: {version})")
async with ClientSession() as client:
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:
self.log.debug("Got 200. Reading...")
data = await res.read()
+45 -4
View File
@@ -1,11 +1,15 @@
from aiohttp.web import middleware, Response
import ssl
import certifi
import ssl
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())
ssl_ctx = ssl.create_default_context(cafile=certifi.where())
user = None
group = None
def get_ssl_context():
return ssl_ctx
@@ -17,4 +21,41 @@ def get_csrf_token():
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/"):
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
from os import getenv
# Full imports
import aiohttp_cors
# Partial imports
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 = {
"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",
"server_host": getenv("SERVER_HOST", "127.0.0.1"),
"server_port": int(getenv("SERVER_PORT", "1337")),
@@ -14,26 +39,10 @@ CONFIG = {
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")
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"]])
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})")
+8 -8
View File
@@ -4,12 +4,13 @@
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
rm -rf ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest nightly build and install it
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 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 disable plugin_loader 2> /dev/null
@@ -37,10 +38,9 @@ Type=simple
User=root
Restart=always
ExecStart=/home/deck/homebrew/services/PluginLoader
WorkingDirectory=/home/deck/homebrew/services
Environment=PLUGIN_PATH=/home/deck/homebrew/plugins
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=${HOMEBREW_FOLDER}/services
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
[Install]
WantedBy=multi-user.target
Vendored Executable → Regular
+4 -3
View File
@@ -4,12 +4,13 @@
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
rm -rf ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest release and install it
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..."
HOMEBREW_FOLDER=/home/deck/homebrew
USER_DIR="$(getent passwd $SUDO_USER | cut -d: -f6)"
HOMEBREW_FOLDER="${USER_DIR}/homebrew"
# Create folder structure
rm -rf ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/services
sudo -u deck mkdir -p ${HOMEBREW_FOLDER}/plugins
rm -rf "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/services"
sudo -u $SUDO_USER mkdir -p "${HOMEBREW_FOLDER}/plugins"
# Download latest release and install it
curl -L https://github.com/SteamDeckHomebrew/PluginLoader/releases/latest/download/PluginLoader --output ${HOMEBREW_FOLDER}/services/PluginLoader
chmod +x ${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"
systemctl --user stop plugin_loader 2> /dev/null
systemctl --user disable plugin_loader 2> /dev/null
systemctl stop plugin_loader 2> /dev/null
systemctl disable plugin_loader 2> /dev/null
rm -f /etc/systemd/system/plugin_loader.service
cat > /etc/systemd/system/plugin_loader.service <<- EOM
rm -f "/etc/systemd/system/plugin_loader.service"
cat > "/etc/systemd/system/plugin_loader.service" <<- EOM
[Unit]
Description=SteamDeck Plugin Loader
[Service]
Type=simple
User=root
Restart=always
ExecStart=/home/deck/homebrew/services/PluginLoader
WorkingDirectory=/home/deck/homebrew/services
Environment=PLUGIN_PATH=/home/deck/homebrew/plugins
ExecStart=${HOMEBREW_FOLDER}/services/PluginLoader
WorkingDirectory=${HOMEBREW_FOLDER}/services
Environment=PLUGIN_PATH=${HOMEBREW_FOLDER}/plugins
[Install]
WantedBy=multi-user.target
EOM
+8 -5
View File
@@ -1,17 +1,20 @@
#!/bin/sh
[ "$UID" -eq 0 ] || exec sudo "$0" "$@"
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
sudo systemctl disable --now plugin_loader.service > /dev/null
sudo rm -f /home/deck/.config/systemd/user/plugin_loader.service
sudo rm -f /etc/systemd/system/plugin_loader.service
sudo rm -f "${USER_DIR}/.config/systemd/user/plugin_loader.service"
sudo rm -f "/etc/systemd/system/plugin_loader.service"
# Remove temporary folder if it exists from the install process
rm -rf /tmp/plugin_loader
rm -rf "/tmp/plugin_loader"
# Cleanup services folder
sudo rm ${HOMEBREW_FOLDER}/services/PluginLoader
sudo rm "${HOMEBREW_FOLDER}/services/PluginLoader"