Handle suspend sleep mode and USB cleanup. Thx to @hadobedo

This commit is contained in:
Roman Tarasov committed 2026-05-10 16:01:59 +03:00
1 parent d4c4cda739
commit 9fa6ad94de
16 files changed
+421 -37

No files matched your search

+4 -2
View File
@@ -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.
+2
View 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.
+4
View File
@@ -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]);
+8
View File
@@ -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.
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+13
View File
@@ -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;
+26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+20
View File
@@ -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");
}
}