@@ -62,6 +62,7 @@ namespace
6262 const ULONG DEFAULT_GROUP_FLUSH_DELAY = 0 ;
6363 const ULONG DEFAULT_APPLY_IDLE_TIMEOUT = 10 ; // seconds
6464 const ULONG DEFAULT_APPLY_ERROR_TIMEOUT = 60 ; // seconds
65+ const bool DEFAULT_REPORT_ERRORS = false ;
6566
6667 void parseLong (const string& input, ULONG& output)
6768 {
@@ -79,36 +80,53 @@ namespace
7980 output = false ;
8081 }
8182
82- void configError (const string& type, const string& key, const string& value)
83+ void configError (CheckStatusWrapper* status, const string& type, const string& key, const string& value)
8384 {
84- raiseError (" %s specifies %s: %s" , key.c_str (), type.c_str (), value.c_str ());
85+ string msg;
86+ if (!(status->getState () & IStatus::STATE_ERRORS))
87+ {
88+ msg.printf (" Incorrect entry in %s" , REPLICATION_CFGFILE);
89+ (Arg::Gds (isc_random) << Arg::Str (msg)).appendTo (status);
90+ }
91+
92+ msg.printf (" %s specifies %s: %s" , key.c_str (), type.c_str (), value.c_str ());
93+ (Arg::Gds (isc_random) << Arg::Str (msg)).appendTo (status);
8594 }
8695
87- void checkAccess (const PathName& path, const string& key)
96+ bool checkAccess (CheckStatusWrapper* status, const PathName& path, const string& key)
8897 {
8998 if (path.hasData () && !PathUtils::canAccess (path, 6 ))
90- configError (" missing or inaccessible directory" , key, path.c_str ());
99+ {
100+ configError (status, " missing or inaccessible directory" , key, path.c_str ());
101+ return false ;
102+ }
103+ return true ;
91104 }
92105
93106 void composeError (CheckStatusWrapper* status, const Exception& ex)
94107 {
95- string prefix;
96- prefix.printf (" Incorrect entry in %s" , REPLICATION_CFGFILE);
97-
98108 Arg::StatusVector sv;
99- sv << Arg::Gds (isc_random) << Arg::Str (prefix);
109+
110+ if (!(status->getState () & IStatus::STATE_ERRORS))
111+ {
112+ string prefix;
113+ prefix.printf (" Incorrect entry in %s" , REPLICATION_CFGFILE);
114+ sv << Arg::Gds (isc_random) << Arg::Str (prefix);
115+ }
116+
117+ sv << Arg::StatusVector (status);
100118 sv << Arg::StatusVector (ex);
101119
102120 status->setErrors (sv.value ());
103121 }
104122
105- void parseExternalValue (const string& key, const string& value, string& output)
123+ bool parseExternalValue (CheckStatusWrapper* status, const string& key, const string& value, string& output)
106124 {
107125 const auto pos = key.rfind (' _' );
108126 if (pos == string::npos)
109127 {
110128 output = value.c_str ();
111- return ;
129+ return true ;
112130 }
113131
114132 string temp;
@@ -118,7 +136,10 @@ namespace
118136 {
119137 fb_utils::readenv (value.c_str (), temp);
120138 if (temp.isEmpty ())
121- configError (" missing environment variable" , key, value);
139+ {
140+ configError (status, " missing environment variable" , key, value);
141+ return false ;
142+ }
122143 }
123144 else if (key_source.equals (KEY_SUFFIX_FILE))
124145 {
@@ -129,44 +150,65 @@ namespace
129150
130151 AutoPtr<FILE> file (os_utils::fopen (filename.c_str (), " rt" ));
131152 if (!file)
132- configError (" missing or inaccessible file" , key, filename.c_str ());
153+ {
154+ configError (status, " missing or inaccessible file" , key, filename.c_str ());
155+ return false ;
156+ }
133157
134158 if (temp.LoadFromFile (file))
135159 temp.alltrim (" \r " );
136160
137161 if (temp.isEmpty ())
138- configError (" first empty line of file" , key, filename.c_str ());
162+ {
163+ configError (status, " first empty line of file" , key, filename.c_str ());
164+ return false ;
165+ }
139166 }
140167
141168 output = temp.c_str ();
169+ return true ;
142170 }
143171
144- void parseSyncReplica (const ConfigFile::Parameters& params, SyncReplica& output)
172+ bool parseSyncReplica (CheckStatusWrapper* status, const ConfigFile::Parameters& params, SyncReplica& output)
145173 {
174+ bool result = true ;
146175 for (const auto & el : params)
147176 {
148177 const string key (el.name .c_str ());
149178 const string value (el.value );
150179
151180 if (value.isEmpty ())
152- continue ;
181+ {
182+ configError (status, " empty value" , output.database , key);
183+ result = false ;
184+ }
153185
154186 if (key.find (" username" ) == 0 )
155187 {
156188 if (output.username .hasData ())
157- configError (" multiple values" , output.database , " username" );
158- parseExternalValue (key, value, output.username );
189+ {
190+ configError (status, " multiple values" , output.database , " username" );
191+ result = false ;
192+ }
193+ result &= parseExternalValue (status, key, value, output.username );
159194 output.username .rtrim (" " );
160195 }
161196 else if (key.find (" password" ) == 0 )
162197 {
163198 if (output.password .hasData ())
164- configError (" multiple values" , output.database , " password" );
165- parseExternalValue (key, value, output.password );
199+ {
200+ configError (status, " multiple values" , output.database , " password" );
201+ result = false ;
202+ }
203+ result &= parseExternalValue (status, key, value, output.password );
166204 }
167205 else
168- configError (" unknown parameter" , output.database , key);
206+ {
207+ configError (status, " unknown key" , output.database , key);
208+ result = false ;
209+ }
169210 }
211+ return result;
170212 }
171213}
172214
@@ -194,7 +236,7 @@ Config::Config()
194236 applyErrorTimeout(DEFAULT_APPLY_ERROR_TIMEOUT),
195237 pluginName(getPool()),
196238 logErrors(true ),
197- reportErrors(false ),
239+ reportErrors(DEFAULT_REPORT_ERRORS ),
198240 disableOnError(true ),
199241 cascadeReplication(false )
200242{
@@ -234,6 +276,7 @@ Config* Config::get(const PathName& lookupName)
234276{
235277 fb_assert (lookupName.hasData ());
236278
279+ bool reportErrors = DEFAULT_REPORT_ERRORS;
237280 try
238281 {
239282 const PathName filename =
@@ -245,7 +288,8 @@ Config* Config::get(const PathName& lookupName)
245288
246289 AutoPtr<Config> config (FB_NEW Config);
247290
248- bool defaultFound = false , exactMatch = false ;
291+ FbLocalStatus localStatus;
292+ bool defaultFound = false , exactMatch = false , replicaSkip = false ;
249293
250294 for (const auto & section : cfgFile.getParameters ())
251295 {
@@ -283,15 +327,24 @@ Config* Config::get(const PathName& lookupName)
283327 string value (el.value );
284328
285329 if (value.isEmpty ())
330+ {
331+ configError (&localStatus, " empty value of key" ,
332+ exactMatch ? lookupName.c_str () : section.name .c_str (),
333+ key);
286334 continue ;
335+ }
287336
288337 if (key == " sync_replica" )
289338 {
290339 SyncReplica syncReplica (config->getPool ());
291340 if (el.sub )
292341 {
293342 syncReplica.database = value;
294- parseSyncReplica (el.sub ->getParameters (), syncReplica);
343+ if (!parseSyncReplica (&localStatus, el.sub ->getParameters (), syncReplica))
344+ {
345+ replicaSkip = true ;
346+ continue ;
347+ }
295348 }
296349 else
297350 splitConnectionString (value, syncReplica.database , syncReplica.username , syncReplica.password );
@@ -324,7 +377,12 @@ Config* Config::get(const PathName& lookupName)
324377 {
325378 config->journalDirectory = value.c_str ();
326379 PathUtils::ensureSeparator (config->journalDirectory );
327- checkAccess (config->journalDirectory , key);
380+ if (!checkAccess (&localStatus, config->journalDirectory , key))
381+ {
382+ config->journalDirectory .erase ();
383+ replicaSkip = true ;
384+ continue ;
385+ }
328386 }
329387 else if (key == " journal_file_prefix" )
330388 {
@@ -338,7 +396,11 @@ Config* Config::get(const PathName& lookupName)
338396 {
339397 config->archiveDirectory = value.c_str ();
340398 PathUtils::ensureSeparator (config->archiveDirectory );
341- checkAccess (config->archiveDirectory , key);
399+ if (!checkAccess (&localStatus, config->archiveDirectory , key))
400+ {
401+ config->archiveDirectory .erase ();
402+ continue ;
403+ }
342404 }
343405 else if (key == " journal_archive_command" )
344406 {
@@ -359,6 +421,7 @@ Config* Config::get(const PathName& lookupName)
359421 else if (key == " report_errors" )
360422 {
361423 parseBoolean (value, config->reportErrors );
424+ reportErrors = config->reportErrors ;
362425 }
363426 else if (key == " disable_on_error" )
364427 {
@@ -368,12 +431,28 @@ Config* Config::get(const PathName& lookupName)
368431 {
369432 parseBoolean (value, config->cascadeReplication );
370433 }
434+ else if ((key != " journal_source_directory" ) &&
435+ (key != " source_guid" ) &&
436+ (key != " verbose_logging" ) &&
437+ (key != " apply_idle_timeout" ) &&
438+ (key != " apply_error_timeout" ))
439+ {
440+ configError (&localStatus, " unknown key" ,
441+ exactMatch ? lookupName.c_str () : section.name .c_str (),
442+ key);
443+ }
371444 }
372445
373446 if (exactMatch)
374447 break ;
375448 }
376449
450+ if (localStatus->getState () & IStatus::STATE_ERRORS)
451+ logPrimaryStatus (lookupName, &localStatus);
452+
453+ if (replicaSkip && !config->disableOnError )
454+ raiseError (" One or more replicas configured with errors" );
455+
377456 // TODO: As soon as plugin name is moved into RDB$PUBLICATIONS,
378457 // delay config parse until real replication start
379458 if (config->pluginName .hasData ())
@@ -396,13 +475,18 @@ Config* Config::get(const PathName& lookupName)
396475
397476 return config.release ();
398477 }
478+
479+ if (replicaSkip)
480+ raiseError (" All configured replicas skipped" );
399481 }
400482 catch (const Exception& ex)
401483 {
402484 FbLocalStatus localStatus;
403485 composeError (&localStatus, ex);
404486
405487 logPrimaryStatus (lookupName, &localStatus);
488+ if (reportErrors)
489+ localStatus.raise ();
406490 }
407491
408492 return nullptr ;
@@ -414,6 +498,7 @@ Config* Config::get(const PathName& lookupName)
414498void Config::enumerate (ReplicaList& replicas)
415499{
416500 PathName dbName;
501+ FbLocalStatus localStatus;
417502
418503 try
419504 {
@@ -466,11 +551,19 @@ void Config::enumerate(ReplicaList& replicas)
466551 {
467552 config->sourceDirectory = value.c_str ();
468553 PathUtils::ensureSeparator (config->sourceDirectory );
554+ if (!checkAccess (&localStatus, config->sourceDirectory , key))
555+ {
556+ config->sourceDirectory .erase ();
557+ continue ;
558+ }
469559 }
470560 else if (key == " source_guid" )
471561 {
472562 if (!StringToGuid (&config->sourceGuid , value.c_str ()))
473- configError (" invalid (misformatted) value" , key, value);
563+ {
564+ configError (&localStatus, " invalid (misformatted) value" , key, value);
565+ continue ;
566+ }
474567 }
475568 else if (key == " verbose_logging" )
476569 {
@@ -500,11 +593,11 @@ void Config::enumerate(ReplicaList& replicas)
500593 }
501594 catch (const Exception& ex)
502595 {
503- FbLocalStatus localStatus;
504596 composeError (&localStatus, ex);
597+ }
505598
599+ if (localStatus->getState () & IStatus::STATE_ERRORS)
506600 logReplicaStatus (dbName, &localStatus);
507- }
508601}
509602
510603// This routine is used for split input connection string to parts
0 commit comments