From 4c3dbbd14160567e862ecba467343f68b4361b5b Mon Sep 17 00:00:00 2001 From: CyberMask367 Date: Thu, 15 Oct 2026 23:18:55 +0100 Subject: [PATCH] updated Documentation for 1.0.0 release Refresh the install and build guides for the Setup PKG workflow, and remove the obsolete evict helper and its references. --- README.md | 133 +++++++++++++++++------- docs/BUILDING.md | 46 ++++++++- docs/INSTALLER.md | 155 +++++++++++++++++----------- docs/PRODUCTION.md | 5 +- docs/research/proc-table-offsets.md | 7 +- scripts/build_evict.sh | 24 ----- tools/evict.c | 105 ------------------- 7 files changed, 235 insertions(+), 240 deletions(-) delete mode 100644 scripts/build_evict.sh delete mode 100644 tools/evict.c diff --git a/README.md b/README.md index a3c707c..92f8561 100644 --- a/README.md +++ b/README.md @@ -86,45 +86,62 @@ Details: [`docs/DAEMON.md`](docs/DAEMON.md) · full index at [`docs/`](docs/) ## Install (5 minutes) -> ⚠️ **No PKG — beta is `.elf` only.** Grab the latest test build -> (`orbisrpc_test-build-*.elf`) from the Discord -> (**[discord.gg/BWEyfcT7ZQ](https://discord.gg/BWEyfcT7ZQ)**). This is still -> a test build, not a full release — expect bugs, report them in -> `testers-chat`. +> ✅ **Released as 1.0.0.** Grab `orbisRPC-1.0.0.pkg` from +> [releases](https://github.com/SirHumza/orbisRPC/releases/latest). -1. Before launching, delete the `/data/orbisRPC` folder from your PS4 if - you have one there from an earlier build. -2. Put the `.elf` on the console at `/data/payloads/` (FTP it over, or - USB) — that's where GoldHEN looks for payloads. -3. Launch it from the payload list. First run creates the config file, - then says there's no token yet. -4. Open `/data/orbisRPC/config.json` and add your account token in the - `"token": "SET_ME"` slot. Don't know where the token comes from? - Follow [docs/TOKEN_SETUP.md](docs/TOKEN_SETUP.md) — wrong tokens are +1. Install the PKG on the PS4. +2. Open **orbisRPC Setup** from the home screen. +3. Pick **INSTALL / UPDATE**. It copies the payload into + `/data/payloads/`, writes your config, and offers to add orbisRPC to the + GoldHEN auto-run queue so it starts on every boot. It also offers to add a + Sony TMDB exception to [nanoDNS](https://github.com/peterrsr/nanoDNS) if + you run it — skip either prompt if you don't want it. +4. Enter your Discord token when asked. Don't know where the token comes + from? Follow [docs/TOKEN_SETUP.md](docs/TOKEN_SETUP.md) — wrong tokens are the #1 install failure. -5. After setting the token, re-run the payload from `/data/payloads`. - Launch a game and watch Discord. Give feedback in `testers-chat`. +5. Restart the console (or re-jailbreak). Launch a game and watch Discord. -After a reboot just re-jailbreak and launch the payload from -`/data/payloads` again. +After that the daemon starts on boot. The app's **UNINSTALL** entry removes +the payload and your configs again. -**Upgrading test builds:** as usual, delete the `/data/orbisRPC` folder -before using a new test build — and you'll have to re-add your token in -the config. +
+Installing the payload by hand instead -**Auto-run (not recommended on test builds):** you can drop the `.elf` in -`/data/payloads` and add it to the autorun queue in GoldHEN settings, but -test builds change fast — stick to manual runs for now. +You can skip the PKG and run the daemon yourself: -**Official release:** will ship a PKG installer that sets everything up -for you — including autorun and the nanoDNS exception fix. +1. Delete the `/data/orbisRPC` folder from your PS4 if you have one from an + earlier build. +2. Put `orbisRPC.elf` on the console at `/data/payloads/` (FTP it over, or + USB) — that's where GoldHEN looks for payloads. +3. Launch it from the payload list. First run creates the config file, then + says there's no token yet. +4. Open `/data/orbisRPC/config.json` and put your account token in the + `"token": "SET_ME"` slot. +5. Re-run the payload from `/data/payloads`. Launch a game and watch Discord. + +The installer is the better path — it does the token, the auto-run entry and +the nanoDNS exception for you — but the manual route works, and it's what you +want if you'd rather not install an app. + +To start on boot by hand, add `orbisRPC.elf` to the auto-run queue in GoldHEN +settings. + +
✅ Firmware: confirmed working on 9.00 through 13.52 but older fimwares should work. -### What's in the latest test build (0.83) +### What's new in 1.0.0 -- Added a community custom list on github to resolve for apps or homebrew that are not on pkg-zone (everyone can contribute to the list). -- dropped the payload bin validation path. +- **orbisRPC Setup** — a real installer app with install/update, settings and + uninstall. Handles the token, GoldHEN auto-run, and the nanoDNS TMDB + exception. +- **In-app update check** — both the app and the daemon notice a newer + release and tell you. Neither replaces the payload on its own; the installer + is what does that. +- **Community title list** — [`config/custom.json`](config/custom.json) + resolves apps and homebrew pkg-zone doesn't carry. Anyone can contribute by + opening a pull request. +- Dropped the payload `.bin` validation path. ## What you'll see @@ -231,12 +248,38 @@ nanoDNS — then reboot. (This is the step most people miss.) ## Building +Two toolchains are involved. Point them at yours once per shell: + ```bash -./scripts/build_sdk_from_source.sh # SDK from git (REQUIRED - see note) -PS4_SDK_SRC=~/ps4-payload-sdk-src \ -PS4_PAYLOAD_SDK=~/ps4-payload-sdk ./scripts/build_sdk.sh # daemon payload -make -f installer/Makefile # Setup PKG (OpenOrbis toolchain, llvmshim) -make -C tests test # host unit tests +export OO_PS4_TOOLCHAIN="$HOME/OpenOrbis-PS4-Toolchain" # installer app +export PS4_PAYLOAD_SDK="$HOME/ps4-payload-sdk" # daemon payload +export PS4_SDK_SRC="$HOME/ps4-payload-sdk-src" # 13.52 firmware gate +``` + +Build the PS4 SDK from git once, before anything else: + +```bash +./scripts/build_sdk_from_source.sh +``` + +Then, from the repo root: + +| Command | Builds | Output | +|---|---|---| +| `make all` | payload → stage into the app → package | `output/orbisRPC-1.0.0.elf` + `output/orbisRPC-1.0.0.pkg` | +| `make payload` | daemon payload only | `output/orbisRPC-1.0.0.elf` | +| `make installer` | Setup PKG only | `output/orbisRPC-1.0.0.pkg` | +| `make elf` | compile + link the app, no packaging | — | +| `make gfx` | artwork only (PNG → BMP) | — | +| `make clean` | remove build output | — | + +`make all` stages the freshly built payload into `installer/assets/` before +packaging, so the PKG never ships a stale ELF. + +Host-side checks, no PS4 toolchain needed: + +```bash +make -C tests test # unit tests make -C tests asan # ASan/UBSan python3 tests/e2e_consumer.py # contracts + linkage gate ``` @@ -244,11 +287,29 @@ python3 tests/e2e_consumer.py # contracts + linkage gate ⚠️ **Build the SDK from git, not the release ZIP.** The SDK's CRT refuses to start on firmware it does not list, and terminates the payload before `main()` with no log line. The released SDK (v0.9) lists 13.50 but not 13.52, so a -ZIP-built payload cannot boot on 13.52. See -[`docs/research/sdk-13.52.md`](docs/research/sdk-13.52.md). +ZIP-built payload cannot boot on 13.52. `PS4_SDK_SRC` is what enables the +build-time check for this; without it the build still succeeds and dies at +runtime. See [`docs/research/sdk-13.52.md`](docs/research/sdk-13.52.md). +Installer app internals: [`docs/INSTALLER.md`](docs/INSTALLER.md). Build/test/CI details: [`docs/BUILDING.md`](docs/BUILDING.md). +### Adding a title to the community list + +Names for apps pkg-zone doesn't carry live in +[`config/custom.json`](config/custom.json). Add one with: + +```bash +python3 scripts/add_custom_title.py # asks for TITLEID, name, cover URL +python3 scripts/add_custom_title.py --list +python3 scripts/add_custom_title.py --remove ORPC00042 +``` + +Then open a pull request. **The file must stay compact** — `"id":"ORPC00042"`, +no space after the colon. The payload scans for that exact byte run, so +pretty-printing the file makes every lookup miss silently. The script writes it +correctly for you. + ## Developing - CI runs host tests, ASan, contract checks, the SDK payload build with a diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 130f237..56f8822 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -2,23 +2,59 @@ ## Requirements -- macOS or Linux with `clang-18`, `lld-18`, `llvm-18`. +- Linux or macOS with `clang-18`, `lld-18`, `llvm-18`. +- The [OpenOrbis PS4 toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain) + for the Setup app — `OO_PS4_TOOLCHAIN`. - The ps4-payload-sdk **built from git** — never the release ZIP. The SDK's CRT gates startup on a per-firmware table (`crt/patch.c`), and the v0.9 ZIP predates the 13.52 offsets, so a ZIP-built payload dies before `main()` with no log line. Full story: [research/sdk-13.52.md](research/sdk-13.52.md). +```bash +export OO_PS4_TOOLCHAIN="$HOME/OpenOrbis-PS4-Toolchain" +export PS4_PAYLOAD_SDK="$HOME/ps4-payload-sdk" +export PS4_SDK_SRC="$HOME/ps4-payload-sdk-src" +./scripts/build_sdk_from_source.sh # once +``` + +## Root `Makefile` + +| Target | Builds | Output | +|---|---|---| +| `make all` | payload → stage into `installer/assets/` → package | `output/orbisRPC-.elf` + `output/orbisRPC-.pkg` | +| `make payload` | daemon payload only | `output/orbisRPC-.elf` | +| `make installer` | Setup PKG only | `output/orbisRPC-.pkg` | +| `make pkg` | stage + package, no payload build | — | +| `make elf` | app compile + link, no packaging | — | +| `make gfx` | `installer/artwork/*.png` → `assets/images/*.bmp` | — | +| `make clean` | remove build output, including `output/` | — | + +`payload` needs the ps4 SDK; `installer` and everything else need +`OO_PS4_TOOLCHAIN`. `all` needs both, so it is the one target that requires +the full environment. + +The payload name inside the package is derived from `#define PAYLOAD_NAME` in +`installer/paths.h`, and the same string is what `pkg.gp4` lists — so the +staged copy, the packaged file, and the console destination cannot drift apart. +Staging fails loudly if any other `.elf` is sitting in `installer/assets/`, +because `pkg.gp4` is checked in and a stale entry there would ship on every +future build. + +App internals: [INSTALLER.md](INSTALLER.md). + ## Scripts (`scripts/`) | Script | What | |---|---| | `build_sdk_from_source.sh` | Clones `ps4-payload-dev/sdk`, pins the ref, refuses unless `crt/patch.c` has the firmware cases, `make DESTDIR=… clean install`. Run once. | -| `build_sdk.sh` | Compiles the daemon (`orbisrpc/*.c` + vendored sqlite + mbedTLS) and links `build-sdk/orbisrpc_sdk.elf` with daemon-world libs only. Needs `PS4_PAYLOAD_SDK` (and `PS4_SDK_SRC` for the firmware gate). | -| `build_evict.sh` | Builds `tools/evict.c` → `build-sdk/evict.elf`. Hot-swap helper: stops a running daemon without rebooting. Not needed for the reboot-based tester flow. | -| `build.sh` | Legacy OpenOrbis flavor. Not the shipping path. | +| `build_sdk.sh` | Compiles the daemon (`orbisrpc/*.c` + vendored sqlite + mbedTLS) and links `build-sdk/orbisrpc_sdk.elf` with daemon-world libs only. Needs `PS4_PAYLOAD_SDK` (and `PS4_SDK_SRC` for the firmware gate). Called by `make payload`. | +| `build_pkg.sh` | Wrapper that exports `OO_PS4_TOOLCHAIN` and runs `make`. | +| `gfx2bmp.py` | Artwork PNG → BMP, used by `make gfx`. | +| `gen_icon_header.py` | Regenerates `orbisrpc/icon_embedded.h` from the notification icon. | +| `add_custom_title.py` | Adds/removes entries in `config/custom.json`. | +| `build.sh` | Legacy OpenOrbis flavor of the payload build. Not the shipping path. | | `make_ca_bundle.py` | Regenerates the curated CA bundle the daemon pins TLS against. | -| `make_manifest.py` | Update-manifest tooling for the signed self-update path. | ## The one rule diff --git a/docs/INSTALLER.md b/docs/INSTALLER.md index 1f01c3e..98a6339 100644 --- a/docs/INSTALLER.md +++ b/docs/INSTALLER.md @@ -1,80 +1,109 @@ -# Installer (Setup PKG, `ORPC00001`) +# Installer (orbisRPC Setup, `ORPC00001`) -> Status: design + code exist in `installer/`, but **no PKG ships with -> test builds** — testers install the `.elf` directly (see repo README). -> The PKG arrives with the official release and will also set up autorun -> and the nanoDNS exception. +The PS4 side ships as one app: **orbisRPC Setup**. It installs the daemon +payload, holds the settings UI, updates, and uninstalls. Title ID `ORPC00001`, +content ID `IV0000-ORPC00001_00-ORBISRPCSETUP000`. -## What it does +Source lives in [`installer/`](../installer), built by the root +[`Makefile`](../Makefile). -One linear flow, forward-only (every No skips ahead, nothing loops back): -confirm → evict old daemon (via `sceKernelLoadStartModule`) → copy -`evict.elf` + `orbisrpc.bin` to `/data/payloads/` (mkdir -p + -byte-count + FNV hash read-back) → pre-saved config with token -(skips entry when valid) → done. -Read-only status screen available via decline. +## The menu -There is no WiFi check. The installer runs sandboxed, so its socket probe -measures the app's network stack, not the payload's — and a FAILED line -reads like a verdict on Discord itself. The daemon reports real reachability -in its own log once started. +| Entry | What it does | +|---|---| +| **INSTALL / UPDATE** | Installs or updates the daemon payload and config | +| **SETTINGS** | Presence text, toggles, preferences — writes `config.json` | +| **UNINSTALL** | Removes the payload and the configs from the console | -The installer never boots anything directly. Starting the daemon is -the `/data/payloads/` directory — pick `evict.elf` first -(removes old orbisrpc instance), then pick `orbisrpc.bin`. -No loopback ports, no injection, no boot proof to go wrong. +## What it writes + +All paths from [`installer/paths.h`](../installer/paths.h). The payload name is +`orbisRPC.elf` everywhere — packaged as `assets/orbisRPC.elf`, installed to +`/data/payloads/orbisRPC.elf`. + +| Path | What | +|---|---| +| `/data/payloads/orbisRPC.elf` | the daemon payload | +| `/data/payloads/orbisRPC.elf.png` | icon for the GoldHEN launch row | +| `/data/orbisRPC/config.json` | config, including the Discord token | +| `/data/orbisRPC/version` | installed version marker | +| `/data/orbisRPC/install.log` | install-step log | +| `/data/orbisRPC/net.log` | connectivity log | +| `/data/orbisRPC/icon.png` | icon | +| `/data/GoldHEN/payloads.ini` | auto-run queue entry | +| `/data/nanodns/nanodns.ini` | optional Sony TMDB exception | +| `/data/temp/` | staging for downloads (`orbisRPC.elf`, `config.json`) | ## Install flow ``` -confirm → sceKernelLoadStartModule(evict.elf) — kills old daemon - → copy evict.elf to /data/payloads/evict.elf - → copy orbisrpc.bin to /data/payloads/orbisrpc.bin - → save config.json with Discord token (pre-populated) - → (token entry skipped if already valid) - → done +already installed? -> yes: report and stop (uninstall first) + \-> no: +GitHub release newer than local? -> yes: download payload + config to /data/temp/ +preflight: /app0/assets/orbisRPC.elf present? -> no: stop with "missing its payload" +copy payload -> /data/payloads/orbisRPC.elf +copy icon -> /data/payloads/orbisRPC.elf.png +write config -> /data/orbisRPC/config.json (atomic: tmp + fsync + rename) +write version -> /data/orbisRPC/version +ask: GoldHEN present? -> add /user/data/payloads entry + payloads.ini +ask: nanoDNS present? -> add tmdb.np.dl.playstation.net exception +restart prompt if an update replaced a running payload ``` -The `evict.elf` is launched via `sceKernelLoadStartModule` during -install — it reads `daemon.lock`, kills the running daemon, exits. -`evict.elf` stays in `/data/payloads/` for future manual use. - -## Navigation law - -No branch ever returns to start. Cancel/skip always moves forward. The only -exits are Done, explicit close, or a payload write failure. - -Buttons: X = enter, O = back. The confirm dialog uses `YESNO` focused on -Yes — the OpenOrbis default `YESNO_FOCUS_NO` puts the console's confirm -button on No, which inverts the whole wizard. +Both the GoldHEN and nanoDNS prompts are **opt-in**, **idempotent** (an entry +that is already present reports "Nothing was changed"), and **skipped silently** +when that tool isn't installed. Neither is required — the daemon works without +them. ## Console facts encoded -- Splash is hidden first thing in `main()` so module loading in - `ui_init()` happens with the splash already gone — no visible delay - between splash hide and first dialog. -- Dialog/IME/IME-backend sysmodules + internal SYSTEM/USER/COMMON/PAD - modules load before use (order matters — `sceCommonDialogInitialize()` - precedes external module loads; PAD is unloaded on exit). -- `stat()`/`fstat()` lie about sizes inside the sandbox (observed 4096/4160 - for a 2071552-byte file), so install proof is byte count + FNV hash on - read-back, and status uses open-existence, never stat. -- Config writes are atomic (tmp+fsync+rename) with read-back proof. -- IME wait is bounded; asset key charset validated (bad keys blank the - activity). -- `evict.elf` is loaded via `sceKernelLoadStartModule` because `kill()` - is blocked by the sandbox (EPERM). - -## Auto-start - -GoldHEN AutoRun: enable it for `orbisrpc` once and the daemon -starts on every jailbreak. The queue is menu-managed; the installer -shows where, it can't write the queue itself. +- `stat()`/`fstat()` lie about sizes inside the sandbox (observed 4096/4160 for + a 2 MB file), so install proof is byte count + FNV hash on read-back, and + existence checks use `open()`, never `stat()`. +- Config writes are atomic (tmp + fsync + rename) with read-back proof. +- The splash is hidden first thing in `main()` so sysmodule loading happens with + no visible gap before the first dialog. +- Dialog / IME / IME-backend sysmodules and the internal SYSTEM, USER, COMMON + and PAD modules load before use. Order matters: + `sceCommonDialogInitialize()` precedes the external module loads. PAD is + unloaded on exit. +- Buttons: X = enter, O = back. The confirm dialog uses `YESNO` focused on Yes — + OpenOrbis's `YESNO_FOCUS_NO` default puts the console's confirm button on No, + which inverts the whole wizard. +- No branch returns to start; cancel and skip always move forward. +- `updatecheck` hits the GitHub releases API. It reports a newer version and + downloads to `/data/temp/`; the daemon separately only *notifies* and never + replaces itself. ## Building -`make -f installer/Makefile` (`OO_PS4_TOOLCHAIN`, llvmshim). Staged assets: -`daemon.elf`, `evict.elf`, `config.json`. The PKG ships all three — -the installer copies both payloads, launches evict.elf to kill the -old daemon, and pre-saves the config token. -Output: `IV0000-ORPC00001_00-ORBISRPCSETUP000.pkg`. +```bash +export OO_PS4_TOOLCHAIN="$HOME/OpenOrbis-PS4-Toolchain" +make installer # or `make all` for payload + PKG +``` + +| Target | Output | +|---|---| +| `make all` | `output/orbisRPC-.elf` + `output/orbisRPC-.pkg` | +| `make installer` | `output/orbisRPC-.pkg` | +| `make elf` | compile + link only, no packaging | +| `make gfx` | `installer/artwork/*.png` → `installer/assets/images/*.bmp` | + +The version comes from `VERSION` at the top of the [`Makefile`](../Makefile), +and is stamped into the output filenames. + +`make installer` stages `build-sdk/orbisrpc_sdk.elf` into +`installer/assets/orbisRPC.elf` first — run `make payload` (or `make all`) at +least once beforehand, or staging fails with a clear error. Staging refuses to +run if any *other* `.elf` is sitting in `installer/assets/`, because `pkg.gp4` +is a checked-in file and a stray entry there would ship forever. + +Artwork: PNGs live in `installer/artwork/`, converted to BMP by +`scripts/gfx2bmp.py`. `make gfx` is enough after changing one. + +## Uninstall + +The **UNINSTALL** entry removes the payload and its icon from +`/data/payloads/`, the GoldHEN auto-run entry, the nanoDNS exception, and +`/data/orbisRPC/`. It leaves the app itself installed — remove the PKG +afterwards if you want it gone. \ No newline at end of file diff --git a/docs/PRODUCTION.md b/docs/PRODUCTION.md index f72e38e..53915a2 100644 --- a/docs/PRODUCTION.md +++ b/docs/PRODUCTION.md @@ -25,7 +25,6 @@ libkernel.so). Running on-console at 192.168.1.136 via elfldr:9021. gate, safe mode, signed all-or-nothing staging, boot rollback. - `cfg.c`/`lock.c`/`timesync.c` — schema'd fallible config, sysctl-liveness single instance, SNTP wall clock for timer ms. -- `tools/evict.c` — SIGTERM-then-SIGKILL deploy rotation for the locked daemon. ## State machine @@ -50,7 +49,7 @@ Sandbox param.sfo source, details-ID duplication, user `titles` overrides, `home_art`, shipped logo default, app.db SQLite names, self-learning map, presence-builder test seam, small badge, strncpy NUL hardening, home_art validation, 4004 survival, cfg_save return, -evict.elf deploy tool, CI ASan+e2e jobs. +CI ASan+e2e jobs. ## Not fixed (external / out of scope) @@ -70,7 +69,7 @@ evict.elf deploy tool, CI ASan+e2e jobs. ## Hardware matrix (proven) Gateway ready, TLS-ECDHE suite, game detection + ticking timer post-SNTP, -mp: art serving (phone), duplicate-ID gone, evict clean-stop rotations, +mp: art serving (phone), duplicate-ID gone, reboot recovery. Pending eyes: appdb name flip, badge render, logo tile. ## Readiness diff --git a/docs/research/proc-table-offsets.md b/docs/research/proc-table-offsets.md index c2a44d8..c544a8f 100644 --- a/docs/research/proc-table-offsets.md +++ b/docs/research/proc-table-offsets.md @@ -89,7 +89,6 @@ process table and now use `procwalk.c`, which keeps the self-describing position: - `lock.c` — single-instance guard -- `tools/evict.c` — stop a running daemon before replacing it Measured, old logic vs `procwalk.c`: @@ -99,9 +98,9 @@ Measured, old logic vs `procwalk.c`: | record 736 B, name @611 | **0** | 1 | | record 520 B, name @120 | **0** | 1 | -A hit is corroboration, not proof: records are zero-padded, so both callers -reject `pid <= 0` before searching, and their failure biases are opposite -(`lock.c` assumes a peer on an unreadable table; `evict.c` refuses to kill). +A hit is corroboration, not proof: records are zero-padded, so the caller +rejects `pid <= 0` before searching, and its failure bias is to assume a peer +holds the lock (an unreadable table means stand down, not that the peer died). ## Cost of the change diff --git a/scripts/build_evict.sh b/scripts/build_evict.sh deleted file mode 100644 index 3a4cc5b..0000000 --- a/scripts/build_evict.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# build_evict.sh - evict.elf via ps4-payload-sdk (daemon-world linkage only). -# Usage: PS4_PAYLOAD_SDK=/path ./scripts/build_evict.sh -set -e -SDK="${PS4_PAYLOAD_SDK:-$HOME/ps4-payload-sdk/ps4-payload-sdk}" -CC="$SDK/bin/orbis-clang" -export PS4_PAYLOAD_SDK="$SDK" -export PATH="$HOME/llvmshim:$PATH" -OUT="build-sdk" -mkdir -p "$OUT" -if [ ! -x "$CC" ]; then - echo "FAIL: $CC not found. Build the SDK from git first:" - echo " ./scripts/build_sdk_from_source.sh" - exit 1 -fi -echo "=== evict (SDK) ===" -# evict.c shares orbisrpc/procwalk.c with the daemon so the kinfo_proc record -# walk stays offset-free in one place. A private second copy in evict.c is -# exactly how the 9.00-only +447/+72 offsets survived there in the first place. -"$CC" -O2 -Wall -DORBISRPC_SDK_PAYLOAD -Iorbisrpc -c -o "$OUT/procwalk_evict.o" orbisrpc/procwalk.c \ - || { echo "FAIL: procwalk"; exit 1; } -"$CC" -O2 -Wall -DORBISRPC_SDK_PAYLOAD -Iorbisrpc -o "$OUT/evict.elf" \ - tools/evict.c "$OUT/procwalk_evict.o" || { echo "FAIL: evict"; exit 1; } -ls -la "$OUT/evict.elf" diff --git a/tools/evict.c b/tools/evict.c deleted file mode 100644 index e2f13c3..0000000 --- a/tools/evict.c +++ /dev/null @@ -1,105 +0,0 @@ -/* evict.c - stop a running orbisRPC daemon so a fresh payload can take over. - * Reads /data/orbisRPC/daemon.lock, verifies the holder is a live "Payload" - * process via the same offset-free sysctl walk as lock.c/procwalk.c, then - * SIGTERM (clean stop: banks session, clears presence, releases lock) with a - * SIGKILL fallback. Refuses to signal anything it cannot confirm is a live - * Payload peer. - * Build: PS4_PAYLOAD_SDK=... ./scripts/build_evict.sh -> build-sdk/evict.elf - * Run: send via elfldr:9021 or GoldHEN BinLoader:9020, then send the fresh - * orbisrpc_sdk.elf the same way. - * Result: /data/orbisRPC/evict.txt (+ klog). Takes up to ~34 s: 24 x 1 s - * waiting for a clean stop before the SIGKILL fallback. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../orbisrpc/procwalk.h" - -#define LOCK_PATH "/data/orbisRPC/daemon.lock" -#define RESULT_PATH "/data/orbisRPC/evict.txt" - -static void report(const char *msg){ - printf("evict: %s\n", msg); - int fd = open(RESULT_PATH, O_CREAT|O_TRUNC|O_WRONLY, 0644); - if(fd >= 0){ (void)write(fd, msg, strlen(msg)); (void)write(fd, "\n", 1); close(fd); } -} - -/* 1 = pid is a live "Payload" process, 0 otherwise. Mirrors lock.c. - * - * This used to read the name at byte 447 and the pid at byte 72 of each - * kinfo_proc record. Those are 9.00 offsets; on newer firmware they address - * the wrong bytes, so every live payload looked dead and evict would delete - * the lock and report STALE while the daemon kept running -- leaving exactly - * the two-daemon pileup it exists to prevent. - * - * Narrow by exact process name first, then search the record for the pid. - * Unlike lock.c the bias is the other way here: this gates a kill(), so an - * unreadable table must report "not live" and let the caller do nothing. */ -static int payload_live(int pid){ - if(pid <= 0 || pid == (int)getpid()) return 0; - int mib[4] = { 1, 14, 8, 0 }; - size_t sz = 0; - if(sysctl(mib, 4, NULL, &sz, NULL, 0) != 0) return 0; - static unsigned char buf[256*1024]; - if(sz > sizeof buf) return 0; /* unreadable table: fail closed, do not kill */ - if(sysctl(mib, 4, buf, &sz, NULL, 0) != 0) return 0; - const unsigned char *rec = NULL; - size_t recsz = 0; - int rc = procwalk_find_comm(buf, sz, "Payload", &rec, &recsz); - if(rc != 0) return 0; /* malformed, or no Payload process at all */ - return procwalk_record_has_i32(rec, recsz, pid); -} - -int main(void){ - char msg[128]; - int fd = open(LOCK_PATH, O_RDONLY); - if(fd < 0){ report("NO_LOCK nothing running"); return 0; } - char b[32]; ssize_t n = read(fd, b, sizeof b - 1); close(fd); - if(n <= 0){ report("LOCK_UNREADABLE"); return 1; } - b[n] = 0; - int pid = 0; - if(sscanf(b, "%d", &pid) != 1 || pid <= 0){ - remove(LOCK_PATH); - report("LOCK_GARBAGE removed"); - return 0; - } - if(!payload_live(pid)){ - remove(LOCK_PATH); - snprintf(msg, sizeof msg, "STALE holder=%d not a live Payload; lock removed", pid); - report(msg); - return 0; - } - if(kill(pid, SIGTERM) != 0 && errno == ESRCH){ - remove(LOCK_PATH); - report("HOLDER_GONE lock removed"); - return 0; - } - /* Grace period: clean stop banks session + releases lock. */ - for(int i = 0; i < 24; i++){ - sleep(1); - if(!payload_live(pid)){ - remove(LOCK_PATH); - snprintf(msg, sizeof msg, "EVICTED holder=%d clean stop", pid); - report(msg); - return 0; - } - } - /* Wedged (e.g. stuck in blocking connect): SIGKILL fallback. */ - (void)kill(pid, SIGKILL); - for(int i = 0; i < 10; i++){ - sleep(1); - if(!payload_live(pid)){ - remove(LOCK_PATH); - snprintf(msg, sizeof msg, "EVICTED holder=%d SIGKILL fallback", pid); - report(msg); - return 0; - } - } - snprintf(msg, sizeof msg, "STUCK holder=%d still alive; reboot console", pid); - report(msg); - return 2; -}