v0.7.13 - -t
This commit is contained in:
563
src/config.c
563
src/config.c
@@ -63,6 +63,7 @@ typedef enum {
|
||||
|
||||
// Forward declarations for new admin API functions
|
||||
int populate_default_config_values(void);
|
||||
int populate_all_config_values_atomic(const char* admin_pubkey, const char* relay_pubkey);
|
||||
int process_admin_config_event(cJSON* event, char* error_message, size_t error_size);
|
||||
void invalidate_config_cache(void);
|
||||
|
||||
@@ -220,6 +221,8 @@ static int refresh_unified_cache_from_table(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Starting unified cache refresh from database");
|
||||
|
||||
// Log config table row count at start of refresh_unified_cache_from_table
|
||||
sqlite3_stmt* count_stmt;
|
||||
const char* count_sql = "SELECT COUNT(*) FROM config";
|
||||
@@ -236,30 +239,50 @@ static int refresh_unified_cache_from_table(void) {
|
||||
|
||||
// Load critical config values from table
|
||||
const char* admin_pubkey = get_config_value_from_table("admin_pubkey");
|
||||
DEBUG_TRACE("get_config_value_from_table('admin_pubkey') returned: %s",
|
||||
admin_pubkey ? admin_pubkey : "NULL");
|
||||
DEBUG_LOG("Loading admin_pubkey from config table: %s", admin_pubkey ? admin_pubkey : "NULL");
|
||||
if (admin_pubkey) {
|
||||
DEBUG_LOG("Setting admin_pubkey in cache: %s", admin_pubkey);
|
||||
strncpy(g_unified_cache.admin_pubkey, admin_pubkey, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||
DEBUG_TRACE("Set admin_pubkey in cache: %s", g_unified_cache.admin_pubkey);
|
||||
free((char*)admin_pubkey);
|
||||
} else {
|
||||
DEBUG_LOG("No admin_pubkey found in config table");
|
||||
}
|
||||
|
||||
const char* relay_pubkey = get_config_value_from_table("relay_pubkey");
|
||||
DEBUG_TRACE("get_config_value_from_table('relay_pubkey') returned: %s",
|
||||
relay_pubkey ? relay_pubkey : "NULL");
|
||||
DEBUG_LOG("Loading relay_pubkey from config table: %s", relay_pubkey ? relay_pubkey : "NULL");
|
||||
if (relay_pubkey) {
|
||||
DEBUG_LOG("Setting relay_pubkey in cache: %s", relay_pubkey);
|
||||
strncpy(g_unified_cache.relay_pubkey, relay_pubkey, sizeof(g_unified_cache.relay_pubkey) - 1);
|
||||
g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0';
|
||||
DEBUG_TRACE("Set relay_pubkey in cache: %s", g_unified_cache.relay_pubkey);
|
||||
free((char*)relay_pubkey);
|
||||
} else {
|
||||
DEBUG_LOG("No relay_pubkey found in config table");
|
||||
}
|
||||
|
||||
// Load auth-related config
|
||||
const char* auth_required = get_config_value_from_table("auth_required");
|
||||
g_unified_cache.auth_required = (auth_required && strcmp(auth_required, "true") == 0) ? 1 : 0;
|
||||
DEBUG_TRACE("Loaded auth_required from table: %s (cache value: %d)",
|
||||
auth_required ? auth_required : "NULL", g_unified_cache.auth_required);
|
||||
if (auth_required) free((char*)auth_required);
|
||||
|
||||
const char* admin_enabled = get_config_value_from_table("admin_enabled");
|
||||
g_unified_cache.admin_enabled = (admin_enabled && strcmp(admin_enabled, "true") == 0) ? 1 : 0;
|
||||
DEBUG_TRACE("Loaded admin_enabled from table: %s (cache value: %d)",
|
||||
admin_enabled ? admin_enabled : "NULL", g_unified_cache.admin_enabled);
|
||||
if (admin_enabled) free((char*)admin_enabled);
|
||||
|
||||
const char* max_file_size = get_config_value_from_table("max_file_size");
|
||||
g_unified_cache.max_file_size = max_file_size ? atol(max_file_size) : 104857600; // 100MB default
|
||||
DEBUG_TRACE("Loaded max_file_size from table: %s (cache value: %ld)",
|
||||
max_file_size ? max_file_size : "NULL", g_unified_cache.max_file_size);
|
||||
if (max_file_size) free((char*)max_file_size);
|
||||
|
||||
const char* nip42_mode = get_config_value_from_table("nip42_mode");
|
||||
@@ -271,6 +294,8 @@ static int refresh_unified_cache_from_table(void) {
|
||||
} else {
|
||||
g_unified_cache.nip42_mode = 1; // Optional/enabled
|
||||
}
|
||||
DEBUG_TRACE("Loaded nip42_mode from table: %s (cache value: %d)",
|
||||
nip42_mode, g_unified_cache.nip42_mode);
|
||||
free((char*)nip42_mode);
|
||||
} else {
|
||||
g_unified_cache.nip42_mode = 1; // Default to optional/enabled
|
||||
@@ -287,6 +312,8 @@ static int refresh_unified_cache_from_table(void) {
|
||||
// Load NIP-70 protected events config
|
||||
const char* nip70_enabled = get_config_value_from_table("nip70_protected_events_enabled");
|
||||
g_unified_cache.nip70_protected_events_enabled = (nip70_enabled && strcmp(nip70_enabled, "true") == 0) ? 1 : 0;
|
||||
DEBUG_TRACE("Loaded nip70_protected_events_enabled from table: %s (cache value: %d)",
|
||||
nip70_enabled ? nip70_enabled : "NULL", g_unified_cache.nip70_protected_events_enabled);
|
||||
if (nip70_enabled) free((char*)nip70_enabled);
|
||||
|
||||
// Load NIP-11 relay info fields directly into cache (avoid circular dependency)
|
||||
@@ -368,6 +395,9 @@ static int refresh_unified_cache_from_table(void) {
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
DEBUG_LOG("Configuration cache refreshed (%d entries)", config_count);
|
||||
DEBUG_LOG("Final cache state - admin_pubkey: %s, relay_pubkey: %s",
|
||||
g_unified_cache.admin_pubkey[0] ? g_unified_cache.admin_pubkey : "EMPTY",
|
||||
g_unified_cache.relay_pubkey[0] ? g_unified_cache.relay_pubkey : "EMPTY");
|
||||
|
||||
// Log config table row count at end of refresh_unified_cache_from_table
|
||||
if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
|
||||
@@ -381,44 +411,131 @@ static int refresh_unified_cache_from_table(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get admin pubkey from cache (with automatic refresh)
|
||||
const char* get_admin_pubkey_cached(void) {
|
||||
// First check without holding lock: whether we need refresh
|
||||
|
||||
// Unified cache getter function - handles all config values through unified cache
|
||||
const char* get_cached_config_value(const char* key) {
|
||||
if (!key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check cache validity and refresh if needed
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int need_refresh = (!g_unified_cache.cache_valid || time(NULL) > g_unified_cache.cache_expires);
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
if (need_refresh) {
|
||||
// Perform refresh, which locks internally
|
||||
DEBUG_TRACE("Cache refresh needed for key '%s', calling refresh_unified_cache_from_table()", key);
|
||||
refresh_unified_cache_from_table();
|
||||
}
|
||||
|
||||
// Now read under lock
|
||||
// Return value from cache based on key
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
const char* result = g_unified_cache.admin_pubkey[0] ? g_unified_cache.admin_pubkey : NULL;
|
||||
|
||||
const char* result = NULL;
|
||||
|
||||
// Handle string fields from unified cache
|
||||
if (strcmp(key, "admin_pubkey") == 0) {
|
||||
result = g_unified_cache.admin_pubkey[0] ? g_unified_cache.admin_pubkey : NULL;
|
||||
DEBUG_TRACE("Returning admin_pubkey from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_pubkey") == 0) {
|
||||
result = g_unified_cache.relay_pubkey[0] ? g_unified_cache.relay_pubkey : NULL;
|
||||
DEBUG_TRACE("Returning relay_pubkey from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_name") == 0) {
|
||||
result = g_unified_cache.relay_info.name[0] ? g_unified_cache.relay_info.name : NULL;
|
||||
DEBUG_TRACE("Returning relay_name from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_description") == 0) {
|
||||
result = g_unified_cache.relay_info.description[0] ? g_unified_cache.relay_info.description : NULL;
|
||||
DEBUG_TRACE("Returning relay_description from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_contact") == 0) {
|
||||
result = g_unified_cache.relay_info.contact[0] ? g_unified_cache.relay_info.contact : NULL;
|
||||
DEBUG_TRACE("Returning relay_contact from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_software") == 0) {
|
||||
result = g_unified_cache.relay_info.software[0] ? g_unified_cache.relay_info.software : NULL;
|
||||
DEBUG_TRACE("Returning relay_software from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_version") == 0) {
|
||||
result = g_unified_cache.relay_info.version[0] ? g_unified_cache.relay_info.version : NULL;
|
||||
DEBUG_TRACE("Returning relay_version from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "supported_nips") == 0) {
|
||||
result = g_unified_cache.relay_info.supported_nips_str[0] ? g_unified_cache.relay_info.supported_nips_str : NULL;
|
||||
DEBUG_TRACE("Returning supported_nips from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "language_tags") == 0) {
|
||||
result = g_unified_cache.relay_info.language_tags_str[0] ? g_unified_cache.relay_info.language_tags_str : NULL;
|
||||
DEBUG_TRACE("Returning language_tags from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "relay_countries") == 0) {
|
||||
result = g_unified_cache.relay_info.relay_countries_str[0] ? g_unified_cache.relay_info.relay_countries_str : NULL;
|
||||
DEBUG_TRACE("Returning relay_countries from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "posting_policy") == 0) {
|
||||
result = g_unified_cache.relay_info.posting_policy[0] ? g_unified_cache.relay_info.posting_policy : NULL;
|
||||
DEBUG_TRACE("Returning posting_policy from cache: %s", result ? result : "NULL");
|
||||
} else if (strcmp(key, "payments_url") == 0) {
|
||||
result = g_unified_cache.relay_info.payments_url[0] ? g_unified_cache.relay_info.payments_url : NULL;
|
||||
DEBUG_TRACE("Returning payments_url from cache: %s", result ? result : "NULL");
|
||||
} else {
|
||||
DEBUG_TRACE("Key '%s' not found in unified cache", key);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get relay pubkey from cache (with automatic refresh)
|
||||
const char* get_relay_pubkey_cached(void) {
|
||||
// First check without holding lock: whether we need refresh
|
||||
// Get integer config value from unified cache
|
||||
int get_cached_config_int(const char* key, int default_value) {
|
||||
if (!key) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// Check cache validity and refresh if needed
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
int need_refresh = (!g_unified_cache.cache_valid || time(NULL) > g_unified_cache.cache_expires);
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
if (need_refresh) {
|
||||
// Perform refresh, which locks internally
|
||||
DEBUG_TRACE("Cache refresh needed for int key '%s'", key);
|
||||
refresh_unified_cache_from_table();
|
||||
}
|
||||
|
||||
// Now read under lock
|
||||
// Return value from cache based on key
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
const char* result = g_unified_cache.relay_pubkey[0] ? g_unified_cache.relay_pubkey : NULL;
|
||||
|
||||
int result = default_value;
|
||||
|
||||
// Handle integer fields from unified cache
|
||||
if (strcmp(key, "auth_required") == 0) {
|
||||
result = g_unified_cache.auth_required;
|
||||
DEBUG_TRACE("Returning auth_required from cache: %d", result);
|
||||
} else if (strcmp(key, "admin_enabled") == 0) {
|
||||
result = g_unified_cache.admin_enabled;
|
||||
DEBUG_TRACE("Returning admin_enabled from cache: %d", result);
|
||||
} else if (strcmp(key, "max_file_size") == 0) {
|
||||
result = g_unified_cache.max_file_size;
|
||||
DEBUG_TRACE("Returning max_file_size from cache: %d", result);
|
||||
} else if (strcmp(key, "nip42_mode") == 0) {
|
||||
result = g_unified_cache.nip42_mode;
|
||||
DEBUG_TRACE("Returning nip42_mode from cache: %d", result);
|
||||
} else if (strcmp(key, "nip42_challenge_timeout") == 0) {
|
||||
result = g_unified_cache.nip42_challenge_timeout;
|
||||
DEBUG_TRACE("Returning nip42_challenge_timeout from cache: %d", result);
|
||||
} else if (strcmp(key, "nip42_time_tolerance") == 0) {
|
||||
result = g_unified_cache.nip42_time_tolerance;
|
||||
DEBUG_TRACE("Returning nip42_time_tolerance from cache: %d", result);
|
||||
} else if (strcmp(key, "nip70_protected_events_enabled") == 0) {
|
||||
result = g_unified_cache.nip70_protected_events_enabled;
|
||||
DEBUG_TRACE("Returning nip70_protected_events_enabled from cache: %d", result);
|
||||
} else {
|
||||
DEBUG_TRACE("Integer key '%s' not found in unified cache, using default: %d", key, default_value);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get boolean config value from unified cache
|
||||
int get_cached_config_bool(const char* key, int default_value) {
|
||||
// Boolean values are stored as integers (0/1) in the cache
|
||||
return get_cached_config_int(key, default_value);
|
||||
}
|
||||
|
||||
|
||||
// ================================
|
||||
// UTILITY FUNCTIONS
|
||||
// ================================
|
||||
@@ -606,64 +723,34 @@ const char* get_config_value(const char* key) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Special fast path for frequently accessed keys via unified cache
|
||||
if (strcmp(key, "admin_pubkey") == 0) {
|
||||
const char* cached_value = get_admin_pubkey_cached();
|
||||
return safe_strdup_from_static(cached_value);
|
||||
}
|
||||
if (strcmp(key, "relay_pubkey") == 0) {
|
||||
const char* cached_value = get_relay_pubkey_cached();
|
||||
// Try unified cache first for all supported keys
|
||||
const char* cached_value = get_cached_config_value(key);
|
||||
if (cached_value) {
|
||||
return safe_strdup_from_static(cached_value);
|
||||
}
|
||||
|
||||
// For other keys, try config table first
|
||||
// For other keys, try config table directly
|
||||
const char* table_value = get_config_value_from_table(key);
|
||||
if (table_value) {
|
||||
return table_value;
|
||||
}
|
||||
|
||||
// Fallback to legacy event-based config for backward compatibility
|
||||
// Use unified cache buffer instead of static buffer
|
||||
|
||||
if (!g_current_config) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Look for key in current configuration tags
|
||||
cJSON* tags = cJSON_GetObjectItem(g_current_config, "tags");
|
||||
if (!tags || !cJSON_IsArray(tags)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
|
||||
cJSON* tag = NULL;
|
||||
cJSON_ArrayForEach(tag, tags) {
|
||||
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
|
||||
cJSON* tag_key = cJSON_GetArrayItem(tag, 0);
|
||||
cJSON* tag_value = cJSON_GetArrayItem(tag, 1);
|
||||
|
||||
if (tag_key && tag_value &&
|
||||
cJSON_IsString(tag_key) && cJSON_IsString(tag_value)) {
|
||||
|
||||
if (strcmp(cJSON_GetStringValue(tag_key), key) == 0) {
|
||||
strncpy(g_unified_cache.temp_buffer, cJSON_GetStringValue(tag_value),
|
||||
sizeof(g_unified_cache.temp_buffer) - 1);
|
||||
g_unified_cache.temp_buffer[sizeof(g_unified_cache.temp_buffer) - 1] = '\0';
|
||||
const char* result = safe_strdup_from_static(g_unified_cache.temp_buffer);
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int get_config_int(const char* key, int default_value) {
|
||||
// Try unified cache first for integer values
|
||||
if (strcmp(key, "auth_required") == 0 ||
|
||||
strcmp(key, "admin_enabled") == 0 ||
|
||||
strcmp(key, "max_file_size") == 0 ||
|
||||
strcmp(key, "nip42_mode") == 0 ||
|
||||
strcmp(key, "nip42_challenge_timeout") == 0 ||
|
||||
strcmp(key, "nip42_time_tolerance") == 0 ||
|
||||
strcmp(key, "nip70_protected_events_enabled") == 0) {
|
||||
return get_cached_config_int(key, default_value);
|
||||
}
|
||||
|
||||
// Fall back to string parsing for other keys
|
||||
const char* str_value = get_config_value(key);
|
||||
if (!str_value) {
|
||||
return default_value;
|
||||
@@ -684,6 +771,14 @@ int get_config_int(const char* key, int default_value) {
|
||||
}
|
||||
|
||||
int get_config_bool(const char* key, int default_value) {
|
||||
// Try unified cache first for boolean values
|
||||
if (strcmp(key, "auth_required") == 0 ||
|
||||
strcmp(key, "admin_enabled") == 0 ||
|
||||
strcmp(key, "nip70_protected_events_enabled") == 0) {
|
||||
return get_cached_config_bool(key, default_value);
|
||||
}
|
||||
|
||||
// Fall back to string parsing for other keys
|
||||
const char* str_value = get_config_value(key);
|
||||
if (!str_value) {
|
||||
return default_value;
|
||||
@@ -1258,13 +1353,23 @@ int first_time_startup_sequence(const cli_options_t* cli_options) {
|
||||
strncpy(g_temp_relay_privkey, relay_privkey, sizeof(g_temp_relay_privkey) - 1);
|
||||
g_temp_relay_privkey[sizeof(g_temp_relay_privkey) - 1] = '\0';
|
||||
|
||||
// 6. Handle configuration setup - defaults will be populated after database initialization
|
||||
// 6. Populate complete config table with all values atomically
|
||||
if (populate_all_config_values_atomic(admin_pubkey, relay_pubkey) != 0) {
|
||||
DEBUG_ERROR("Failed to populate complete config table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 7. Apply CLI overrides atomically (after complete config table exists)
|
||||
if (apply_cli_overrides_atomic(cli_options) != 0) {
|
||||
DEBUG_ERROR("Failed to apply CLI overrides");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// CLI overrides will be applied after database initialization in main.c
|
||||
// This prevents "g_db is NULL" errors during first-time startup
|
||||
// 8. Store relay private key in temporary storage for later secure storage
|
||||
strncpy(g_temp_relay_privkey, relay_privkey, sizeof(g_temp_relay_privkey) - 1);
|
||||
g_temp_relay_privkey[sizeof(g_temp_relay_privkey) - 1] = '\0';
|
||||
|
||||
// 10. Print admin private key for user to save (only if we generated a new key)
|
||||
// 9. Print admin private key for user to save (only if we generated a new key)
|
||||
if (generated_admin_key) {
|
||||
printf("\n");
|
||||
printf("=================================================================\n");
|
||||
@@ -1294,7 +1399,7 @@ int first_time_startup_sequence(const cli_options_t* cli_options) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int startup_existing_relay(const char* relay_pubkey) {
|
||||
int startup_existing_relay(const char* relay_pubkey, const cli_options_t* cli_options) {
|
||||
if (!relay_pubkey) {
|
||||
DEBUG_ERROR("Invalid relay pubkey for existing relay startup");
|
||||
return -1;
|
||||
@@ -1302,28 +1407,16 @@ int startup_existing_relay(const char* relay_pubkey) {
|
||||
|
||||
printf(" Relay pubkey: %s\n", relay_pubkey);
|
||||
|
||||
// Log config table row count at start of startup_existing_relay
|
||||
if (g_db) {
|
||||
sqlite3_stmt* count_stmt;
|
||||
const char* count_sql = "SELECT COUNT(*) FROM config";
|
||||
if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(count_stmt) == SQLITE_ROW) {
|
||||
// Row count check completed
|
||||
}
|
||||
sqlite3_finalize(count_stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// Store relay pubkey in unified cache
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
strncpy(g_unified_cache.relay_pubkey, relay_pubkey, sizeof(g_unified_cache.relay_pubkey) - 1);
|
||||
g_unified_cache.relay_pubkey[sizeof(g_unified_cache.relay_pubkey) - 1] = '\0';
|
||||
|
||||
|
||||
// Mark cache as valid to prevent premature refresh from database before it's initialized
|
||||
int cache_timeout = get_cache_timeout();
|
||||
g_unified_cache.cache_expires = time(NULL) + cache_timeout;
|
||||
g_unified_cache.cache_valid = 1;
|
||||
|
||||
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
// Set database path
|
||||
@@ -1337,19 +1430,28 @@ int startup_existing_relay(const char* relay_pubkey) {
|
||||
g_database_path[sizeof(g_database_path) - 1] = '\0';
|
||||
free(db_name);
|
||||
|
||||
// NOTE: admin_pubkey will be loaded from config table after database initialization
|
||||
// in main.c by calling add_pubkeys_to_config_table() which handles migration
|
||||
// NOTE: Database is already initialized in main.c before calling this function
|
||||
// Config table should already exist with complete configuration
|
||||
|
||||
// Log config table row count at end of startup_existing_relay
|
||||
if (g_db) {
|
||||
sqlite3_stmt* count_stmt;
|
||||
const char* count_sql = "SELECT COUNT(*) FROM config";
|
||||
if (sqlite3_prepare_v2(g_db, count_sql, -1, &count_stmt, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(count_stmt) == SQLITE_ROW) {
|
||||
// Row count check completed
|
||||
}
|
||||
sqlite3_finalize(count_stmt);
|
||||
// Check if CLI overrides need to be applied
|
||||
int has_overrides = 0;
|
||||
if (cli_options) {
|
||||
if (cli_options->port_override > 0) has_overrides = 1;
|
||||
if (cli_options->admin_pubkey_override[0] != '\0') has_overrides = 1;
|
||||
if (cli_options->relay_privkey_override[0] != '\0') has_overrides = 1;
|
||||
}
|
||||
|
||||
if (has_overrides) {
|
||||
// Apply CLI overrides to existing database
|
||||
DEBUG_INFO("Applying CLI overrides to existing database");
|
||||
if (apply_cli_overrides_atomic(cli_options) != 0) {
|
||||
DEBUG_ERROR("Failed to apply CLI overrides to existing database");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// No CLI overrides - load cache immediately from existing config table
|
||||
DEBUG_INFO("No CLI overrides - loading cache immediately from existing config table");
|
||||
refresh_unified_cache_from_table();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1829,7 +1931,7 @@ int process_configuration_event(const cJSON* event) {
|
||||
|
||||
// Verify it's from the admin
|
||||
const char* event_pubkey = cJSON_GetStringValue(pubkey_obj);
|
||||
const char* admin_pubkey = get_admin_pubkey_cached();
|
||||
const char* admin_pubkey = get_config_value("admin_pubkey");
|
||||
if (admin_pubkey && strlen(admin_pubkey) > 0) {
|
||||
if (strcmp(event_pubkey, admin_pubkey) != 0) {
|
||||
DEBUG_ERROR("Configuration event not from authorized admin");
|
||||
@@ -2003,7 +2105,7 @@ int apply_configuration_from_event(const cJSON* event) {
|
||||
cJSON* pubkey_obj = cJSON_GetObjectItem(event, "pubkey");
|
||||
if (pubkey_obj) {
|
||||
const char* event_pubkey = cJSON_GetStringValue(pubkey_obj);
|
||||
const char* cached_admin_pubkey = get_admin_pubkey_cached();
|
||||
const char* cached_admin_pubkey = get_config_value("admin_pubkey");
|
||||
if (!cached_admin_pubkey || strlen(cached_admin_pubkey) == 0) {
|
||||
// Update cache with admin pubkey from event
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
@@ -2154,7 +2256,9 @@ int update_config_in_table(const char* key, const char* value) {
|
||||
return (rc == SQLITE_DONE) ? 0 : -1;
|
||||
}
|
||||
|
||||
// Populate default config values (only inserts missing keys, doesn't replace existing)
|
||||
// DEPRECATED: This function is no longer used in the unified startup flow.
|
||||
// The new populate_all_config_values_atomic() function handles all config creation atomically.
|
||||
// This function is kept for backward compatibility but should not be called in new code.
|
||||
int populate_default_config_values(void) {
|
||||
DEBUG_TRACE("Entering populate_default_config_values()");
|
||||
|
||||
@@ -2318,7 +2422,9 @@ int populate_default_config_values(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add dynamically generated pubkeys to config table
|
||||
// DEPRECATED: This function is no longer used in the unified startup flow.
|
||||
// The new populate_all_config_values_atomic() function handles pubkey storage atomically.
|
||||
// This function is kept for backward compatibility but should not be called in new code.
|
||||
int add_pubkeys_to_config_table(void) {
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not available for pubkey storage");
|
||||
@@ -2345,67 +2451,73 @@ int add_pubkeys_to_config_table(void) {
|
||||
|
||||
// For existing relays, admin_pubkey might already be in the database but not in cache yet
|
||||
// Try to load it from the database config table first
|
||||
const char* admin_pubkey_from_db = NULL;
|
||||
if (!admin_pubkey_cache || strlen(admin_pubkey_cache) != 64) {
|
||||
admin_pubkey_from_db = get_config_value_from_table("admin_pubkey");
|
||||
if (admin_pubkey_from_db && strlen(admin_pubkey_from_db) == 64) {
|
||||
// Update cache with the value from config table
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
strncpy(g_unified_cache.admin_pubkey, admin_pubkey_from_db, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
DEBUG_INFO("✓ Loaded admin_pubkey from config table into cache");
|
||||
// Free the allocated string and return success - pubkey already in table
|
||||
free((char*)admin_pubkey_from_db);
|
||||
return 0;
|
||||
}
|
||||
if (admin_pubkey_from_db) {
|
||||
free((char*)admin_pubkey_from_db);
|
||||
}
|
||||
DEBUG_LOG("Checking for existing admin_pubkey in config table");
|
||||
const char* admin_pubkey_from_db = get_config_value_from_table("admin_pubkey");
|
||||
DEBUG_TRACE("get_config_value_from_table('admin_pubkey') returned: %s (length: %zu)",
|
||||
admin_pubkey_from_db ? admin_pubkey_from_db : "NULL",
|
||||
admin_pubkey_from_db ? strlen(admin_pubkey_from_db) : 0);
|
||||
DEBUG_LOG("get_config_value_from_table returned: %s", admin_pubkey_from_db ? admin_pubkey_from_db : "NULL");
|
||||
if (admin_pubkey_from_db && strlen(admin_pubkey_from_db) == 64) {
|
||||
DEBUG_LOG("Found valid admin_pubkey in config table: %s", admin_pubkey_from_db);
|
||||
// Update cache with the value from config table
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
strncpy(g_unified_cache.admin_pubkey, admin_pubkey_from_db, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||
DEBUG_TRACE("Loaded admin_pubkey from config table into cache: %s", g_unified_cache.admin_pubkey);
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
DEBUG_INFO("✓ Loaded admin_pubkey from config table into cache");
|
||||
// Free the allocated string and return success - pubkey already in table
|
||||
free((char*)admin_pubkey_from_db);
|
||||
return 0;
|
||||
} else {
|
||||
DEBUG_LOG("No valid admin_pubkey found in config table (length: %zu)", admin_pubkey_from_db ? strlen(admin_pubkey_from_db) : 0);
|
||||
}
|
||||
if (admin_pubkey_from_db) {
|
||||
free((char*)admin_pubkey_from_db);
|
||||
}
|
||||
|
||||
// If not in config table, try loading from old event-based config (migration scenario)
|
||||
const char* sql = "SELECT pubkey FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const char* event_pubkey = (const char*)sqlite3_column_text(stmt, 0);
|
||||
if (event_pubkey && strlen(event_pubkey) == 64) {
|
||||
// Store in config table for future use
|
||||
if (set_config_value_in_table("admin_pubkey", event_pubkey, "string",
|
||||
"Administrator public key", "authentication", 0) == 0) {
|
||||
// If not in config table, try loading from old event-based config (migration scenario)
|
||||
const char* sql = "SELECT pubkey FROM events WHERE kind = 33334 ORDER BY created_at DESC LIMIT 1";
|
||||
sqlite3_stmt* stmt;
|
||||
|
||||
// Update cache
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
strncpy(g_unified_cache.admin_pubkey, event_pubkey, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
const char* event_pubkey = (const char*)sqlite3_column_text(stmt, 0);
|
||||
if (event_pubkey && strlen(event_pubkey) == 64) {
|
||||
// Store in config table for future use
|
||||
if (set_config_value_in_table("admin_pubkey", event_pubkey, "string",
|
||||
"Administrator public key", "authentication", 0) == 0) {
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
DEBUG_INFO("✓ Migrated admin_pubkey from old config event to config table");
|
||||
return 0;
|
||||
}
|
||||
// Update cache
|
||||
pthread_mutex_lock(&g_unified_cache.cache_lock);
|
||||
strncpy(g_unified_cache.admin_pubkey, event_pubkey, sizeof(g_unified_cache.admin_pubkey) - 1);
|
||||
g_unified_cache.admin_pubkey[sizeof(g_unified_cache.admin_pubkey) - 1] = '\0';
|
||||
pthread_mutex_unlock(&g_unified_cache.cache_lock);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
DEBUG_INFO("✓ Migrated admin_pubkey from old config event to config table");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
|
||||
// Use cache value for storage (either from first-time startup or just loaded from DB)
|
||||
const char* admin_pubkey = admin_pubkey_cache;
|
||||
const char* relay_pubkey = relay_pubkey_cache;
|
||||
|
||||
|
||||
if (!admin_pubkey || strlen(admin_pubkey) != 64) {
|
||||
DEBUG_ERROR("Admin pubkey not available or invalid for config table storage");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (!relay_pubkey || strlen(relay_pubkey) != 64) {
|
||||
DEBUG_ERROR("Relay pubkey not available or invalid for config table storage");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Store admin pubkey in config table
|
||||
if (set_config_value_in_table("admin_pubkey", admin_pubkey, "string",
|
||||
"Administrator public key", "authentication", 0) != 0) {
|
||||
@@ -2419,7 +2531,7 @@ int add_pubkeys_to_config_table(void) {
|
||||
DEBUG_ERROR("Failed to store relay_pubkey in config table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
DEBUG_INFO("✓ Dynamically generated pubkeys added to config table");
|
||||
printf(" Admin pubkey: %s\n", admin_pubkey ? admin_pubkey : "NULL");
|
||||
printf(" Relay pubkey: %s\n", relay_pubkey ? relay_pubkey : "NULL");
|
||||
@@ -2990,7 +3102,7 @@ int handle_kind_23456_unified(cJSON* event, char* error_message, size_t error_si
|
||||
}
|
||||
|
||||
const char* sender_pubkey = cJSON_GetStringValue(pubkey_obj);
|
||||
const char* admin_pubkey = get_admin_pubkey_cached();
|
||||
const char* admin_pubkey = get_config_value("admin_pubkey");
|
||||
|
||||
if (!admin_pubkey) {
|
||||
DEBUG_ERROR("error: admin pubkey not available for authorization check");
|
||||
@@ -4400,6 +4512,179 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
|
||||
|
||||
|
||||
|
||||
// ================================
|
||||
// UNIFIED STARTUP FUNCTIONS
|
||||
// ================================
|
||||
|
||||
// Apply CLI overrides to existing config table in a single atomic operation
|
||||
int apply_cli_overrides_atomic(const cli_options_t* cli_options) {
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not available for CLI overrides");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!cli_options) {
|
||||
DEBUG_ERROR("CLI options not provided");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check if there are any CLI overrides to apply
|
||||
int has_overrides = 0;
|
||||
if (cli_options->port_override > 0) has_overrides = 1;
|
||||
if (cli_options->admin_pubkey_override[0] != '\0') has_overrides = 1;
|
||||
if (cli_options->relay_privkey_override[0] != '\0') has_overrides = 1;
|
||||
|
||||
if (!has_overrides) {
|
||||
DEBUG_INFO("No CLI overrides to apply");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Applying CLI overrides atomically");
|
||||
|
||||
// Begin transaction
|
||||
char* err_msg = NULL;
|
||||
int rc = sqlite3_exec(g_db, "BEGIN IMMEDIATE TRANSACTION", NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to begin CLI overrides transaction: %s", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Apply port override
|
||||
if (cli_options->port_override > 0) {
|
||||
char port_str[16];
|
||||
snprintf(port_str, sizeof(port_str), "%d", cli_options->port_override);
|
||||
|
||||
if (update_config_in_table("relay_port", port_str) != 0) {
|
||||
DEBUG_ERROR("Failed to update relay_port override");
|
||||
sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_INFO("Applied CLI override: relay_port = %s", port_str);
|
||||
}
|
||||
|
||||
// Apply admin_pubkey override
|
||||
if (cli_options->admin_pubkey_override[0] != '\0') {
|
||||
if (update_config_in_table("admin_pubkey", cli_options->admin_pubkey_override) != 0) {
|
||||
DEBUG_ERROR("Failed to update admin_pubkey override");
|
||||
sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_INFO("Applied CLI override: admin_pubkey");
|
||||
}
|
||||
|
||||
// Apply relay_privkey override
|
||||
if (cli_options->relay_privkey_override[0] != '\0') {
|
||||
if (update_config_in_table("relay_privkey", cli_options->relay_privkey_override) != 0) {
|
||||
DEBUG_ERROR("Failed to update relay_privkey override");
|
||||
sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
DEBUG_INFO("Applied CLI override: relay_privkey");
|
||||
}
|
||||
|
||||
// Commit transaction
|
||||
rc = sqlite3_exec(g_db, "COMMIT", NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to commit CLI overrides transaction: %s", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_exec(g_db, "ROLLBACK", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Invalidate cache to force refresh with new values
|
||||
invalidate_config_cache();
|
||||
|
||||
DEBUG_INFO("Successfully applied CLI overrides atomically");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Populate all config values atomically in a single transaction
|
||||
int populate_all_config_values_atomic(const char* admin_pubkey, const char* relay_pubkey) {
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!admin_pubkey || !relay_pubkey) {
|
||||
DEBUG_ERROR("Admin pubkey or relay pubkey not available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
char* err_msg = NULL;
|
||||
int rc = sqlite3_exec(g_db, "BEGIN TRANSACTION;", NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to begin transaction: %s", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Prepare INSERT statement
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
const char* sql = "INSERT INTO config (key, value) VALUES (?, ?)";
|
||||
rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare statement: %s", sqlite3_errmsg(g_db));
|
||||
sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Insert all default config values
|
||||
for (size_t i = 0; i < DEFAULT_CONFIG_COUNT; i++) {
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_bind_text(stmt, 1, DEFAULT_CONFIG_VALUES[i].key, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, DEFAULT_CONFIG_VALUES[i].value, -1, SQLITE_STATIC);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR("Failed to insert config key '%s': %s",
|
||||
DEFAULT_CONFIG_VALUES[i].key, sqlite3_errmsg(g_db));
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert admin_pubkey
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_bind_text(stmt, 1, "admin_pubkey", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, admin_pubkey, -1, SQLITE_STATIC);
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR("Failed to insert admin_pubkey: %s", sqlite3_errmsg(g_db));
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Insert relay_pubkey
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_bind_text(stmt, 1, "relay_pubkey", -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, relay_pubkey, -1, SQLITE_STATIC);
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR("Failed to insert relay_pubkey: %s", sqlite3_errmsg(g_db));
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Commit transaction
|
||||
rc = sqlite3_exec(g_db, "COMMIT;", NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to commit transaction: %s", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_exec(g_db, "ROLLBACK;", NULL, NULL, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Successfully populated all config values atomically");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ================================
|
||||
// CONFIGURATION CACHE MANAGEMENT
|
||||
// ================================
|
||||
@@ -4655,7 +4940,7 @@ int migrate_config_from_events_to_table(void) {
|
||||
DEBUG_INFO("Migrating configuration from events to config table...");
|
||||
|
||||
// Load the most recent configuration event from database
|
||||
const char* relay_pubkey = get_relay_pubkey_cached();
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
cJSON* config_event = load_config_event_from_database(relay_pubkey);
|
||||
if (!config_event) {
|
||||
DEBUG_INFO("No existing configuration event found - migration not needed");
|
||||
@@ -4803,7 +5088,7 @@ cJSON* generate_config_event_from_table(void) {
|
||||
const char* relay_pubkey = get_config_value("relay_pubkey");
|
||||
if (!relay_pubkey || strlen(relay_pubkey) != 64) {
|
||||
// Try to get from unified cache
|
||||
relay_pubkey = get_relay_pubkey_cached();
|
||||
relay_pubkey = get_config_value("relay_pubkey");
|
||||
if (!relay_pubkey || strlen(relay_pubkey) != 64) {
|
||||
DEBUG_ERROR("Relay pubkey not available for config event generation");
|
||||
return NULL;
|
||||
|
||||
Reference in New Issue
Block a user