mirror of
https://github.com/SirHumza/orbisRPC.git
synced 2026-10-06 06:00:35 +02:00
god-tier: diag census, v1.1.0, arch docs, CI version gate
- daemon writes diag.json at boot (fw, kinfo-table hit, msgbuf / sandbox / shellcore reachability) for the firmware matrix - focus_msgbuf_ok() reachability probe + host test - version 1.1.0 (version.h + installer Makefile) - ARCHITECTURE.md: binaries, detection cascade, gateway states, no-exit policy, on-disk state, installer, compat baseline - CI consistency job: version.h vs Makefile parity gate
This commit is contained in:
1 parent
e26d25c47a
commit
43246c3ab3
8 files changed
+164
-4
No files matched your search
@@ -38,3 +38,14 @@ jobs:
|
||||
echo "$NEEDED" | grep -q libSceNet.sprx || { echo "missing libSceNet"; exit 1; }
|
||||
if echo "$NEEDED" | grep -q 'libkernel\.so'; then echo "FORBIDDEN: app-world libkernel.so"; exit 1; fi
|
||||
echo "linkage OK"
|
||||
consistency:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: version parity (version.h vs installer Makefile)
|
||||
run: |
|
||||
V_H=$(grep -oE '#define ORBISRPC_VERSION "[^"]+"' orbisrpc/version.h | cut -d'"' -f2)
|
||||
V_MK=$(grep -oE '^VERSION[[:space:]]*:=.*' installer/Makefile | sed 's/.*:= *//')
|
||||
echo "version.h=$V_H Makefile=$V_MK"
|
||||
[ -n "$V_H" ] && [ "$V_H" = "$V_MK" ] || { echo "VERSION MISMATCH"; exit 1; }
|
||||
echo "versions match"
|
||||
@@ -0,0 +1,86 @@
|
||||
# orbisRPC Architecture
|
||||
|
||||
Discord Rich Presence for jailbroken PS4. No PC at runtime: the daemon
|
||||
talks to the Discord gateway directly.
|
||||
|
||||
## Binaries
|
||||
|
||||
| Binary | Built by | Runs as |
|
||||
|---|---|---|
|
||||
| `orbisrpc.elf` | `scripts/build.sh` (OpenOrbis) | BinLoader / Payload Guest payload |
|
||||
| `orbisrpc_sdk.elf` | `scripts/build_sdk.sh` (ps4-payload-sdk) | PKG `assets/daemon.elf`, copied to `/data/payloads/orbisrpc.bin` |
|
||||
| Installer PKG (`ORPC00001`) | `installer/Makefile` | Setup app: SDL2 UI, token entry, file staging |
|
||||
| GoldHEN plugin | frozen | not shipped; payload is the supported path |
|
||||
|
||||
Both payload builds share `orbisrpc/*.c`. `ORBISRPC_SDK_PAYLOAD`
|
||||
selects toolchain/libc differences only — detection strategy is
|
||||
identical (probe-first, see below).
|
||||
|
||||
## Detection cascade (ordered, first signal wins)
|
||||
|
||||
1. **msgbuf events** (`focus.c`): `AppFocusChanged [...]` in
|
||||
kern.msgbuf. Event-driven, sees system screens (settings/browser),
|
||||
resolves multi-app ambiguity. Unreadable buffer = skip.
|
||||
2. **ShellCoreUtil dlopen** (`detect.c` `scu_init`): `sceShellCoreUtilIsAppLaunched`.
|
||||
Firmware-independent (libkernel exports dlopen/dlsym). Fail = skip.
|
||||
3. **sysctl eboot scan** with the verified kinfo table (`fw.c`):
|
||||
exact offset only on firmwares in the table (currently 9.00);
|
||||
everywhere else a bounded in-record scan. Never reads past recsz,
|
||||
never assumes.
|
||||
4. **Sandbox/save fallbacks**: `/mnt/sandbox/<TITLE>_000`, freshest
|
||||
savedata / app dir / `app.pkg` atime.
|
||||
|
||||
New title needs 2 consecutive polls (~2s debounce). Same title
|
||||
returning within 10 min resumes its timer; cross-restart resume seeds
|
||||
from `session.json` (validated: 9-char id, sane epoch).
|
||||
|
||||
## Gateway
|
||||
|
||||
Explicit states, logged on transition: `DOWN -> CONNECTING ->
|
||||
HELLO_WAIT -> IDENTIFYING -> READY`. Fresh IDENTIFY every connect
|
||||
(no RESUME). Close 4004 (bad token) is a warning + backoff + config
|
||||
reload — never an exit. Reconnect: exponential backoff with
|
||||
deterministic jitter, 60s cap for 10 fails, then 10-min cadence.
|
||||
Presence re-posts after every (re)connect plus a 15-min reconcile.
|
||||
|
||||
## No-exit policy
|
||||
|
||||
The daemon never exits on transient failure: no token (waits, FTP
|
||||
edit lands without reboot), network drops, corrupt session file,
|
||||
detection misses. Exits only on: stop request, lock held by a peer
|
||||
(stands down), superseded generation, fatal `/data` unwritable.
|
||||
|
||||
Crash safety: consecutive unclean boots (never marked healthy after
|
||||
`HEALTH_STABLE_SECS`) enter safe mode. Single-writer lock
|
||||
(`/data/orbisRPC/lock`, O_EXCL + pid liveness). `daemon.gen`
|
||||
generation protocol: installer bumps, older daemons exit cleanly.
|
||||
|
||||
## On-disk state (`/data/orbisRPC/`)
|
||||
|
||||
| File | What |
|
||||
|---|---|
|
||||
| `config.json` | token, prefs, learned titles. Atomic save, `0600` |
|
||||
| `session.json` | live session for timer resume. Validated on load |
|
||||
| `status.json` | heartbeat (state/title/version/ts), every 60s |
|
||||
| `diag.json` | boot census: fw, kinfo-table hit, msgbuf/sandbox/shellcore reachability |
|
||||
| `daemon.gen` | install generation for supersede |
|
||||
| `install.log` | installer stage log (errno + sizes per step) |
|
||||
| `log.txt` | daemon log, mirrored to klog |
|
||||
| `playtime.log` | append-only ledger, readers total it |
|
||||
|
||||
## Installer
|
||||
|
||||
Thin layer: preflight assets, `copy_file` with FNV read-back proof
|
||||
(no stat — sandbox lies), config preserved on reinstall, 3-try IME
|
||||
token entry with FTP fallback, gen bump, done screen reports live
|
||||
daemon state from `status.json`. Every step logs to `install.log`;
|
||||
crash guard turns faults into log lines. UI is custom SDL2
|
||||
(`installer/ui.c`, same `ui.h` API); font is build-time rasterized
|
||||
(`scripts/genfont.py` -> `installer/font.h`).
|
||||
|
||||
## Compatibility
|
||||
|
||||
Verified baseline: **9.00**. All other firmwares: probe-and-degrade
|
||||
by design, `diag.json` proves per-box signal availability. The
|
||||
firmware matrix fills in from user-submitted `diag.json` files —
|
||||
see `SUPPORT.md` and `docs/`.
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# Flow: daemon payload -> token -> done (gen bump supersedes old daemon).
|
||||
# Payload Guest reads /data/payloads/ on this console.
|
||||
TITLE := orbisRPC Setup
|
||||
VERSION := 1.0.0
|
||||
VERSION := 1.1.0
|
||||
TITLE_ID := ORPC00001
|
||||
CONTENT_ID := IV0000-ORPC00001_00-ORBISRPCSETUP000
|
||||
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
#include "ws.h"
|
||||
#include "discord.h"
|
||||
#include "detect.h"
|
||||
#include "focus.h"
|
||||
#include "fw.h"
|
||||
#include "updater.h"
|
||||
#include "version.h"
|
||||
#include "jsonlite.h"
|
||||
#include "art.h"
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
@@ -181,6 +185,51 @@ static void pres_set(pres_state_t *cur, pres_state_t next){
|
||||
log_msg("STATE: presence %s -> %s", pres_name(*cur), pres_name(next));
|
||||
*cur = next;
|
||||
}
|
||||
/* diag.json: one boot-time signal census for the firmware matrix.
|
||||
* Users paste this single file instead of "it doesn't work": daemon
|
||||
* version, firmware, and which of the four focus signals this box
|
||||
* actually has. Read-only probes, never fatal. */
|
||||
extern void *dlopen(const char *filename, int flags);
|
||||
extern int dlclose(void *handle);
|
||||
static void diag_write(void){
|
||||
char fw[16] = "";
|
||||
int noff = 0, mrec = 0;
|
||||
fw_version(fw, sizeof fw);
|
||||
int kinfo_known = (fw_kinfo(&noff, &mrec) == 0);
|
||||
int msgbuf = focus_msgbuf_ok();
|
||||
int sandbox = 0;
|
||||
{
|
||||
DIR *d = opendir("/mnt/sandbox");
|
||||
if(d){ sandbox = 1; closedir(d); }
|
||||
}
|
||||
int scu = 0;
|
||||
{
|
||||
void *h = dlopen("libSceShellCoreUtil.sprx", 0);
|
||||
if(h){ scu = 1; dlclose(h); }
|
||||
}
|
||||
jl_val_t *r = jl_new_object();
|
||||
if(!r) return;
|
||||
jl_obj_set(r, "version", jl_new_string(ORBISRPC_VERSION));
|
||||
jl_obj_set(r, "fw", jl_new_string(fw));
|
||||
jl_obj_set(r, "kinfo_table", jl_new_number((double)kinfo_known));
|
||||
jl_obj_set(r, "msgbuf", jl_new_number((double)msgbuf));
|
||||
jl_obj_set(r, "sandbox", jl_new_number((double)sandbox));
|
||||
jl_obj_set(r, "shellcore", jl_new_number((double)scu));
|
||||
jl_obj_set(r, "ts", jl_new_number((double)time(NULL)));
|
||||
char *s = jl_stringify(r);
|
||||
jl_free(r);
|
||||
if(!s) return;
|
||||
FILE *f = fopen("/data/orbisRPC/diag.json.new", "wb");
|
||||
if(f){
|
||||
int ok = (fputs(s, f) >= 0) && (fflush(f) == 0);
|
||||
if(ok){ int fd = fileno(f); if(fd < 0 || fsync(fd) != 0) ok = 0; }
|
||||
if(fclose(f) != 0) ok = 0;
|
||||
if(ok) rename("/data/orbisRPC/diag.json.new",
|
||||
"/data/orbisRPC/diag.json");
|
||||
else remove("/data/orbisRPC/diag.json.new");
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
/* fixed_game_name != NULL -> post presence for that game only, no detection.
|
||||
* NULL -> poll the foreground app like the payload daemon does.
|
||||
* Returns 0 normal stop, 1 config error, 2 auth-fatal (bad token). */
|
||||
@@ -204,6 +253,7 @@ int daemon_run(const char *fixed_game_name){
|
||||
}
|
||||
log_init(LOG_PATH);
|
||||
log_msg("orbisRPC daemon start — build %s %s", __DATE__, __TIME__);
|
||||
diag_write();
|
||||
/* Single writer: a second launch (or a stale pileup from repeated
|
||||
* injections) stands down instead of fighting over the gateway. */
|
||||
{
|
||||
|
||||
@@ -133,3 +133,11 @@ int detect_system_screen(char *out_tid, size_t cap){
|
||||
}
|
||||
return FOCUS_UNKNOWN;
|
||||
}
|
||||
|
||||
int focus_msgbuf_ok(void){
|
||||
for(int i = 0; kPaths[i]; i++){
|
||||
int fd = open(kPaths[i], O_RDONLY);
|
||||
if(fd >= 0){ close(fd); return 1; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -22,4 +22,7 @@ int focus_classify(const char *tid);
|
||||
* Returns FOCUS_* class, writes tid when parsed. Never crashes, never
|
||||
* blocks: unreadable buffer just means FOCUS_UNKNOWN. */
|
||||
int detect_system_screen(char *out_tid, size_t cap);
|
||||
/* Reachability probe for the census: 1 if any candidate msgbuf path
|
||||
* opens, 0 otherwise. No parsing, no logging, no sticky state. */
|
||||
int focus_msgbuf_ok(void);
|
||||
#endif
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
/* version.h - single source of truth for the release version.
|
||||
* Bump on every GitHub release; the on-console updater compares this
|
||||
* against the latest release tag. */
|
||||
* Bump on every GitHub release; status.json/diag.json report this
|
||||
* so support can tell which build a log came from. */
|
||||
#ifndef ORBISRPC_VERSION_H
|
||||
#define ORBISRPC_VERSION_H
|
||||
#define ORBISRPC_VERSION "1.0.0"
|
||||
#define ORBISRPC_VERSION "1.1.0"
|
||||
#define ORBISRPC_REPO "SirHumza/orbisRPC"
|
||||
#endif
|
||||
@@ -673,6 +673,8 @@ static void test_focus(void) {
|
||||
assert(focus_classify("NPXS20001") == FOCUS_SYSTEM);
|
||||
assert(focus_classify("XXXX00000") == FOCUS_UNKNOWN);
|
||||
assert(focus_classify(NULL) == FOCUS_UNKNOWN);
|
||||
/* no msgbuf device on the host: reachability must report 0, no crash */
|
||||
assert(focus_msgbuf_ok() == 0);
|
||||
}
|
||||
|
||||
static void test_fw(void) {
|
||||
|
||||
Reference in new issue
Block a user