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;
-}