in host/cxpslib/pssettingsconfigurator.cpp [19:212]
PSSettingsPtr PSSettingsConfigurator::ReadSettingsFromFile(
bool& fileUnavailable,
std::string& knownToFailHeader,
std::string& knownToFailContent)
{
fileUnavailable = true;
if (!boost::filesystem::exists(m_settingsLckFilePath) ||
!boost::filesystem::exists(m_settingsFilePath))
{
return PSSettingsPtr();
}
fileUnavailable = false;
boost::system::error_code ec;
time_t t = boost::filesystem::last_write_time(m_settingsFilePath, ec);
if (ec)
{
t = 0;
}
if (!t && m_MTime == t)
{
return PSSettingsPtr();
}
std::string headerStr;
std::string settingsFileContent;
// TODO-SanKumar-2002: This constructor throws, if the lock file is not
// present. Instead only expect the actual setting file to present, while
// creating this file, if not present.
boost::interprocess::file_lock flock(m_settingsLckFilePath.string().c_str());
{
boost::interprocess::sharable_lock<boost::interprocess::file_lock> sharableFlock(flock);
// TODO-SanKumar-2002: This lock is not cancellable, which could be
// replaced with Timed sharable_lock. The problem is that the lock seems
// to be spinning repeatedly instead of blocking. Gotta check before usage.
std::ifstream file;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file.open(m_settingsFilePath.string().c_str(), std::ios_base::binary);
if (m_serverOptions->psSettingsIncludesHeader())
{
std::getline(file, headerStr);
}
std::size_t sz = static_cast<std::size_t>(boost::filesystem::file_size(m_settingsFilePath));
sz -= file.tellg();
settingsFileContent.resize(sz, '\0');
file.read(&settingsFileContent[0], sz);
}
if (headerStr == knownToFailHeader &&
settingsFileContent == knownToFailContent)
{
// By doing this precheck, we avoid repeated logging for a known
// bad file, which will keep on failing.
return PSSettingsPtr();
// TODO-SanKumar-2002: Rate controlled log (say once in 10/25 times) in
// both +ve and -ve cases.
}
if ((m_serverOptions->psSettingsIncludesHeader() && headerStr.empty()) ||
(settingsFileContent.empty()))
{
CXPS_LOG_ERROR(AT_LOC << "Header line or content missing in the cache settings file");
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
if ((m_serverOptions->psSettingsIncludesHeader()) &&
(m_serverOptions->psSettingsEnforceMajorVersionCheck() ||
m_serverOptions->psSettingsVerifyHeader()))
{
CacheDataHeader parsedHeader;
try
{
parsedHeader =
JSON::consumer<CacheDataHeader>::convert(headerStr, true); // std::move() candidate
}
catch (const std::exception & ex)
{
CXPS_LOG_ERROR(AT_LOC <<
"Json serialization failed for the header in the cached settings file - " << ex.what());
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
if (m_serverOptions->psSettingsEnforceMajorVersionCheck())
{
if (parsedHeader.Version.empty())
{
CXPS_LOG_ERROR(AT_LOC << "Empty version found in the cached settings file");
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
std::vector<std::string> versionParts;
boost::split(versionParts, parsedHeader.Version, boost::is_any_of("."));
int majorVersion = boost::lexical_cast<int>(versionParts[0]);
int minorVersion = 0;
if (versionParts.size() != 1)
{
minorVersion = boost::lexical_cast<int>(versionParts[1]);
}
if (majorVersion != CacheDataHeader::CURRENT_CACHED_DATA_MAJOR_VERSION)
{
CXPS_LOG_ERROR(AT_LOC <<
"Major version of the cached settings file - " << majorVersion <<
" doesn't match the expected major version - " <<
CacheDataHeader::CURRENT_CACHED_DATA_MAJOR_VERSION);
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
if (m_serverOptions->psSettingsEnforceMinorVersionCheck() &&
minorVersion != CacheDataHeader::CURRENT_CACHED_DATA_MINOR_VERSION)
{
CXPS_LOG_ERROR(AT_LOC <<
"Minor version of the cached settings file - " << minorVersion <<
" doesn't match the expected minor version - " <<
CacheDataHeader::CURRENT_CACHED_DATA_MINOR_VERSION);
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
}
if (m_serverOptions->psSettingsVerifyHeader() &&
!parsedHeader.IsMatchingContent(settingsFileContent))
{
CXPS_LOG_ERROR(AT_LOC <<
"Checksum validation failed for the cached settings file - " <<
parsedHeader.Checksum << " (" << parsedHeader.ChecksumType << ")");
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
}
// the contents of the file are good, so update the cached time and content
m_MTime = t;
if (m_settingsFileContent == settingsFileContent)
{
return PSSettingsPtr();
}
PSSettingsPtr parsedSettingsPtr;
try
{
parsedSettingsPtr = boost::make_shared<PSSettings>(
JSON::consumer<PSSettings>::convert(settingsFileContent, true)); // std::move() candidate
m_settingsFileContent = settingsFileContent;
}
catch (const std::exception &ex)
{
CXPS_LOG_ERROR(AT_LOC <<
"Json serialization failed for the content in the cached settings file - " << ex.what());
knownToFailHeader = headerStr;
knownToFailContent = settingsFileContent;
return PSSettingsPtr();
}
// TODO-SanKumar-2002: By setting these values even in +ve case, we could
// avoid unnecessary JSON parsings as well as callbacks at the caller. Adverse
// effect would be that if any failed callbacks / other intermittent logic
// errors wouldn't be retried.
knownToFailHeader.clear();
knownToFailContent.clear();
return parsedSettingsPtr;
}