Injectors: no re-injection once the unpatch has started

On SIGTERM the periodic (and debounced) re-injection of injector.mjs and
helper.mjs could still run while or after the unpatch was evaluated, leaving
the page patched after the service was gone (e.g. a feature just turned off).
helper.mjs also gets a timeout on /json/list, like the injector.
This commit is contained in:
Pierre Kisters committed 2026-09-29 01:32:31 +02:00
1 parent b0131338de
commit b942352663
2 files changed
+8 -2

No files matched your search

+5 -1
View File
@@ -11,6 +11,7 @@ const [, , patchPath, unpatchPath, xdotool = 'xdotool'] = process.argv;
const PATCH = readFileSync(patchPath, 'utf8');
const UNPATCH = readFileSync(unpatchPath, 'utf8');
let current = null; // CDP `call` of the live session, if any
let stopping = false;
const CDP = 'http://127.0.0.1:8080/json/list';
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// :0 is the Steam session's Xwayland, where the VR app windows live.
@@ -64,7 +65,7 @@ function handle(msg) {
}
async function session() {
const list = await (await fetch(CDP)).json();
const list = await (await fetch(CDP, { signal: AbortSignal.timeout(3000) })).json();
const t = list.find((x) => x.title === 'SharedJSContext');
if (!t) throw new Error('SharedJSContext not found');
const ws = new WebSocket(t.webSocketDebuggerUrl);
@@ -75,6 +76,7 @@ async function session() {
});
let last; // logged when it changes (e.g. a signature error once, not every 15 s)
const inject = async () => {
if (stopping) return; // never re-patch after the unpatch started
const r = await call('Runtime.evaluate', { expression: PATCH, returnByValue: true });
const v = r.result?.result?.value ?? r.result?.exceptionDetails?.exception?.description;
if (v !== last || process.env.VRKBD_VERBOSE) console.log('inject:', v);
@@ -114,6 +116,8 @@ async function session() {
}
for (const sig of ['SIGTERM', 'SIGINT']) process.on(sig, async () => {
if (stopping) return;
stopping = true;
releaseAll();
if (current) {
const r = await Promise.race([current('Runtime.evaluate', { expression: UNPATCH, returnByValue: true }), sleep(2000)]);
+3 -1
View File
@@ -20,7 +20,8 @@
// Patches must be idempotent expressions; their (awaited) result value is
// logged when it changes, e.g. "patched" once, then "unchanged" silently.
// On SIGTERM/SIGINT every live session evaluates its patches' unpatch
// expressions (if any) (each with a timeout), then the process exits.
// expressions (if any, each with a timeout) and injects nothing after that,
// then the process exits.
//
// Persistent state ("state": true): one JSON value per patch, kept in
// $XDG_STATE_HOME/steam-frame-nix/ui-patches/<name>.json (default
@@ -118,6 +119,7 @@ function open(endpoint, target, list) {
const evaluate = (expression) => call('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true });
const inject = async () => {
for (const p of list) {
if (stopping) return; // never re-patch after the unpatch started
if (p.state) await evaluate(prelude(p.name));
const v = String(value(await evaluate(p.patch)));
if (last.get(p.name) !== v && v !== 'unchanged') console.log(`${tag} ${p.name}: ${v}`);