Fix snd0Info paths

This commit is contained in:
Roman Tarasov committed 2026-05-10 18:24:29 +03:00
1 parent 9fa6ad94de
commit cbd0fce4f9
4 files changed
+219 -23

No files matched your search

+4
View File
@@ -7,8 +7,12 @@ struct AppDbTitleList;
// Close the cached app.db handle and release related resources.
void shutdown_app_db(void);
// Normalize existing snd0info rows during process startup.
bool app_db_run_startup_maintenance(void);
// Update snd0 metadata for a registered title.
int update_snd0info(const char *title_id);
// Normalize an existing snd0Info path for one title.
int normalize_snd0info_for_title(const char *title_id);
// Check whether a title ID exists in a cached app.db title list.
bool app_db_title_list_contains(const struct AppDbTitleList *list,
const char *title_id);
+2
View File
@@ -500,6 +500,8 @@ int main(void) {
cleanup_staged_mount_links();
log_debug("[STARTUP] cleanup_duplicate_title_mounts begin");
cleanup_duplicate_title_mounts();
if (!app_db_run_startup_maintenance())
log_debug(" [DB] startup snd0info maintenance unavailable");
log_debug("[STARTUP] scanner startup sync begin");
if (!sm_scanner_run_startup_sync()) {
log_debug("[STARTUP] scanner startup sync aborted");
+172 -19
View File
@@ -11,15 +11,23 @@
static sqlite3 *g_app_db;
static sqlite3_stmt *g_app_db_stmt_update_snd0;
static sqlite3_stmt *g_app_db_stmt_normalize_snd0;
static struct AppDbTitleList g_app_db_title_cache;
static bool g_app_db_title_cache_ready = false;
static time_t g_app_db_title_cache_mtime = 0;
#define APP_DB_STARTUP_MAINTENANCE_RETRIES 3
#define APP_DB_STARTUP_MAINTENANCE_BUSY_TIMEOUT_MS 250
static void close_app_db(void) {
if (g_app_db_stmt_update_snd0) {
sqlite3_finalize(g_app_db_stmt_update_snd0);
g_app_db_stmt_update_snd0 = NULL;
}
if (g_app_db_stmt_normalize_snd0) {
sqlite3_finalize(g_app_db_stmt_normalize_snd0);
g_app_db_stmt_normalize_snd0 = NULL;
}
if (g_app_db) {
sqlite3_close(g_app_db);
g_app_db = NULL;
@@ -89,6 +97,111 @@ static int app_db_prepare_with_retry(const char *sql, sqlite3_stmt **stmt_out,
return -1;
}
static bool app_db_exec_with_retry(const char *sql, int max_attempts,
const char *label, int *changes_out) {
if (changes_out)
*changes_out = 0;
for (int attempt = 0; attempt < max_attempts; attempt++) {
if (!ensure_app_db_open())
return false;
char *err_msg = NULL;
int rc = sqlite3_exec(g_app_db, sql, NULL, NULL, &err_msg);
if (rc == SQLITE_OK) {
if (changes_out)
*changes_out = sqlite3_changes(g_app_db);
sqlite3_free(err_msg);
return true;
}
if (app_db_wait_retry(rc, attempt, max_attempts, false)) {
sqlite3_free(err_msg);
continue;
}
if (err_msg) {
log_debug(" [DB] exec failed for %s: rc=%d err=%s", label, rc, err_msg);
} else {
log_debug(" [DB] exec failed for %s: rc=%d err=%s", label, rc,
sqlite3_errmsg(g_app_db));
}
sqlite3_free(err_msg);
close_app_db();
return false;
}
close_app_db();
return false;
}
static int app_db_query_int(const char *sql, const char *label) {
sqlite3_stmt *stmt = NULL;
int prep_rc = app_db_prepare_with_retry(sql, &stmt,
APP_DB_STARTUP_MAINTENANCE_RETRIES,
label);
if (prep_rc != SQLITE_OK)
return -1;
int result = -1;
for (int attempt = 0; attempt < APP_DB_QUERY_BUSY_RETRIES; attempt++) {
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
result = sqlite3_column_int(stmt, 0);
break;
}
if (app_db_wait_retry(rc, attempt, APP_DB_QUERY_BUSY_RETRIES, false))
continue;
log_debug(" [DB] query failed for %s: rc=%d err=%s", label, rc,
sqlite3_errmsg(g_app_db));
break;
}
sqlite3_finalize(stmt);
return result;
}
bool app_db_run_startup_maintenance(void) {
if (!ensure_app_db_open())
return false;
(void)sqlite3_busy_timeout(g_app_db,
APP_DB_STARTUP_MAINTENANCE_BUSY_TIMEOUT_MS);
const char *backfill_count_sql =
"SELECT COUNT(*) FROM tbl_contentinfo "
"WHERE instr(snd0Info, '/user/app/') > 0 "
"OR instr(snd0Info, '/sce_sys') > 0;";
int pending_backfill =
app_db_query_int(backfill_count_sql, "snd0info normalize backfill check");
if (pending_backfill < 0) {
close_app_db();
return false;
}
int changes = 0;
if (pending_backfill > 0) {
const char *backfill_sql =
"UPDATE tbl_contentinfo "
"SET snd0Info = replace(replace(snd0Info, '/user/app/', "
"'/user/appmeta/'), '/sce_sys', '') "
"WHERE instr(snd0Info, '/user/app/') > 0 "
"OR instr(snd0Info, '/sce_sys') > 0;";
if (!app_db_exec_with_retry(backfill_sql,
APP_DB_STARTUP_MAINTENANCE_RETRIES,
"snd0info startup normalize", &changes))
return false;
}
log_debug(" [DB] snd0info startup maintenance done rows=%d pending=%d",
changes, pending_backfill);
close_app_db();
return true;
}
int update_snd0info(const char *title_id) {
if (!g_app_db_stmt_update_snd0) {
const char *sql =
@@ -107,10 +220,8 @@ int update_snd0info(const char *title_id) {
sqlite3_clear_bindings(g_app_db_stmt_update_snd0);
if (sqlite3_bind_text(g_app_db_stmt_update_snd0, 1, title_id, -1,
SQLITE_TRANSIENT) != SQLITE_OK) {
if (g_app_db) {
log_debug(" [DB] bind failed for snd0info update: %s",
sqlite3_errmsg(g_app_db));
}
log_debug(" [DB] bind failed for snd0info update: %s",
sqlite3_errmsg(g_app_db));
close_app_db();
return -1;
}
@@ -125,10 +236,55 @@ int update_snd0info(const char *title_id) {
if (app_db_wait_retry(rc, attempt, APP_DB_UPDATE_BUSY_RETRIES, false))
continue;
if (g_app_db) {
log_debug(" [DB] step failed for snd0info update: rc=%d err=%s", rc,
log_debug(" [DB] step failed for snd0info update: rc=%d err=%s", rc,
sqlite3_errmsg(g_app_db));
close_app_db();
return -1;
}
close_app_db();
return -1;
}
int normalize_snd0info_for_title(const char *title_id) {
if (!g_app_db_stmt_normalize_snd0) {
const char *sql =
"UPDATE tbl_contentinfo "
"SET snd0Info = replace(replace(snd0Info, '/user/app/', "
"'/user/appmeta/'), '/sce_sys', '') "
"WHERE titleId = ?1 "
"AND (instr(snd0Info, '/user/app/') > 0 "
"OR instr(snd0Info, '/sce_sys') > 0);";
int prep_rc = app_db_prepare_with_retry(sql, &g_app_db_stmt_normalize_snd0,
APP_DB_PREPARE_BUSY_RETRIES,
"snd0info normalize");
if (prep_rc != SQLITE_OK)
return -1;
}
for (int attempt = 0; attempt < APP_DB_UPDATE_BUSY_RETRIES; attempt++) {
sqlite3_reset(g_app_db_stmt_normalize_snd0);
sqlite3_clear_bindings(g_app_db_stmt_normalize_snd0);
if (sqlite3_bind_text(g_app_db_stmt_normalize_snd0, 1, title_id, -1,
SQLITE_TRANSIENT) != SQLITE_OK) {
log_debug(" [DB] bind failed for snd0info normalize: %s",
sqlite3_errmsg(g_app_db));
close_app_db();
return -1;
}
int rc = sqlite3_step(g_app_db_stmt_normalize_snd0);
if (rc == SQLITE_DONE) {
int changes = sqlite3_changes(g_app_db);
close_app_db();
return changes;
}
if (app_db_wait_retry(rc, attempt, APP_DB_UPDATE_BUSY_RETRIES, false))
continue;
log_debug(" [DB] step failed for snd0info normalize: rc=%d err=%s", rc,
sqlite3_errmsg(g_app_db));
close_app_db();
return -1;
}
@@ -176,26 +332,23 @@ static bool load_app_db_title_list(struct AppDbTitleList *list) {
"WHERE titleId != '' "
"ORDER BY titleId;";
sqlite3_stmt *stmt = NULL;
int prep_rc = app_db_prepare_with_retry(sql, &stmt, APP_DB_PREPARE_BUSY_RETRIES,
int prep_rc = app_db_prepare_with_retry(sql, &stmt,
APP_DB_PREPARE_BUSY_RETRIES,
"title list query");
if (prep_rc != SQLITE_OK) {
close_app_db();
if (prep_rc != SQLITE_OK)
return false;
}
int busy_attempts = 0;
while (!should_stop_requested()) {
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
const char *title_id = (const char *)sqlite3_column_text(stmt, 0);
if (title_id && title_id[0] != '\0') {
if (!append_app_db_title(list, title_id)) {
log_debug(" [DB] title list allocation failed");
sqlite3_finalize(stmt);
close_app_db();
free_app_db_title_list(list);
return false;
}
if (!append_app_db_title(list, title_id)) {
log_debug(" [DB] title list allocation failed");
sqlite3_finalize(stmt);
close_app_db();
free_app_db_title_list(list);
return false;
}
continue;
}
@@ -213,7 +366,7 @@ static bool load_app_db_title_list(struct AppDbTitleList *list) {
}
log_debug(" [DB] title list query failed: rc=%d err=%s", rc,
(g_app_db ? sqlite3_errmsg(g_app_db) : "unknown"));
sqlite3_errmsg(g_app_db));
sqlite3_finalize(stmt);
close_app_db();
free_app_db_title_list(list);
+41 -4
View File
@@ -6,6 +6,7 @@
#include <sys/event.h>
#include <sys/select.h>
#include "sm_appdb.h"
#include "sm_fakelib.h"
#include "sm_game_lifecycle.h"
#include "sm_kstuff.h"
@@ -38,6 +39,8 @@ static pthread_cond_t g_game_lifecycle_start_cond = PTHREAD_COND_INITIALIZER;
static bool g_game_lifecycle_start_ready = false;
static bool g_game_lifecycle_start_success = false;
static _Atomic pid_t g_active_game_pid = 0;
static pthread_mutex_t g_active_game_mutex = PTHREAD_MUTEX_INITIALIZER;
static char g_active_game_title_id[MAX_TITLE_ID];
static bool register_game_exit_watch(int kq, pid_t pid);
@@ -48,6 +51,32 @@ static void publish_active_game_pid(pid_t pid) {
sm_scanner_wake();
}
static void publish_active_game(pid_t pid, const char *title_id) {
pthread_mutex_lock(&g_active_game_mutex);
if (pid > 0 && title_id && title_id[0] != '\0')
(void)strlcpy(g_active_game_title_id, title_id,
sizeof(g_active_game_title_id));
else
g_active_game_title_id[0] = '\0';
pthread_mutex_unlock(&g_active_game_mutex);
publish_active_game_pid(pid);
}
static bool consume_active_game_title(pid_t pid,
char title_id_out[MAX_TITLE_ID]) {
bool matched = false;
pthread_mutex_lock(&g_active_game_mutex);
if (pid > 0 && atomic_load(&g_active_game_pid) == pid &&
g_active_game_title_id[0] != '\0') {
(void)strlcpy(title_id_out, g_active_game_title_id, MAX_TITLE_ID);
matched = true;
}
if (matched)
g_active_game_title_id[0] = '\0';
pthread_mutex_unlock(&g_active_game_mutex);
return matched;
}
static bool is_supported_title_id(const char *title_id) {
return strncmp(title_id, "PPSA", 4) == 0 || strncmp(title_id, "CUSA", 4) == 0;
}
@@ -145,7 +174,7 @@ static bool dispatch_game_launch(int kq, pid_t pid, uint64_t exec_time_us,
log_debug(" [GAME] started: %s pid=%ld app_id=0x%08X", title_id,
(long)pid, app_id);
publish_active_game_pid(pid);
publish_active_game(pid, title_id);
sm_kstuff_game_on_exec(pid, title_id, app_id, exec_time_us);
sm_fakelib_game_on_exec(pid, title_id);
return true;
@@ -392,6 +421,8 @@ static void handle_game_exec(int kq, pid_t pid) {
}
static void handle_game_exit(pid_t pid) {
char title_id[MAX_TITLE_ID] = {0};
bool had_active_title = consume_active_game_title(pid, title_id);
pending_game_launch_t *entry = find_pending_game_launch(pid);
if (entry)
clear_pending_game_launch(entry);
@@ -399,6 +430,12 @@ static void handle_game_exit(pid_t pid) {
publish_active_game_pid(0);
sm_fakelib_game_on_exit(pid);
sm_kstuff_game_on_exit(pid);
if (had_active_title) {
int snd0_updates = normalize_snd0info_for_title(title_id);
if (snd0_updates >= 0)
log_debug(" [DB] snd0info normalized after game exit rows=%d title=%s",
snd0_updates, title_id);
}
}
static void restore_suspended_game_if_alive(int kq, pid_t pid) {
@@ -467,7 +504,7 @@ static void *game_lifecycle_watcher_main(void *arg) {
clear_all_pending_game_launches();
sm_fakelib_game_shutdown();
sm_kstuff_game_shutdown();
publish_active_game_pid(0);
publish_active_game(0, NULL);
sleep_cleanup_done = true;
}
@@ -527,7 +564,7 @@ static void *game_lifecycle_watcher_main(void *arg) {
clear_all_pending_game_launches();
sm_fakelib_game_shutdown();
sm_kstuff_game_shutdown();
publish_active_game_pid(0);
publish_active_game(0, NULL);
close(kq);
log_debug(" [GAME] lifecycle watcher stopped");
return NULL;
@@ -600,7 +637,7 @@ void stop_game_lifecycle_watcher(void) {
(void)pthread_join(g_game_lifecycle_thread, NULL);
g_game_lifecycle_thread_started = false;
g_game_lifecycle_stop_requested = 0;
publish_active_game_pid(0);
publish_active_game(0, NULL);
close_game_lifecycle_wake_pipe();
}