config: support env-based SQL overrides
Some checks failed
build / Linux asan (push) Has been cancelled
build / Linux release (push) Has been cancelled
build / FreeBSD build (push) Has been cancelled

This commit is contained in:
server
2026-04-14 09:41:31 +02:00
parent b46a2661df
commit 14e084d691
2 changed files with 347 additions and 166 deletions

View File

@@ -47,6 +47,15 @@ extern void WriteVersion();
namespace
{
struct SQLConnectionConfig
{
char host[64];
char database[64];
char user[64];
char password[64];
int port;
};
const char* BoolState(bool value)
{
return value ? "on" : "off";
@@ -57,6 +66,112 @@ const char* EmptyToLabel(const std::string& value, const char* fallback)
return value.empty() ? fallback : value.c_str();
}
bool CopyEnvString(const char* env_name, char* dest, size_t dest_size)
{
const char* value = std::getenv(env_name);
if (!value)
return false;
strlcpy(dest, value, dest_size);
return true;
}
bool CopyEnvInt(const char* env_name, int* dest)
{
const char* value = std::getenv(env_name);
if (!value)
return false;
str_to_number(*dest, value);
return true;
}
bool HasSQLConfig(const SQLConnectionConfig& config)
{
return config.host[0] && config.database[0] && config.user[0] && config.password[0];
}
bool ParseSQLConfig(const char* value, SQLConnectionConfig* config, const char* label)
{
int token_count = sscanf(
value,
" %63s %63s %63s %63s %d ",
config->host,
config->database,
config->user,
config->password,
&config->port);
if (token_count < 4 || !HasSQLConfig(*config))
{
fprintf(stderr, "%s syntax: <host db user password [port]>\n", label);
return false;
}
return true;
}
bool LoadSQLConfig(const char* key, const char* env_prefix, SQLConnectionConfig* config)
{
char line[256 + 1];
bool loaded_from_file = false;
if (CConfig::instance().GetValue(key, line, sizeof(line) - 1))
{
if (!ParseSQLConfig(line, config, key))
return false;
loaded_from_file = true;
}
bool overridden = false;
const std::string prefix = env_prefix;
overridden |= CopyEnvString((prefix + "_HOST").c_str(), config->host, sizeof(config->host));
overridden |= CopyEnvString((prefix + "_DB").c_str(), config->database, sizeof(config->database));
overridden |= CopyEnvString((prefix + "_USER").c_str(), config->user, sizeof(config->user));
overridden |= CopyEnvString((prefix + "_PASSWORD").c_str(), config->password, sizeof(config->password));
overridden |= CopyEnvInt((prefix + "_PORT").c_str(), &config->port);
if (overridden)
sys_log(0, "CONFIG: %s overridden from environment", key);
if (!loaded_from_file && !overridden)
{
sys_err("%s not configured", key);
return false;
}
if (!HasSQLConfig(*config))
{
sys_err("%s incomplete after applying config/environment overrides", key);
return false;
}
return true;
}
bool ConnectDatabase(int slot, const SQLConnectionConfig& config, const char* label)
{
sys_log(0, "connecting to MySQL server (%s)", label);
int retry_count = 5;
do
{
if (CDBManager::instance().Connect(slot, config.host, config.port, config.database, config.user, config.password))
{
sys_log(0, " OK");
return true;
}
sys_log(0, " failed, retrying in 5 seconds");
fprintf(stderr, " failed, retrying in 5 seconds");
sleep(5);
}
while (retry_count--);
return false;
}
void LogStartupSummary(int heart_fps, int player_id_start)
{
sys_log(0,
@@ -274,120 +389,43 @@ int Start()
g_stLocaleNameColumn = szBuf;
}
char szAddr[64], szDB[64], szUser[64], szPassword[64];
int iPort;
char line[256+1];
SQLConnectionConfig player_sql = {};
SQLConnectionConfig account_sql = {};
SQLConnectionConfig common_sql = {};
SQLConnectionConfig hotbackup_sql = {};
if (CConfig::instance().GetValue("SQL_PLAYER", line, 256))
{
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
sys_log(0, "connecting to MySQL server (player)");
int iRetry = 5;
do
{
if (CDBManager::instance().Connect(SQL_PLAYER, szAddr, iPort, szDB, szUser, szPassword))
{
sys_log(0, " OK");
break;
}
sys_log(0, " failed, retrying in 5 seconds");
fprintf(stderr, " failed, retrying in 5 seconds");
sleep(5);
} while (iRetry--);
fprintf(stderr, "Success PLAYER\n");
SetPlayerDBName(szDB);
}
else
{
sys_err("SQL_PLAYER not configured");
if (!LoadSQLConfig("SQL_PLAYER", "METIN2_PLAYER_SQL", &player_sql))
return false;
}
if (CConfig::instance().GetValue("SQL_ACCOUNT", line, 256))
{
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
sys_log(0, "connecting to MySQL server (account)");
int iRetry = 5;
do
{
if (CDBManager::instance().Connect(SQL_ACCOUNT, szAddr, iPort, szDB, szUser, szPassword))
{
sys_log(0, " OK");
break;
}
sys_log(0, " failed, retrying in 5 seconds");
fprintf(stderr, " failed, retrying in 5 seconds");
sleep(5);
} while (iRetry--);
fprintf(stderr, "Success ACCOUNT\n");
}
else
{
sys_err("SQL_ACCOUNT not configured");
if (!ConnectDatabase(SQL_PLAYER, player_sql, "player"))
return false;
}
if (CConfig::instance().GetValue("SQL_COMMON", line, 256))
{
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
sys_log(0, "connecting to MySQL server (common)");
fprintf(stderr, "Success PLAYER\n");
SetPlayerDBName(player_sql.database);
int iRetry = 5;
do
{
if (CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword))
{
sys_log(0, " OK");
break;
}
sys_log(0, " failed, retrying in 5 seconds");
fprintf(stderr, " failed, retrying in 5 seconds");
sleep(5);
} while (iRetry--);
fprintf(stderr, "Success COMMON\n");
}
else
{
sys_err("SQL_COMMON not configured");
if (!LoadSQLConfig("SQL_ACCOUNT", "METIN2_ACCOUNT_SQL", &account_sql))
return false;
}
if (CConfig::instance().GetValue("SQL_HOTBACKUP", line, 256))
{
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
sys_log(0, "connecting to MySQL server (hotbackup)");
int iRetry = 5;
do
{
if (CDBManager::instance().Connect(SQL_HOTBACKUP, szAddr, iPort, szDB, szUser, szPassword))
{
sys_log(0, " OK");
break;
}
sys_log(0, " failed, retrying in 5 seconds");
fprintf(stderr, " failed, retrying in 5 seconds");
sleep(5);
}
while (iRetry--);
fprintf(stderr, "Success HOTBACKUP\n");
}
else
{
sys_err("SQL_HOTBACKUP not configured");
if (!ConnectDatabase(SQL_ACCOUNT, account_sql, "account"))
return false;
}
fprintf(stderr, "Success ACCOUNT\n");
if (!LoadSQLConfig("SQL_COMMON", "METIN2_COMMON_SQL", &common_sql))
return false;
if (!ConnectDatabase(SQL_COMMON, common_sql, "common"))
return false;
fprintf(stderr, "Success COMMON\n");
if (!LoadSQLConfig("SQL_HOTBACKUP", "METIN2_HOTBACKUP_SQL", &hotbackup_sql))
return false;
if (!ConnectDatabase(SQL_HOTBACKUP, hotbackup_sql, "hotbackup"))
return false;
fprintf(stderr, "Success HOTBACKUP\n");
if (!CNetPoller::instance().Create())
{