Skip to content

Do not connect to the database if the replication configuration is incorrect #8381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 111 additions & 27 deletions src/jrd/replication/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,38 +79,53 @@ namespace
output = false;
}

void configError(const string& type, const string& key, const string& value)
void configError(CheckStatusWrapper* status, const string& type, const string& key, const string& value)
{
string msg;
if (!(status->getState() & IStatus::STATE_ERRORS))
{
msg.printf("Incorrect entry in %s", REPLICATION_CFGFILE);
(Arg::Gds(isc_random) << Arg::Str(msg)).appendTo(status);
}

msg.printf("%s specifies %s: %s", key.c_str(), type.c_str(), value.c_str());
raiseError(msg.c_str());
(Arg::Gds(isc_random) << Arg::Str(msg)).appendTo(status);
}

void checkAccess(const PathName& path, const string& key)
bool checkAccess(CheckStatusWrapper* status, const PathName& path, const string& key)
{
if (path.hasData() && !PathUtils::canAccess(path, 6))
configError("missing or inaccessible directory", key, path.c_str());
{
configError(status, "missing or inaccessible directory", key, path.c_str());
return false;
}
return true;
}

void composeError(CheckStatusWrapper* status, const Exception& ex)
{
string prefix;
prefix.printf("Incorrect entry in %s", REPLICATION_CFGFILE);

Arg::StatusVector sv;
sv << Arg::Gds(isc_random) << Arg::Str(prefix);

if (!(status->getState() & IStatus::STATE_ERRORS))
{
string prefix;
prefix.printf("Incorrect entry in %s", REPLICATION_CFGFILE);
sv << Arg::Gds(isc_random) << Arg::Str(prefix);
}

sv << Arg::StatusVector(status);
sv << Arg::StatusVector(ex);

status->setErrors(sv.value());
}

void parseExternalValue(const string& key, const string& value, string& output)
bool parseExternalValue(CheckStatusWrapper* status, const string& key, const string& value, string& output)
{
const auto pos = key.rfind('_');
if (pos == string::npos)
{
output = value.c_str();
return;
return true;
}

string temp;
Expand All @@ -120,7 +135,10 @@ namespace
{
fb_utils::readenv(value.c_str(), temp);
if (temp.isEmpty())
configError("missing environment variable", key, value);
{
configError(status, "missing environment variable", key, value);
return false;
}
}
else if (key_source.equals(KEY_SUFFIX_FILE))
{
Expand All @@ -131,44 +149,65 @@ namespace

AutoPtr<FILE> file(os_utils::fopen(filename.c_str(), "rt"));
if (!file)
configError("missing or inaccessible file", key, filename.c_str());
{
configError(status, "missing or inaccessible file", key, filename.c_str());
return false;
}

if (temp.LoadFromFile(file))
temp.alltrim("\r");

if (temp.isEmpty())
configError("first empty line of file", key, filename.c_str());
{
configError(status, "first empty line of file", key, filename.c_str());
return false;
}
}

output = temp.c_str();
return true;
}

void parseSyncReplica(const ConfigFile::Parameters& params, SyncReplica& output)
bool parseSyncReplica(CheckStatusWrapper* status, const ConfigFile::Parameters& params, SyncReplica& output)
{
bool result = true;
for (const auto& el : params)
{
const string key(el.name.c_str());
const string value(el.value);

if (value.isEmpty())
continue;
{
configError(status, "empty value", output.database, key);
result = false;
}

if (key.find("username") == 0)
{
if (output.username.hasData())
configError("multiple values", output.database, "username");
parseExternalValue(key, value, output.username);
{
configError(status, "multiple values", output.database, "username");
result = false;
}
result &= parseExternalValue(status, key, value, output.username);
output.username.rtrim(" ");
}
else if (key.find("password") == 0)
{
if (output.password.hasData())
configError("multiple values", output.database, "password");
parseExternalValue(key, value, output.password);
{
configError(status, "multiple values", output.database, "password");
result = false;
}
result &= parseExternalValue(status, key, value, output.password);
}
else
configError("unknown parameter", output.database, key);
{
configError(status, "unknown key", output.database, key);
result = false;
}
}
return result;
}
}

Expand Down Expand Up @@ -245,7 +284,8 @@ Config* Config::get(const PathName& lookupName)

AutoPtr<Config> config(FB_NEW Config);

bool defaultFound = false, exactMatch = false;
FbLocalStatus localStatus;
bool defaultFound = false, exactMatch = false, replicaSkip = false;

for (const auto& section : cfgFile.getParameters())
{
Expand Down Expand Up @@ -283,15 +323,24 @@ Config* Config::get(const PathName& lookupName)
string value(el.value);

if (value.isEmpty())
{
configError(&localStatus, "empty value of key",
exactMatch ? lookupName.c_str() : section.name.c_str(),
key);
continue;
}

if (key == "sync_replica")
{
SyncReplica syncReplica(config->getPool());
if (el.sub)
{
syncReplica.database = value;
parseSyncReplica(el.sub->getParameters(), syncReplica);
if (!parseSyncReplica(&localStatus, el.sub->getParameters(), syncReplica))
{
replicaSkip = true;
continue;
}
}
else
splitConnectionString(value, syncReplica.database, syncReplica.username, syncReplica.password);
Expand Down Expand Up @@ -324,7 +373,12 @@ Config* Config::get(const PathName& lookupName)
{
config->journalDirectory = value.c_str();
PathUtils::ensureSeparator(config->journalDirectory);
checkAccess(config->journalDirectory, key);
if (!checkAccess(&localStatus, config->journalDirectory, key))
{
config->journalDirectory.erase();
replicaSkip = true;
continue;
}
}
else if (key == "journal_file_prefix")
{
Expand All @@ -338,7 +392,11 @@ Config* Config::get(const PathName& lookupName)
{
config->archiveDirectory = value.c_str();
PathUtils::ensureSeparator(config->archiveDirectory);
checkAccess(config->archiveDirectory, key);
if (!checkAccess(&localStatus, config->archiveDirectory, key))
{
config->archiveDirectory.erase();
continue;
}
}
else if (key == "journal_archive_command")
{
Expand Down Expand Up @@ -368,12 +426,28 @@ Config* Config::get(const PathName& lookupName)
{
parseBoolean(value, config->cascadeReplication);
}
else if ((key != "journal_source_directory") &&
(key != "source_guid") &&
(key != "verbose_logging") &&
(key != "apply_idle_timeout") &&
(key != "apply_error_timeout"))
{
configError(&localStatus, "unknown key",
exactMatch ? lookupName.c_str() : section.name.c_str(),
key);
}
}

if (exactMatch)
break;
}

if (localStatus->getState() & IStatus::STATE_ERRORS)
logPrimaryStatus(lookupName, &localStatus);

if (replicaSkip && !config->disableOnError)
raiseError("One or more replicas configured with errors");

// TODO: As soon as plugin name is moved into RDB$PUBLICATIONS,
// delay config parse until real replication start
if (config->pluginName.hasData())
Expand Down Expand Up @@ -403,6 +477,7 @@ Config* Config::get(const PathName& lookupName)
composeError(&localStatus, ex);

logPrimaryStatus(lookupName, &localStatus);
localStatus.raise();
}

return nullptr;
Expand All @@ -414,6 +489,7 @@ Config* Config::get(const PathName& lookupName)
void Config::enumerate(ReplicaList& replicas)
{
PathName dbName;
FbLocalStatus localStatus;

try
{
Expand Down Expand Up @@ -466,12 +542,20 @@ void Config::enumerate(ReplicaList& replicas)
{
config->sourceDirectory = value.c_str();
PathUtils::ensureSeparator(config->sourceDirectory);
if (!checkAccess(&localStatus, config->sourceDirectory, key))
{
config->sourceDirectory.erase();
continue;
}
}
else if (key == "source_guid")
{
config->sourceGuid = Guid::fromString(value);
if (!config->sourceGuid)
configError("invalid (misformatted) value", key, value);
{
configError(&localStatus, "invalid (misformatted) value", key, value);
continue;
}
}
else if (key == "verbose_logging")
{
Expand Down Expand Up @@ -501,11 +585,11 @@ void Config::enumerate(ReplicaList& replicas)
}
catch (const Exception& ex)
{
FbLocalStatus localStatus;
composeError(&localStatus, ex);
}

if (localStatus->getState() & IStatus::STATE_ERRORS)
logReplicaStatus(dbName, &localStatus);
}
}

// This routine is used for split input connection string to parts
Expand Down
Loading