8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 15:23:03 +01:00

Fix use :@ characters and add sub-section to configure username and password for sync_replica (#8327)

* Add sub-section to configure username and password for sync_replica with the ability to read its from a file or from an environment variable

Also fix use :@ characters in username and password

* Corrections after Dmitry Yemanov review

Fix opening a file if it is specified in absolute path
Also fix the error message if there are not enough permissions to open the file

* Add fixup separators also revert prefix if filename has relative path

* Create a parseSyncReplica function and move the code to better read it

* Add explicit to constructor SyncReplica class with a single non-default parameter

---------

Co-authored-by: Andrey Kravchenko <andrey.kravchenko@red-soft.ru>
This commit is contained in:
Andrey Kravchenko 2024-12-03 09:49:40 +03:00 committed by GitHub
parent 4a1f4e2bc4
commit b8f24cdd61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 132 additions and 28 deletions

View File

@ -26,6 +26,8 @@
#include "../common/isc_f_proto.h"
#include "../common/status.h"
#include "../common/StatusArg.h"
#include "../common/utils_proto.h"
#include "../common/os/os_utils.h"
#include "../jrd/constants.h"
#include "Utils.h"
@ -50,6 +52,8 @@ using namespace Replication;
namespace
{
const char* REPLICATION_CFGFILE = "replication.conf";
constexpr const char* KEY_ENV = "env";
constexpr const char* KEY_FILE = "file";
const ULONG DEFAULT_BUFFER_SIZE = 1024 * 1024; // 1 MB
const ULONG DEFAULT_SEGMENT_SIZE = 16 * 1024 * 1024; // 16 MB
@ -99,6 +103,69 @@ namespace
status->setErrors(sv.value());
}
void parseSyncReplica(const ConfigFile::Parameters& params, SyncReplica& output)
{
for (const auto& el : params)
{
string key(el.name.c_str());
string value(el.value);
if (value.isEmpty())
continue;
auto pos = key.rfind('_');
if (pos != string::npos)
{
const string key_source = key.substr(pos + 1);
if (key_source.equals(KEY_ENV))
{
fb_utils::readenv(value.c_str(), value);
if (value.isEmpty())
configError("missing environment variable", output.database, value);
key = key.substr(0, pos);
}
else if (key_source.equals(KEY_FILE))
{
PathName filename = value.c_str();
PathUtils::fixupSeparators(filename);
if (PathUtils::isRelative(filename))
filename = fb_utils::getPrefix(IConfigManager::DIR_CONF, filename.c_str());
AutoPtr<FILE> file(os_utils::fopen(filename.c_str(), "rt"));
if (!file)
configError("missing or inaccessible file", output.database, filename.c_str());
// skip first empty lines
value = "";
do
{
if (feof(file))
break;
if (!value.LoadFromFile(file))
break;
value.alltrim(" \t\r");
} while (value.isEmpty());
if (value.isEmpty())
configError("empty file", output.database, filename.c_str());
key = key.substr(0, pos);
}
}
if (key == "username")
output.username = value.c_str();
else if (key == "password")
output.password = value.c_str();
else
configError("unknown parameter", output.database, key);
}
}
}
@ -216,7 +283,16 @@ Config* Config::get(const PathName& lookupName)
if (key == "sync_replica")
{
config->syncReplicas.add(value);
SyncReplica syncReplica(config->getPool());
if (el.sub)
{
syncReplica.database = value;
parseSyncReplica(el.sub->getParameters(), syncReplica);
}
else
splitConnectionString(value, syncReplica.database, syncReplica.username, syncReplica.password);
config->syncReplicas.add(syncReplica);
}
else if (key == "buffer_size")
{
@ -427,3 +503,35 @@ void Config::enumerate(ReplicaList& replicas)
logReplicaStatus(dbName, &localStatus);
}
}
// This routine is used for split input connection string to parts
// input => [<username>[:<password>]@]<database>
//
// Examples:
// server2:/my/replica/database.fdb
// john:smith@server2:/my/replica/database.fdb
void Config::splitConnectionString(const string& input, string& database, string& username, string& password)
{
database = input;
auto pos = database.rfind('@');
if (pos != string::npos)
{
//john:smith
const string temp = database.substr(0, pos);
//server2:/my/replica/database.fdb
database = database.substr(pos + 1);
pos = temp.find(':');
if (pos != string::npos)
{
username = temp.substr(0, pos);
password = temp.substr(pos + 1);
}
else
{
username = temp;
}
}
}

View File

@ -33,6 +33,21 @@
namespace Replication
{
struct SyncReplica
{
explicit SyncReplica(MemoryPool& p)
: database(p), username(p), password(p)
{}
SyncReplica(MemoryPool& p, const SyncReplica& other)
: database(p, other.database), username(p, other.username), password(p, other.password)
{}
Firebird::string database;
Firebird::string username;
Firebird::string password;
};
struct Config : public Firebird::GlobalStorage
{
typedef Firebird::HalfStaticArray<Config*, 4> ReplicaList;
@ -42,6 +57,8 @@ namespace Replication
static Config* get(const Firebird::PathName& dbName);
static void enumerate(ReplicaList& replicas);
static void splitConnectionString(const Firebird::string& input, Firebird::string& database,
Firebird::string& username, Firebird::string& password);
Firebird::PathName dbName;
ULONG bufferSize;
@ -55,7 +72,7 @@ namespace Replication
Firebird::PathName archiveDirectory;
Firebird::string archiveCommand;
ULONG archiveTimeout;
Firebird::ObjectsArray<Firebird::string> syncReplicas;
Firebird::ObjectsArray<SyncReplica> syncReplicas;
Firebird::PathName sourceDirectory;
std::optional<Firebird::Guid> sourceGuid;
bool verboseLogging;

View File

@ -128,39 +128,18 @@ Manager::Manager(const string& dbId,
for (const auto& iter : m_config->syncReplicas)
{
string database = iter;
string login, password;
auto pos = database.find('@');
if (pos != string::npos)
{
const string temp = database.substr(0, pos);
database = database.substr(pos + 1);
pos = temp.find(':');
if (pos != string::npos)
{
login = temp.substr(0, pos);
password = temp.substr(pos + 1);
}
else
{
login = temp;
}
}
ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE);
dpb.insertByte(isc_dpb_no_db_triggers, 1);
if (login.hasData())
if (iter.username.hasData())
{
dpb.insertString(isc_dpb_user_name, login);
dpb.insertString(isc_dpb_user_name, iter.username);
if (password.hasData())
dpb.insertString(isc_dpb_password, password);
if (iter.password.hasData())
dpb.insertString(isc_dpb_password, iter.password);
}
const auto attachment = provider->attachDatabase(&localStatus, database.c_str(),
const auto attachment = provider->attachDatabase(&localStatus, iter.database.c_str(),
dpb.getBufferLength(), dpb.getBuffer());
if (localStatus->getState() & IStatus::STATE_ERRORS)
{