diff options
| author | Derek J. Clark <derkejohn.clark@gmail.com> | 2022-08-08 11:32:14 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-08 11:32:14 -0700 |
| commit | 20094c5f752046a33287ef372fa973e919e83226 (patch) | |
| tree | ed298251be183fc5f3151b19a10f7499c7d66e0d /backend | |
| parent | 198591dbd7b0b6dd311ab9265d4cda4a733ee280 (diff) | |
| download | decky-loader-20094c5f752046a33287ef372fa973e919e83226.tar.gz decky-loader-20094c5f752046a33287ef372fa973e919e83226.zip | |
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
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/browser.py | 35 | ||||
| -rw-r--r-- | backend/helpers.py | 49 | ||||
| -rw-r--r-- | backend/main.py | 49 |
3 files changed, 94 insertions, 39 deletions
diff --git a/backend/browser.py b/backend/browser.py index c58a97e5..190f8bd5 100644 --- a/backend/browser.py +++ b/backend/browser.py @@ -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 - -import json +from zipfile import ZipFile -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() diff --git a/backend/helpers.py b/backend/helpers.py index e8c2ce5b..68ac7cb3 100644 --- a/backend/helpers.py +++ b/backend/helpers.py @@ -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')
\ No newline at end of file + 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 diff --git a/backend/main.py b/backend/main.py index 02cc0d56..4129f5db 100644 --- a/backend/main.py +++ b/backend/main.py @@ -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})") |
