mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-21 14:12:29 -05:00
Handle suspend sleep mode and USB cleanup. Thx to @hadobedo
This commit is contained in:
1 parent
d4c4cda739
commit
9fa6ad94de
16 files changed
+421
-37
No files matched your search
@@ -35,11 +35,13 @@ bool mount_backport_overlay(const char *mount_point,
|
||||
const char *title_id);
|
||||
// Unmount all managed /system_ex/app/<title_id> mount stacks on shutdown.
|
||||
void shutdown_title_mounts(void);
|
||||
// Return true when path is equal to root or is under it.
|
||||
bool path_matches_root_or_child(const char *path, const char *root);
|
||||
// Remove stale mount links and optionally restore image-backed mounts.
|
||||
void cleanup_mount_links(const char *removed_source_root,
|
||||
bool unmount_system_ex_bind);
|
||||
// Unmount and remove title links that point under a source root.
|
||||
void cleanup_mount_links_for_source_unmount(const char *source_root);
|
||||
// Unmount and remove title links backed by USB sources or USB-backed images.
|
||||
void cleanup_usb_mount_links_for_suspend(void);
|
||||
// Recursively copy a directory tree.
|
||||
int copy_dir(const char *src, const char *dst);
|
||||
// Copy a single file.
|
||||
|
||||
@@ -15,6 +15,8 @@ bool unmount_image(const char *file_path, int unit_id, attach_backend_t backend)
|
||||
void cleanup_stale_image_mounts(void);
|
||||
// Reconcile cached image mounts that belong to a specific scan root.
|
||||
void cleanup_stale_image_mounts_for_root(const char *root);
|
||||
// Unmount cached image mounts backed by USB storage during suspend.
|
||||
bool unmount_usb_image_mounts_for_suspend(void);
|
||||
// Unmount every cached image mount during shutdown.
|
||||
bool shutdown_image_mounts(void);
|
||||
// Remove empty directories left under the image mount root.
|
||||
|
||||
@@ -12,6 +12,10 @@ const char *get_filename_component(const char *path);
|
||||
bool path_exists(const char *path);
|
||||
// Return true when a path lives under the image mount root.
|
||||
bool is_under_image_mount_base(const char *path);
|
||||
// Return true when path is equal to root or is under it.
|
||||
bool path_matches_root_or_child(const char *path, const char *root);
|
||||
// Return true when a path lives on USB-backed storage.
|
||||
bool is_usb_storage_path(const char *path);
|
||||
// Build "<scan_path>/backports" for a managed scan root.
|
||||
bool build_backports_root_path(const char *scan_path, char out[MAX_PATH]);
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@ pid_t find_pid_by_name(const char *name, bool exclude_self);
|
||||
bool should_stop_requested(void);
|
||||
// Request graceful shutdown with a descriptive source string.
|
||||
void request_shutdown_stop(const char *reason);
|
||||
// Return true when the payload is paused for system suspend/resume.
|
||||
bool runtime_sleep_mode_active(void);
|
||||
// Enter or leave sleep mode with a descriptive source string.
|
||||
void request_runtime_sleep_mode(bool active, const char *reason);
|
||||
// Return true when scan/remount work is paused by power-state handling.
|
||||
bool runtime_scan_blocked(void);
|
||||
// Enter or leave scan/remount pause with a descriptive source string.
|
||||
void request_runtime_scan_block(bool active, const char *reason);
|
||||
// Request an immediate scan cycle with a descriptive source string.
|
||||
void request_scan_now(const char *reason);
|
||||
// Consume a pending immediate scan request and copy its reason into caller storage.
|
||||
|
||||
@@ -9,6 +9,8 @@ typedef struct scan_candidate scan_candidate_t;
|
||||
void cleanup_lost_sources_before_scan(void);
|
||||
// Unmount and clean up mounts whose backing sources disappeared under one root.
|
||||
void cleanup_lost_sources_for_scan_root(const char *scan_root);
|
||||
// Immediately unmount runtime mounts backed by USB storage for suspend.
|
||||
void unmount_usb_sources_for_suspend(void);
|
||||
// Scan configured roots and collect install candidates.
|
||||
int collect_scan_candidates(scan_candidate_t *candidates, int max_candidates,
|
||||
int *total_found_out,
|
||||
|
||||
+60
-2
@@ -41,6 +41,8 @@
|
||||
|
||||
static volatile sig_atomic_t g_stop_requested = 0;
|
||||
static atomic_bool g_shutdown_on_going_stop_requested = false;
|
||||
static atomic_bool g_runtime_sleep_mode_active = false;
|
||||
static atomic_bool g_runtime_scan_blocked = false;
|
||||
static _Atomic(uintptr_t) g_shutdown_stop_reason_bits = 0;
|
||||
|
||||
typedef struct {
|
||||
@@ -101,19 +103,75 @@ void request_shutdown_stop(const char *reason) {
|
||||
}
|
||||
g_stop_requested = 1;
|
||||
sm_scanner_wake();
|
||||
wake_game_lifecycle_watcher();
|
||||
}
|
||||
|
||||
bool runtime_sleep_mode_active(void) {
|
||||
return atomic_load_explicit(&g_runtime_sleep_mode_active,
|
||||
memory_order_acquire);
|
||||
}
|
||||
|
||||
bool runtime_scan_blocked(void) {
|
||||
return runtime_sleep_mode_active() ||
|
||||
atomic_load_explicit(&g_runtime_scan_blocked, memory_order_acquire);
|
||||
}
|
||||
|
||||
static void clear_scan_now_request(void) {
|
||||
pthread_mutex_lock(&g_scan_now.reason_mutex);
|
||||
g_scan_now.reason[0] = '\0';
|
||||
pthread_mutex_unlock(&g_scan_now.reason_mutex);
|
||||
}
|
||||
|
||||
void request_runtime_sleep_mode(bool active, const char *reason) {
|
||||
bool previous = atomic_exchange_explicit(&g_runtime_sleep_mode_active, active,
|
||||
memory_order_acq_rel);
|
||||
if (previous == active)
|
||||
return;
|
||||
|
||||
if (active)
|
||||
clear_scan_now_request();
|
||||
|
||||
const char *resolved_reason =
|
||||
(reason && reason[0] != '\0') ? reason : "unknown sleep source";
|
||||
log_debug("[SLEEP] %s by %s", active ? "entered" : "left",
|
||||
resolved_reason);
|
||||
sm_scanner_wake();
|
||||
wake_game_lifecycle_watcher();
|
||||
}
|
||||
|
||||
void request_runtime_scan_block(bool active, const char *reason) {
|
||||
bool previous = atomic_exchange_explicit(&g_runtime_scan_blocked, active,
|
||||
memory_order_acq_rel);
|
||||
if (previous == active)
|
||||
return;
|
||||
|
||||
if (active)
|
||||
clear_scan_now_request();
|
||||
|
||||
const char *resolved_reason =
|
||||
(reason && reason[0] != '\0') ? reason : "unknown scan block source";
|
||||
log_debug("[SCAN] %s by %s", active ? "paused" : "resumed",
|
||||
resolved_reason);
|
||||
sm_scanner_wake();
|
||||
}
|
||||
|
||||
void request_scan_now(const char *reason) {
|
||||
const char *resolved_reason =
|
||||
(reason && reason[0] != '\0') ? reason : "unknown scan source";
|
||||
bool resume_scan =
|
||||
strcmp(resolved_reason, "SceSystemStateMgrInfo=WORKING") == 0;
|
||||
if (runtime_scan_blocked() && !resume_scan)
|
||||
return;
|
||||
|
||||
char log_reason[sizeof(g_scan_now.reason)];
|
||||
bool should_log = false;
|
||||
bool should_log = !resume_scan;
|
||||
|
||||
pthread_mutex_lock(&g_scan_now.reason_mutex);
|
||||
if (g_scan_now.reason[0] == '\0') {
|
||||
(void)strlcpy(g_scan_now.reason, resolved_reason, sizeof(g_scan_now.reason));
|
||||
(void)strlcpy(log_reason, g_scan_now.reason, sizeof(log_reason));
|
||||
should_log = true;
|
||||
} else {
|
||||
should_log = false;
|
||||
}
|
||||
pthread_mutex_unlock(&g_scan_now.reason_mutex);
|
||||
|
||||
|
||||
+52
-11
@@ -830,12 +830,21 @@ bool mount_title_nullfs(const char *title_id, const char *src_path) {
|
||||
IOVEC_ENTRY("fspath"), IOVEC_ENTRY(dst),
|
||||
};
|
||||
|
||||
if (runtime_scan_blocked())
|
||||
return false;
|
||||
|
||||
if (nmount(iov, IOVEC_SIZE(iov), 0) != 0) {
|
||||
log_debug(" [LINK] Failed to auto-mount nullfs title=%s src=%s dst=%s: %s",
|
||||
title_id, src_path, dst, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (runtime_scan_blocked()) {
|
||||
if (unmount(dst, 0) != 0 && errno != ENOENT && errno != EINVAL)
|
||||
(void)unmount(dst, MNT_FORCE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!path_exists(dst_eboot)) {
|
||||
log_debug(" [LINK] mounted nullfs but eboot.bin is missing at target: %s",
|
||||
dst_eboot);
|
||||
@@ -851,19 +860,12 @@ bool mount_title_nullfs(const char *title_id, const char *src_path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool path_matches_root_or_child(const char *path, const char *root) {
|
||||
if (!path || !root || root[0] == '\0')
|
||||
return false;
|
||||
size_t root_len = strlen(root);
|
||||
if (strncmp(path, root, root_len) != 0)
|
||||
return false;
|
||||
return (path[root_len] == '\0' || path[root_len] == '/');
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *removed_source_root;
|
||||
bool unmount_system_ex_bind;
|
||||
bool tried_image_recovery;
|
||||
bool force_remove_matching_source;
|
||||
bool match_usb_sources;
|
||||
} cleanup_mount_links_ctx_t;
|
||||
|
||||
static bool cleanup_mount_links_entry(const char *title_id,
|
||||
@@ -877,6 +879,8 @@ static bool cleanup_mount_links_entry(const char *title_id,
|
||||
|
||||
char source_path[MAX_PATH];
|
||||
char image_source_path[MAX_PATH];
|
||||
source_path[0] = '\0';
|
||||
image_source_path[0] = '\0';
|
||||
bool has_image_source = false;
|
||||
bool should_remove = false;
|
||||
bool matches_removed_source = false;
|
||||
@@ -888,7 +892,14 @@ static bool cleanup_mount_links_entry(const char *title_id,
|
||||
}
|
||||
|
||||
if (!should_remove) {
|
||||
if (ctx->removed_source_root && ctx->removed_source_root[0] != '\0') {
|
||||
if (ctx->match_usb_sources) {
|
||||
matches_removed_source =
|
||||
is_usb_storage_path(source_path) ||
|
||||
(has_image_source && is_usb_storage_path(image_source_path));
|
||||
if (!matches_removed_source)
|
||||
return true;
|
||||
should_remove = true;
|
||||
} else if (ctx->removed_source_root && ctx->removed_source_root[0] != '\0') {
|
||||
matches_removed_source =
|
||||
path_matches_root_or_child(source_path, ctx->removed_source_root) ||
|
||||
(has_image_source &&
|
||||
@@ -896,7 +907,8 @@ static bool cleanup_mount_links_entry(const char *title_id,
|
||||
ctx->removed_source_root));
|
||||
if (!matches_removed_source)
|
||||
return true;
|
||||
should_remove = (has_image_source && !path_exists(image_source_path)) ||
|
||||
should_remove = ctx->force_remove_matching_source ||
|
||||
(has_image_source && !path_exists(image_source_path)) ||
|
||||
source_path_needs_cleanup(source_path,
|
||||
&ctx->tried_image_recovery);
|
||||
} else {
|
||||
@@ -976,11 +988,40 @@ void cleanup_mount_links(const char *removed_source_root,
|
||||
.removed_source_root = removed_source_root,
|
||||
.unmount_system_ex_bind = unmount_system_ex_bind,
|
||||
.tried_image_recovery = false,
|
||||
.force_remove_matching_source = false,
|
||||
.match_usb_sources = false,
|
||||
};
|
||||
for_each_title_app_dir("", !unmount_system_ex_bind, cleanup_mount_links_entry,
|
||||
&ctx);
|
||||
}
|
||||
|
||||
void cleanup_mount_links_for_source_unmount(const char *source_root) {
|
||||
if (!source_root || source_root[0] == '\0')
|
||||
return;
|
||||
|
||||
cleanup_mount_links_ctx_t ctx = {
|
||||
.removed_source_root = source_root,
|
||||
.unmount_system_ex_bind = true,
|
||||
.tried_image_recovery = false,
|
||||
.force_remove_matching_source = true,
|
||||
.match_usb_sources = false,
|
||||
};
|
||||
for_each_title_app_dir(" during source unmount cleanup", false,
|
||||
cleanup_mount_links_entry, &ctx);
|
||||
}
|
||||
|
||||
void cleanup_usb_mount_links_for_suspend(void) {
|
||||
cleanup_mount_links_ctx_t ctx = {
|
||||
.removed_source_root = NULL,
|
||||
.unmount_system_ex_bind = true,
|
||||
.tried_image_recovery = false,
|
||||
.force_remove_matching_source = false,
|
||||
.match_usb_sources = true,
|
||||
};
|
||||
for_each_title_app_dir(" during USB suspend cleanup", false,
|
||||
cleanup_mount_links_entry, &ctx);
|
||||
}
|
||||
|
||||
static bool shutdown_title_mounts_entry(const char *title_id,
|
||||
const title_link_paths_t *paths,
|
||||
void *ctx) {
|
||||
|
||||
+80
-3
@@ -4,6 +4,7 @@
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "sm_fakelib.h"
|
||||
#include "sm_game_lifecycle.h"
|
||||
@@ -169,6 +170,31 @@ static void close_game_lifecycle_wake_pipe(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void drain_game_lifecycle_wake_pipe(void) {
|
||||
char drain_buf[32];
|
||||
while (read(g_game_lifecycle_wake_pipe[0], drain_buf, sizeof(drain_buf)) > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool discard_game_lifecycle_events_nowait(int kq) {
|
||||
struct kevent events[16];
|
||||
struct timespec timeout;
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
|
||||
while (true) {
|
||||
int nev = kevent(kq, NULL, 0, events, sizeof(events) / sizeof(events[0]),
|
||||
&timeout);
|
||||
if (nev < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
log_debug(" [GAME] stale event drain failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (nev == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool set_fd_nonblocking(int fd) {
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0)
|
||||
@@ -375,6 +401,23 @@ static void handle_game_exit(pid_t pid) {
|
||||
sm_kstuff_game_on_exit(pid);
|
||||
}
|
||||
|
||||
static void restore_suspended_game_if_alive(int kq, pid_t pid) {
|
||||
if (pid <= 0 || !is_process_alive(pid))
|
||||
return;
|
||||
|
||||
char title_id[MAX_TITLE_ID];
|
||||
uint32_t app_id = 0;
|
||||
if (!resolve_game_title_id(pid, title_id, &app_id))
|
||||
return;
|
||||
if (!is_supported_title_id(title_id))
|
||||
return;
|
||||
|
||||
uint64_t now_us = monotonic_time_us();
|
||||
log_debug(" [GAME] resumed: %s pid=%ld app_id=0x%08X", title_id,
|
||||
(long)pid, app_id);
|
||||
(void)dispatch_game_launch(kq, pid, now_us, title_id, app_id);
|
||||
}
|
||||
|
||||
static void *game_lifecycle_watcher_main(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
@@ -415,7 +458,41 @@ static void *game_lifecycle_watcher_main(void *arg) {
|
||||
set_game_lifecycle_start_result(true);
|
||||
log_debug(" [GAME] lifecycle watcher started");
|
||||
|
||||
bool sleep_cleanup_done = false;
|
||||
pid_t suspended_game_pid = 0;
|
||||
while (!g_game_lifecycle_stop_requested && !should_stop_requested()) {
|
||||
if (runtime_sleep_mode_active()) {
|
||||
if (!sleep_cleanup_done) {
|
||||
suspended_game_pid = atomic_load(&g_active_game_pid);
|
||||
clear_all_pending_game_launches();
|
||||
sm_fakelib_game_shutdown();
|
||||
sm_kstuff_game_shutdown();
|
||||
publish_active_game_pid(0);
|
||||
sleep_cleanup_done = true;
|
||||
}
|
||||
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(g_game_lifecycle_wake_pipe[0], &readfds);
|
||||
int rc = select(g_game_lifecycle_wake_pipe[0] + 1, &readfds, NULL, NULL,
|
||||
NULL);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
log_debug(" [GAME] sleep wait failed: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
drain_game_lifecycle_wake_pipe();
|
||||
continue;
|
||||
}
|
||||
if (sleep_cleanup_done) {
|
||||
if (!discard_game_lifecycle_events_nowait(kq))
|
||||
break;
|
||||
sleep_cleanup_done = false;
|
||||
restore_suspended_game_if_alive(kq, suspended_game_pid);
|
||||
suspended_game_pid = 0;
|
||||
}
|
||||
|
||||
struct kevent event;
|
||||
struct timespec timeout;
|
||||
const struct timespec *timeout_ptr = compute_game_wait_timeout(&timeout);
|
||||
@@ -431,9 +508,7 @@ static void *game_lifecycle_watcher_main(void *arg) {
|
||||
if (nev > 0) {
|
||||
if (event.filter == EVFILT_READ &&
|
||||
event.ident == (uintptr_t)g_game_lifecycle_wake_pipe[0]) {
|
||||
char drain_buf[32];
|
||||
(void)read(g_game_lifecycle_wake_pipe[0], drain_buf,
|
||||
sizeof(drain_buf));
|
||||
drain_game_lifecycle_wake_pipe();
|
||||
} else if ((event.fflags & NOTE_TRACKERR) != 0) {
|
||||
log_debug(" [GAME] NOTE_TRACKERR for pid=%ld", (long)event.ident);
|
||||
} else {
|
||||
@@ -444,6 +519,8 @@ static void *game_lifecycle_watcher_main(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
if (runtime_sleep_mode_active())
|
||||
continue;
|
||||
poll_game_modules(kq);
|
||||
}
|
||||
|
||||
|
||||
+37
-1
@@ -756,6 +756,8 @@ bool mount_image(const char *file_path, image_fs_type_t fs_type) {
|
||||
return false;
|
||||
if (!stat_image_file(file_path, &st))
|
||||
return false;
|
||||
if (runtime_scan_blocked())
|
||||
return false;
|
||||
|
||||
log_debug(" [IMG] Mounting image (%s): %s -> %s", image_fs_name(fs_type),
|
||||
file_path, mount_point);
|
||||
@@ -777,10 +779,18 @@ bool mount_image(const char *file_path, image_fs_type_t fs_type) {
|
||||
attach_backend, &unit_id, devname, sizeof(devname))) {
|
||||
return false;
|
||||
}
|
||||
if (runtime_scan_blocked()) {
|
||||
(void)detach_attached_unit(attach_backend, unit_id);
|
||||
return false;
|
||||
}
|
||||
if (!perform_image_nmount(file_path, fs_type, attach_backend, unit_id, devname,
|
||||
mount_point, mount_read_only, force_mount)) {
|
||||
return false;
|
||||
}
|
||||
if (runtime_scan_blocked()) {
|
||||
(void)unmount_image(file_path, unit_id, attach_backend);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!validate_mounted_image(file_path, fs_type, attach_backend, unit_id,
|
||||
devname, mount_point)) {
|
||||
@@ -825,7 +835,7 @@ bool unmount_image(const char *file_path, int unit_id, attach_backend_t backend)
|
||||
|
||||
// Remove mount.lnk and unmount /system_ex/app/<titleid> that point to this
|
||||
// source before unmounting the virtual disk itself.
|
||||
cleanup_mount_links(mount_point, true);
|
||||
cleanup_mount_links_for_source_unmount(mount_point);
|
||||
clear_cached_game(mount_point);
|
||||
|
||||
// Unmount stacked layers (unionfs over image fs).
|
||||
@@ -979,6 +989,32 @@ void cleanup_stale_image_mounts_for_root(const char *root) {
|
||||
}
|
||||
}
|
||||
|
||||
bool unmount_usb_image_mounts_for_suspend(void) {
|
||||
bool all_unmounted = true;
|
||||
|
||||
for (int k = 0; k < MAX_IMAGE_MOUNTS; k++) {
|
||||
image_cache_entry_t cached_entry;
|
||||
if (!get_image_cache_entry(k, &cached_entry))
|
||||
continue;
|
||||
if (!is_usb_storage_path(cached_entry.path))
|
||||
continue;
|
||||
|
||||
log_debug(" [IMG][%s] USB suspend unmount: %s",
|
||||
attach_backend_name(cached_entry.backend), cached_entry.path);
|
||||
if (unmount_image(cached_entry.path, cached_entry.unit_id,
|
||||
cached_entry.backend)) {
|
||||
invalidate_image_cache_entry(k);
|
||||
continue;
|
||||
}
|
||||
|
||||
all_unmounted = false;
|
||||
log_debug(" [IMG][%s] USB suspend unmount pending: %s",
|
||||
attach_backend_name(cached_entry.backend), cached_entry.path);
|
||||
}
|
||||
|
||||
return all_unmounted;
|
||||
}
|
||||
|
||||
void cleanup_mount_dirs(void) {
|
||||
DIR *d = opendir(IMAGE_MOUNT_BASE);
|
||||
if (!d) {
|
||||
|
||||
+8
-5
@@ -170,7 +170,7 @@ static bool mount_and_install(const char *src_path, const char *title_id,
|
||||
restage_staging = (!is_remount || should_register);
|
||||
restage_appmeta = (!is_remount || appmeta_missing);
|
||||
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return false;
|
||||
|
||||
// COPY FILES
|
||||
@@ -215,7 +215,7 @@ static bool mount_and_install(const char *src_path, const char *title_id,
|
||||
metadata_restaged = true;
|
||||
}
|
||||
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return false;
|
||||
|
||||
if (!mount_title_nullfs(title_id, src_path)) {
|
||||
@@ -320,10 +320,13 @@ static bool mount_and_install(const char *src_path, const char *title_id,
|
||||
// --- Execution (per discovered candidate) ---
|
||||
void process_scan_candidates(const scan_candidate_t *candidates,
|
||||
int candidate_count) {
|
||||
if (runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
sm_install_poll_pending();
|
||||
|
||||
for (int i = 0; i < candidate_count; i++) {
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
const scan_candidate_t *c = &candidates[i];
|
||||
@@ -347,7 +350,7 @@ void process_scan_candidates(const scan_candidate_t *candidates,
|
||||
if (!sm_install_queue_candidate(c, has_src_snd0)) {
|
||||
log_debug(" [REG] Failed to queue staged install: %s (%s)",
|
||||
c->title_name, c->title_id);
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
uint8_t failed_attempts = bump_failed_mount_attempts(c->title_id);
|
||||
@@ -365,7 +368,7 @@ void process_scan_candidates(const scan_candidate_t *candidates,
|
||||
cache_game_entry(c->path, c->title_id, c->title_name);
|
||||
}
|
||||
} else {
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
uint8_t failed_attempts = bump_failed_mount_attempts(c->title_id);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "sm_game_cache.h"
|
||||
#include "sm_limits.h"
|
||||
#include "sm_log.h"
|
||||
#include "sm_runtime.h"
|
||||
#include "sm_time.h"
|
||||
#include "sm_title_state.h"
|
||||
|
||||
@@ -265,6 +266,9 @@ static void poll_pending_installs(void) {
|
||||
}
|
||||
|
||||
uint64_t sm_install_next_wake_us(uint64_t now_us) {
|
||||
if (runtime_scan_blocked())
|
||||
return 0;
|
||||
|
||||
if (!install_queue_active())
|
||||
return 0;
|
||||
|
||||
@@ -290,10 +294,16 @@ uint64_t sm_install_next_wake_us(uint64_t now_us) {
|
||||
}
|
||||
|
||||
void sm_install_poll_pending(void) {
|
||||
if (runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
poll_pending_installs();
|
||||
}
|
||||
|
||||
void sm_install_service_pending(void) {
|
||||
if (runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
if (!install_queue_active())
|
||||
return;
|
||||
|
||||
@@ -379,6 +389,9 @@ static void drop_queued_install_entry(pending_install_entry_t *entry) {
|
||||
}
|
||||
|
||||
bool sm_install_submit_queued(void) {
|
||||
if (runtime_scan_blocked())
|
||||
return true;
|
||||
|
||||
int queued_count = count_queued_installs();
|
||||
if (queued_count <= 0)
|
||||
return true;
|
||||
|
||||
@@ -20,6 +20,32 @@ bool is_under_image_mount_base(const char *path) {
|
||||
path[image_prefix_len] == '/');
|
||||
}
|
||||
|
||||
bool path_matches_root_or_child(const char *path, const char *root) {
|
||||
if (!path || !root || root[0] == '\0')
|
||||
return false;
|
||||
size_t root_len = strlen(root);
|
||||
if (strncmp(path, root, root_len) != 0)
|
||||
return false;
|
||||
return path[root_len] == '\0' || path[root_len] == '/';
|
||||
}
|
||||
|
||||
bool is_usb_storage_path(const char *path) {
|
||||
static const char *usb_roots[] = {
|
||||
"/mnt/usb0", "/mnt/usb1", "/mnt/usb2", "/mnt/usb3",
|
||||
"/mnt/usb4", "/mnt/usb5", "/mnt/usb6", "/mnt/usb7",
|
||||
"/mnt/ext0",
|
||||
};
|
||||
|
||||
if (!path || path[0] == '\0')
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < sizeof(usb_roots) / sizeof(usb_roots[0]); i++) {
|
||||
if (path_matches_root_or_child(path, usb_roots[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool build_backports_root_path(const char *scan_path, char out[MAX_PATH]) {
|
||||
if (is_under_image_mount_base(scan_path))
|
||||
return false;
|
||||
|
||||
+12
-2
@@ -489,12 +489,22 @@ void cleanup_lost_sources_for_scan_root(const char *scan_root) {
|
||||
prune_path_state_for_root(scan_root);
|
||||
}
|
||||
|
||||
void unmount_usb_sources_for_suspend(void) {
|
||||
log_debug("[SLEEP] USB-backed mount cleanup begin");
|
||||
cleanup_usb_mount_links_for_suspend();
|
||||
if (!unmount_usb_image_mounts_for_suspend()) {
|
||||
log_debug("[SLEEP] some USB-backed image mounts remain busy");
|
||||
}
|
||||
cleanup_mount_dirs();
|
||||
log_debug("[SLEEP] USB-backed mount cleanup done");
|
||||
}
|
||||
|
||||
static void collect_scan_candidates_from_root(
|
||||
const char *scan_path, scan_candidate_t *candidates, int max_candidates,
|
||||
int *candidate_count, const struct AppDbTitleList *app_db_titles,
|
||||
bool app_db_titles_ready, char discovered_param_roots[][MAX_PATH],
|
||||
int *discovered_param_root_count, bool *unstable_found_out) {
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return;
|
||||
|
||||
unsigned int scan_depth = runtime_config()->scan_depth;
|
||||
@@ -557,7 +567,7 @@ int collect_scan_candidates(scan_candidate_t *candidates, int max_candidates,
|
||||
log_debug(" [DB] app.db title list unavailable for this scan cycle");
|
||||
|
||||
for (int i = 0; i < get_scan_path_count(); i++) {
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
break;
|
||||
collect_scan_candidates_from_root(get_scan_path(i), candidates,
|
||||
max_candidates,
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ bool sm_scan_tree_walk(const char *scan_root, const char *dir_path,
|
||||
unsigned int depth_from_root,
|
||||
unsigned int remaining_depth,
|
||||
const sm_scan_tree_callbacks_t *callbacks, void *ctx) {
|
||||
if (should_stop_requested())
|
||||
if (should_stop_requested() || runtime_scan_blocked())
|
||||
return true;
|
||||
|
||||
if (depth_from_root > 0u &&
|
||||
@@ -79,7 +79,7 @@ bool sm_scan_tree_walk(const char *scan_root, const char *dir_path,
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
if (should_stop_requested()) {
|
||||
if (should_stop_requested() || runtime_scan_blocked()) {
|
||||
closedir(d);
|
||||
return true;
|
||||
}
|
||||
|
||||
+91
-9
@@ -3,6 +3,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include "sm_config_mount.h"
|
||||
#include "sm_fakelib.h"
|
||||
@@ -886,7 +887,7 @@ static void apply_runtime_config_reload_effects(const runtime_config_t *old_cfg,
|
||||
}
|
||||
|
||||
static bool should_abort_scan_cycle(void) {
|
||||
return should_stop_requested();
|
||||
return should_stop_requested() || runtime_scan_blocked();
|
||||
}
|
||||
|
||||
static bool run_full_scan_cycle(bool startup_sync, const char *reason,
|
||||
@@ -937,7 +938,7 @@ static bool run_full_scan_cycle(bool startup_sync, const char *reason,
|
||||
total_found_games);
|
||||
}
|
||||
|
||||
return !should_stop_requested();
|
||||
return !should_abort_scan_cycle();
|
||||
}
|
||||
|
||||
static bool run_targeted_scan_cycle(int scan_root_index,
|
||||
@@ -971,7 +972,7 @@ static bool run_targeted_scan_cycle(int scan_root_index,
|
||||
if (unstable_found_out)
|
||||
*unstable_found_out = unstable_found;
|
||||
|
||||
return !should_stop_requested();
|
||||
return !should_abort_scan_cycle();
|
||||
}
|
||||
|
||||
static int find_pending_cleanup_scan_root(void) {
|
||||
@@ -1177,6 +1178,24 @@ static bool drain_scanner_events_nowait(int kq) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool discard_scanner_events_nowait(int kq) {
|
||||
struct kevent events[SCANNER_EVENT_BATCH];
|
||||
struct timespec timeout;
|
||||
memset(&timeout, 0, sizeof(timeout));
|
||||
|
||||
while (true) {
|
||||
int nev = kevent(kq, NULL, 0, events, SCANNER_EVENT_BATCH, &timeout);
|
||||
if (nev < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
log_debug(" [SCAN] stale event drain failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if (nev == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static char g_scanner_shutdown_reason[128];
|
||||
|
||||
static void request_scanner_shutdown(const char *reason) {
|
||||
@@ -1237,10 +1256,19 @@ void sm_scanner_wake(void) {
|
||||
}
|
||||
|
||||
bool sm_scanner_run_startup_sync(void) {
|
||||
if (should_stop_requested())
|
||||
return false;
|
||||
while (!should_stop_requested()) {
|
||||
while (runtime_scan_blocked() && !should_stop_requested())
|
||||
sceKernelUsleep(200000);
|
||||
|
||||
return run_full_scan_cycle(true, NULL, NULL);
|
||||
if (should_stop_requested())
|
||||
return false;
|
||||
if (run_full_scan_cycle(true, NULL, NULL))
|
||||
return true;
|
||||
if (!runtime_scan_blocked())
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sm_scanner_run_loop(void) {
|
||||
@@ -1281,6 +1309,7 @@ void sm_scanner_run_loop(void) {
|
||||
|
||||
uint64_t next_full_resync_us =
|
||||
monotonic_time_us() + scanner_full_resync_interval_us();
|
||||
bool was_sleeping = false;
|
||||
|
||||
while (true) {
|
||||
if (should_stop_requested()) {
|
||||
@@ -1288,11 +1317,42 @@ void sm_scanner_run_loop(void) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (runtime_scan_blocked()) {
|
||||
was_sleeping = true;
|
||||
fd_set readfds;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(g_scanner_wake_pipe[0], &readfds);
|
||||
int rc = select(g_scanner_wake_pipe[0] + 1, &readfds, NULL, NULL, NULL);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
log_debug(" [SCAN] sleep wait failed: %s", strerror(errno));
|
||||
close(kq);
|
||||
clear_scanner_watch_entries();
|
||||
request_scanner_shutdown("scanner sleep wait failed");
|
||||
return;
|
||||
}
|
||||
drain_scanner_wake_pipe();
|
||||
continue;
|
||||
}
|
||||
if (was_sleeping) {
|
||||
was_sleeping = false;
|
||||
if (!discard_scanner_events_nowait(kq)) {
|
||||
close(kq);
|
||||
clear_scanner_watch_entries();
|
||||
request_scanner_shutdown("scanner stale event drain failed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char scan_reason[128];
|
||||
if (consume_scan_now_request(scan_reason, sizeof(scan_reason))) {
|
||||
bool unstable_found = false;
|
||||
if (!run_full_scan_cycle(false, scan_reason, &unstable_found))
|
||||
if (!run_full_scan_cycle(false, scan_reason, &unstable_found)) {
|
||||
if (runtime_scan_blocked())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
clear_all_dirty_scan_roots();
|
||||
if (!rebuild_all_scan_root_watch_trees(kq) ||
|
||||
!drain_scanner_events_nowait(kq)) {
|
||||
@@ -1354,8 +1414,11 @@ void sm_scanner_run_loop(void) {
|
||||
|
||||
if (next_full_resync_us != 0 && now_us >= next_full_resync_us) {
|
||||
bool unstable_found = false;
|
||||
if (!run_full_scan_cycle(false, NULL, &unstable_found))
|
||||
if (!run_full_scan_cycle(false, NULL, &unstable_found)) {
|
||||
if (runtime_scan_blocked())
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
clear_all_dirty_scan_roots();
|
||||
if (!rebuild_all_scan_root_watch_trees(kq) ||
|
||||
!drain_scanner_events_nowait(kq)) {
|
||||
@@ -1384,6 +1447,8 @@ void sm_scanner_run_loop(void) {
|
||||
|
||||
int dirty_root_index = find_due_dirty_scan_root(now_us);
|
||||
if (dirty_root_index >= 0) {
|
||||
bool cleanup_pending =
|
||||
g_scanner_root_states[dirty_root_index].cleanup_pending;
|
||||
bool rebuild_watch_tree =
|
||||
g_scanner_root_states[dirty_root_index].watch_tree_stale;
|
||||
uint8_t rebuild_watch_tree_depth =
|
||||
@@ -1401,8 +1466,25 @@ void sm_scanner_run_loop(void) {
|
||||
clear_scan_root_watch_tree_state(dirty_root_index);
|
||||
|
||||
bool unstable_found = false;
|
||||
if (!run_targeted_scan_cycle(dirty_root_index, &unstable_found))
|
||||
if (!run_targeted_scan_cycle(dirty_root_index, &unstable_found)) {
|
||||
if (runtime_scan_blocked()) {
|
||||
scanner_root_state_t *state =
|
||||
&g_scanner_root_states[dirty_root_index];
|
||||
if (cleanup_pending)
|
||||
schedule_scan_root_cleanup(dirty_root_index);
|
||||
schedule_scan_root_dirty(dirty_root_index, monotonic_time_us(), true);
|
||||
if (rebuild_watch_tree) {
|
||||
state->watch_tree_stale = true;
|
||||
state->watch_tree_rebuild_depth = rebuild_watch_tree_depth;
|
||||
state->watch_tree_rebuild_kind = rebuild_watch_tree_kind;
|
||||
(void)strlcpy(state->watch_tree_rebuild_path,
|
||||
rebuild_watch_tree_path,
|
||||
sizeof(state->watch_tree_rebuild_path));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (rebuild_watch_tree &&
|
||||
!rebuild_scan_root_watch_subtree(kq, dirty_root_index,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "sm_limits.h"
|
||||
#include "sm_log.h"
|
||||
#include "sm_runtime.h"
|
||||
#include "sm_scan.h"
|
||||
#include "sm_shellcore_flags.h"
|
||||
|
||||
#define SHELLCORE_FLAG_WAITMODE_OR 2u
|
||||
@@ -610,6 +611,8 @@ static void poll_shellcore_flag(shellcore_flag_monitor_t *flag) {
|
||||
int rc;
|
||||
bool changed = false;
|
||||
bool entered_shutdown_on_going = false;
|
||||
bool entered_main_on_standby = false;
|
||||
bool entered_suspend_on_going = false;
|
||||
bool entered_resume_working = false;
|
||||
bool is_system_state_mgr_info = false;
|
||||
unsigned current_state = 0;
|
||||
@@ -646,6 +649,14 @@ static void poll_shellcore_flag(shellcore_flag_monitor_t *flag) {
|
||||
current_state == SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING &&
|
||||
(!flag->has_last_pattern ||
|
||||
previous_state != SYSTEM_STATE_MGR_STATE_SHUTDOWN_ON_GOING);
|
||||
entered_main_on_standby =
|
||||
current_state == SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY &&
|
||||
(!flag->has_last_pattern ||
|
||||
previous_state != SYSTEM_STATE_MGR_STATE_MAIN_ON_STANDBY);
|
||||
entered_suspend_on_going =
|
||||
current_state == SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING &&
|
||||
(!flag->has_last_pattern ||
|
||||
previous_state != SYSTEM_STATE_MGR_STATE_SUSPEND_ON_GOING);
|
||||
entered_resume_working =
|
||||
flag->has_last_pattern &&
|
||||
current_state == SYSTEM_STATE_MGR_STATE_WORKING &&
|
||||
@@ -664,8 +675,17 @@ static void poll_shellcore_flag(shellcore_flag_monitor_t *flag) {
|
||||
if (entered_shutdown_on_going) {
|
||||
request_shutdown_stop("SceSystemStateMgrInfo=SHUTDOWN_ON_GOING");
|
||||
}
|
||||
if (entered_main_on_standby) {
|
||||
request_runtime_scan_block(true, "SceSystemStateMgrInfo=MAIN_ON_STANDBY");
|
||||
unmount_usb_sources_for_suspend();
|
||||
}
|
||||
if (entered_suspend_on_going) {
|
||||
request_runtime_sleep_mode(true, "SceSystemStateMgrInfo=SUSPEND_ON_GOING");
|
||||
}
|
||||
if (entered_resume_working) {
|
||||
request_scan_now("SceSystemStateMgrInfo=WORKING");
|
||||
request_runtime_sleep_mode(false, "SceSystemStateMgrInfo=WORKING");
|
||||
request_runtime_scan_block(false, "SceSystemStateMgrInfo=WORKING");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user