From 24fba2be21f384a498a254677d7443c72a4bddc2 Mon Sep 17 00:00:00 2001 From: Roman Tarasov Date: Wed, 3 Jun 2026 21:13:07 +0300 Subject: [PATCH] Reduce scanner directory watchers by default --- README.md | 2 ++ config.ini.example | 12 +++++++++ include/sm_types.h | 2 ++ src/sm_config_mount.c | 23 ++++++++++++++++ src/sm_scanner.c | 63 +++++++++++++++++++++++++++++-------------- 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fb77cb8..eaa148b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ Supported keys (all optional): - `image_rw=` (repeatable; force read-write mode for this image filename) - `image_sector=:` (repeatable; force sector size for this image filename) - `scan_depth=<1..2>` (`1` = scan only first-level subfolders, `2` = also scan one additional nested level; default: `1`) +- `watch_subdirs=1|0` (`1` keeps vnode watchers on scan root subdirectories; default: `0`) +- `watch_image_files=1|0` (`1` keeps vnode watchers on discovered image files; default: `1`) - `recursive_scan=1|0` (deprecated compatibility key; `1` forces `scan_depth=2`) - `scan_interval_seconds=<1..3600>` (full scan loop interval; default: `15`) - `stability_wait_seconds=<0..3600>` (minimum source age before processing; default: `10`) diff --git a/config.ini.example b/config.ini.example index 9ba6b62..f55685b 100644 --- a/config.ini.example +++ b/config.ini.example @@ -45,6 +45,18 @@ # Default: 1 # scan_depth=1 +# Directory watcher granularity: +# 1 -> keep vnode watchers on scan root subdirectories +# 0 -> watch only scan roots/parents and rely on targeted/full scans for children +# Default: 0 +# watch_subdirs=0 + +# Image file watcher granularity: +# 1 -> keep vnode watchers on discovered image files +# 0 -> detect image file changes through targeted/full scans +# Default: 1 +# watch_image_files=1 + # Legacy compatibility: # recursive_scan=1 forces scan_depth=2 # Default: disabled diff --git a/include/sm_types.h b/include/sm_types.h index 1f0bc28..244df4d 100644 --- a/include/sm_types.h +++ b/include/sm_types.h @@ -116,6 +116,8 @@ typedef struct runtime_config { bool kstuff_game_auto_toggle; bool kstuff_crash_detection_enabled; bool legacy_recursive_scan_forced; + bool watch_subdirs; + bool watch_image_files; char global_fakelib_path[MAX_PATH]; uint32_t global_fakelib_exclude_title_count; char global_fakelib_exclude_title_ids[MAX_FAKELIB_EXCLUDE_RULES][MAX_TITLE_ID]; diff --git a/src/sm_config_mount.c b/src/sm_config_mount.c index 4f6787b..e9d866d 100644 --- a/src/sm_config_mount.c +++ b/src/sm_config_mount.c @@ -257,6 +257,8 @@ static void init_runtime_config_defaults(runtime_config_state_t *state) { state->cfg.kstuff_game_auto_toggle = true; state->cfg.kstuff_crash_detection_enabled = true; state->cfg.legacy_recursive_scan_forced = false; + state->cfg.watch_subdirs = false; + state->cfg.watch_image_files = true; (void)strlcpy(state->cfg.global_fakelib_path, DEFAULT_GLOBAL_FAKELIB_PATH, sizeof(state->cfg.global_fakelib_path)); state->cfg.scan_depth = DEFAULT_SCAN_DEPTH; @@ -1241,6 +1243,24 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st continue; } + if (strcasecmp(key, "watch_subdirs") == 0) { + if (!parse_bool_ini(value, &bval)) { + log_debug(" [CFG] invalid bool at line %d: %s=%s", line_no, key, value); + continue; + } + state->cfg.watch_subdirs = bval; + continue; + } + + if (strcasecmp(key, "watch_image_files") == 0) { + if (!parse_bool_ini(value, &bval)) { + log_debug(" [CFG] invalid bool at line %d: %s=%s", line_no, key, value); + continue; + } + state->cfg.watch_image_files = bval; + continue; + } + if (strcasecmp(key, "backport_fakelib") == 0) { if (!parse_bool_ini(value, &bval)) { log_debug(" [CFG] invalid bool at line %d: %s=%s", line_no, key, value); @@ -1488,6 +1508,7 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st "legacy_recursive_scan_forced=%d backport_fakelib=%d " "global_fakelib=%d global_fakelib_priority=%s " "global_fakelib_path=%s global_fakelib_exclude=%u " + "watch_subdirs=%d watch_image_files=%d " "kstuff_game_auto_toggle=%d kstuff_crash_detection=%d " "kstuff_pause_delay_image_s=%u kstuff_pause_delay_direct_s=%u " "exfat_backend=%s ufs_backend=%s " @@ -1505,6 +1526,8 @@ static config_load_status_t load_runtime_config_state(runtime_config_state_t *st state->cfg.global_fakelib_mount_first ? "game" : "global", state->cfg.global_fakelib_path, state->cfg.global_fakelib_exclude_title_count, + state->cfg.watch_subdirs ? 1 : 0, + state->cfg.watch_image_files ? 1 : 0, state->cfg.kstuff_game_auto_toggle ? 1 : 0, state->cfg.kstuff_crash_detection_enabled ? 1 : 0, state->cfg.kstuff_pause_delay_image_seconds, diff --git a/src/sm_scanner.c b/src/sm_scanner.c index b0262c8..27874f4 100644 --- a/src/sm_scanner.c +++ b/src/sm_scanner.c @@ -240,7 +240,12 @@ static bool fakelib_runtime_config_changed(const runtime_config_t *old_cfg, static uint32_t scanner_config_topology_hash(void) { uint32_t hash = 2166136261u; int scan_path_count = get_scan_path_count(); - uint32_t values[] = {runtime_config()->scan_depth, (uint32_t)scan_path_count}; + uint32_t values[] = { + runtime_config()->scan_depth, + runtime_config()->watch_subdirs ? 1u : 0u, + runtime_config()->watch_image_files ? 1u : 0u, + (uint32_t)scan_path_count, + }; for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) { for (unsigned shift = 0; shift < 32u; shift += 8u) { @@ -584,6 +589,7 @@ static bool resolve_watch_tree_rebuild_target(const scanner_watch_entry_t *entry typedef struct { int kq; int scan_root_index; + bool watch_subdirs; } register_watch_tree_ctx_t; static sm_scan_tree_dir_visit_t register_watch_directory_visit( @@ -592,9 +598,11 @@ static sm_scan_tree_dir_visit_t register_watch_directory_visit( scanner_watch_kind_t kind = (depth_from_root == 0u) ? SCANNER_WATCH_SCAN_ROOT : SCANNER_WATCH_SCAN_SUBDIR; - if (!register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, dir_path, kind, - (uint8_t)depth_from_root)) { - return SM_SCAN_TREE_DIR_ABORT; + if (depth_from_root == 0u || ctx->watch_subdirs) { + if (!register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, dir_path, + kind, (uint8_t)depth_from_root)) { + return SM_SCAN_TREE_DIR_ABORT; + } } return SM_SCAN_TREE_DIR_DESCEND; @@ -637,19 +645,28 @@ static bool rebuild_scan_root_watch_tree(int kq, int scan_root_index) { return true; } - unsigned int scan_depth = get_scan_depth_for_root(scan_root); - - register_watch_tree_ctx_t walk_ctx = { - .kq = kq, - .scan_root_index = scan_root_index, - }; - sm_scan_tree_callbacks_t callbacks = { - .on_directory = register_watch_directory_visit, - .on_image_file = register_watch_image_visit, - }; - if (!sm_scan_tree_walk(scan_root, scan_root, 0u, scan_depth, &callbacks, - &walk_ctx)) { - return false; + const runtime_config_t *cfg = runtime_config(); + if (!cfg->watch_subdirs && !cfg->watch_image_files) { + if (!register_scanner_watch_entry(kq, scan_root_index, scan_root, + SCANNER_WATCH_SCAN_ROOT, 0u)) { + return false; + } + } else { + unsigned int scan_depth = get_scan_depth_for_root(scan_root); + register_watch_tree_ctx_t walk_ctx = { + .kq = kq, + .scan_root_index = scan_root_index, + .watch_subdirs = cfg->watch_subdirs, + }; + sm_scan_tree_callbacks_t callbacks = { + .on_directory = register_watch_directory_visit, + .on_image_file = + cfg->watch_image_files ? register_watch_image_visit : NULL, + }; + if (!sm_scan_tree_walk(scan_root, scan_root, 0u, scan_depth, &callbacks, + &walk_ctx)) { + return false; + } } char backport_root[MAX_PATH]; @@ -690,10 +707,15 @@ static bool rebuild_scan_root_watch_subtree(int kq, int scan_root_index, return true; } - unsigned int scan_depth = get_scan_depth_for_root(scan_root); - if (!remove_scan_root_watch_entries_for_path(scan_root_index, rebuild_path)) return false; + const runtime_config_t *cfg = runtime_config(); + if (!cfg->watch_subdirs && !cfg->watch_image_files) { + clear_scan_root_watch_tree_state(scan_root_index); + return true; + } + + unsigned int scan_depth = get_scan_depth_for_root(scan_root); if (rebuild_depth > scan_depth) { clear_scan_root_watch_tree_state(scan_root_index); return true; @@ -702,10 +724,11 @@ static bool rebuild_scan_root_watch_subtree(int kq, int scan_root_index, register_watch_tree_ctx_t walk_ctx = { .kq = kq, .scan_root_index = scan_root_index, + .watch_subdirs = cfg->watch_subdirs, }; sm_scan_tree_callbacks_t callbacks = { .on_directory = register_watch_directory_visit, - .on_image_file = register_watch_image_visit, + .on_image_file = cfg->watch_image_files ? register_watch_image_visit : NULL, }; if (!sm_scan_tree_walk(scan_root, rebuild_path, rebuild_depth, scan_depth - rebuild_depth, &callbacks, &walk_ctx)) {