Compare commits

..

5 Commits

Author SHA1 Message Date
AAGaming 166c7ea8a7 Work around account switching failing to open the CEF debugger socket (#668)
* Work around account switching failing to open the CEF debugger socket

this automates lsof and gdb to force close the socket before steam finishes shutting down (from RegisterForShutdownStart)

* lint

* fix LD_LIBRARY_PATH for gdb
2024-08-06 20:25:39 -07:00
WerWolvTranslationBot ddc807340c Translated using Weblate (Japanese) (#669)
Currently translated at 100.0% (158 of 158 strings)

Translation: Decky/Decky
Translate-URL: https://weblate.werwolv.net/projects/decky/decky/ja/

Co-authored-by: Tak-attack <tak.bts@gmail.com>
2024-08-06 08:00:19 +00:00
AAGaming 7fc51c8b7d shut down properly when using runpydeck 2024-08-05 16:47:13 -04:00
AAGaming 131f0961ff Rewrite router/tabs/toaster hooks (#661) 2024-08-05 14:07:10 -04:00
Sims 75aa1e4851 Sims/pyinstaller misuse fix (#657)
* Fix misuse of pyinstaller

* Fix breaking change

* Fix pywright import errors
2024-08-05 00:00:49 +02:00
11 changed files with 115 additions and 24 deletions
+1 -1
View File
@@ -120,7 +120,7 @@
"dependsOn": [
"checkforsettings"
],
"command": "ssh ${config:deckuser}@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PATH=${config:deckdir}/.local/bin:$PATH; export PLUGIN_PATH=${config:deckdir}/homebrew/plugins; export CHOWN_PLUGIN_PATH=0; export LOG_LEVEL=DEBUG; cd ${config:deckdir}/homebrew/dev/pluginloader/backend; echo '${config:deckpass}' | poetry run sh -c \"cd ${config:deckdir}/homebrew/services; sudo -SE env \"PATH=\\$PATH\" python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py\"'",
"command": "ssh -t ${config:deckuser}@${config:deckip} -p ${config:deckport} ${config:deckkey} 'export PATH=${config:deckdir}/.local/bin:$PATH; export PLUGIN_PATH=${config:deckdir}/homebrew/plugins; export CHOWN_PLUGIN_PATH=0; export LOG_LEVEL=DEBUG; cd ${config:deckdir}/homebrew/dev/pluginloader/backend; echo '${config:deckpass}' | poetry run sh -c \"cd ${config:deckdir}/homebrew/services; sudo -SE env \"PATH=\\$PATH\" python3 ${config:deckdir}/homebrew/dev/pluginloader/backend/main.py\"'",
"problemMatcher": []
},
{
+5 -1
View File
@@ -93,7 +93,11 @@ def get_system_pythonpaths() -> list[str]:
proc = subprocess.run(["python3" if localplatform.ON_LINUX else "python", "-c", "import sys; print('\\n'.join(x for x in sys.path if x))"],
# TODO make this less insane
capture_output=True, user=localplatform.localplatform._get_user_id() if localplatform.ON_LINUX else None, env={} if localplatform.ON_LINUX else None) # pyright: ignore [reportPrivateUsage]
return [x.strip() for x in proc.stdout.decode().strip().split("\n")]
proc.check_returncode()
versions = [x.strip() for x in proc.stdout.decode().strip().split("\n")]
return [x for x in versions if x and not x.isspace()]
except Exception as e:
logger.warn(f"Failed to execute get_system_pythonpaths(): {str(e)}")
return []
+24 -1
View File
@@ -95,12 +95,14 @@
}
},
"PluginListIndex": {
"freeze": "アップデートを凍結",
"hide": "クイックアクセス: 非表示",
"no_plugin": "プラグインがインストールされていません!",
"plugin_actions": "プラグインアクション",
"reinstall": "再インストール",
"reload": "再読み込み",
"show": "クイックアクセス: 表示",
"unfreeze": "アップデートを許可",
"uninstall": "アンインストール",
"update_all_other": "{{count}} 個のプラグインをアップデート",
"update_to": "{{name}} を更新"
@@ -185,9 +187,19 @@
"SettingsIndex": {
"developer_title": "開発者",
"general_title": "一般",
"plugins_title": "プラグイン"
"plugins_title": "プラグイン",
"testing_title": "テスト"
},
"Store": {
"download_progress_info": {
"download_zip": "プラグインのダウンロード中",
"increment_count": "ダウンロード数の増加",
"installing_plugin": "プラグインのインストール中",
"open_zip": "zipファイルを展開中",
"parse_zip": "zipファイルの解析中",
"start": "初期化中",
"uninstalling_previous": "以前のコピーのアンインストール"
},
"store_contrib": {
"desc": "Decky Plugin Storeに貢献したい場合は、GitHubのSteamDeckHomebrew/decky-plugin-templateリポジトリを確認してください。 開発と配布に関する情報は README で入手できます。",
"label": "貢献"
@@ -211,6 +223,10 @@
"about": "概要",
"alph_asce": "アルファベット(Z to A)",
"alph_desc": "アルファベット(A to Z)",
"date_asce": "古い順",
"date_desc": "新しい順",
"downloads_asce": "ダウンロード数が少ない順",
"downloads_desc": "ダウンロード数が多い順",
"title": "閲覧"
},
"store_testing_cta": "Decky Loaderチームを支援するために、新しいプラグインのテストを検討してください!",
@@ -231,6 +247,13 @@
"testing": "テスト"
}
},
"Testing": {
"download": "ダウンロード",
"error": "PRのインストールエラー",
"header": "Decky Loaderの以下のバージョンは、公開されているサードパーティのPull Requestからビルドされたものです。 Decky Loaderチームはその機能や安全性を検証しておらず、内容も古い可能性があります。",
"loading": "Pull Requestの読み込み中...",
"start_download_toast": "PR #{{id}}のダウンロード中"
},
"TitleView": {
"decky_store_desc": "Deckyストアを開く",
"settings_desc": "Decky設定を開く"
@@ -37,6 +37,9 @@ def get_live_reload() -> bool:
def get_keep_systemd_service() -> bool:
return os.getenv("KEEP_SYSTEMD_SERVICE", "0") == "1"
def get_use_cef_close_workaround() -> bool:
return ON_LINUX and os.getenv("USE_CEF_CLOSE_WORKAROUND", "1") == "1"
def get_log_level() -> int:
return {"CRITICAL": 50, "ERROR": 40, "WARNING": 30, "INFO": 20, "DEBUG": 10}[
os.getenv("LOG_LEVEL", "INFO")
@@ -1,3 +1,5 @@
from re import compile
from asyncio import Lock
import os, pwd, grp, sys, logging
from subprocess import call, run, DEVNULL, PIPE, STDOUT
from ..enums import UserType
@@ -227,3 +229,39 @@ def get_unprivileged_user() -> str:
user = 'deck'
return user
# Works around the CEF debugger TCP socket not closing properly when Steam restarts
# Group 1 is PID, group 2 is FD. this also filters for "steamwebhelper" in the process name.
cef_socket_lsof_regex = compile(r"^p(\d+)(?:\s|.)+csteamwebhelper(?:\s|.)+f(\d+)(?:\s|.)+TST=LISTEN")
close_cef_socket_lock = Lock()
async def close_cef_socket():
async with close_cef_socket_lock:
if _get_effective_user_id() != 0:
logger.warn("Can't close CEF socket as Decky isn't running as root.")
return
# Look for anything listening TCP on port 8080
lsof = run(["lsof", "-F", "-iTCP:8080", "-sTCP:LISTEN"], capture_output=True, text=True)
if lsof.returncode != 0 or len(lsof.stdout) < 1:
logger.error(f"lsof call failed in close_cef_socket! return code: {str(lsof.returncode)}")
return
lsof_data = cef_socket_lsof_regex.match(lsof.stdout)
if not lsof_data:
logger.error("lsof regex match failed in close_cef_socket!")
return
pid = lsof_data.group(1)
fd = lsof_data.group(2)
logger.info(f"Closing CEF socket with PID {pid} and FD {fd}")
# Use gdb to inject a close() call for the socket fd into steamwebhelper
gdb_ret = run(["gdb", "--nx", "-p", pid, "--batch", "--eval-command", f"call (int)close({fd})"], env={"LD_LIBRARY_PATH": ""})
if gdb_ret.returncode != 0:
logger.error(f"Failed to close CEF socket with gdb! return code: {str(gdb_ret.returncode)}", exc_info=True)
return
logger.info("CEF socket closed")
@@ -55,4 +55,7 @@ def get_unprivileged_user() -> str:
return os.getenv("UNPRIVILEGED_USER", os.getlogin())
async def restart_webhelper() -> bool:
return True # Stubbed
return True # Stubbed
async def close_cef_socket():
return # Stubbed
+4 -5
View File
@@ -7,14 +7,16 @@ from .localplatform.localplatform import (chmod, chown, service_stop, service_st
get_privileged_path, restart_webhelper)
if hasattr(sys, '_MEIPASS'):
chmod(sys._MEIPASS, 755) # type: ignore
# Full imports
import multiprocessing
multiprocessing.freeze_support()
from asyncio import AbstractEventLoop, CancelledError, Task, all_tasks, current_task, gather, new_event_loop, set_event_loop, sleep
from logging import basicConfig, getLogger
from os import path
from traceback import format_exc
import multiprocessing
import aiohttp_cors # pyright: ignore [reportMissingTypeStubs]
# Partial imports
from aiohttp import client_exceptions
from aiohttp.web import Application, Response, Request, get, run_app, static # pyright: ignore [reportUnknownVariableType]
@@ -224,9 +226,6 @@ def main():
# Fix windows/flask not recognising that .js means 'application/javascript'
import mimetypes
mimetypes.add_type('application/javascript', '.js')
# Required for multiprocessing support in frozen files
multiprocessing.freeze_support()
else:
if get_effective_user_id() != 0:
logger.warning(f"decky is running as an unprivileged user, this is not officially supported and may cause issues")
@@ -1,9 +1,9 @@
import sys
from os import path, environ
from signal import SIG_IGN, SIGINT, SIGTERM, getsignal, signal
from importlib.util import module_from_spec, spec_from_file_location
from json import dumps, loads
from logging import getLogger
from sys import exit, path as syspath, modules as sysmodules
from traceback import format_exc
from asyncio import (get_event_loop, new_event_loop,
set_event_loop)
@@ -13,7 +13,7 @@ from .messages import SocketResponseDict, SocketMessageType
from ..localplatform.localsocket import LocalSocket
from ..localplatform.localplatform import setgid, setuid, get_username, get_home_path
from ..enums import UserType
from .. import helpers
from .. import helpers, settings, injector # pyright: ignore [reportUnusedImport]
from typing import List, TypeVar, Any
@@ -78,12 +78,12 @@ class SandboxedPlugin:
environ["DECKY_PLUGIN_AUTHOR"] = self.author
# append the plugin's `py_modules` to the recognized python paths
syspath.append(path.join(environ["DECKY_PLUGIN_DIR"], "py_modules"))
sys.path.append(path.join(environ["DECKY_PLUGIN_DIR"], "py_modules"))
#TODO: FIX IN A LESS CURSED WAY
keys = [key for key in sysmodules if key.startswith("decky_loader.")]
keys = [key for key in sys.modules if key.startswith("decky_loader.")]
for key in keys:
sysmodules[key.replace("decky_loader.", "")] = sysmodules[key]
sys.modules[key.replace("decky_loader.", "")] = sys.modules[key]
from .imports import decky
async def emit(event: str, *args: Any) -> None:
@@ -95,9 +95,9 @@ class SandboxedPlugin:
# copy the docstring over so we don't have to duplicate it
emit.__doc__ = decky.emit.__doc__
decky.emit = emit
sysmodules["decky"] = decky
sys.modules["decky"] = decky
# provided for compatibility
sysmodules["decky_plugin"] = decky
sys.modules["decky_plugin"] = decky
spec = spec_from_file_location("_", self.file)
assert spec is not None
@@ -123,7 +123,7 @@ class SandboxedPlugin:
get_event_loop().create_task(socket.setup_server())
except:
self.log.error("Failed to start " + self.name + "!\n" + format_exc())
exit(0)
sys.exit(0)
try:
get_event_loop().run_forever()
except SystemExit:
@@ -180,7 +180,7 @@ class SandboxedPlugin:
loop = get_event_loop()
loop.call_soon_threadsafe(loop.stop)
exit(0)
sys.exit(0)
d: SocketResponseDict = {"type": SocketMessageType.RESPONSE, "res": None, "success": True, "id": data["id"]}
try:
+6 -2
View File
@@ -20,9 +20,8 @@ from .browser import PluginInstallRequest, PluginInstallType
if TYPE_CHECKING:
from .main import PluginManager
from .injector import inject_to_tab, get_gamepadui_tab, close_old_tabs, get_tab
from .localplatform.localplatform import ON_WINDOWS
from . import helpers
from .localplatform.localplatform import service_stop, service_start, get_home_path, get_username
from .localplatform.localplatform import ON_WINDOWS, service_stop, service_start, get_home_path, get_username, get_use_cef_close_workaround, close_cef_socket
class FilePickerObj(TypedDict):
file: Path
@@ -78,6 +77,7 @@ class Utilities:
context.ws.add_route("utilities/get_tab_id", self.get_tab_id)
context.ws.add_route("utilities/get_user_info", self.get_user_info)
context.ws.add_route("utilities/http_request", self.http_request_legacy)
context.ws.add_route("utilities/close_cef_socket", self.close_cef_socket)
context.ws.add_route("utilities/_call_legacy_utility", self._call_legacy_utility)
context.web_app.add_routes([
@@ -287,6 +287,10 @@ class Utilities:
await service_stop(helpers.SSHD_UNIT)
return True
async def close_cef_socket(self):
if get_use_cef_close_workaround():
await close_cef_socket()
async def filepicker_ls(self,
path: str | None = None,
include_files: bool = True,
+5 -4
View File
@@ -1,5 +1,6 @@
// import reloadFix from './reload';
import restartFix from './restart';
// import restartFix from './restart';
import cefSocketFix from './socket';
let fixes: Function[] = [];
export function deinitSteamFixes() {
@@ -7,6 +8,6 @@ export function deinitSteamFixes() {
}
export async function initSteamFixes() {
// fixes.push(await reloadFix());
fixes.push(await restartFix());
fixes.push(cefSocketFix());
// fixes.push(await restartFix());
}
+16
View File
@@ -0,0 +1,16 @@
import Logger from '../logger';
const logger = new Logger('CEFSocketFix');
const closeCEFSocket = DeckyBackend.callable<[], void>('utilities/close_cef_socket');
export default function cefSocketFix() {
const reg = window.SteamClient?.User?.RegisterForShutdownStart(async () => {
logger.log('Closing CEF socket before shutdown');
await closeCEFSocket();
});
if (reg) logger.debug('CEF shutdown handler ready');
return () => reg?.unregister();
}