mirror of
https://github.com/ItsBlurf/BFpilot.git
synced 2026-10-18 23:06:53 -05:00
Prepare BFpilot v0.2.1 compatibility test build
This commit is contained in:
1 parent
d79ee1542d
commit
3a146e70b2
28 files changed
+2324
-712
No files matched your search
@@ -4,7 +4,6 @@ gen/
|
||||
*.a
|
||||
*.so
|
||||
*.elf
|
||||
!test-builds/**/*.elf
|
||||
|
||||
# Local environment / editor noise
|
||||
.env
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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++;
|
||||
|
||||
@@ -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
@@ -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 | | |
|
||||
@@ -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"
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* BFpilot - early payload entry marker.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
void bfpilot_boot_marker(const char *payload_name, const char *build_mode);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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.
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user