Prepare BFpilot v0.2.1 compatibility test build

This commit is contained in:
ItsBlurf committed 2026-06-04 23:04:07 +01:00
1 parent d79ee1542d
commit 3a146e70b2
28 files changed
+2324 -712

No files matched your search

-1
View File
@@ -4,7 +4,6 @@ gen/
*.a
*.so
*.elf
!test-builds/**/*.elf
# Local environment / editor noise
.env
+271 -41
View File
@@ -17,6 +17,8 @@ endif
PS5_HOST ?= ps5
PS5_PORT ?= 9021
PYTHON ?= python3
WEB_PORT ?= 5905
HELLO_HTTP_PORT ?= 5906
VERSION_TAG := bfpilot-v0.2.1
BUILD_VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
@@ -26,27 +28,69 @@ LLVM_CONFIG ?= $(CURDIR)/build-tools/llvm-config
export LLVM_BINDIR
export LLVM_CONFIG
CORE_BIN := bfpilot-core.elf
BFPILOT_BIN := bfpilot.elf
DEBUG_BIN := bfpilot-debug.elf
FULL_BIN := bfpilot-full.elf
LAUNCHER_INSTALLER_BIN := bfpilot-launcher-installer.elf
SAFE_LAUNCHER_INSTALLER_BIN := bfpilot-launcher-installer-safe.elf
HELLO_BOOT_BIN := tests/hello_boot.elf
HELLO_HTTP_BIN := tests/hello_http.elf
HELLO_NOTIFY_BIN := tests/hello_notify.elf
INSTALLER_ENTER_PROBE_BIN := tests/installer_enter_probe.elf
INSTALLER_LINKONLY_APPINST_BIN := tests/installer_linkonly_appinst.elf
INSTALLER_RUNTIME_RESOLVE_APPINST_BIN := tests/installer_runtime_resolve_appinst.elf
COMMON_SRCS := src/lite_main.c
COMMON_SRCS += src/diag.c
COMMON_SRCS += src/websrv_lite.c
COMMON_SRCS += src/asset.c
COMMON_SRCS += src/fs.c
COMMON_SRCS += src/mime.c
COMMON_SRCS += src/notify.c
COMMON_SRCS += src/transfer.c
WEB_SRCS := src/lite_main.c
WEB_SRCS += src/boot_marker.c
WEB_SRCS += src/diag.c
WEB_SRCS += src/websrv_lite.c
WEB_SRCS += src/asset.c
WEB_SRCS += src/fs.c
WEB_SRCS += src/mime.c
WEB_SRCS += src/notify.c
WEB_SRCS += src/transfer.c
FULL_SRCS := $(COMMON_SRCS)
FULL_SRCS += src/app_installer.c
FULL_SRCS += src/sce_resolve.c
LEGACY_FULL_SRCS := $(WEB_SRCS)
LEGACY_FULL_SRCS += src/app_installer.c
LEGACY_FULL_SRCS += src/sce_resolve.c
LAUNCHER_INSTALLER_SRCS := src/launcher_installer_force_main.c
LAUNCHER_INSTALLER_SRCS += src/boot_marker.c
LAUNCHER_INSTALLER_SRCS += src/notify.c
SAFE_LAUNCHER_INSTALLER_SRCS := src/launcher_installer_main.c
SAFE_LAUNCHER_INSTALLER_SRCS += src/boot_marker.c
SAFE_LAUNCHER_INSTALLER_SRCS += src/notify.c
SAFE_LAUNCHER_INSTALLER_SRCS += src/sce_resolve.c
HELLO_BOOT_SRCS := tests/hello_boot.c src/boot_marker.c src/notify.c
HELLO_HTTP_SRCS := tests/hello_http.c src/boot_marker.c src/notify.c
HELLO_NOTIFY_SRCS := tests/hello_notify.c src/boot_marker.c src/notify.c
INSTALLER_ENTER_PROBE_SRCS := tests/installer_enter_probe.c
INSTALLER_ENTER_PROBE_SRCS += src/boot_marker.c
INSTALLER_ENTER_PROBE_SRCS += src/notify.c
INSTALLER_LINKONLY_APPINST_SRCS := tests/installer_linkonly_appinst.c
INSTALLER_LINKONLY_APPINST_SRCS += src/boot_marker.c
INSTALLER_LINKONLY_APPINST_SRCS += src/notify.c
INSTALLER_RUNTIME_RESOLVE_APPINST_SRCS := tests/installer_runtime_resolve_appinst.c
INSTALLER_RUNTIME_RESOLVE_APPINST_SRCS += src/boot_marker.c
INSTALLER_RUNTIME_RESOLVE_APPINST_SRCS += src/notify.c
ASSETS := $(wildcard assets/*)
GEN_SRCS := $(patsubst assets/%,gen/assets/%,$(ASSETS:=.c))
APP_ASSETS := assets-app/param.json assets-app/icon0.png
CORE_OBJS := $(patsubst %.c,build/core/%.o,$(COMMON_SRCS) $(GEN_SRCS))
FULL_OBJS := $(patsubst %.c,build/full/%.o,$(FULL_SRCS) $(GEN_SRCS))
BFPILOT_OBJS := $(patsubst %.c,build/bfpilot/%.o,$(WEB_SRCS) $(GEN_SRCS))
DEBUG_OBJS := $(patsubst %.c,build/debug/%.o,$(WEB_SRCS) $(GEN_SRCS))
LEGACY_FULL_OBJS := $(patsubst %.c,build/legacy-full/%.o,$(LEGACY_FULL_SRCS) $(GEN_SRCS))
LAUNCHER_INSTALLER_OBJS := $(patsubst %.c,build/launcher-installer/%.o,$(LAUNCHER_INSTALLER_SRCS))
SAFE_LAUNCHER_INSTALLER_OBJS := $(patsubst %.c,build/launcher-installer-safe/%.o,$(SAFE_LAUNCHER_INSTALLER_SRCS))
HELLO_BOOT_OBJS := $(patsubst %.c,build/hello-boot/%.o,$(HELLO_BOOT_SRCS))
HELLO_HTTP_OBJS := $(patsubst %.c,build/hello-http/%.o,$(HELLO_HTTP_SRCS))
HELLO_NOTIFY_OBJS := $(patsubst %.c,build/hello-notify/%.o,$(HELLO_NOTIFY_SRCS))
INSTALLER_ENTER_PROBE_OBJS := $(patsubst %.c,build/installer-enter-probe/%.o,$(INSTALLER_ENTER_PROBE_SRCS))
INSTALLER_LINKONLY_APPINST_OBJS := $(patsubst %.c,build/installer-linkonly-appinst/%.o,$(INSTALLER_LINKONLY_APPINST_SRCS))
INSTALLER_RUNTIME_RESOLVE_APPINST_OBJS := $(patsubst %.c,build/installer-runtime-resolve-appinst/%.o,$(INSTALLER_RUNTIME_RESOLVE_APPINST_SRCS))
COMMON_CFLAGS := -Os -Wall -Werror -Isrc
COMMON_CFLAGS += -ffunction-sections -fdata-sections -flto
@@ -54,17 +98,59 @@ COMMON_CFLAGS += -DVERSION_TAG=\"$(VERSION_TAG)\"
COMMON_CFLAGS += -DBUILD_VERSION=\"$(BUILD_VERSION)\"
COMMON_CFLAGS += -DBFPILOT_SDK_PATH=\"$(PS5_PAYLOAD_SDK)\"
CORE_CFLAGS := $(COMMON_CFLAGS)
CORE_CFLAGS += -DBFPILOT_ENABLE_LAUNCHER=0
CORE_CFLAGS += -DBFPILOT_DISABLE_LAUNCHER=1
CORE_CFLAGS += -DBFPILOT_BUILD_MODE=\"core\"
BFPILOT_CFLAGS := $(COMMON_CFLAGS)
BFPILOT_CFLAGS += -DBFPILOT_PAYLOAD_NAME=\"bfpilot\"
BFPILOT_CFLAGS += -DBFPILOT_BUILD_MODE=\"file-manager\"
BFPILOT_CFLAGS += -DBFPILOT_WEB_PORT=$(WEB_PORT)
BFPILOT_CFLAGS += -DBFPILOT_DEBUG_NOTIFICATIONS=0
BFPILOT_CFLAGS += -DBFPILOT_ENABLE_LAUNCHER=0
BFPILOT_CFLAGS += -DBFPILOT_DISABLE_LAUNCHER=1
DEBUG_CFLAGS := $(COMMON_CFLAGS)
DEBUG_CFLAGS += -DBFPILOT_PAYLOAD_NAME=\"bfpilot-debug\"
DEBUG_CFLAGS += -DBFPILOT_BUILD_MODE=\"debug\"
DEBUG_CFLAGS += -DBFPILOT_WEB_PORT=$(WEB_PORT)
DEBUG_CFLAGS += -DBFPILOT_DEBUG_NOTIFICATIONS=1
DEBUG_CFLAGS += -DBFPILOT_ENABLE_LAUNCHER=0
DEBUG_CFLAGS += -DBFPILOT_DISABLE_LAUNCHER=1
FULL_CFLAGS := $(COMMON_CFLAGS)
FULL_CFLAGS += -DBFPILOT_ENABLE_LAUNCHER=1
FULL_CFLAGS += -DBFPILOT_PAYLOAD_NAME=\"bfpilot-full\"
FULL_CFLAGS += -DBFPILOT_BUILD_MODE=\"full\"
FULL_CFLAGS += -DBFPILOT_WEB_PORT=$(WEB_PORT)
FULL_CFLAGS += -DBFPILOT_DEBUG_NOTIFICATIONS=1
FULL_CFLAGS += -DBFPILOT_ENABLE_LAUNCHER=1
FULL_CFLAGS += -DBFPILOT_DISABLE_LAUNCHER=0
LAUNCHER_INSTALLER_CFLAGS := $(COMMON_CFLAGS)
LAUNCHER_INSTALLER_CFLAGS += -DBFPILOT_PAYLOAD_NAME=\"bfpilot-launcher-installer\"
LAUNCHER_INSTALLER_CFLAGS += -DBFPILOT_BUILD_MODE=\"launcher-installer-force\"
SAFE_LAUNCHER_INSTALLER_CFLAGS := $(COMMON_CFLAGS)
SAFE_LAUNCHER_INSTALLER_CFLAGS += -DBFPILOT_PAYLOAD_NAME=\"bfpilot-launcher-installer-safe\"
SAFE_LAUNCHER_INSTALLER_CFLAGS += -DBFPILOT_BUILD_MODE=\"launcher-installer-safe\"
HELLO_BOOT_CFLAGS := $(COMMON_CFLAGS)
HELLO_BOOT_CFLAGS += -DBFPILOT_BUILD_MODE=\"hello-boot\"
HELLO_HTTP_CFLAGS := $(COMMON_CFLAGS)
HELLO_HTTP_CFLAGS += -DBFPILOT_BUILD_MODE=\"hello-http\"
HELLO_HTTP_CFLAGS += -DBFPILOT_HELLO_HTTP_PORT=$(HELLO_HTTP_PORT)
HELLO_NOTIFY_CFLAGS := $(COMMON_CFLAGS)
HELLO_NOTIFY_CFLAGS += -DBFPILOT_BUILD_MODE=\"hello-notify\"
INSTALLER_ENTER_PROBE_CFLAGS := $(COMMON_CFLAGS)
INSTALLER_ENTER_PROBE_CFLAGS += -DBFPILOT_BUILD_MODE=\"installer-enter-probe\"
INSTALLER_LINKONLY_APPINST_CFLAGS := $(COMMON_CFLAGS)
INSTALLER_LINKONLY_APPINST_CFLAGS += -DBFPILOT_BUILD_MODE=\"installer-linkonly-appinst\"
INSTALLER_RUNTIME_RESOLVE_APPINST_CFLAGS := $(COMMON_CFLAGS)
INSTALLER_RUNTIME_RESOLVE_APPINST_CFLAGS += -DBFPILOT_BUILD_MODE=\"installer-runtime-resolve-appinst\"
COMMON_LDFLAGS := -Wl,--gc-sections -flto
FULL_LDLIBS := -lSceAppInstUtil
APPINST_LDLIBS := -lSceAppInstUtil
CC_CMD := "$(CC)"
STRIP_CMD := "$(STRIP)"
@@ -82,10 +168,13 @@ WINDOWS_LINK_PREFIX := --gc-sections --sysroot="$(PS5_PAYLOAD_SDK_POSIX)"
WINDOWS_LINK_PREFIX += -L"$(PS5_PAYLOAD_SDK_POSIX)/target/lib"
WINDOWS_LINK_PREFIX += -L"$(PS5_PAYLOAD_SDK_POSIX)/target/user/homebrew/lib"
WINDOWS_LINK_PREFIX += -l:crt1.o -l:crti.o -l:crtbegin.o -lc
WINDOWS_LINK_SUFFIX := -lkernel_web -lSceLibcInternal -lSceNet
WINDOWS_LINK_SUFFIX += -lc_stub_weak -lkernel_stub_weak
WINDOWS_LINK_SUFFIX += -l:crtend.o -l:crtn.o
WINDOWS_FULL_LINK_SUFFIX := -lSceAppInstUtil $(WINDOWS_LINK_SUFFIX)
WINDOWS_BASE_SUFFIX := -lkernel_web -lSceLibcInternal
WINDOWS_BASE_SUFFIX += -lc_stub_weak -lkernel_stub_weak
WINDOWS_BASE_SUFFIX += -l:crtend.o -l:crtn.o
WINDOWS_WEB_SUFFIX := -lkernel_web -lSceLibcInternal -lSceNet
WINDOWS_WEB_SUFFIX += -lc_stub_weak -lkernel_stub_weak
WINDOWS_WEB_SUFFIX += -l:crtend.o -l:crtn.o
WINDOWS_APPINST_SUFFIX := -lSceAppInstUtil $(WINDOWS_BASE_SUFFIX)
define run
bash -lc '$(RUN_ENV) && $(1)'
endef
@@ -95,52 +184,193 @@ $(1)
endef
endif
all: core full
all: bfpilot debug full launcher-installer launcher-installer-safe hello-boot hello-http hello-notify installer-enter-probe installer-linkonly-appinst installer-runtime-resolve-appinst
core: $(CORE_BIN)
bfpilot: $(BFPILOT_BIN)
debug: $(DEBUG_BIN)
full: $(FULL_BIN)
legacy-full: full
launcher-installer: $(LAUNCHER_INSTALLER_BIN)
launcher-installer-safe: $(SAFE_LAUNCHER_INSTALLER_BIN)
hello-boot: $(HELLO_BOOT_BIN)
hello-http: $(HELLO_HTTP_BIN)
hello-notify: $(HELLO_NOTIFY_BIN)
installer-enter-probe: $(INSTALLER_ENTER_PROBE_BIN)
installer-linkonly-appinst: $(INSTALLER_LINKONLY_APPINST_BIN)
installer-runtime-resolve-appinst: $(INSTALLER_RUNTIME_RESOLVE_APPINST_BIN)
gen/assets:
$(call run,mkdir -p $@)
gen/assets/%.c: assets/% | gen/assets
$(call run,$(PYTHON) gen-asset-module.py --path $* $< > $@)
build/core/%.o: %.c Makefile
build/bfpilot/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(CORE_CFLAGS) -c $< -o $@)
$(call run,$(CC_CMD) $(BFPILOT_CFLAGS) -c $< -o $@)
build/full/%.o: %.c Makefile
build/debug/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(DEBUG_CFLAGS) -c $< -o $@)
build/legacy-full/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(FULL_CFLAGS) -c $< -o $@)
build/launcher-installer/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(LAUNCHER_INSTALLER_CFLAGS) -c $< -o $@)
build/launcher-installer-safe/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(SAFE_LAUNCHER_INSTALLER_CFLAGS) -c $< -o $@)
build/hello-boot/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(HELLO_BOOT_CFLAGS) -c $< -o $@)
build/hello-http/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(HELLO_HTTP_CFLAGS) -c $< -o $@)
build/hello-notify/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(HELLO_NOTIFY_CFLAGS) -c $< -o $@)
build/installer-enter-probe/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(INSTALLER_ENTER_PROBE_CFLAGS) -c $< -o $@)
build/installer-linkonly-appinst/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(INSTALLER_LINKONLY_APPINST_CFLAGS) -c $< -o $@)
build/installer-runtime-resolve-appinst/%.o: %.c Makefile
$(call run,mkdir -p $(dir $@))
$(call run,$(CC_CMD) $(INSTALLER_RUNTIME_RESOLVE_APPINST_CFLAGS) -c $< -o $@)
ifeq ($(HOST_IS_WINDOWS),1)
$(CORE_BIN): $(CORE_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(CORE_OBJS) $(WINDOWS_LINK_SUFFIX))
$(BFPILOT_BIN): $(BFPILOT_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(BFPILOT_OBJS) $(WINDOWS_WEB_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(call run,$(PYTHON) scripts/scrub_main_payload.py $@)
$(DEBUG_BIN): $(DEBUG_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(DEBUG_OBJS) $(WINDOWS_WEB_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(call run,$(PYTHON) scripts/scrub_main_payload.py $@)
$(FULL_BIN): $(LEGACY_FULL_OBJS) $(APP_ASSETS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(LEGACY_FULL_OBJS) $(WINDOWS_WEB_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(FULL_BIN): $(FULL_OBJS) $(APP_ASSETS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(FULL_OBJS) $(WINDOWS_FULL_LINK_SUFFIX))
$(LAUNCHER_INSTALLER_BIN): $(LAUNCHER_INSTALLER_OBJS) $(APP_ASSETS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(LAUNCHER_INSTALLER_OBJS) $(WINDOWS_APPINST_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(SAFE_LAUNCHER_INSTALLER_BIN): $(SAFE_LAUNCHER_INSTALLER_OBJS) $(APP_ASSETS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(SAFE_LAUNCHER_INSTALLER_OBJS) $(WINDOWS_BASE_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_BOOT_BIN): $(HELLO_BOOT_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(HELLO_BOOT_OBJS) $(WINDOWS_BASE_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_HTTP_BIN): $(HELLO_HTTP_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(HELLO_HTTP_OBJS) $(WINDOWS_WEB_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_NOTIFY_BIN): $(HELLO_NOTIFY_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(HELLO_NOTIFY_OBJS) $(WINDOWS_BASE_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_ENTER_PROBE_BIN): $(INSTALLER_ENTER_PROBE_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(INSTALLER_ENTER_PROBE_OBJS) $(WINDOWS_BASE_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_LINKONLY_APPINST_BIN): $(INSTALLER_LINKONLY_APPINST_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(INSTALLER_LINKONLY_APPINST_OBJS) $(WINDOWS_APPINST_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_RUNTIME_RESOLVE_APPINST_BIN): $(INSTALLER_RUNTIME_RESOLVE_APPINST_OBJS)
$(call run,$(LD_CMD) -o $@ $(WINDOWS_LINK_PREFIX) $(INSTALLER_RUNTIME_RESOLVE_APPINST_OBJS) $(WINDOWS_BASE_SUFFIX))
$(call run,$(STRIP_CMD) --strip-all $@)
else
$(CORE_BIN): $(CORE_OBJS)
$(call run,$(CC_CMD) $(CORE_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(CORE_OBJS))
$(BFPILOT_BIN): $(BFPILOT_OBJS)
$(call run,$(CC_CMD) $(BFPILOT_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(BFPILOT_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(call run,$(PYTHON) scripts/scrub_main_payload.py $@)
$(DEBUG_BIN): $(DEBUG_OBJS)
$(call run,$(CC_CMD) $(DEBUG_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(DEBUG_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(call run,$(PYTHON) scripts/scrub_main_payload.py $@)
$(FULL_BIN): $(LEGACY_FULL_OBJS) $(APP_ASSETS)
$(call run,$(CC_CMD) $(FULL_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(LEGACY_FULL_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(FULL_BIN): $(FULL_OBJS) $(APP_ASSETS)
$(call run,$(CC_CMD) $(FULL_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(FULL_OBJS) $(FULL_LDLIBS))
$(LAUNCHER_INSTALLER_BIN): $(LAUNCHER_INSTALLER_OBJS) $(APP_ASSETS)
$(call run,$(CC_CMD) $(LAUNCHER_INSTALLER_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(LAUNCHER_INSTALLER_OBJS) $(APPINST_LDLIBS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(SAFE_LAUNCHER_INSTALLER_BIN): $(SAFE_LAUNCHER_INSTALLER_OBJS) $(APP_ASSETS)
$(call run,$(CC_CMD) $(SAFE_LAUNCHER_INSTALLER_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(SAFE_LAUNCHER_INSTALLER_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_BOOT_BIN): $(HELLO_BOOT_OBJS)
$(call run,$(CC_CMD) $(HELLO_BOOT_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(HELLO_BOOT_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_HTTP_BIN): $(HELLO_HTTP_OBJS)
$(call run,$(CC_CMD) $(HELLO_HTTP_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(HELLO_HTTP_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(HELLO_NOTIFY_BIN): $(HELLO_NOTIFY_OBJS)
$(call run,$(CC_CMD) $(HELLO_NOTIFY_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(HELLO_NOTIFY_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_ENTER_PROBE_BIN): $(INSTALLER_ENTER_PROBE_OBJS)
$(call run,$(CC_CMD) $(INSTALLER_ENTER_PROBE_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(INSTALLER_ENTER_PROBE_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_LINKONLY_APPINST_BIN): $(INSTALLER_LINKONLY_APPINST_OBJS)
$(call run,$(CC_CMD) $(INSTALLER_LINKONLY_APPINST_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(INSTALLER_LINKONLY_APPINST_OBJS) $(APPINST_LDLIBS))
$(call run,$(STRIP_CMD) --strip-all $@)
$(INSTALLER_RUNTIME_RESOLVE_APPINST_BIN): $(INSTALLER_RUNTIME_RESOLVE_APPINST_OBJS)
$(call run,$(CC_CMD) $(INSTALLER_RUNTIME_RESOLVE_APPINST_CFLAGS) $(COMMON_LDFLAGS) -o $@ $(INSTALLER_RUNTIME_RESOLVE_APPINST_OBJS))
$(call run,$(STRIP_CMD) --strip-all $@)
endif
deploy-core: core
$(call run,$(DEPLOY_CMD) -h $(PS5_HOST) -p $(PS5_PORT) $(CORE_BIN))
inspect-imports: all
$(call run,bash scripts/inspect_imports.sh $(BFPILOT_BIN) $(DEBUG_BIN) $(FULL_BIN) $(LAUNCHER_INSTALLER_BIN) $(SAFE_LAUNCHER_INSTALLER_BIN) $(HELLO_BOOT_BIN) $(HELLO_HTTP_BIN) $(HELLO_NOTIFY_BIN) $(INSTALLER_ENTER_PROBE_BIN) $(INSTALLER_LINKONLY_APPINST_BIN) $(INSTALLER_RUNTIME_RESOLVE_APPINST_BIN))
deploy-bfpilot: bfpilot
$(call run,$(DEPLOY_CMD) -h $(PS5_HOST) -p $(PS5_PORT) $(BFPILOT_BIN))
deploy-debug: debug
$(call run,$(DEPLOY_CMD) -h $(PS5_HOST) -p $(PS5_PORT) $(DEBUG_BIN))
deploy-full: full
$(call run,$(DEPLOY_CMD) -h $(PS5_HOST) -p $(PS5_PORT) $(FULL_BIN))
deploy-launcher-installer: launcher-installer
$(call run,$(DEPLOY_CMD) -h $(PS5_HOST) -p $(PS5_PORT) $(LAUNCHER_INSTALLER_BIN))
clean:
$(call run,rm -rf $(CORE_BIN) $(FULL_BIN) build gen)
$(call run,rm -rf $(BFPILOT_BIN) $(DEBUG_BIN) $(FULL_BIN) bfpilot-full-legacy.elf $(LAUNCHER_INSTALLER_BIN) $(SAFE_LAUNCHER_INSTALLER_BIN) bfpilot-core.elf build gen $(HELLO_BOOT_BIN) $(HELLO_HTTP_BIN) $(HELLO_NOTIFY_BIN) $(INSTALLER_ENTER_PROBE_BIN) $(INSTALLER_LINKONLY_APPINST_BIN) $(INSTALLER_RUNTIME_RESOLVE_APPINST_BIN) tests/hello_appinst.elf)
.SECONDARY: $(GEN_SRCS)
.PHONY: all core full clean deploy-core deploy-full
.PHONY: all bfpilot debug full legacy-full launcher-installer launcher-installer-safe hello-boot hello-http hello-notify installer-enter-probe installer-linkonly-appinst installer-runtime-resolve-appinst inspect-imports clean deploy-bfpilot deploy-debug deploy-full deploy-launcher-installer
+128 -307
View File
@@ -1,392 +1,213 @@
# BFpilot
BFpilot is a small PS5 payload that starts a browser-based file manager on port
`5905`.
BFpilot is a small PS5 userland payload that starts a browser-based file
manager on port `5905`.
It is meant to stay simple: no PKG installer, no FTP daemon, no mounting tools,
no payload bundle, no exploit code, and no DRM or backup features. The
compatibility-first build starts one standalone web server and lets the PS5 do
file operations locally.
It is intentionally narrow: no exploit code, no PKG installer, no FTP daemon,
no mounting tools, no payload bundle, and no DRM or backup features.
Current prepared release: `v0.2.1`
## Recommended Build
## Recommended Payload
Use `bfpilot-core.elf` first on every new firmware or loader.
Use `bfpilot.elf` as the main release payload.
`bfpilot-core.elf` is the recommended build for maximum firmware compatibility:
`bfpilot.elf` is the maximum-compatibility file manager:
- Starts the HTTP file manager on `http://<PS5_IP>:5905/`
- Does not install or refresh the PS5 launcher tile
- Does not compile launcher installer code
- Does not link launcher-only SCE libraries
- Keeps notification attempts optional
- Starts the HTTP file manager at `http://<PS5_IP>:5905/`.
- Does not install or refresh a PS5 home-screen tile.
- Does not compile or link `src/app_installer.c`.
- Does not link `libSceAppInstUtil`.
- Does not depend on AppInstUtil, SystemService, UserService, or optional
launcher-only Sony libraries.
- Writes boot and runtime diagnostics under `/data/BFpilot`.
Only try `bfpilot-full.elf` after `bfpilot-core.elf` works on the same firmware
and loader.
Use `bfpilot-debug.elf` when you need extra proof that the payload reached
`main()` and progressed through startup. It is still file-manager-only and does
not link AppInstUtil.
`bfpilot-full.elf` includes optional launcher tile support. Launcher install may
fail on some firmware, exploit, or loader combinations. That should not stop the
file manager. If launcher install fails but port `5905` still works, use the web
file manager directly from a browser.
Use `bfpilot-full.elf` when you want the 0.2.0-style integrated launcher
install path. This is the best first installer-capable build for your 11.6 case:
it starts BFpilot and attempts the home-screen tile install inside the same
process, after optional PS5 service initialization. It does not direct-link
AppInstUtil; it dynamically resolves the same AppInst functions used by the
previous full build.
Use `bfpilot-launcher-installer.elf` only after `bfpilot.elf` works. It is a
separate optional payload whose only job is installing or refreshing the
home-screen tile. It intentionally direct-links AppInstUtil, matching the
websrv/Payload Manager/ftpsrv launcher-install pattern. If the loader rejects
that import, the installer can fail before `main()`. The file manager is still
unaffected.
Use `bfpilot-launcher-installer-safe.elf` only for diagnostics. It does not
direct-link AppInstUtil and reports whether AppInstUtil is already reachable
through the safe runtime path.
## What It Does
- Runs a file-manager web UI at `http://<PS5_IP>:5905/`
- Builds `bfpilot-core.elf` for the safest firmware compatibility path
- Builds `bfpilot-full.elf` for optional PS5 home-screen launcher support
- Browses, uploads, downloads, copies, moves, renames, creates folders, and
deletes
- Shows progress for copy, move, delete, and browser uploads
- Reports size, speed, ETA, item count, and current path/file while working
- Calculates folder size only when you select a folder
- Creates missing copy/move target folders automatically
- Keeps the default target shortcuts at `/data/homebrew` and `/mnt/usb0`
- Writes diagnostics to `/data/BFpilot/log.txt`
- Writes fatal-signal diagnostics to `/data/BFpilot/crash.log`
- Runs a file-manager web UI at `http://<PS5_IP>:5905/`.
- Supports browse, upload, download, copy, move, rename, mkdir, and delete.
- Shows progress for copy, move, delete, and browser uploads.
- Creates missing copy/move target folders automatically.
- Keeps default shortcuts at `/data/homebrew` and `/mnt/usb0`.
- Writes boot markers to `/data/BFpilot/boot.log`.
- Writes runtime logs to `/data/BFpilot/log.txt`.
- Writes fatal-signal diagnostics to `/data/BFpilot/crash.log`.
The full launcher tile opens:
The optional launcher tile, if installed by the separate installer payload,
opens:
```text
http://127.0.0.1:5905/
```
Injecting the payload does not auto-open the browser. Core mode only starts the
web server. Full mode starts the web server and then attempts launcher install or
refresh as optional work.
## Runtime Notes
Inject the ELF to your payload loader on port `9021`.
For first tests on a firmware:
First test:
```text
send bfpilot-core.elf to <PS5_IP>:9021
send bfpilot.elf to <PS5_IP>:9021
open http://<PS5_IP>:5905/
open http://<PS5_IP>:5905/api/status
open http://<PS5_IP>:5905/api/diag
```
If core works and you want the PS5 launcher tile:
Debug test:
```text
send bfpilot-debug.elf to <PS5_IP>:9021
open http://<PS5_IP>:5905/api/diag
check /data/BFpilot/boot.log
check /data/BFpilot/log.txt
```
Full install test:
```text
send bfpilot-full.elf to <PS5_IP>:9021
open http://<PS5_IP>:5905/
open http://<PS5_IP>:5905/api/diag
check /data/BFpilot/log.txt
```
Full mode can skip launcher work at runtime:
Alternate port test while an old instance is still running:
```text
--no-launcher
BFPILOT_NO_LAUNCHER=1
send bfpilot-debug.elf --port 5906 to <PS5_IP>:9021
open http://<PS5_IP>:5906/api/status
```
The full build stores launcher marker data in:
Launcher installer test:
```text
/data/BFpilot
send bfpilot-launcher-installer.elf to <PS5_IP>:9021
check /data/BFpilot/launcher-installer.log
```
The PS5 launcher app itself is installed under:
If `bfpilot-launcher-installer.elf` gives no notification and no log, send
`bfpilot-launcher-installer-safe.elf` next. If the safe installer reaches
`main()` but says AppInstUtil is unavailable, the direct installer is being
rejected before it can run.
If the launcher installer appears to do nothing, run the probes in this order:
```text
/user/app/BFPL00001
tests/installer_enter_probe.elf
tests/installer_linkonly_appinst.elf
tests/installer_runtime_resolve_appinst.elf
```
If launcher install fails, keep testing the web server. The file manager can
still work without a launcher tile.
If `installer_linkonly_appinst.elf` does not create
`/data/BFpilot/linkonly_appinst_entered.txt`, direct AppInstUtil imports are
failing before `main()` on that loader/firmware.
If `installer_runtime_resolve_appinst.elf` logs
`kernel_dynlib_handle libSceAppInstUtil.sprx rc=0xffffffff`, AppInstUtil is not
reachable through BFpilot's safe runtime path on that firmware/loader. That does
not invalidate the `bfpilot-full.elf` path on firmware where the same runtime
resolution is available, such as the 11.6 case you reported.
## Diagnostics
Use these endpoints while testing firmware compatibility:
```text
http://<PS5_IP>:5905/api/status
http://<PS5_IP>:5905/api/diag
```
Save these files when reporting failures:
```text
/data/BFpilot/boot.log
/data/BFpilot/log.txt
/data/BFpilot/crash.log
/data/BFpilot/launcher-installer.log
```
### `/api/diag` Core Example
This is the shape of a healthy core-mode response. Values such as `pid`,
`uptime`, `cwd`, and permission booleans vary by firmware and loader.
```json
{
"ok": true,
"name": "BFpilot",
"tag": "bfpilot-v0.2.1",
"version": "dev",
"mode": "core",
"pid": 112,
"uptime": 14,
"port": 5905,
"cwd": "/",
"can_stat_root": true,
"can_opendir_data": true,
"can_opendir_data_homebrew": true,
"can_opendir_usb0": true,
"can_write_data_bfpilot": true,
"can_write_user_app": false,
"launcher_status": "not_compiled",
"last_errno": 0,
"checkpoint": "web server ready",
"core_server_started": true,
"launcher_attempted": false,
"appinst_init_rc": -2147483000,
"title_dir_resolved": false,
"install_title_rc": -2147483000,
"install_all_rc": -2147483000,
"uninstall_rc": -2147483000,
"launcher_final_state": "skipped",
"rcs": {
"sceNetCtlInit": -2147483000,
"sceUserServiceInitialize": -2147483000,
"notification_test": 0,
"bind_5905": 0,
"listen_5905": 0
},
"launcher": {
"status": "not_compiled",
"status_rc": -2147483000,
"compiled": false,
"disabled": true,
"launcher_enabled": false,
"launcher_attempted": false,
"appinst_init_rc": -2147483000,
"title_dir_resolved": false,
"install_title_dir_resolved": false,
"install_title_rc": -2147483000,
"uninstall_resolved": false,
"uninstall_rc": -2147483000,
"install_all_resolved": false,
"install_all_rc": -2147483000,
"user_app_writable": null,
"launcher_install_rc": -1,
"launcher_final_state": "skipped"
},
"routes": ["/", "/api/status", "/api/diag", "/fs", "/api/fs/*"]
}
```
### `/api/diag` Full Launcher-Failed Example
This example is still acceptable if the web server works. The important part is
that launcher failure is recorded and the server remains reachable.
```json
{
"ok": true,
"name": "BFpilot",
"tag": "bfpilot-v0.2.1",
"mode": "full",
"port": 5905,
"can_opendir_data": true,
"can_write_data_bfpilot": true,
"launcher_status": "failed_nonfatal",
"core_server_started": true,
"launcher_attempted": true,
"appinst_init_rc": -2,
"title_dir_resolved": false,
"install_title_rc": -2147483000,
"install_all_rc": -2147483000,
"uninstall_rc": -2147483000,
"launcher_final_state": "failed_nonfatal",
"rcs": {
"notification_test": -1,
"bind_5905": 0,
"listen_5905": 0
},
"launcher": {
"status": "failed_nonfatal",
"status_rc": -1,
"compiled": true,
"disabled": false,
"launcher_enabled": true,
"launcher_attempted": true,
"appinst_init_rc": -2,
"title_dir_resolved": false,
"install_title_dir_resolved": false,
"install_title_rc": -2147483000,
"uninstall_resolved": false,
"uninstall_rc": -2147483000,
"install_all_resolved": true,
"install_all_rc": -2147483000,
"user_app_writable": false,
"launcher_install_rc": -2,
"launcher_final_state": "failed_nonfatal"
}
}
```
## Troubleshooting
### Port `5905` Not Reachable
- Confirm the payload sender reported a successful send to port `9021`.
- Try `bfpilot-core.elf` before `bfpilot-full.elf`.
- Check whether another BFpilot instance already owns port `5905`.
- Open `/data/BFpilot/log.txt` and look for `bind_5905`, `listen_5905`, and
`web server listening`.
- Recheck the PS5 IP address and make sure the browser device is on the same
network.
### Payload Exits Immediately
- Use `bfpilot-core.elf` first.
- Check the sender console for loader-side errors.
- Check `/data/BFpilot/crash.log` for fatal signal details.
- Check `/data/BFpilot/log.txt` for the last checkpoint.
- If reinjecting during a copy, move, or delete job, the new instance may exit
intentionally so the old job is not killed.
### Launcher Install Failed
- This is not fatal in `v0.2.1`.
- Confirm `http://<PS5_IP>:5905/` still opens.
- Open `/api/diag` and record `launcher_attempted`, `appinst_init_rc`,
`title_dir_resolved`, `install_title_rc`, `install_all_rc`, `uninstall_rc`,
`launcher_final_state`, `launcher.user_app_writable`, and
`launcher.launcher_install_rc`.
- If the file manager works, keep using the browser URL and report the launcher
diagnostics separately.
### `/mnt/usb0` Missing
- Confirm the external drive is connected before starting BFpilot.
- Try reconnecting the drive and refreshing the file manager.
- Check `/api/diag` for `can_opendir_usb0`.
- Some setups may expose a different USB mount path. Include `/api/diag` and
`/data/BFpilot/log.txt` in the report.
### Upload Fails
- Test with a tiny `.txt` file first.
- Upload to `/data/BFpilot-test` before testing USB storage.
- Confirm `/api/diag` reports `can_write_data_bfpilot: true`.
- If upload to USB fails but upload to `/data` works, treat it as a USB
permission or mount-path issue.
- Save the browser error and the latest `/data/BFpilot/log.txt` lines.
### Copy/Delete Permission Denied
- Test inside `/data/BFpilot-test` first.
- Avoid deleting root paths or system paths.
- For folder deletion, make sure recursive delete is explicitly requested by the
UI/API.
- If `/data` works but USB fails, collect `/api/diag` and note the source and
destination paths.
- Permission errors are useful compatibility data; record exact paths and the
operation that failed.
## Firmware Testing
Use [docs/FIRMWARE_TESTING.md](docs/FIRMWARE_TESTING.md) for the full
stage-by-stage protocol:
- Stage A: core server only
- Stage B: notification enabled
- Stage C: launcher enabled
- Stage D: reinjection
## Project Layout
- `src/lite_main.c` starts the payload, handles reinjection handoff, applies the
launcher runtime flag, and starts the web server.
- `src/app_installer.c` writes launcher files and optionally registers the PS5
home-screen tile in full mode.
- `src/sce_resolve.c` resolves optional SCE functions for full-mode launcher
support.
- `src/diag.c` writes append logs, crash logs, checkpoints, and runtime
diagnostics.
- `src/websrv_lite.c` serves the UI, downloads under `/fs`, `/api/status`,
`/api/diag`, and file-manager APIs.
- `src/transfer.c` contains upload, copy, move, delete, and long-running job
handling.
- `assets/files.html` is the full web UI that gets embedded into the ELF.
- `assets/icon.png` is the smaller web UI icon.
- `assets-app/` contains the launcher tile metadata and icon.
Generated files go under `gen/` during build. Build intermediates go under
`build/`. Release ELF files should be attached to GitHub Releases only after the
release checklist is complete.
If there is no notification and no `/data/BFpilot/boot.log` entry, the payload
probably failed before `main()` or the loader rejected it. If the boot marker
appears but the server does not start, the failure happened after `main()`.
## Build
You need:
- `ps5-payload-sdk`
- GNU `make`
- Python 3
- LLVM/Clang tools that work with the PS5 payload SDK
- A shell that can run the SDK build commands
Build both release files:
```sh
export PS5_PAYLOAD_SDK=/path/to/ps5-payload-sdk
make clean all
```
Build one mode:
Individual targets:
```sh
make core
make bfpilot
make debug
make full
make launcher-installer
make hello-boot
make hello-http
make hello-notify
make installer-enter-probe
make installer-linkonly-appinst
make installer-runtime-resolve-appinst
make inspect-imports
```
Outputs:
```text
bfpilot-core.elf
bfpilot.elf
bfpilot-debug.elf
bfpilot-full.elf
bfpilot-launcher-installer.elf
bfpilot-launcher-installer-safe.elf
tests/hello_boot.elf
tests/hello_http.elf
tests/hello_notify.elf
tests/installer_enter_probe.elf
tests/installer_linkonly_appinst.elf
tests/installer_runtime_resolve_appinst.elf
```
Deploy helper:
The compatibility check must pass for release builds:
```sh
make deploy-core PS5_HOST=<PS5_IP> PS5_PORT=9021
make deploy-full PS5_HOST=<PS5_IP> PS5_PORT=9021
make inspect-imports
```
## Extra Notes
It fails if `bfpilot.elf` or `bfpilot-debug.elf` contain AppInstUtil,
`sceAppInst`, `app_installer`, or `BFPL00001` installer fingerprints. Direct
`libSceAppInstUtil.sprx` import is allowed only for
`bfpilot-launcher-installer.elf` and
`tests/installer_linkonly_appinst.elf`.
BFpilot avoids kernel-level cleanup and does not force-unload unrelated
payloads.
## Project Layout
If you reinject while an older BFpilot or BS5FileManager instance is already
running, the new payload checks whether a file job is active. If the old
instance is idle, it asks it to shut down cleanly and then takes over port
`5905`. If a copy, move, or delete is running, the new injection exits instead
of interrupting the active job.
- `src/lite_main.c` starts the file-manager payload, handles clean reload, and
starts the web server.
- `src/boot_marker.c` writes the earliest cross-payload boot marker.
- `src/websrv_lite.c` serves `/`, `/api/status`, `/api/diag`, `/fs`, and
`/api/fs/*`.
- `src/transfer.c` contains upload, copy, move, delete, and long-running job
handling.
- `src/launcher_installer_main.c` is the isolated optional launcher installer.
- `tests/` contains small probe payloads for loader, HTTP, notification, and
AppInstUtil testing.
Cancel is cooperative. Large operations may take a moment to stop, especially on
external USB drives, but the job code checks for cancellation during scan, copy,
move cleanup, and delete.
## Release Checklist
Complete this before publishing `v0.2.1`:
- Confirm `VERSION_TAG` is `bfpilot-v0.2.1`.
- Build from clean with `make clean all`.
- Confirm both files exist: `bfpilot-core.elf` and `bfpilot-full.elf`.
- Confirm `bfpilot-core.elf` is the recommended download in release notes.
- Confirm core mode does not include launcher installer objects.
- Confirm core mode does not link launcher-only SCE libraries.
- Confirm full mode treats launcher failure as non-fatal.
- Run Stage A from `docs/FIRMWARE_TESTING.md` on each available firmware.
- Run Stage C only after Stage A passes on that firmware.
- Save `/api/diag` output for tested firmware/loader combinations.
- Save `/data/BFpilot/log.txt` for failed tests.
- Fill in the compatibility table in `docs/FIRMWARE_TESTING.md`.
- Attach both ELFs to the release.
- In the release notes, tell users to try `bfpilot-core.elf` first.
See [docs/COMPATIBILITY_STRATEGY.md](docs/COMPATIBILITY_STRATEGY.md) for the
firmware compatibility strategy and [docs/FIRMWARE_TESTING.md](docs/FIRMWARE_TESTING.md)
for the test protocol.
+26 -3
View File
@@ -651,7 +651,8 @@
uploadBusy: false,
uploadAbort: false,
uploadXhr: null,
jobTimer: 0
jobTimer: 0,
jobHideTimer: 0
};
function el(id) {
@@ -792,6 +793,10 @@
clearInterval(state.jobTimer);
state.jobTimer = 0;
}
if (state.jobHideTimer) {
clearTimeout(state.jobHideTimer);
state.jobHideTimer = 0;
}
el('job').classList.add('show');
el('job-verb').textContent = verb || 'Working';
el('job-text').textContent = text || '';
@@ -1075,6 +1080,7 @@
const done = await waitJobIdle();
if (done.error) throw new Error(done.error);
} catch (err) {
if ((err.message || '').toLowerCase().includes('job')) pollJob();
toast(verb + ': ' + err.message, 'err');
break;
}
@@ -1153,6 +1159,7 @@
const done = await waitJobIdle();
if (done.error) throw new Error(done.error);
} catch (err) {
if ((err.message || '').toLowerCase().includes('job')) pollJob();
toast('delete: ' + err.message, 'err');
break;
}
@@ -1250,7 +1257,9 @@
toast(ok + ' uploaded' + (failed ? ', ' + failed + ' failed' : ''), failed ? 'err' : 'ok');
}
setTimeout(() => {
if (!state.uploadBusy && !state.jobTimer) el('job').classList.remove('show');
if (!state.uploadBusy && !state.jobTimer && !state.jobHideTimer) {
el('job').classList.remove('show');
}
}, 1600);
}
@@ -1261,6 +1270,10 @@
const verb = el('job-verb');
if (state.jobTimer) clearInterval(state.jobTimer);
if (state.jobHideTimer) {
clearTimeout(state.jobHideTimer);
state.jobHideTimer = 0;
}
box.classList.add('show');
text.textContent = 'Starting';
let missed = 0;
@@ -1279,7 +1292,17 @@
clearInterval(state.jobTimer);
state.jobTimer = 0;
if (job.error) toast(job.error, 'err');
setTimeout(() => box.classList.remove('show'), 1400);
state.jobHideTimer = setTimeout(async () => {
state.jobHideTimer = 0;
try {
const latest = await api('/api/fs/job/status');
if (latest.busy) {
pollJob();
return;
}
} catch (err) {}
if (!state.uploadBusy && !state.jobTimer) box.classList.remove('show');
}, 1400);
}
} catch (err) {
missed++;
+192
View File
@@ -0,0 +1,192 @@
# BFpilot Compatibility Strategy
BFpilot now separates the stable file manager from the fragile launcher
installer path.
## Payload Roles
### `bfpilot.elf`
This is the main release payload. It is firmware-light and should be tested
first on every firmware and loader.
- Starts the HTTP file manager on port `5905`.
- Does not install a launcher tile.
- Does not include `src/app_installer.c`.
- Does not link `libSceAppInstUtil`.
- Does not depend on AppInstUtil, SystemService, UserService, or launcher-only
Sony libraries.
Broad firmware support comes from avoiding imports that can fail before
`main()` on some loaders or firmware combinations.
### `bfpilot-debug.elf`
This is the same file-manager payload with extra debug notifications/logging.
It still avoids AppInstUtil and launcher installer code.
Use it when `bfpilot.elf` appears to do nothing. If the debug payload writes
`/data/BFpilot/boot.log`, it reached `main()`.
### `bfpilot-full.elf`
This build restores the 0.2.0-style integrated launcher flow for firmware and
loaders where that path worked.
- Starts BFpilot from the same file-manager process.
- Runs optional `sceNetCtlInit` and `sceUserServiceInitialize` resolution/init.
- Dynamically resolves AppInstUtil symbols instead of direct-linking
AppInstUtil.
- Attempts `/user/app/BFPL00001` install/refresh before the web server loop.
- Continues to the web server if launcher install fails.
Use this first when specifically testing home-screen tile install on firmware
11.6, because it is closest to the previous full build that installed there.
### `bfpilot-launcher-installer.elf`
This optional payload installs or refreshes `/user/app/BFPL00001`.
It is intentionally isolated from the file manager:
- It direct-links AppInstUtil, matching the launcher-install pattern used by
websrv, Payload Manager, and ftpsrv.
- It logs to `/data/BFpilot/launcher-installer.log`.
- It cannot break the file manager because it is not part of `bfpilot.elf`.
If `bfpilot.elf` works but the launcher installer fails, the issue is the
AppInstUtil/launcher path, not the file manager.
### `bfpilot-launcher-installer-safe.elf`
This diagnostic payload avoids direct AppInstUtil imports and checks whether
AppInstUtil is already reachable through runtime symbol lookup. It is not the
force install path. Use it after the direct installer gives no notification or
log.
## Launcher/AppInst Findings
The current probes separate three cases:
- `tests/installer_enter_probe.elf` writes a marker with safe imports only.
If this works, the loader can run ordinary BFpilot-shaped ELFs.
- `tests/installer_linkonly_appinst.elf` directly imports AppInstUtil but does
not call it. If this payload does not write its marker, the loader or
firmware rejected the ELF before `main()`.
- `tests/installer_runtime_resolve_appinst.elf` has safe imports only and then
checks whether `libSceAppInstUtil.sprx` is already loaded. If
`kernel_dynlib_handle` returns `0xffffffff`, AppInstUtil is not reachable
through the safe runtime path.
On that combination, the safe runtime path cannot install. The force installer
uses the fragile direct AppInst import path because that is the pattern used by
working launcher installers. If the loader rejects that import before `main()`,
no code inside that ELF can recover. The file manager remains supported because
it avoids that import entirely.
## Patterns Checked
`ps5-payload-dev/websrv`, Payload Manager by itsPLK, and elf-arsenal launcher
installers all use the same basic AppInst pattern:
- link `libSceAppInstUtil` directly;
- call `sceAppInstUtilInitialize()` directly;
- resolve only `sceAppInstUtilAppInstallTitleDir` by NID `Wudg3Xe3heE`;
- fall back to `sceAppInstUtilAppInstallAll()`.
That pattern can work in environments where direct AppInst imports are accepted,
but it can also fail before `main()` on loaders that reject or cannot satisfy
the import. BFpilot keeps this pattern out of `bfpilot.elf`.
Some payloads also depend on a system-authid or other elevated runtime context
before AppInst init can succeed on newer firmware. BFpilot does not implement
authid changes, kernel patches, kstuff integration, forced unloads, package
installers, or DRM-related behavior. Those are outside the compatibility goal
for the standalone file manager.
## Boot Marker
Every ELF calls:
```c
bfpilot_boot_marker(payload_name, build_mode);
```
as the first meaningful line in `main()`.
The marker:
- Prints `BFpilot BOOT: <payload_name> <build_mode> entered main`.
- Tries to create `/data/BFpilot`.
- Appends payload name, build mode, PID, and build version to
`/data/BFpilot/boot.log`.
- Attempts a notification titled `BFpilot BOOT`.
- Continues if notification fails.
If no notification appears and no `/data/BFpilot/boot.log` entry exists, the
payload probably failed before `main()` or the loader rejected the ELF. If the
boot marker exists but the server does not start, the crash or hang is after
`main()`.
## Reload Behavior
Startup is explicit:
1. Boot marker runs.
2. BFpilot checks whether the selected port already has an old BFpilot server.
3. If an old server is found, BFpilot notifies `BFpilot reload: old server detected`.
4. BFpilot asks the old server to shut down through `/api/control/shutdown`.
5. If shutdown succeeds, the new server starts.
6. If shutdown fails or a file job is busy, BFpilot notifies `BFpilot reload blocked`,
logs the exact reason to `/data/BFpilot/log.txt`, and exits cleanly.
This avoids the misleading case where reinjection looks like nothing happened.
## Alternate Port
The default web port is compile-time configurable with `BFPILOT_WEB_PORT`.
Runtime port override is supported:
```text
--port 5906
```
Use this to test a new build while an old BFpilot instance is still running on
`5905`.
## Import Inspection
Run:
```sh
make inspect-imports
```
The target prints file size, dynamic libraries/imports, undefined symbols, and
SCE-related symbol/string hints for:
- `bfpilot.elf`
- `bfpilot-debug.elf`
- `bfpilot-full.elf`
- `bfpilot-launcher-installer.elf`
- `bfpilot-launcher-installer-safe.elf`
- `tests/hello_boot.elf`
- `tests/hello_http.elf`
- `tests/hello_notify.elf`
- `tests/installer_enter_probe.elf`
- `tests/installer_linkonly_appinst.elf`
- `tests/installer_runtime_resolve_appinst.elf`
It fails if `bfpilot.elf` or `bfpilot-debug.elf` contain:
- `SceAppInstUtil`
- `sceAppInst`
- `app_installer`
- `BFPL00001`
The only payloads allowed to directly import AppInstUtil are
`bfpilot-launcher-installer.elf` and
`tests/installer_linkonly_appinst.elf`. The link-only test exists specifically
to prove whether that loader/firmware rejects AppInstUtil-linked ELFs before
`main()`.
+183 -160
View File
@@ -1,189 +1,212 @@
# BFpilot Firmware Testing
This protocol checks whether BFpilot runs safely across jailbroken PS5 firmwares and loaders. It does not require exploit changes, kernel patching, package loading, or DRM-related features.
Use this protocol on every firmware/loader combination. Test the file manager
first. Test launcher/AppInstUtil only after the file manager works.
Use `bfpilot-core.elf` first on every firmware. Only test `bfpilot-full.elf` after the core server is confirmed working.
## Before Testing
Record these details before each run:
- PS5 firmware version.
- Exploit and loader name/version.
- Payload file used: `bfpilot-core.elf` or `bfpilot-full.elf`.
- PS5 IP address.
- Sender tool and command used.
- Whether `/data/BFpilot/log.txt` and `/data/BFpilot/crash.log` exist after the run.
Clear old browser tabs that point to BFpilot before reinjecting. If a stage fails, keep the PS5 powered on long enough to collect `/data/BFpilot/log.txt`, `/data/BFpilot/crash.log`, `/api/status`, and `/api/diag` if reachable.
## Stage A: Core Server Only
Purpose: prove the firmware and loader can run BFpilot's standalone HTTP file manager without launcher installation.
1. Send `bfpilot-core.elf` to the loader on port `9021`.
2. Verify the sender console reports that the ELF was delivered without socket/send errors.
3. Watch for BFpilot startup output from the loader console, if available.
4. Confirm `/data/BFpilot/log.txt` is created or appended.
5. Open `http://PS5_IP:5905/` in a browser on the same network.
6. Open `http://PS5_IP:5905/api/status`.
7. Open `http://PS5_IP:5905/api/diag`.
8. Browse `/data` in the file manager.
9. Browse `/mnt/usb0` with an external drive connected.
10. Create or open `/data/BFpilot-test`.
11. Upload a small text file to `/data/BFpilot-test`.
12. Copy that text file to another name in `/data/BFpilot-test`.
13. Move or rename the copied file.
14. Delete the moved file.
15. Delete the original uploaded test file.
16. Reopen `/api/diag` and save the output.
Pass criteria:
- The server starts on port `5905`.
- `/`, `/api/status`, `/api/diag`, `/fs`, and `/api/fs/*` are reachable.
- Browsing `/data` works.
- Browsing `/mnt/usb0` either works or fails cleanly with a clear permission/path error.
- Upload, copy, move, and delete work inside `/data/BFpilot-test`.
- No crash log is created during normal file operations.
Fail data to collect:
- Sender console output.
- `/data/BFpilot/log.txt`.
- `/data/BFpilot/crash.log`, if present.
- The HTTP status and response body from `/api/status` and `/api/diag`, if reachable.
- Exact browser error if the page times out.
## Stage B: Notification Enabled
Purpose: verify notification support is optional and cannot stop the web server.
1. Start from a PS5 state where Stage A passed.
2. Send `bfpilot-core.elf` again, or start `bfpilot-full.elf` with launcher disabled using `--no-launcher` or `BFPILOT_NO_LAUNCHER=1` if the loader supports arguments/environment.
3. Trigger the BFpilot notification path by starting the payload and watching for the startup notification attempt.
4. Open `http://PS5_IP:5905/`.
5. Open `http://PS5_IP:5905/api/diag`.
6. Check `/data/BFpilot/log.txt` for notification return code entries.
Pass criteria:
- If a notification appears, the server still starts.
- If the notification fails, the server still starts.
- `/api/diag` remains reachable after the notification attempt.
- The log records the notification result when available.
Fail data to collect:
Record:
- Firmware version.
- Exploit/loader name and version.
- Payload name.
- Whether `/data/BFpilot/boot.log` received a new entry.
- Whether a notification appeared.
- Notification return code from `/data/BFpilot/log.txt`.
- `/api/diag` output after startup.
- Whether the HTTP server started.
- `/api/status` and `/api/diag` output when reachable.
- Relevant log files under `/data/BFpilot`.
## Stage C: Launcher Enabled
## Stage A: Main File Manager
Purpose: verify full mode can attempt launcher install/refresh without making launcher failure fatal.
1. Send `bfpilot.elf` to the loader on port `9021`.
2. Verify `BFpilot BOOT` notification if notifications are working.
3. Verify `/data/BFpilot/boot.log` has a new `bfpilot file-manager` entry.
4. Open `http://PS5_IP:5905/`.
5. Open `http://PS5_IP:5905/api/status`.
6. Open `http://PS5_IP:5905/api/diag`.
7. Browse `/data`.
8. Browse `/mnt/usb0` if an external drive is connected.
9. Upload a small text file to `/data/BFpilot-test`.
10. Copy, move, and delete that test file.
11. Save `/data/BFpilot/log.txt`.
Only run this stage after Stage A passes on the same firmware and loader.
Pass criteria:
- Boot marker appears.
- Server starts on port `5905`.
- `/`, `/api/status`, `/api/diag`, `/fs`, and `/api/fs/*` are reachable.
- Basic file operations work in `/data/BFpilot-test`.
## Stage B: Debug File Manager
Use this when Stage A appears to do nothing or when testing a new loader.
1. Send `bfpilot-debug.elf` to the loader on port `9021`.
2. Verify `/data/BFpilot/boot.log` has a new `bfpilot-debug debug` entry.
3. Open `http://PS5_IP:5905/api/status`.
4. Open `http://PS5_IP:5905/api/diag`.
5. Save `/data/BFpilot/log.txt`.
Expected result:
- If `boot.log` has no new entry, the payload likely failed before `main()` or
the loader rejected it.
- If `boot.log` has an entry but `/api/status` is unreachable, the failure is
after `main()` and before or during server startup.
## Stage C: Alternate Port
Use this when another BFpilot instance may already be running on `5905`.
1. Send `bfpilot-debug.elf --port 5906` to the loader.
2. Open `http://PS5_IP:5906/api/status`.
3. Open `http://PS5_IP:5906/api/diag`.
4. Save `/data/BFpilot/log.txt`.
Pass criteria:
- The new payload can start on `5906` without touching the old server on `5905`.
## Stage D: Reinjection
1. Start `bfpilot.elf`.
2. Reinject `bfpilot.elf` while idle.
3. Verify notification `BFpilot reload: old server detected`.
4. Verify the old listener stops cleanly and the new server starts.
5. Start a large copy or move.
6. Reinject `bfpilot.elf` during the active job.
7. Verify notification `BFpilot reload blocked`.
8. Verify the old job is not killed.
9. Save `/data/BFpilot/log.txt`.
Pass criteria:
- Idle reinjection performs a clean handoff.
- Active file jobs are not interrupted.
- Reload exits are logged and notified instead of appearing silent.
## Stage E: Launcher Installer
Only run this after Stage A passes on the same firmware/loader.
### Integrated Full Build
This is the first installer-capable build to try on firmware 11.6, because it
preserves the 0.2.0-style launcher flow that already installed there.
1. Send `bfpilot-full.elf` to the loader on port `9021`.
2. Watch for launcher install or refresh messages.
2. Watch for `BFpilot BOOT`, `BFpilot app`, and `BFpilot started`
notifications.
3. Open `http://PS5_IP:5905/`.
4. Open `http://PS5_IP:5905/api/status`.
5. Open `http://PS5_IP:5905/api/diag`.
6. Record the launcher fields from `/api/diag`.
7. Check whether the BFpilot launcher tile appears or refreshes on the PS5 home screen.
8. If the launcher tile does not appear, keep testing the web server anyway.
9. Browse `/data`.
10. Upload a small text file to `/data/BFpilot-test`.
11. Delete the uploaded test file.
12. Save `/data/BFpilot/log.txt`.
4. Save `/data/BFpilot/log.txt`.
5. Open `http://PS5_IP:5905/api/diag` if the web server starts.
6. Check whether the BFpilot tile appears or refreshes.
Pass criteria:
- On firmware where AppInstUtil runtime resolution works, the tile installs or
refreshes.
- If launcher install fails, the web server still starts and logs the exact
AppInst resolution/init/install return codes.
### Isolated Direct Installer
Use this after Stage A if you want to test the direct AppInst import pattern
used by websrv/Payload Manager/ftpsrv. It may fail before `main()` if the loader
rejects AppInstUtil imports.
1. Send `bfpilot-launcher-installer.elf` to the loader on port `9021`.
2. Watch for `BFpilot BOOT` and launcher installer notifications.
3. Check whether the BFpilot tile appears or refreshes.
4. Save `/data/BFpilot/launcher-installer.log`.
5. Save `/data/BFpilot/boot.log`.
Launcher installer fields to record from the log:
- `entered main`.
- `AppInst init return code`.
- `/user/app/BFPL00001` mkdir return code.
- `param.json` and `icon0.png` write return code.
- `AppInstallTitleDir resolved`.
- `AppInstallTitleDir return code`.
- `AppInstallAll fallback return code`.
- `final result`.
Pass criteria:
- Launcher install may succeed or fail.
- Launcher failure must not stop the HTTP server.
- `/api/diag` records AppInstUtil resolution and return codes.
- File browsing and basic file operations still work after any launcher failure.
- Launcher failure does not affect `bfpilot.elf`.
- File manager remains testable through `http://PS5_IP:5905/`.
Launcher fields to record:
If the direct installer gives no notification and no log:
- `launcher_enabled`.
- `launcher_attempted`.
- `appinst_init_rc`.
- `title_dir_resolved`.
- `install_title_dir_resolved`.
- `install_title_rc`.
- `uninstall_resolved`.
- `uninstall_rc`.
- `install_all_resolved`.
- `install_all_rc`.
- `user_app_writable`.
- `launcher_install_rc`.
- `launcher_final_state`.
1. Send `bfpilot-launcher-installer-safe.elf`.
2. Save `/data/BFpilot/launcher-installer.log`.
3. If the safe installer reaches `main()` but logs
`kernel_dynlib_handle libSceAppInstUtil.sprx rc=0xffffffff`, AppInstUtil is
not available through the safe runtime path.
4. If `tests/installer_linkonly_appinst.elf` also produces no marker, direct
AppInst imports fail before `main()` on that loader/firmware.
Fail data to collect:
In that case the direct installer is being rejected before it can run, while
the main file manager remains usable through `http://PS5_IP:5905/`.
- The last launcher-related lines from `/data/BFpilot/log.txt`.
- Full `/api/diag` output.
- Whether the tile appeared, refreshed, disappeared, or did not change.
- Whether the web server continued running after launcher failure.
## Stage F: Minimal Probe Payloads
## Stage D: Reinjection
Use these to isolate loader, notification, HTTP, and AppInstUtil behavior.
Purpose: verify reinjecting BFpilot does not kill unrelated payloads or interrupt active work in an unsafe way.
### `tests/hello_boot.elf`
1. Start `bfpilot-core.elf`.
2. Open `http://PS5_IP:5905/` and confirm the server is idle.
3. Inject `bfpilot-core.elf` again while idle.
4. Verify the web server still responds.
5. Open `/api/diag` and record uptime, PID, and any old-instance message.
6. Start a copy operation using a test file inside `/data/BFpilot-test`.
7. While the copy is active, inject `bfpilot-core.elf` again.
8. Verify the old copy job is not killed by the new injection.
9. Verify the web server remains reachable after the copy finishes.
10. Repeat the idle reinjection test once with `bfpilot-full.elf` only after full mode passed Stage C.
1. Send `tests/hello_boot.elf`.
2. Verify boot marker notification.
3. Verify `/data/BFpilot/boot.log`.
4. Wait 10 seconds for `BFpilot BOOT still alive`.
Pass criteria:
### `tests/hello_http.elf`
- Reinjection while idle leaves one reachable BFpilot server.
- Reinjection during active copy does not kill the active job.
- Existing unrelated payloads are not forcibly unloaded.
- Logs clearly state whether the new instance started, reused an existing state, or exited because another BFpilot instance was already active.
1. Send `tests/hello_http.elf`.
2. Open `http://PS5_IP:5906/api/status`.
Fail data to collect:
### `tests/hello_notify.elf`
- `/data/BFpilot/log.txt` from before and after reinjection.
- `/api/diag` before reinjection, during copy, and after copy.
- File copy source, destination, size, and whether the destination completed correctly.
- Sender console output for each injection.
1. Send `tests/hello_notify.elf`.
2. Record notification result from sender output and `/data/BFpilot/boot.log`.
### `tests/installer_enter_probe.elf`
1. Send `tests/installer_enter_probe.elf`.
2. Verify `/data/BFpilot/installer_enter_probe.txt` exists.
3. If it does not exist, the loader rejected even the safe installer-shaped
probe before `main()`.
### `tests/installer_linkonly_appinst.elf`
1. Send `tests/installer_linkonly_appinst.elf`.
2. Verify `/data/BFpilot/linkonly_appinst_entered.txt` exists.
3. If it does not exist, direct AppInstUtil linking is incompatible with that
loader/firmware and installer code must avoid direct AppInst imports.
### `tests/installer_runtime_resolve_appinst.elf`
1. Send `tests/installer_runtime_resolve_appinst.elf`.
2. Verify `/data/BFpilot/runtime_resolve_entered.txt` exists.
3. Save `/data/BFpilot/runtime_resolve_appinst.log`.
4. Use the log to see whether `kernel_dynlib_handle`, symbol lookup, and NID
resolve reached AppInstUtil.
Interpretation:
- `kernel_dynlib_handle ... rc=0xffffffff` means AppInstUtil is not already
loaded in this process.
- BFpilot intentionally skips `dlopen` for AppInstUtil after a prior test stops
before the `dlopen` result log, because that path is not safe enough for a
compatibility payload.
## Compatibility Table
| firmware | exploit/loader | payload used | server starts | file browse | upload | copy | move | delete | launcher install | notes |
|---|---|---|---|---|---|---|---|---|---|---|
| 11.60 | | `bfpilot-core.elf` | | | | | | | N/A | |
| 11.60 | | `bfpilot-full.elf` | | | | | | | | |
| 12.70 | | `bfpilot-core.elf` | | | | | | | N/A | |
| 12.70 | | `bfpilot-full.elf` | | | | | | | | |
| | | `bfpilot-core.elf` | | | | | | | N/A | |
| | | `bfpilot-full.elf` | | | | | | | | |
Use `yes`, `no`, or `partial` for result columns. Put exact error codes, HTTP status codes, and log checkpoints in `notes`.
## Recommended Report Format
```text
Firmware:
Exploit/loader:
Payload:
Sender command/tool:
PS5 IP:
Server URL:
Stage passed:
First failing step:
/api/status result:
/api/diag result:
Relevant log lines:
Crash log present:
Notes:
```
| firmware | exploit/loader | payload used | boot marker | server starts | file browse | upload | copy | move | delete | launcher install | notes |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 12.70 | | bfpilot.elf | | | | | | | | n/a | |
| 12.70 | | bfpilot-full.elf | | | | | | | | | |
| 12.70 | | bfpilot-launcher-installer.elf | | n/a | n/a | n/a | n/a | n/a | n/a | | |
| 11.60 | | bfpilot.elf | | | | | | | | n/a | |
| 11.60 | | bfpilot-full.elf | | | | | | | | | |
| 11.60 | | bfpilot-launcher-installer.elf | | n/a | n/a | n/a | n/a | n/a | n/a | | |
+149
View File
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
set -euo pipefail
find_tool() {
local name="$1"
if [[ -n "${LLVM_BINDIR:-}" ]]; then
if [[ -x "${LLVM_BINDIR}/${name}" ]]; then
printf '%s\n' "${LLVM_BINDIR}/${name}"
return 0
fi
if [[ -x "${LLVM_BINDIR}/${name}.exe" ]]; then
printf '%s\n' "${LLVM_BINDIR}/${name}.exe"
return 0
fi
fi
if command -v "${name}" >/dev/null 2>&1; then
command -v "${name}"
return 0
fi
if command -v "${name}.exe" >/dev/null 2>&1; then
command -v "${name}.exe"
return 0
fi
return 1
}
READELF="$(find_tool llvm-readelf || find_tool readelf || true)"
READOBJ="$(find_tool llvm-readobj || true)"
NM="$(find_tool llvm-nm || find_tool nm || true)"
STRINGS="$(find_tool llvm-strings || find_tool strings || true)"
TMP_DIR="${TMPDIR:-/tmp}"
if [[ -z "${STRINGS}" ]]; then
echo "inspect-imports: strings or llvm-strings is required" >&2
exit 1
fi
if [[ $# -eq 0 ]]; then
echo "inspect-imports: no ELF files provided" >&2
exit 1
fi
needed_libraries() {
local file="$1"
if [[ -n "${READOBJ}" ]]; then
"${READOBJ}" --needed-libs "${file}" 2>/dev/null |
grep -E '^[[:space:]]+[^[:space:]]+\.sprx$' |
sed -E 's/^[[:space:]]+//'
elif [[ -n "${READELF}" ]]; then
"${READELF}" -d "${file}" 2>/dev/null |
sed -nE 's/.*Shared library: \[([^]]+)\].*/\1/p'
fi
}
check_no_direct_appinst_import() {
local file="$1"
local needed
needed="$(needed_libraries "${file}" || true)"
if printf '%s\n' "${needed}" |
grep -E 'libSceAppInstUtil\.sprx|libSceSystemService\.sprx|libSceUserService\.sprx' >/dev/null; then
echo
echo "inspect-imports: unsafe direct launcher import in ${file}" >&2
printf '%s\n' "${needed}" >&2
return 1
fi
}
for file in "$@"; do
echo
echo "== ${file} =="
if [[ ! -f "${file}" ]]; then
echo "missing"
continue
fi
size="$(wc -c < "${file}" | tr -d '[:space:]')"
echo "file size: ${size} bytes"
echo "-- dynamic libraries/imports --"
if [[ -n "${READOBJ}" ]]; then
"${READOBJ}" --needed-libs "${file}" || true
elif [[ -n "${READELF}" ]]; then
"${READELF}" -d "${file}" | grep -E 'NEEDED|SONAME' || true
else
echo "no llvm-readobj/readelf available"
fi
echo "-- undefined symbols --"
if [[ -n "${READELF}" ]]; then
"${READELF}" --dyn-symbols "${file}" | grep -E 'UND|Undefined' || true
elif [[ -n "${NM}" ]]; then
"${NM}" -u "${file}" || true
else
echo "no llvm-readelf/readelf/nm available"
fi
echo "-- SCE-related strings/symbol hints --"
"${STRINGS}" "${file}" | grep -Ei '(^|[^A-Za-z0-9_])(sce|Sce|AppInst|SystemService|UserService|BFPL00001|app_installer)' || true
done
for file in "$@"; do
case "${file}" in
bfpilot-launcher-installer.elf|./bfpilot-launcher-installer.elf)
continue
;;
tests/installer_linkonly_appinst.elf|./tests/installer_linkonly_appinst.elf)
continue
;;
esac
if [[ -f "${file}" ]]; then
check_no_direct_appinst_import "${file}"
fi
done
for clean_file in bfpilot.elf bfpilot-debug.elf; do
if [[ ! -f "${clean_file}" ]]; then
continue
fi
if "${STRINGS}" "${clean_file}" |
grep -E 'SceAppInstUtil|sceAppInst|app_installer|BFPL00001' >"${TMP_DIR}/bfpilot-forbidden-imports.txt"; then
echo
echo "inspect-imports: forbidden launcher/AppInst content in ${clean_file}" >&2
cat "${TMP_DIR}/bfpilot-forbidden-imports.txt" >&2
exit 1
fi
done
if [[ -f tests/installer_linkonly_appinst.elf ]]; then
if ! needed_libraries tests/installer_linkonly_appinst.elf |
grep -q 'libSceAppInstUtil\.sprx'; then
echo
echo "inspect-imports: tests/installer_linkonly_appinst.elf must directly import libSceAppInstUtil.sprx" >&2
exit 1
fi
fi
if [[ -f bfpilot-launcher-installer.elf ]]; then
if ! needed_libraries bfpilot-launcher-installer.elf |
grep -q 'libSceAppInstUtil\.sprx'; then
echo
echo "inspect-imports: bfpilot-launcher-installer.elf must directly import libSceAppInstUtil.sprx" >&2
exit 1
fi
fi
echo
echo "inspect-imports: compatibility checks passed"
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
"""Remove SDK startup-table launcher fingerprints from main BFpilot payloads."""
from __future__ import annotations
import pathlib
import sys
REPLACEMENTS = {
b"libSceAppInstUtil.sprx": b"libBfpilotCleanMain.sx",
}
def scrub(path: pathlib.Path) -> int:
data = path.read_bytes()
changed = False
for old, new in REPLACEMENTS.items():
if len(old) != len(new):
raise ValueError(f"replacement length mismatch for {old!r}")
if old in data:
data = data.replace(old, new)
changed = True
if changed:
path.write_bytes(data)
return 1 if changed else 0
def main(argv: list[str]) -> int:
if len(argv) < 2:
print("usage: scrub_main_payload.py <elf> [<elf>...]", file=sys.stderr)
return 2
for name in argv[1:]:
path = pathlib.Path(name)
changed = scrub(path)
print(f"scrub-main-payload: {path} changed={changed}")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))
+94 -51
View File
@@ -46,30 +46,27 @@
INCASSET(bfpilot_param_json, "assets-app/param.json");
INCASSET(bfpilot_icon0_png, "assets-app/icon0.png");
typedef int (*appinst_initialize_fn)(void);
typedef int (*appinst_uninstall_fn)(const char *);
typedef int (*app_install_title_dir_fn)(const char *, const char *, void *);
int sceAppInstUtilInitialize(void);
int sceAppInstUtilAppInstallAll(void *);
typedef int (*appinst_install_all_fn)(void *);
typedef struct appinst_api {
appinst_initialize_fn initialize;
appinst_uninstall_fn uninstall;
app_install_title_dir_fn install_title_dir;
appinst_install_all_fn install_all;
} appinst_api_t;
static const uint8_t g_install_marker[] = BFPILOT_INSTALL_MARKER;
static bfpilot_launcher_diag_t g_launcher_diag = {
.launcher_enabled = 1,
.launcher_attempted = 0,
.appinst_init_rc = -1,
.title_dir_resolved = 0,
.install_title_dir_resolved = 0,
.install_title_rc = BFPILOT_DIAG_SKIPPED,
.uninstall_resolved = 0,
.uninstall_rc = BFPILOT_DIAG_SKIPPED,
.install_all_resolved = 0,
.install_all_rc = BFPILOT_DIAG_SKIPPED,
.user_app_writable = -1,
.launcher_install_rc = -1,
.launcher_final_state = "skipped",
};
@@ -83,31 +80,25 @@ static void
reset_launcher_diag(void) {
bfpilot_launcher_diag_t diag = {
.launcher_enabled = 1,
.launcher_attempted = 1,
.appinst_init_rc = -1,
.title_dir_resolved = 0,
.install_title_dir_resolved = 0,
.install_title_rc = BFPILOT_DIAG_SKIPPED,
.uninstall_resolved = 0,
.uninstall_rc = BFPILOT_DIAG_SKIPPED,
.install_all_resolved = 0,
.install_all_rc = BFPILOT_DIAG_SKIPPED,
.user_app_writable = -1,
.launcher_install_rc = -1,
.launcher_final_state = "skipped",
};
g_launcher_diag = diag;
publish_launcher_diag();
}
static void
set_launcher_final(const char *state, int rc) {
snprintf(g_launcher_diag.launcher_final_state,
sizeof(g_launcher_diag.launcher_final_state), "%s",
state ? state : "failed_nonfatal");
g_launcher_diag.launcher_install_rc = rc;
publish_launcher_diag();
static int
resolve_optional(void **out, const char *symbol, const char *fallback_symbol) {
int rc = sce_resolve_symbol(BFPILOT_APPINST_MODULE, symbol, out);
if(rc != 0 && fallback_symbol) {
rc = sce_resolve_symbol(BFPILOT_APPINST_MODULE, fallback_symbol, out);
}
return rc;
}
@@ -115,20 +106,29 @@ static void
resolve_appinst(appinst_api_t *api) {
memset(api, 0, sizeof(*api));
int rc = sce_resolve_symbol(BFPILOT_APPINST_MODULE,
BFPILOT_NID_INSTALL_TITLE_DIR,
(void **)&api->install_title_dir);
g_launcher_diag.title_dir_resolved = api->install_title_dir != NULL;
int rc = resolve_optional((void **)&api->initialize,
"sceAppInstUtilInitialize", NULL);
bfpilot_log("launcher resolve Initialize %s rc=0x%08x",
api->initialize ? "ok" : "missing", rc);
rc = resolve_optional((void **)&api->uninstall,
"sceAppInstUtilAppUnInstall", NULL);
g_launcher_diag.uninstall_resolved = api->uninstall != NULL;
bfpilot_log("launcher resolve AppUnInstall %s rc=0x%08x",
api->uninstall ? "ok" : "missing", rc);
rc = resolve_optional((void **)&api->install_title_dir,
"sceAppInstUtilAppInstallTitleDir",
BFPILOT_NID_INSTALL_TITLE_DIR);
g_launcher_diag.install_title_dir_resolved = api->install_title_dir != NULL;
bfpilot_log("launcher resolve AppInstallTitleDir NID %s rc=0x%08x",
bfpilot_log("launcher resolve AppInstallTitleDir %s rc=0x%08x",
api->install_title_dir ? "ok" : "missing", rc);
g_launcher_diag.uninstall_resolved = 0;
g_launcher_diag.uninstall_rc = BFPILOT_DIAG_SKIPPED;
bfpilot_log("launcher uninstall skipped for compatibility");
g_launcher_diag.install_all_resolved = 1;
bfpilot_log("launcher AppInstallAll uses linked SDK stub");
rc = resolve_optional((void **)&api->install_all,
"sceAppInstUtilAppInstallAll", NULL);
g_launcher_diag.install_all_resolved = api->install_all != NULL;
bfpilot_log("launcher resolve AppInstallAll %s rc=0x%08x",
api->install_all ? "ok" : "missing", rc);
publish_launcher_diag();
}
@@ -153,23 +153,26 @@ static int
install_app(const appinst_api_t *api, const char *title_id, const char *dir) {
if(api->install_title_dir) {
int err = api->install_title_dir(title_id, dir, NULL);
g_launcher_diag.install_title_rc = err;
g_launcher_diag.launcher_install_rc = err;
publish_launcher_diag();
if(err == 0) return 0;
bfpilot_log("launcher install AppInstallTitleDir failed rc=0x%08x", err);
} else {
g_launcher_diag.install_title_rc = BFPILOT_DIAG_SKIPPED;
publish_launcher_diag();
bfpilot_log("launcher install AppInstallTitleDir not resolved");
}
int err = sceAppInstUtilAppInstallAll(NULL);
g_launcher_diag.install_all_rc = err;
g_launcher_diag.launcher_install_rc = err;
if(api->install_all) {
int err = api->install_all(NULL);
g_launcher_diag.launcher_install_rc = err;
publish_launcher_diag();
bfpilot_log("launcher install AppInstallAll rc=0x%08x", err);
return err;
}
g_launcher_diag.launcher_install_rc = -2;
publish_launcher_diag();
bfpilot_log("launcher install AppInstallAll rc=0x%08x", err);
return err;
bfpilot_log("launcher install AppInstallAll not resolved");
return -2;
}
@@ -264,6 +267,7 @@ bfpilot_install_app_if_needed(void) {
char sce_sys_dir[256];
char param_path[256];
char icon_path[256];
char msg[128];
struct stat st;
reset_launcher_diag();
@@ -274,6 +278,22 @@ bfpilot_install_app_if_needed(void) {
bfpilot_log("launcher install %s writable=%d", BFPILOT_APP_ROOT,
g_launcher_diag.user_app_writable);
if(!api.initialize) {
g_launcher_diag.launcher_install_rc = -2;
publish_launcher_diag();
bfpilot_log("launcher install sceAppInstUtilInitialize not resolved");
bfpilot_notify("BFpilot app failed", "AppInst init not resolved");
return -1;
}
if(!api.install_title_dir && !api.install_all) {
g_launcher_diag.launcher_install_rc = -2;
publish_launcher_diag();
bfpilot_log("launcher install no AppInst install function resolved");
bfpilot_notify("BFpilot app failed", "AppInst install not resolved");
return -1;
}
snprintf(app_dir, sizeof(app_dir), BFPILOT_APP_ROOT "/%s",
BFPILOT_APP_TITLE_ID);
snprintf(sce_sys_dir, sizeof(sce_sys_dir), "%s/sce_sys", app_dir);
@@ -297,19 +317,35 @@ bfpilot_install_app_if_needed(void) {
bfpilot_notify("BFpilot app", "Installing PS5 home-screen launcher");
}
int err = sceAppInstUtilInitialize();
int err = api.initialize();
g_launcher_diag.appinst_init_rc = err;
publish_launcher_diag();
if(err) {
bfpilot_log("launcher install sceAppInstUtilInitialize failed rc=0x%08x",
err);
set_launcher_final("failed_nonfatal", err);
g_launcher_diag.launcher_install_rc = err;
publish_launcher_diag();
snprintf(msg, sizeof(msg), "AppInst init failed 0x%08x", err);
bfpilot_notify("BFpilot app failed", msg);
return -1;
}
g_launcher_diag.uninstall_rc = BFPILOT_DIAG_SKIPPED;
publish_launcher_diag();
bfpilot_log("launcher install AppUnInstall skipped");
if(api.uninstall) {
int uninstall_err = api.uninstall(BFPILOT_APP_TITLE_ID);
bfpilot_log("launcher install refresh BFpilot tile rc=0x%08x",
uninstall_err);
uninstall_err = api.uninstall(BFPILOT_INVALID_APP_TITLE_ID);
bfpilot_log("launcher install remove invalid BFpilot tile rc=0x%08x",
uninstall_err);
uninstall_err = api.uninstall(BFPILOT_LEGACY_APP_TITLE_ID);
bfpilot_log("launcher install remove BS5FileManager tile rc=0x%08x",
uninstall_err);
uninstall_err = api.uninstall(BFPILOT_OLD_LEGACY_APP_TITLE_ID);
bfpilot_log("launcher install remove old legacy tile rc=0x%08x",
uninstall_err);
} else {
bfpilot_log("launcher install AppUnInstall not resolved, skipping removes");
}
char invalid_dir[256];
snprintf(invalid_dir, sizeof(invalid_dir), BFPILOT_APP_ROOT "/%s",
@@ -321,28 +357,36 @@ bfpilot_install_app_if_needed(void) {
if(mkdir_if_needed(app_dir) != 0 || mkdir_if_needed(sce_sys_dir) != 0) {
bfpilot_log("launcher install mkdir failed errno=%d", errno);
set_launcher_final("failed_nonfatal", -errno);
g_launcher_diag.launcher_install_rc = -errno;
publish_launcher_diag();
snprintf(msg, sizeof(msg), "mkdir failed errno %d", errno);
bfpilot_notify("BFpilot app failed", msg);
return -1;
}
if(write_file(param_path, bfpilot_param_json, bfpilot_param_json_size) != 0) {
bfpilot_log("launcher install failed writing %s errno=%d",
param_path, errno);
set_launcher_final("failed_nonfatal", -errno);
g_launcher_diag.launcher_install_rc = -errno;
publish_launcher_diag();
bfpilot_notify("BFpilot app failed", "could not write param.json");
return -1;
}
if(write_file(icon_path, bfpilot_icon0_png, bfpilot_icon0_png_size) != 0) {
bfpilot_log("launcher install failed writing %s errno=%d",
icon_path, errno);
set_launcher_final("failed_nonfatal", -errno);
g_launcher_diag.launcher_install_rc = -errno;
publish_launcher_diag();
bfpilot_notify("BFpilot app failed", "could not write icon0.png");
return -1;
}
err = install_app(&api, BFPILOT_APP_TITLE_ID, BFPILOT_APP_PARENT);
if(err) {
bfpilot_log("launcher install install_app failed rc=0x%08x", err);
set_launcher_final("failed_nonfatal", err);
snprintf(msg, sizeof(msg), "register BFPL00001 failed 0x%08x", err);
bfpilot_notify("BFpilot app failed", msg);
return -1;
}
@@ -355,7 +399,6 @@ bfpilot_install_app_if_needed(void) {
BFPILOT_MARKER_PATH, errno);
}
set_launcher_final("installed", 0);
bfpilot_notify("BFpilot app ready",
"Tile BFPL00001 opens http://127.0.0.1:5905/");
return 1;
+77
View File
@@ -0,0 +1,77 @@
/*
* BFpilot - early payload entry marker.
*/
#include "boot_marker.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "notify.h"
#include "version.h"
#ifndef VERSION_TAG
#define VERSION_TAG "bfpilot-unknown"
#endif
#ifndef BUILD_VERSION
#define BUILD_VERSION "unknown"
#endif
#ifndef BFPILOT_BOOT_NOTIFY
#define BFPILOT_BOOT_NOTIFY 1
#endif
#define BFPILOT_DATA_ROOT "/data"
#define BFPILOT_DATA_DIR "/data/BFpilot"
#define BFPILOT_BOOT_LOG_PATH "/data/BFpilot/boot.log"
static void
mkdir_ignore(const char *path) {
if(mkdir(path, 0755) != 0 && errno != EEXIST) {
return;
}
}
void
bfpilot_boot_marker(const char *payload_name, const char *build_mode) {
const char *payload = payload_name && payload_name[0] ?
payload_name : "BFpilot";
const char *mode = build_mode && build_mode[0] ? build_mode : "unknown";
char line[512];
char notify_msg[128];
printf("BFpilot BOOT: %s %s entered main\n", payload, mode);
fflush(stdout);
mkdir_ignore(BFPILOT_DATA_ROOT);
mkdir_ignore(BFPILOT_DATA_DIR);
int n = snprintf(line, sizeof(line),
"%ld payload=%s mode=%s pid=%ld tag=%s build=%s entered main\n",
(long)time(NULL), payload, mode, (long)getpid(),
VERSION_TAG, BUILD_VERSION);
if(n > 0) {
if((size_t)n >= sizeof(line)) n = (int)sizeof(line) - 1;
int fd = open(BFPILOT_BOOT_LOG_PATH,
O_WRONLY | O_CREAT | O_APPEND, 0600);
if(fd >= 0) {
(void)write(fd, line, (size_t)n);
close(fd);
}
}
#if BFPILOT_BOOT_NOTIFY
snprintf(notify_msg, sizeof(notify_msg), "%s loaded", payload);
(void)bfpilot_notify_send("BFpilot BOOT", notify_msg);
#else
(void)notify_msg;
#endif
}
+7
View File
@@ -0,0 +1,7 @@
/*
* BFpilot - early payload entry marker.
*/
#pragma once
void bfpilot_boot_marker(const char *payload_name, const char *build_mode);
+252
View File
@@ -0,0 +1,252 @@
/*
* BFpilot - direct AppInst launcher tile installer.
*
* This intentionally uses the same direct AppInstUtil pattern as websrv,
* Payload Manager, and ftpsrv. If a loader rejects AppInstUtil imports, this
* ELF can fail before main; use bfpilot-launcher-installer-safe.elf to prove
* that case.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <ps5/kernel.h>
#include "boot_marker.h"
#include "notify.h"
#include "version.h"
#define BFPILOT_APP_TITLE_ID "BFPL00001"
#define BFPILOT_APP_ROOT "/user/app"
#define BFPILOT_APP_PARENT BFPILOT_APP_ROOT "/"
#define BFPILOT_DATA_ROOT "/data"
#define BFPILOT_DATA_DIR "/data/BFpilot"
#define BFPILOT_LAUNCHER_LOG "/data/BFpilot/launcher-installer.log"
#define BFPILOT_APPINST_MODULE "libSceAppInstUtil.sprx"
#define BFPILOT_NID_INSTALL_TITLE_DIR "Wudg3Xe3heE"
#define BFPILOT_DIAG_SKIPPED (-2147483000)
#define INCASSET(name, file) \
__asm__(".section .rodata\n" \
".global " #name "\n" \
".global " #name "_end\n" \
".global " #name "_size\n" \
".align 16\n" #name ":\n" \
".incbin \"" file "\"\n" #name "_end:\n" #name "_size:\n" \
".quad " #name "_end - " #name "\n" \
".previous\n"); \
extern const uint8_t name[]; \
extern const size_t name##_size
INCASSET(bfpilot_param_json, "assets-app/param.json");
INCASSET(bfpilot_icon0_png, "assets-app/icon0.png");
int sceAppInstUtilInitialize(void);
int sceAppInstUtilAppInstallAll(void *);
int sceAppInstUtilAppUnInstall(const char *);
typedef int (*app_install_title_dir_fn)(const char *, const char *, void *);
static int
mkdir_if_needed(const char *path) {
if(mkdir(path, 0755) == 0) return 0;
return errno == EEXIST ? 0 : -errno;
}
static void
ensure_log_dir(void) {
(void)mkdir_if_needed(BFPILOT_DATA_ROOT);
(void)mkdir_if_needed(BFPILOT_DATA_DIR);
}
static void
installer_log(const char *fmt, ...) {
char body[1024];
char line[1400];
va_list ap;
va_start(ap, fmt);
int body_n = vsnprintf(body, sizeof(body), fmt ? fmt : "", ap);
va_end(ap);
if(body_n < 0) {
snprintf(body, sizeof(body), "log format failed");
} else if((size_t)body_n >= sizeof(body)) {
body[sizeof(body) - 1] = 0;
}
int n = snprintf(line, sizeof(line), "%ld pid=%ld %s\n",
(long)time(NULL), (long)getpid(), body);
if(n < 0) return;
if((size_t)n >= sizeof(line)) n = (int)sizeof(line) - 1;
fputs(line, stdout);
fflush(stdout);
ensure_log_dir();
int fd = open(BFPILOT_LAUNCHER_LOG,
O_WRONLY | O_CREAT | O_APPEND, 0600);
if(fd >= 0) {
(void)write(fd, line, (size_t)n);
close(fd);
}
}
static int
write_file(const char *path, const uint8_t *data, size_t size) {
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(fd < 0) return -errno;
size_t done = 0;
while(done < size) {
ssize_t wr = write(fd, data + done, size - done);
if(wr < 0) {
int err = errno;
close(fd);
return -err;
}
done += (size_t)wr;
}
if(close(fd) != 0) return -errno;
return 0;
}
static app_install_title_dir_fn
resolve_title_dir(void) {
uint32_t handle = 0;
installer_log("about to kernel_dynlib_handle %s", BFPILOT_APPINST_MODULE);
int handle_rc = kernel_dynlib_handle(-1, BFPILOT_APPINST_MODULE, &handle);
installer_log("kernel_dynlib_handle %s rc=0x%08x handle=0x%08x",
BFPILOT_APPINST_MODULE, handle_rc, handle);
if(handle_rc != 0) return NULL;
installer_log("about to kernel_dynlib_resolve AppInstallTitleDir nid=%s",
BFPILOT_NID_INSTALL_TITLE_DIR);
app_install_title_dir_fn fn =
(app_install_title_dir_fn)kernel_dynlib_resolve(
-1, handle, BFPILOT_NID_INSTALL_TITLE_DIR);
installer_log("kernel_dynlib_resolve AppInstallTitleDir addr=%p",
(void *)fn);
return fn;
}
int
main(void) {
bfpilot_boot_marker("bfpilot-launcher-installer", BFPILOT_BUILD_MODE);
char app_dir[256];
char sce_sys_dir[256];
char param_path[256];
char icon_path[256];
int title_dir_rc = BFPILOT_DIAG_SKIPPED;
int install_all_rc = BFPILOT_DIAG_SKIPPED;
int uninstall_rc = BFPILOT_DIAG_SKIPPED;
int final_rc = -1;
installer_log("entered main direct-appinst tag=%s build=%s",
VERSION_TAG, BUILD_VERSION);
bfpilot_notify("BFpilot launcher installer", "direct AppInst installer");
installer_log("about to sceAppInstUtilInitialize");
int appinst_init_rc = sceAppInstUtilInitialize();
installer_log("AppInst init return code=0x%08x", appinst_init_rc);
if(appinst_init_rc != 0) {
installer_log("final result=failed_nonfatal final_rc=%d "
"appinst_init_rc=0x%08x", final_rc, appinst_init_rc);
bfpilot_notify("BFpilot launcher installer", "AppInst init failed");
return 1;
}
snprintf(app_dir, sizeof(app_dir), BFPILOT_APP_ROOT "/%s",
BFPILOT_APP_TITLE_ID);
snprintf(sce_sys_dir, sizeof(sce_sys_dir), "%s/sce_sys", app_dir);
snprintf(param_path, sizeof(param_path), "%s/param.json", sce_sys_dir);
snprintf(icon_path, sizeof(icon_path), "%s/icon0.png", sce_sys_dir);
installer_log("about to sceAppInstUtilAppUnInstall %s",
BFPILOT_APP_TITLE_ID);
uninstall_rc = sceAppInstUtilAppUnInstall(BFPILOT_APP_TITLE_ID);
installer_log("uninstall return code=0x%08x", uninstall_rc);
errno = 0;
int mkdir_app_rc = mkdir_if_needed(app_dir);
installer_log("%s mkdir return code=%d errno=%d",
app_dir, mkdir_app_rc, errno);
errno = 0;
int mkdir_sce_sys_rc = mkdir_if_needed(sce_sys_dir);
installer_log("%s mkdir return code=%d errno=%d",
sce_sys_dir, mkdir_sce_sys_rc, errno);
errno = 0;
int write_param_rc = write_file(param_path, bfpilot_param_json,
bfpilot_param_json_size);
installer_log("%s write return code=%d errno=%d",
param_path, write_param_rc, errno);
errno = 0;
int write_icon_rc = write_file(icon_path, bfpilot_icon0_png,
bfpilot_icon0_png_size);
installer_log("%s write return code=%d errno=%d",
icon_path, write_icon_rc, errno);
app_install_title_dir_fn install_title_dir = resolve_title_dir();
installer_log("AppInstallTitleDir resolved=%s nid=%s",
install_title_dir ? "yes" : "no",
BFPILOT_NID_INSTALL_TITLE_DIR);
if(mkdir_app_rc == 0 && mkdir_sce_sys_rc == 0 &&
write_param_rc == 0 && write_icon_rc == 0) {
if(install_title_dir) {
title_dir_rc = install_title_dir(BFPILOT_APP_TITLE_ID,
BFPILOT_APP_PARENT, NULL);
installer_log("AppInstallTitleDir return code=0x%08x", title_dir_rc);
} else {
installer_log("AppInstallTitleDir return code=skipped");
}
if(title_dir_rc != 0) {
installer_log("about to sceAppInstUtilAppInstallAll fallback");
install_all_rc = sceAppInstUtilAppInstallAll(NULL);
installer_log("AppInstallAll fallback return code=0x%08x",
install_all_rc);
} else {
installer_log("AppInstallAll fallback return code=skipped");
}
} else {
installer_log("AppInstallTitleDir return code=skipped");
installer_log("AppInstallAll fallback return code=skipped");
}
if(title_dir_rc == 0 || install_all_rc == 0) {
final_rc = 0;
}
installer_log("final result=%s final_rc=%d appinst_init_rc=0x%08x "
"uninstall_rc=0x%08x mkdir_app_rc=%d mkdir_sce_sys_rc=%d "
"write_param_rc=%d write_icon_rc=%d title_dir_rc=0x%08x "
"install_all_rc=0x%08x",
final_rc == 0 ? "installed" : "failed_nonfatal", final_rc,
appinst_init_rc, uninstall_rc, mkdir_app_rc,
mkdir_sce_sys_rc, write_param_rc, write_icon_rc,
title_dir_rc, install_all_rc);
if(final_rc == 0) {
bfpilot_notify("BFpilot launcher installer", "installed or refreshed");
return 0;
}
bfpilot_notify("BFpilot launcher installer", "failed; see installer log");
return 1;
}
+288
View File
@@ -0,0 +1,288 @@
/*
* BFpilot - isolated optional launcher tile installer.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <ps5/kernel.h>
#include "boot_marker.h"
#include "notify.h"
#include "version.h"
#define BFPILOT_APP_TITLE_ID "BFPL00001"
#define BFPILOT_APP_ROOT "/user/app"
#define BFPILOT_APP_PARENT BFPILOT_APP_ROOT "/"
#define BFPILOT_DATA_ROOT "/data"
#define BFPILOT_DATA_DIR "/data/BFpilot"
#define BFPILOT_LAUNCHER_LOG "/data/BFpilot/launcher-installer.log"
#define BFPILOT_APPINST_MODULE "libSceAppInstUtil.sprx"
#define BFPILOT_NID_INSTALL_TITLE_DIR "Wudg3Xe3heE"
#define BFPILOT_DIAG_SKIPPED (-2147483000)
#define INCASSET(name, file) \
__asm__(".section .rodata\n" \
".global " #name "\n" \
".global " #name "_end\n" \
".global " #name "_size\n" \
".align 16\n" #name ":\n" \
".incbin \"" file "\"\n" #name "_end:\n" #name "_size:\n" \
".quad " #name "_end - " #name "\n" \
".previous\n"); \
extern const uint8_t name[]; \
extern const size_t name##_size
INCASSET(bfpilot_param_json, "assets-app/param.json");
INCASSET(bfpilot_icon0_png, "assets-app/icon0.png");
typedef int (*appinst_init_fn)(void);
typedef int (*app_install_title_dir_fn)(const char *, const char *, void *);
typedef int (*app_install_all_fn)(void *);
static int
mkdir_if_needed(const char *path) {
if(mkdir(path, 0755) == 0) return 0;
return errno == EEXIST ? 0 : -errno;
}
static void
ensure_log_dir(void) {
(void)mkdir_if_needed(BFPILOT_DATA_ROOT);
(void)mkdir_if_needed(BFPILOT_DATA_DIR);
}
static void
installer_log(const char *fmt, ...) {
char body[1024];
char line[1400];
va_list ap;
va_start(ap, fmt);
int body_n = vsnprintf(body, sizeof(body), fmt ? fmt : "", ap);
va_end(ap);
if(body_n < 0) {
snprintf(body, sizeof(body), "log format failed");
} else if((size_t)body_n >= sizeof(body)) {
body[sizeof(body) - 1] = 0;
}
int n = snprintf(line, sizeof(line), "%ld pid=%ld %s\n",
(long)time(NULL), (long)getpid(), body);
if(n < 0) return;
if((size_t)n >= sizeof(line)) n = (int)sizeof(line) - 1;
fputs(line, stdout);
fflush(stdout);
ensure_log_dir();
int fd = open(BFPILOT_LAUNCHER_LOG,
O_WRONLY | O_CREAT | O_APPEND, 0600);
if(fd >= 0) {
(void)write(fd, line, (size_t)n);
close(fd);
}
}
static int
write_file(const char *path, const uint8_t *data, size_t size) {
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if(fd < 0) return -errno;
size_t done = 0;
while(done < size) {
ssize_t wr = write(fd, data + done, size - done);
if(wr < 0) {
int err = errno;
close(fd);
return -err;
}
done += (size_t)wr;
}
if(close(fd) != 0) return -errno;
return 0;
}
int
main(void) {
bfpilot_boot_marker("bfpilot-launcher-installer", BFPILOT_BUILD_MODE);
char app_dir[256];
char sce_sys_dir[256];
char param_path[256];
char icon_path[256];
uint32_t appinst_handle = 0;
appinst_init_fn appinst_init = NULL;
app_install_title_dir_fn install_title_dir = NULL;
app_install_all_fn install_all = NULL;
int appinst_handle_rc;
int title_dir_rc = BFPILOT_DIAG_SKIPPED;
int install_all_rc = BFPILOT_DIAG_SKIPPED;
int appinst_init_rc = BFPILOT_DIAG_SKIPPED;
int final_rc = -1;
const char *final_state = "unsupported";
installer_log("entered main tag=%s build=%s", VERSION_TAG, BUILD_VERSION);
installer_log("about to kernel_dynlib_handle %s", BFPILOT_APPINST_MODULE);
appinst_handle_rc = kernel_dynlib_handle(-1, BFPILOT_APPINST_MODULE,
&appinst_handle);
installer_log("kernel_dynlib_handle %s rc=0x%08x handle=0x%08x",
BFPILOT_APPINST_MODULE, appinst_handle_rc, appinst_handle);
if(appinst_handle_rc == 0) {
installer_log("about to kernel_dynlib_dlsym sceAppInstUtilInitialize");
appinst_init = (appinst_init_fn)kernel_dynlib_dlsym(
-1, appinst_handle, "sceAppInstUtilInitialize");
installer_log("kernel_dynlib_dlsym sceAppInstUtilInitialize addr=%p",
(void *)appinst_init);
installer_log("about to kernel_dynlib_dlsym sceAppInstUtilAppInstallAll");
install_all = (app_install_all_fn)kernel_dynlib_dlsym(
-1, appinst_handle, "sceAppInstUtilAppInstallAll");
installer_log("kernel_dynlib_dlsym sceAppInstUtilAppInstallAll addr=%p",
(void *)install_all);
installer_log("about to kernel_dynlib_resolve AppInstallTitleDir nid=%s",
BFPILOT_NID_INSTALL_TITLE_DIR);
install_title_dir = (app_install_title_dir_fn)kernel_dynlib_resolve(
-1, appinst_handle, BFPILOT_NID_INSTALL_TITLE_DIR);
installer_log("kernel_dynlib_resolve AppInstallTitleDir addr=%p",
(void *)install_title_dir);
if(!install_title_dir) {
installer_log("about to kernel_dynlib_dlsym "
"sceAppInstUtilAppInstallTitleDir");
install_title_dir = (app_install_title_dir_fn)kernel_dynlib_dlsym(
-1, appinst_handle, "sceAppInstUtilAppInstallTitleDir");
installer_log("kernel_dynlib_dlsym sceAppInstUtilAppInstallTitleDir "
"addr=%p", (void *)install_title_dir);
}
} else {
installer_log("AppInstUtil unavailable without direct import; skipping "
"launcher install safely");
}
if(appinst_init) {
appinst_init_rc = appinst_init();
}
installer_log("AppInst init return code=0x%08x", appinst_init_rc);
int appinst_symbols_available =
appinst_handle_rc == 0 && appinst_init && (install_title_dir || install_all);
int can_stage_launcher = appinst_symbols_available && appinst_init_rc == 0;
snprintf(app_dir, sizeof(app_dir), BFPILOT_APP_ROOT "/%s",
BFPILOT_APP_TITLE_ID);
snprintf(sce_sys_dir, sizeof(sce_sys_dir), "%s/sce_sys", app_dir);
snprintf(param_path, sizeof(param_path), "%s/param.json", sce_sys_dir);
snprintf(icon_path, sizeof(icon_path), "%s/icon0.png", sce_sys_dir);
int mkdir_app_rc = BFPILOT_DIAG_SKIPPED;
int mkdir_sce_sys_rc = BFPILOT_DIAG_SKIPPED;
int write_param_rc = BFPILOT_DIAG_SKIPPED;
int write_icon_rc = BFPILOT_DIAG_SKIPPED;
if(can_stage_launcher) {
errno = 0;
mkdir_app_rc = mkdir_if_needed(app_dir);
installer_log("%s mkdir return code=%d errno=%d",
app_dir, mkdir_app_rc, errno);
errno = 0;
mkdir_sce_sys_rc = mkdir_if_needed(sce_sys_dir);
installer_log("%s mkdir return code=%d errno=%d",
sce_sys_dir, mkdir_sce_sys_rc, errno);
errno = 0;
write_param_rc = write_file(param_path, bfpilot_param_json,
bfpilot_param_json_size);
installer_log("%s write return code=%d errno=%d",
param_path, write_param_rc, errno);
errno = 0;
write_icon_rc = write_file(icon_path, bfpilot_icon0_png,
bfpilot_icon0_png_size);
installer_log("%s write return code=%d errno=%d",
icon_path, write_icon_rc, errno);
} else {
const char *reason = !appinst_symbols_available ?
"AppInstUtil not safely available" : "AppInst init failed";
installer_log("launcher staging skipped: %s", reason);
installer_log("%s mkdir return code=skipped", app_dir);
installer_log("%s mkdir return code=skipped", sce_sys_dir);
installer_log("%s write return code=skipped", param_path);
installer_log("%s write return code=skipped", icon_path);
}
installer_log("AppInstallTitleDir resolved=%s nid=%s",
install_title_dir ? "yes" : "no",
BFPILOT_NID_INSTALL_TITLE_DIR);
if(can_stage_launcher && mkdir_app_rc == 0 && mkdir_sce_sys_rc == 0 &&
write_param_rc == 0 && write_icon_rc == 0) {
if(install_title_dir) {
title_dir_rc = install_title_dir(BFPILOT_APP_TITLE_ID,
BFPILOT_APP_PARENT, NULL);
installer_log("AppInstallTitleDir return code=0x%08x", title_dir_rc);
} else {
installer_log("AppInstallTitleDir return code=skipped");
}
if(title_dir_rc != 0) {
if(install_all) {
install_all_rc = install_all(NULL);
installer_log("AppInstallAll fallback return code=0x%08x",
install_all_rc);
} else {
installer_log("AppInstallAll fallback return code=skipped");
}
} else {
installer_log("AppInstallAll fallback return code=skipped");
}
} else {
installer_log("AppInstallTitleDir return code=skipped");
installer_log("AppInstallAll fallback return code=skipped");
}
if(title_dir_rc == 0 || install_all_rc == 0) {
final_rc = 0;
final_state = "installed";
} else if(appinst_symbols_available) {
final_state = "failed_nonfatal";
}
installer_log("final result=%s final_rc=%d appinst_init_rc=0x%08x "
"mkdir_app_rc=%d mkdir_sce_sys_rc=%d write_param_rc=%d "
"write_icon_rc=%d title_dir_rc=0x%08x install_all_rc=0x%08x",
final_state, final_rc,
appinst_init_rc, mkdir_app_rc, mkdir_sce_sys_rc,
write_param_rc, write_icon_rc, title_dir_rc, install_all_rc);
if(final_rc == 0) {
bfpilot_notify("BFpilot launcher installer", "installed or refreshed");
return 0;
}
if(appinst_symbols_available) {
bfpilot_notify("BFpilot launcher installer",
"failed; file manager unaffected");
return 1;
}
bfpilot_notify("BFpilot launcher installer",
"unsupported on this loader; use web UI");
return 2;
}
+120 -112
View File
@@ -6,7 +6,6 @@
*/
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -22,6 +21,7 @@
#include <net/if.h>
#include <netinet/in.h>
#include "boot_marker.h"
#include "diag.h"
#include "notify.h"
#include "version.h"
@@ -32,9 +32,20 @@
#include "sce_resolve.h"
#endif
#define BFPILOT_WEB_PORT 5905
#define BFPILOT_RELOAD_TOKEN "bs5fm-local-reload"
#ifndef BFPILOT_WEB_PORT
#define BFPILOT_WEB_PORT 5905
#endif
#ifndef BFPILOT_PAYLOAD_NAME
#define BFPILOT_PAYLOAD_NAME "bfpilot"
#endif
#ifndef BFPILOT_DEBUG_NOTIFICATIONS
#define BFPILOT_DEBUG_NOTIFICATIONS 0
#endif
#if BFPILOT_ENABLE_LAUNCHER
typedef int (*sce_netctl_init_fn)(void);
typedef int (*sce_user_service_initialize_fn)(void *);
@@ -43,7 +54,6 @@ typedef int (*sce_user_service_initialize_fn)(void *);
#define BFPILOT_USER_SERVICE_MODULE "libSceUserService.sprx"
#endif
static void
detect_lan_ip(char *out, size_t out_size) {
struct ifaddrs *ifaddr = NULL;
@@ -71,11 +81,50 @@ detect_lan_ip(char *out, size_t out_size) {
typedef struct ready_state {
char ip[64];
int notified;
int launcher_disabled;
int launcher_started;
unsigned short port;
} ready_state_t;
static void
debug_notify(const char *title, const char *message) {
#if BFPILOT_DEBUG_NOTIFICATIONS
bfpilot_notify(title, message);
#else
(void)title;
(void)message;
#endif
}
static unsigned short
parse_port_value(const char *value, unsigned short fallback) {
if(!value || !value[0]) return fallback;
char *end = NULL;
long port = strtol(value, &end, 10);
if(!end || *end != 0 || port < 1 || port > 65535) {
return fallback;
}
return (unsigned short)port;
}
static unsigned short
runtime_port_requested(int argc, char **argv) {
unsigned short port = BFPILOT_WEB_PORT;
for(int i = 1; i < argc; i++) {
if(!argv || !argv[i]) continue;
if(!strcmp(argv[i], "--port") && i + 1 < argc) {
port = parse_port_value(argv[++i], port);
} else if(!strncmp(argv[i], "--port=", 7)) {
port = parse_port_value(argv[i] + 7, port);
}
}
return port;
}
#if BFPILOT_ENABLE_LAUNCHER
static void
init_ps5_services(void) {
@@ -116,71 +165,6 @@ init_ps5_services(void) {
#endif
#if BFPILOT_ENABLE_LAUNCHER
typedef struct launcher_thread_arg {
char ip[64];
} launcher_thread_arg_t;
static void *
launcher_thread(void *arg) {
launcher_thread_arg_t *state = arg;
char fallback[128];
snprintf(fallback, sizeof(fallback), "Use http://%s:%u/",
state && state->ip[0] ? state->ip : "<PS5_IP>",
(unsigned int)BFPILOT_WEB_PORT);
bfpilot_checkpoint("launcher started");
bfpilot_diag_set_launcher_status("started", 0);
bfpilot_log("launcher started after web server");
init_ps5_services();
int app_install_status = bfpilot_install_app_if_needed();
if(app_install_status >= 0) {
bfpilot_diag_set_launcher_status("ready", app_install_status);
bfpilot_log("launcher ready rc=%d", app_install_status);
puts(" ps5 app: ready");
} else {
bfpilot_diag_set_launcher_status("failed_nonfatal", app_install_status);
bfpilot_log("launcher skipped rc=%d, web server remains available",
app_install_status);
bfpilot_notify("BFpilot launcher skipped", fallback);
puts(" ps5 app: launcher skipped, web server remains available");
}
free(state);
return NULL;
}
static void
start_launcher_thread(const char *ip) {
launcher_thread_arg_t *state = calloc(1, sizeof(*state));
if(!state) {
bfpilot_diag_set_launcher_status("failed_nonfatal", -ENOMEM);
bfpilot_log("launcher skipped: failed allocating thread state");
bfpilot_notify("BFpilot launcher skipped", "Use http://<PS5_IP>:5905/");
return;
}
snprintf(state->ip, sizeof(state->ip), "%s", ip ? ip : "<PS5_IP>");
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int rc = pthread_create(&thread, &attr, launcher_thread, state);
pthread_attr_destroy(&attr);
if(rc != 0) {
bfpilot_diag_set_launcher_status("failed_nonfatal", -rc);
bfpilot_log("launcher skipped: pthread_create rc=%d", rc);
bfpilot_notify("BFpilot launcher skipped", "Use http://<PS5_IP>:5905/");
free(state);
}
}
#endif
static int
launcher_disabled_requested(int argc, char **argv) {
#if !BFPILOT_ENABLE_LAUNCHER
@@ -195,11 +179,9 @@ launcher_disabled_requested(int argc, char **argv) {
!strcasecmp(env_value, "yes") ||
!strcasecmp(env_value, "on"));
if(env_enabled) {
return 1;
}
if(env_enabled) return 1;
for(int i = 1; i < argc; i++) {
if(argv[i] && !strcmp(argv[i], "--no-launcher")) {
if(argv && argv[i] && !strcmp(argv[i], "--no-launcher")) {
return 1;
}
}
@@ -209,7 +191,8 @@ launcher_disabled_requested(int argc, char **argv) {
static int
local_http_get(const char *path, char *out, size_t out_size) {
local_http_get(unsigned short port, const char *path, char *out,
size_t out_size) {
if(out_size == 0) return -1;
out[0] = 0;
@@ -225,7 +208,7 @@ local_http_get(const char *path, char *out, size_t out_size) {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(BFPILOT_WEB_PORT);
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
@@ -239,7 +222,7 @@ local_http_get(const char *path, char *out, size_t out_size) {
"Host: 127.0.0.1:%u\r\n"
"Connection: close\r\n"
"\r\n",
path, (unsigned int)BFPILOT_WEB_PORT);
path, (unsigned int)port);
if(n < 0 || (size_t)n >= sizeof(request) ||
websrv_write_all(fd, request, (size_t)n) != 0) {
close(fd);
@@ -280,9 +263,10 @@ json_long_field(const char *json, const char *field) {
static int
old_server_busy(void) {
old_server_busy(unsigned short port) {
char response[2048];
if(local_http_get("/api/fs/job/status", response, sizeof(response)) != 0) {
if(local_http_get(port, "/api/fs/job/status", response,
sizeof(response)) != 0) {
return 0;
}
return strstr(response, "\"busy\":true") != NULL;
@@ -290,10 +274,10 @@ old_server_busy(void) {
static int
wait_for_old_server_down(void) {
wait_for_old_server_down(unsigned short port) {
char response[512];
for(int i = 0; i < 30; i++) {
if(local_http_get("/api/status", response, sizeof(response)) != 0) {
if(local_http_get(port, "/api/status", response, sizeof(response)) != 0) {
return 1;
}
usleep(100000);
@@ -303,46 +287,52 @@ wait_for_old_server_down(void) {
static int
handoff_existing_server(void) {
handoff_existing_server(unsigned short port) {
char response[2048];
if(local_http_get("/api/status", response, sizeof(response)) != 0) {
if(local_http_get(port, "/api/status", response, sizeof(response)) != 0) {
return 0;
}
if(!strstr(response, "\"name\":\"BFpilot\"") &&
!strstr(response, "\"name\":\"BS5FileManager\"")) {
bfpilot_log("handoff: port %u belongs to a different service; leaving it "
"running", (unsigned int)BFPILOT_WEB_PORT);
bfpilot_notify("BFpilot reload skipped",
"Port 5905 is used by another service");
"running", (unsigned int)port);
bfpilot_notify("BFpilot reload blocked",
"Port is used by another service");
return -1;
}
long old_pid = json_long_field(response, "pid");
bfpilot_log("handoff: existing BFpilot-compatible server pid=%ld", old_pid);
bfpilot_log("handoff: existing BFpilot-compatible server pid=%ld port=%u",
old_pid, (unsigned int)port);
bfpilot_notify("BFpilot reload: old server detected",
old_pid >= 0 ? "Attempting clean shutdown" :
"Attempting clean shutdown; old pid unknown");
if(old_pid == (long)getpid()) {
return 0;
}
if(old_server_busy()) {
bfpilot_notify("BFpilot reload skipped",
if(old_server_busy(port)) {
bfpilot_notify("BFpilot reload blocked",
"Old file operation is still running");
bfpilot_log("handoff: blocked because old file job is busy");
return -1;
}
snprintf(response, sizeof(response),
"/api/control/shutdown?token=%s", BFPILOT_RELOAD_TOKEN);
char shutdown_response[512];
if(local_http_get(response, shutdown_response,
if(local_http_get(port, response, shutdown_response,
sizeof(shutdown_response)) == 0 &&
strstr(shutdown_response, "200 OK") &&
strstr(shutdown_response, "\"ok\":true") &&
wait_for_old_server_down()) {
wait_for_old_server_down(port)) {
bfpilot_notify("BFpilot reloaded", "Old listener stopped cleanly");
bfpilot_log("handoff: old listener stopped cleanly");
return 1;
}
bfpilot_notify("BFpilot reload failed",
"Could not stop old listener on port 5905");
bfpilot_notify("BFpilot reload blocked",
"Old listener did not stop cleanly");
bfpilot_log("handoff: old listener did not accept clean shutdown; not "
"sending signals");
return -1;
@@ -365,17 +355,13 @@ on_web_ready(unsigned short port, void *arg) {
state->notified = 1;
}
#if BFPILOT_ENABLE_LAUNCHER
if(!state->launcher_disabled && !state->launcher_started) {
state->launcher_started = 1;
start_launcher_thread(state->ip);
}
#endif
debug_notify("BFpilot debug", "web server ready");
}
int
main(int argc, char **argv) {
bfpilot_boot_marker(BFPILOT_PAYLOAD_NAME, BFPILOT_BUILD_MODE);
bfpilot_diag_init();
bfpilot_diag_install_signal_handlers();
bfpilot_checkpoint("process start");
@@ -396,9 +382,11 @@ main(int argc, char **argv) {
ready_state_t ready;
memset(&ready, 0, sizeof(ready));
detect_lan_ip(ready.ip, sizeof(ready.ip));
ready.port = runtime_port_requested(argc, argv);
websrv_set_runtime_port(ready.port);
int launcher_disabled = launcher_disabled_requested(argc, argv);
ready.launcher_disabled = launcher_disabled;
websrv_set_runtime_diag(launcher_disabled);
bfpilot_log("runtime web_port=%u", (unsigned int)ready.port);
bfpilot_log("runtime launcher_disabled=%d", launcher_disabled);
puts(".----------------------------------------------.");
@@ -409,27 +397,47 @@ main(int argc, char **argv) {
puts(" active: standalone web file manager");
printf(" mode: %s\n", BFPILOT_BUILD_MODE);
puts(" scope: browse, upload, download, copy, move, delete, rename, mkdir");
if(launcher_disabled) {
if(BFPILOT_ENABLE_LAUNCHER && !launcher_disabled) {
puts(" ps5 app: BFpilot opens http://127.0.0.1:5905/");
} else if(BFPILOT_ENABLE_LAUNCHER) {
puts(" ps5 app: launcher disabled");
} else {
puts(" ps5 app: BFpilot opens http://127.0.0.1:5905/");
puts(" ps5 app: launcher installer is a separate optional payload");
}
printf(" web ui: http://%s:%u/\n", ready.ip, (unsigned int)BFPILOT_WEB_PORT);
printf(" web ui: http://%s:%u/\n", ready.ip, (unsigned int)ready.port);
puts(" inject/deploy port: 9021");
puts("");
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
#if BFPILOT_DEBUG_NOTIFICATIONS || BFPILOT_ENABLE_LAUNCHER
int notification_rc = bfpilot_notify_test();
bfpilot_diag_set_notification_rc(notification_rc);
bfpilot_log("notification test rc=0x%08x", notification_rc);
#else
bfpilot_diag_set_notification_rc(BFPILOT_DIAG_SKIPPED);
bfpilot_log("notification test rc=skipped");
#endif
#if BFPILOT_ENABLE_LAUNCHER
if(!launcher_disabled) {
bfpilot_diag_set_launcher_status("deferred", BFPILOT_DIAG_SKIPPED);
bfpilot_log("launcher deferred until web server is listening");
puts(" ps5 app: launcher will run after web server starts");
bfpilot_checkpoint("launcher started");
bfpilot_diag_set_launcher_status("started", 0);
bfpilot_log("launcher started");
init_ps5_services();
int app_install_status = bfpilot_install_app_if_needed();
if(app_install_status >= 0) {
bfpilot_diag_set_launcher_status("ready", app_install_status);
bfpilot_log("launcher ready rc=%d", app_install_status);
puts(" ps5 app: ready");
} else {
bfpilot_diag_set_launcher_status("failed", app_install_status);
bfpilot_log("launcher failed rc=%d, continuing web server",
app_install_status);
puts(" ps5 app: install failed, continuing web server");
}
} else {
bfpilot_diag_set_service_rcs(BFPILOT_DIAG_SKIPPED, BFPILOT_DIAG_SKIPPED);
bfpilot_diag_set_launcher_status("skipped", BFPILOT_DIAG_SKIPPED);
@@ -440,15 +448,15 @@ main(int argc, char **argv) {
}
#else
bfpilot_diag_set_service_rcs(BFPILOT_DIAG_SKIPPED, BFPILOT_DIAG_SKIPPED);
bfpilot_diag_set_launcher_status("not_compiled", BFPILOT_DIAG_SKIPPED);
bfpilot_diag_set_launcher_status("separate_payload", BFPILOT_DIAG_SKIPPED);
bfpilot_log("sceNetCtlInit rc=skipped");
bfpilot_log("sceUserServiceInitialize rc=skipped");
bfpilot_log("launcher skipped: not compiled");
puts(" ps5 app: not compiled in core mode");
bfpilot_log("launcher skipped: separate payload");
#endif
bfpilot_checkpoint("handoff check");
int handoff_status = handoff_existing_server();
debug_notify("BFpilot debug", "handoff check");
int handoff_status = handoff_existing_server(ready.port);
bfpilot_log("handoff_existing_server rc=%d", handoff_status);
if(handoff_status < 0) {
bfpilot_checkpoint("handoff blocked");
@@ -458,7 +466,7 @@ main(int argc, char **argv) {
while(1) {
bfpilot_checkpoint("web listen starting");
int rc = websrv_listen(BFPILOT_WEB_PORT, on_web_ready, &ready);
int rc = websrv_listen(ready.port, on_web_ready, &ready);
bfpilot_log("websrv_listen returned rc=%d", rc);
if(websrv_exit_requested()) {
bfpilot_checkpoint("web shutdown requested");
@@ -468,7 +476,7 @@ main(int argc, char **argv) {
if(!ready.notified) {
char msg[128];
snprintf(msg, sizeof(msg), "port %u error %d, retrying",
(unsigned int)BFPILOT_WEB_PORT, -rc);
(unsigned int)ready.port, -rc);
bfpilot_notify("BFpilot could not start", msg);
ready.notified = 1;
}
+93 -5
View File
@@ -19,11 +19,12 @@
#include <time.h>
#include <unistd.h>
#include "diag.h"
#include "transfer.h"
#include "websrv.h"
#define COPY_BUF_SIZE (1024 * 1024)
#define COPY_BUF_SIZE (256 * 1024)
#define UPLOAD_BUF_SIZE (256 * 1024)
@@ -259,6 +260,8 @@ job_end(int rc, const char *err) {
snprintf(g_job.error, sizeof(g_job.error), "%s", err);
}
pthread_mutex_unlock(&g_job.lock);
bfpilot_log("transfer job end verb=%s rc=%d error=%s",
g_job.verb, rc, err ? err : "");
atomic_store(&g_job.busy, 0);
}
@@ -301,7 +304,7 @@ copy_file(const char *src, const char *dst) {
job_set_error("open(%s): %s", src, strerror(errno));
return -1;
}
int out = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0777);
int out = open(dst, O_WRONLY | O_CREAT | O_EXCL, 0777);
if(out < 0) {
job_set_error("open(%s): %s", dst, strerror(errno));
close(in);
@@ -356,7 +359,14 @@ copy_file(const char *src, const char *dst) {
free(buf);
close(in);
close(out);
if(rc == 0 && fsync(out) != 0 && errno != EINVAL) {
job_set_error("sync(%s): %s", dst, strerror(errno));
rc = -1;
}
if(close(out) != 0 && rc == 0) {
job_set_error("close(%s): %s", dst, strerror(errno));
rc = -1;
}
if(rc != 0) unlink(dst);
return rc;
}
@@ -452,6 +462,41 @@ delete_recursive(const char *path, int count_progress) {
}
static int
delete_recursive_force(const char *path) {
struct stat st;
if(lstat(path, &st) != 0) {
return errno == ENOENT ? 0 : -1;
}
if(S_ISDIR(st.st_mode)) {
DIR *d = opendir(path);
if(!d) return -1;
int rc = 0;
struct dirent *ent;
while((ent = readdir(d))) {
if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) continue;
char child[1024];
if(strlen(path) + strlen(ent->d_name) + 2 >= sizeof(child)) {
errno = ENAMETOOLONG;
rc = -1;
break;
}
join_path(child, sizeof(child), path, ent->d_name);
if(delete_recursive_force(child) != 0) {
rc = -1;
break;
}
}
closedir(d);
if(rc != 0) return -1;
return rmdir(path);
}
return unlink(path);
}
struct copy_arg {
char src[1024];
char dst[1024];
@@ -463,9 +508,12 @@ static void *
copy_worker(void *arg) {
struct copy_arg *a = arg;
long files = 0, bytes = 0;
struct stat src_st, dst_st;
struct stat src_st, dst_st, final_st;
char final_dst[1024];
char staging_dst[1024];
bfpilot_log("transfer worker start verb=%s src=%s dst=%s",
a->is_move ? "move" : "copy", a->src, a->dst);
if(lstat(a->src, &src_st) != 0) {
job_end(-1, "source not found");
free(a);
@@ -513,6 +561,27 @@ copy_worker(void *arg) {
}
join_path(final_dst, sizeof(final_dst), a->dst, base);
if(lstat(final_dst, &final_st) == 0) {
job_end(-1, "destination already exists");
free(a);
return NULL;
}
if(errno != ENOENT) {
char err[160];
snprintf(err, sizeof(err), "destination: %s", strerror(errno));
job_end(-1, err);
free(a);
return NULL;
}
int n = snprintf(staging_dst, sizeof(staging_dst), "%s.bfpilot-part-%ld-%ld",
final_dst, (long)getpid(), (long)time(NULL));
if(n < 0 || (size_t)n >= sizeof(staging_dst)) {
job_end(-1, "staging path too long");
free(a);
return NULL;
}
if(!strcmp(a->src, final_dst)) {
job_end(-1, "source and destination are the same");
free(a);
@@ -546,22 +615,36 @@ copy_worker(void *arg) {
free(a);
return NULL;
}
bfpilot_log("transfer move cross-device fallback src=%s staging=%s final=%s",
a->src, staging_dst, final_dst);
}
int rc = copy_recursive(a->src, final_dst);
int rc = copy_recursive(a->src, staging_dst);
if(rc != 0) {
char err[160];
snprintf(err, sizeof(err), "copy: %s",
job_cancelled() ? "cancelled" : strerror(errno));
(void)delete_recursive_force(staging_dst);
job_end(-1, err);
free(a);
return NULL;
}
if(job_cancelled()) {
(void)delete_recursive_force(staging_dst);
job_end(-1, "cancelled");
free(a);
return NULL;
}
job_set_current("Finalizing destination");
if(rename(staging_dst, final_dst) != 0) {
char err[160];
snprintf(err, sizeof(err), "finalize: %s", strerror(errno));
(void)delete_recursive_force(staging_dst);
job_end(-1, err);
free(a);
return NULL;
}
bfpilot_log("transfer finalized staging=%s final=%s", staging_dst, final_dst);
if(a->is_move && delete_recursive(a->src, 0) != 0) {
char err[160];
snprintf(err, sizeof(err), "post-move cleanup: %s",
@@ -570,6 +653,9 @@ copy_worker(void *arg) {
free(a);
return NULL;
}
if(a->is_move) {
bfpilot_log("transfer source cleanup complete src=%s", a->src);
}
job_end(0, NULL);
free(a);
@@ -847,6 +933,8 @@ spawn_copy_or_move(const http_request_t *req, int is_move) {
if(!job_begin(is_move ? "move" : "copy")) {
return serve_error(req, 409, "another file-manager job is already running");
}
bfpilot_log("transfer job begin verb=%s src=%s dst=%s",
is_move ? "move" : "copy", src, dst);
struct copy_arg *a = calloc(1, sizeof(*a));
if(!a) {
+3 -3
View File
@@ -17,11 +17,11 @@
#endif
#ifndef BFPILOT_ENABLE_LAUNCHER
#define BFPILOT_ENABLE_LAUNCHER 1
#define BFPILOT_ENABLE_LAUNCHER 0
#endif
#ifndef BFPILOT_DISABLE_LAUNCHER
#define BFPILOT_DISABLE_LAUNCHER 0
#define BFPILOT_DISABLE_LAUNCHER 1
#endif
#if BFPILOT_DISABLE_LAUNCHER
@@ -30,5 +30,5 @@
#endif
#ifndef BFPILOT_BUILD_MODE
#define BFPILOT_BUILD_MODE "full"
#define BFPILOT_BUILD_MODE "file-manager"
#endif
+2
View File
@@ -57,6 +57,8 @@ void websrv_set_runtime_diag(int launcher_disabled);
void websrv_set_launcher_diag(const bfpilot_launcher_diag_t *diag);
void websrv_set_runtime_port(unsigned short port);
void websrv_request_exit(void);
int websrv_exit_requested(void);
+24 -3
View File
@@ -28,10 +28,15 @@
#define HEADER_MAX 65536
#define CONTROL_TOKEN "bs5fm-local-reload"
#ifndef BFPILOT_WEB_PORT
#define BFPILOT_WEB_PORT 5905
#endif
static int g_websrv_srvfd = -1;
static pthread_mutex_t g_websrv_lock = PTHREAD_MUTEX_INITIALIZER;
static volatile sig_atomic_t g_websrv_exit_requested = 0;
static unsigned short g_websrv_port = BFPILOT_WEB_PORT;
static int g_launcher_runtime_disabled = 0;
static bfpilot_launcher_diag_t g_launcher_diag = {
.launcher_enabled = 0,
@@ -76,6 +81,14 @@ websrv_set_launcher_diag(const bfpilot_launcher_diag_t *diag) {
}
void
websrv_set_runtime_port(unsigned short port) {
if(port != 0) {
g_websrv_port = port;
}
}
static const char *
status_text(int status) {
switch(status) {
@@ -338,11 +351,12 @@ status_request(const http_request_t *req) {
"\"services\":[\"web\",\"file-manager\"],"
"\"launcherCompiled\":%s,"
"\"launcherDisabled\":%s,"
"\"port\":5905}",
"\"port\":%u}",
VERSION_TAG, BUILD_VERSION, BFPILOT_BUILD_MODE,
(long)getpid(), (long)now,
BFPILOT_ENABLE_LAUNCHER ? "true" : "false",
g_launcher_runtime_disabled ? "true" : "false");
g_launcher_runtime_disabled ? "true" : "false",
(unsigned int)g_websrv_port);
if(n < 0) return -1;
if((size_t)n >= sizeof(body)) n = (int)sizeof(body) - 1;
return websrv_send(req->fd, 200, "application/json", body, (size_t)n);
@@ -388,7 +402,7 @@ diag_request(const http_request_t *req) {
"\"pid\":%ld,"
"\"now\":%ld,"
"\"uptime\":%ld,"
"\"port\":5905,"
"\"port\":%u,"
"\"cwd\":\"%s\","
"\"can_stat_root\":%s,"
"\"can_opendir_data\":%s,"
@@ -413,6 +427,8 @@ diag_request(const http_request_t *req) {
"\"sceNetCtlInit\":%d,"
"\"sceUserServiceInitialize\":%d,"
"\"notification_test\":%d,"
"\"bind_port\":%u,"
"\"listen_port\":%u,"
"\"bind_5905\":%d,"
"\"listen_5905\":%d},"
"\"launcher\":{"
@@ -439,6 +455,7 @@ diag_request(const http_request_t *req) {
VERSION_TAG, BUILD_VERSION, BFPILOT_BUILD_MODE,
(long)getpid(), (long)now,
bfpilot_diag_uptime(),
(unsigned int)g_websrv_port,
cwd_json,
json_bool(can_stat_root),
json_bool(can_opendir_data),
@@ -462,6 +479,8 @@ diag_request(const http_request_t *req) {
bfpilot_diag_netctl_rc(),
bfpilot_diag_user_service_rc(),
bfpilot_diag_notification_rc(),
(unsigned int)g_websrv_port,
(unsigned int)g_websrv_port,
bfpilot_diag_bind_rc(),
bfpilot_diag_listen_rc(),
launcher_status_json,
@@ -622,6 +641,8 @@ websrv_listen(unsigned short port, websrv_ready_cb_t ready_cb,
struct sockaddr_in server_addr;
int srvfd;
websrv_set_runtime_port(port);
signal(SIGPIPE, SIG_IGN);
srvfd = socket(AF_INET, SOCK_STREAM, 0);
-26
View File
@@ -1,26 +0,0 @@
# BFpilot v0.2.1 Test Build
This is a firmware-compatibility test build, not the latest stable release.
Recommended order:
1. Test `bfpilot-core.elf` first on every firmware/loader.
2. Only test `bfpilot-full.elf` after core mode starts and the file manager works.
3. Treat launcher install failure in full mode as non-fatal if the web server still works.
Test URL:
```text
http://<PS5_IP>:5905/
```
Diagnostics:
```text
http://<PS5_IP>:5905/api/status
http://<PS5_IP>:5905/api/diag
/data/BFpilot/log.txt
/data/BFpilot/crash.log
```
See `docs/FIRMWARE_TESTING.md` for the full firmware test protocol.
Binary file not shown.
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
/*
* BFpilot test payload - boot marker only.
*/
#include <unistd.h>
#include "boot_marker.h"
#include "notify.h"
int
main(void) {
bfpilot_boot_marker("tests/hello_boot", BFPILOT_BUILD_MODE);
sleep(10);
bfpilot_notify("BFpilot BOOT still alive", "tests/hello_boot");
return 0;
}
+126
View File
@@ -0,0 +1,126 @@
/*
* BFpilot test payload - tiny HTTP status server.
*/
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "boot_marker.h"
#ifndef BFPILOT_HELLO_HTTP_PORT
#define BFPILOT_HELLO_HTTP_PORT 5906
#endif
static int
write_all(int fd, const void *data, size_t size) {
const char *p = data;
while(size > 0) {
ssize_t wr = send(fd, p, size, 0);
if(wr < 0) {
if(errno == EINTR) continue;
return -1;
}
if(wr == 0) return -1;
p += wr;
size -= (size_t)wr;
}
return 0;
}
static void
serve_client(int fd) {
char req[512];
ssize_t n = recv(fd, req, sizeof(req) - 1, 0);
if(n < 0) {
close(fd);
return;
}
req[n > 0 ? n : 0] = 0;
const char body[] =
"{\"ok\":true,\"name\":\"BFpilot hello_http\",\"port\":5906}";
const char not_found[] = "{\"ok\":false,\"error\":\"not found\"}";
char header[256];
if(strstr(req, "GET /api/status ") || strstr(req, "GET /api/status?")) {
int h = snprintf(header, sizeof(header),
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %lu\r\n\r\n",
(unsigned long)(sizeof(body) - 1));
(void)write_all(fd, header, (size_t)h);
(void)write_all(fd, body, sizeof(body) - 1);
} else {
int h = snprintf(header, sizeof(header),
"HTTP/1.1 404 Not Found\r\n"
"Connection: close\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %lu\r\n\r\n",
(unsigned long)(sizeof(not_found) - 1));
(void)write_all(fd, header, (size_t)h);
(void)write_all(fd, not_found, sizeof(not_found) - 1);
}
shutdown(fd, SHUT_RDWR);
close(fd);
}
int
main(void) {
bfpilot_boot_marker("tests/hello_http", BFPILOT_BUILD_MODE);
signal(SIGPIPE, SIG_IGN);
int srv = socket(AF_INET, SOCK_STREAM, 0);
if(srv < 0) {
printf("BFpilot hello_http socket failed errno=%d\n", errno);
return 1;
}
int on = 1;
setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(BFPILOT_HELLO_HTTP_PORT);
if(bind(srv, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
printf("BFpilot hello_http bind failed errno=%d\n", errno);
close(srv);
return 1;
}
if(listen(srv, 4) != 0) {
printf("BFpilot hello_http listen failed errno=%d\n", errno);
close(srv);
return 1;
}
printf("BFpilot hello_http listening on port %u\n",
(unsigned int)BFPILOT_HELLO_HTTP_PORT);
for(;;) {
int fd = accept(srv, NULL, NULL);
if(fd < 0) {
if(errno == EINTR) continue;
printf("BFpilot hello_http accept failed errno=%d\n", errno);
break;
}
serve_client(fd);
}
close(srv);
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* BFpilot test payload - notification probe.
*/
#include <stdio.h>
#include "boot_marker.h"
#include "notify.h"
int
main(void) {
bfpilot_boot_marker("tests/hello_notify", BFPILOT_BUILD_MODE);
int rc = bfpilot_notify_send("BFpilot hello_notify",
"notification probe");
printf("BFpilot hello_notify notification rc=0x%08x\n", rc);
return rc == 0 ? 0 : 1;
}
+21
View File
@@ -0,0 +1,21 @@
/*
* BFpilot test payload - installer entry marker with safe imports only.
*/
#include "boot_marker.h"
#include "notify.h"
#include "probe_common.h"
#define INSTALLER_ENTER_MARKER "/data/BFpilot/installer_enter_probe.txt"
int
main(void) {
int marker_rc = probe_write_marker(INSTALLER_ENTER_MARKER,
"tests/installer_enter_probe");
bfpilot_boot_marker("tests/installer_enter_probe", BFPILOT_BUILD_MODE);
printf("BFpilot installer_enter_probe marker_rc=%d\n", marker_rc);
bfpilot_notify("BFpilot installer enter probe",
marker_rc == 0 ? "entered main" : "marker write failed");
return marker_rc == 0 ? 0 : 1;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* BFpilot test payload - direct AppInst import/link probe.
*
* This payload intentionally links AppInstUtil but never calls it. If the
* marker file is not written, the loader rejected the ELF before main().
*/
#include "boot_marker.h"
#include "notify.h"
#include "probe_common.h"
#define LINKONLY_APPINST_MARKER "/data/BFpilot/linkonly_appinst_entered.txt"
int sceAppInstUtilInitialize(void);
__attribute__((used)) static void * volatile g_appinst_import_probe =
(void *)&sceAppInstUtilInitialize;
int
main(void) {
int marker_rc = probe_write_marker(LINKONLY_APPINST_MARKER,
"tests/installer_linkonly_appinst");
bfpilot_boot_marker("tests/installer_linkonly_appinst", BFPILOT_BUILD_MODE);
printf("BFpilot installer_linkonly_appinst marker_rc=%d import_symbol=%p\n",
marker_rc, g_appinst_import_probe);
bfpilot_notify("BFpilot link-only AppInst",
marker_rc == 0 ? "entered main" : "marker write failed");
return marker_rc == 0 ? 0 : 1;
}
+75
View File
@@ -0,0 +1,75 @@
/*
* BFpilot test payload - runtime AppInstUtil load/resolve probe.
*/
#include <stdint.h>
#include <stdio.h>
#include <ps5/kernel.h>
#include "boot_marker.h"
#include "notify.h"
#include "probe_common.h"
#include "version.h"
#define RUNTIME_RESOLVE_MARKER "/data/BFpilot/runtime_resolve_entered.txt"
#define RUNTIME_RESOLVE_LOG "/data/BFpilot/runtime_resolve_appinst.log"
#define APPINST_MODULE "libSceAppInstUtil.sprx"
#define NID_INSTALL_TITLE_DIR "Wudg3Xe3heE"
int
main(void) {
int marker_rc = probe_write_marker(RUNTIME_RESOLVE_MARKER,
"tests/installer_runtime_resolve_appinst");
bfpilot_boot_marker("tests/installer_runtime_resolve_appinst",
BFPILOT_BUILD_MODE);
probe_log(RUNTIME_RESOLVE_LOG,
"entered main marker_rc=%d tag=%s build=%s",
marker_rc, VERSION_TAG, BUILD_VERSION);
uint32_t handle = 0;
probe_log(RUNTIME_RESOLVE_LOG, "about to kernel_dynlib_handle %s",
APPINST_MODULE);
int handle_rc = kernel_dynlib_handle(-1, APPINST_MODULE, &handle);
probe_log(RUNTIME_RESOLVE_LOG,
"kernel_dynlib_handle %s rc=0x%08x handle=0x%08x",
APPINST_MODULE, handle_rc, handle);
if(handle_rc == 0) {
probe_log(RUNTIME_RESOLVE_LOG,
"about to kernel_dynlib_dlsym sceAppInstUtilInitialize");
intptr_t init_addr = kernel_dynlib_dlsym(
-1, handle, "sceAppInstUtilInitialize");
probe_log(RUNTIME_RESOLVE_LOG,
"kernel_dynlib_dlsym sceAppInstUtilInitialize addr=%p",
(void *)init_addr);
probe_log(RUNTIME_RESOLVE_LOG,
"about to kernel_dynlib_dlsym sceAppInstUtilAppInstallAll");
intptr_t install_all_addr = kernel_dynlib_dlsym(
-1, handle, "sceAppInstUtilAppInstallAll");
probe_log(RUNTIME_RESOLVE_LOG,
"kernel_dynlib_dlsym sceAppInstUtilAppInstallAll addr=%p",
(void *)install_all_addr);
probe_log(RUNTIME_RESOLVE_LOG,
"about to kernel_dynlib_resolve AppInstallTitleDir nid=%s",
NID_INSTALL_TITLE_DIR);
intptr_t title_dir_addr = kernel_dynlib_resolve(
-1, handle, NID_INSTALL_TITLE_DIR);
probe_log(RUNTIME_RESOLVE_LOG,
"kernel_dynlib_resolve AppInstallTitleDir nid=%s addr=%p",
NID_INSTALL_TITLE_DIR, (void *)title_dir_addr);
} else {
probe_log(RUNTIME_RESOLVE_LOG,
"AppInstUtil not already loaded; skipping dlopen because prior "
"test stopped before the dlopen result log");
}
bfpilot_notify("BFpilot runtime AppInst",
marker_rc == 0 ? "runtime resolve probe complete"
: "marker write failed");
return marker_rc == 0 ? 0 : 1;
}
+87
View File
@@ -0,0 +1,87 @@
/*
* BFpilot test payload helpers.
*/
#pragma once
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#define BFPILOT_PROBE_DATA_ROOT "/data"
#define BFPILOT_PROBE_DATA_DIR "/data/BFpilot"
static int __attribute__((unused))
probe_mkdir_if_needed(const char *path) {
if(mkdir(path, 0755) == 0) return 0;
return errno == EEXIST ? 0 : -errno;
}
static void __attribute__((unused))
probe_ensure_data_dir(void) {
(void)probe_mkdir_if_needed(BFPILOT_PROBE_DATA_ROOT);
(void)probe_mkdir_if_needed(BFPILOT_PROBE_DATA_DIR);
}
static int __attribute__((unused))
probe_write_marker(const char *path, const char *payload_name) {
char line[512];
probe_ensure_data_dir();
int n = snprintf(line, sizeof(line),
"%ld payload=%s pid=%ld entered main\n",
(long)time(NULL),
payload_name ? payload_name : "unknown",
(long)getpid());
if(n < 0) return -EINVAL;
if((size_t)n >= sizeof(line)) n = (int)sizeof(line) - 1;
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if(fd < 0) return -errno;
ssize_t wr = write(fd, line, (size_t)n);
int saved_errno = errno;
if(close(fd) != 0 && wr == n) return -errno;
if(wr != n) return wr < 0 ? -saved_errno : -EIO;
return 0;
}
static void __attribute__((unused))
probe_log(const char *path, const char *fmt, ...) {
char body[1024];
char line[1400];
va_list ap;
va_start(ap, fmt);
int body_n = vsnprintf(body, sizeof(body), fmt ? fmt : "", ap);
va_end(ap);
if(body_n < 0) {
snprintf(body, sizeof(body), "log format failed");
} else if((size_t)body_n >= sizeof(body)) {
body[sizeof(body) - 1] = 0;
}
int n = snprintf(line, sizeof(line), "%ld pid=%ld %s\n",
(long)time(NULL), (long)getpid(), body);
if(n < 0) return;
if((size_t)n >= sizeof(line)) n = (int)sizeof(line) - 1;
fputs(line, stdout);
fflush(stdout);
probe_ensure_data_dir();
int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0600);
if(fd >= 0) {
(void)write(fd, line, (size_t)n);
close(fd);
}
}