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:
parent
4a1f4e2bc4
commit
b8f24cdd61
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user