1114 lines
31 KiB
C++

//----------------------------------------------------------------------------
//
// Copyright (C) Intel Corporation, 2007 - 2008.
//
// File: Options.cpp
//
// Contents: Parses a static and dynamic configuration file.
// Provides access to its elements, with the actual type
// of the element obtained from the configuration file.
//
// Notes:
//----------------------------------------------------------------------------
//------------------
// Includes
//------------------
#include "Options.h"
#include <ace/OS_NS_sys_stat.h>
#include "TypeValidationsExtraction.h"
#include "DataStructures.h"
#include "CFParser.h"
#include "ListParser.h"
#include <memory>
using namespace std;
//------------------
// Defines
//------------------
#define FILES "FILES"
#define PORT "PORT"
#define IP "IP"
#define INET "INET"
#define ZERO_TIME 0
//------------------
// Statics init
//------------------
Options * Options::_instance = NULL;
bool Options::_init = false;
ACE_Mutex Options::_ctor_mutex;
//----------------------------------------
// Options public functions implementation
//----------------------------------------
/*
* Creates the Options Singleton.
* Requires the filenames to parse, and the type of formats that are
* allowed and needed.
* staticFileName - Pointer to the name of the static file to parse.
* dynamicFileName - Pointer to the name of the dynamic file to parse.
* format_static_mandatory - Pointer to the static mandatory format.
* format_static_optional - Pointer to the static optional format.
* format_dynamic_mandatory - Pointer to the dynamic mandatory format.
* format_dynamic_optional - Pointer to the dynamic optional format.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::createInstance ( const ACE_TCHAR * staticFileName,
const ACE_TCHAR * dynamicFileName,
const Format * format_static_mandatory,
const Format * format_static_optional,
const Format * format_dynamic_mandatory,
const Format * format_dynamic_optional)
{
STATUS status = STATUS_FAILURE;
// init is set to false. if a construction is
// performed successfully than init will become true
if (Options::_instance == NULL)
{
// do not allow two constructions simultaneously
Options::_ctor_mutex.acquire();
// do not allow more than one construction at all
if (!_init)
{
Options::_instance = new Options(staticFileName,
dynamicFileName,
format_static_mandatory,
format_static_optional,
format_dynamic_mandatory,
format_dynamic_optional);
}
Options::_ctor_mutex.release();
if (!_init)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Creating Options instance failed.\n")));
delete Options::_instance;
Options::_instance = NULL;
return status;
}
}
status = STATUS_SUCCESS;
return status;
}
/*
* Return pointer to singleton of Options.
* Must be called only after createInstance was called.
* Returns NULL if no such object exist.
*/
Options * Options::instance (void)
{
return Options::_instance;
}
/*
* Default destructor.
* If error occurs at any point that can not be recovered than
* destruction is stopped and not all elements are deleted
*/
Options::~Options (void)
{
ACE_TString sectionName, elementName;
auto_ptr<ElementHash::constOuterIter> hashOuterP_begin(_element_hash.beginOuters());
auto_ptr<ElementHash::constOuterIter> hashOuterP_end(_element_hash.endOuters());
if ((hashOuterP_begin.get() == NULL) || (hashOuterP_end.get() == NULL))
{
return;
}
ElementHash::constOuterIter hashOuterIter = * hashOuterP_begin;
for (; hashOuterIter != *hashOuterP_end; hashOuterIter++)
{
auto_ptr<ElementHash::constInnerIter> hashInnerIterP_begin
(_element_hash.beginInners(hashOuterIter->first));
auto_ptr<ElementHash::constInnerIter> hashInnerIterP_end
(_element_hash.endInners(hashOuterIter->first));
sectionName = hashOuterIter->first;
// Do not exit, it may happen that next iteration will work
if ((hashInnerIterP_begin.get() != NULL) && (hashInnerIterP_end.get() != NULL))
{
ElementHash::constInnerIter hashInnerIter = * hashInnerIterP_begin;
for (; hashInnerIter != *hashInnerIterP_end; hashInnerIter++)
{
elementName = hashInnerIter->first;
deleteElementContent(sectionName, elementName);
}
}
}
_element_hash.cleanHash();
_temp_String_Hash.cleanHash();
}
/*
* Reads the static file and parses its elements.
* After successful usage, the static elements may be received.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::read_static_file(void)
{
STATUS status = STATUS_FAILURE;
// Sanity check
if (_static_config_file == NULL ||
_format_static_mandatory == NULL ||
_format_static_optional == NULL)
{
return status;
}
if (read_file(_static_config_file, _format_static_mandatory, _format_static_optional))
{
return status;
}
if (read_all_external_files() == STATUS_FAILURE)
{
return status;
}
status = STATUS_SUCCESS;
return status;
}
/*
* Reads the dynamic files and parses its elements.
* After successful usage, the dynamic elements may be received.
* Returns -1 on failure.
* Returns 0 if none of the files were changed, and therefore not read.
* Returns 1 on read of only the dynamic config file.
* Returns 2 on read of the dynamic config file and at least one of the external files.
* Returns 3 on read of at least one of the external files. (no read of the dynamic config file).
*/
int Options::read_dynamic_file(void)
{
int ret = 0;
bool readConfigFile = false;
bool readExternalFiles = false;
// Sanity check
if (_dynamic_config_file == NULL ||
_format_dynamic_mandatory == NULL ||
_format_dynamic_optional == NULL)
{
return STATUS_FAILURE;
}
ACE_stat fileStats;
ACE_OS::stat(_dynamic_config_file, &fileStats);
if (_lastModification != fileStats.st_mtime)
{
if (read_file(_dynamic_config_file, _format_dynamic_mandatory, _format_dynamic_optional) != STATUS_SUCCESS)
{
return STATUS_FAILURE;
}
_lastModification = fileStats.st_mtime;
readConfigFile = true;
}
int externalFilesRead = read_all_external_files();
if (externalFilesRead == STATUS_FAILURE)
{
return STATUS_FAILURE;
}
if (externalFilesRead > 0)
{
readExternalFiles = true;
}
if (readConfigFile)
{
if (readExternalFiles)
{
ret = 2;
}
else
{
ret = 1;
}
}
else
{
if (readExternalFiles)
{
ret = 3;
}
}
return ret;
}
//----------------------------------------
// Options private functions implementation
//----------------------------------------
/*
* Constructor.
* Requires the filenames to parse, and the type of formats that are
* allowed and needed.
* staticFileName - Pointer to the name of the static file to parse.
* dynamicFileName - Pointer to the name of the dynamic file to parse.
* format_static_mandatory - Pointer to the static mandatory format.
* format_static_optional - Pointer to the static optional format.
* format_dynamic_mandatory - Pointer to the dynamic mandatory format.
* format_dynamic_optional - Pointer to the dynamic optional format.
*/
Options::Options(const ACE_TCHAR * staticFileName,
const ACE_TCHAR * dynamicFileName,
const Format * format_static_mandatory,
const Format * format_static_optional,
const Format * format_dynamic_mandatory,
const Format * format_dynamic_optional) : _lastModification(ZERO_TIME),
_format_static_mandatory(), _format_static_optional(), _format_dynamic_mandatory(),
_format_dynamic_optional()
{
if ( (staticFileName == NULL) ||
(dynamicFileName == NULL) )
{
return;
}
if ( (format_static_mandatory == NULL) ||
(format_static_optional == NULL) ||
(format_dynamic_mandatory == NULL) ||
(format_dynamic_optional == NULL) )
{
return;
}
if ((ACE_OS::strlen(staticFileName) <= MAXPATHLEN + 1) ||
(ACE_OS::strlen(dynamicFileName) <= MAXPATHLEN + 1))
{
ACE_OS::strcpy (this->_static_config_file, staticFileName);
ACE_OS::strcpy (this->_dynamic_config_file, dynamicFileName);
}
else
{
return;
}
_format_static_mandatory = format_static_mandatory;
_format_static_optional = format_static_optional;
_format_dynamic_mandatory = format_dynamic_mandatory;
_format_dynamic_optional = format_dynamic_optional;
_init = true;
}
/*
* Reads the file and parses its elements according to the formats given.
* This is the main bulk function, after which the static file is parsed.
* fileName - filename to be read
* format_mandatory - mandatory format to use
* format_optional - optional format to use
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::read_file(const ACE_TCHAR * fileName, const Format * format_mandatory, const Format * format_optional)
{
STATUS status = STATUS_FAILURE;
ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex,
locker1,
_mutex, STATUS_FAILURE);
// Sanity check after mutex
if (fileName == NULL ||
format_mandatory == NULL ||
format_optional == NULL)
{
return status;
}
CF_Parser parser;
if (parser.parseToCanonicalStringHash(fileName, _temp_String_Hash) != PARSER_STATUS_SUCCESS)
{
return status;
}
if (build_element_hash(format_mandatory, format_optional) != STATUS_SUCCESS)
{
return status;
}
// Finished building the hash.
// Now it should be checked that all the mandatory values were entered
if (checkFormat(format_mandatory, true) != STATUS_SUCCESS)
{
return status;
}
if (checkFormat(format_optional, false) != STATUS_SUCCESS)
{
return status;
}
status = STATUS_SUCCESS;
return status;
}
/*
* Builds the element hash according to previously entered parsing data to the
* string hash.
* Builds it according to the given formats -
* If an element parsed is missing from the mandatory and optional format than an error is ensued.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::build_element_hash(const Format * mandatory_format, const Format * optional_format)
{
STATUS status = STATUS_FAILURE;
if (mandatory_format == NULL ||
optional_format == NULL)
{
return status;
}
ACE_TString sectionName, elementName, elementValue;
Element_Type_Enum * elementType = NULL;
ACE_TStringHash::constInnerIter hashInnerIter;
ACE_TStringHash::constOuterIter hashOuterIter;
auto_ptr<ACE_TStringHash::constOuterIter> hashOuterP_begin(_temp_String_Hash.beginOuters());
auto_ptr<ACE_TStringHash::constOuterIter> hashOuterP_end(_temp_String_Hash.endOuters());
if ((hashOuterP_begin.get() == NULL) || (hashOuterP_end.get() == NULL))
{
return status;
}
hashOuterIter = * hashOuterP_begin;
for (; hashOuterIter != *hashOuterP_end; hashOuterIter++) {
auto_ptr<ACE_TStringHash::constInnerIter> hashInnerIterP_begin
(_temp_String_Hash.beginInners(hashOuterIter->first));
auto_ptr<ACE_TStringHash::constInnerIter> hashInnerIterP_end
(_temp_String_Hash.endInners(hashOuterIter->first));
if ((hashInnerIterP_begin.get() == NULL) || (hashInnerIterP_end.get() == NULL))
{
return status;
}
hashInnerIter = * hashInnerIterP_begin;
for (; hashInnerIter != *hashInnerIterP_end; hashInnerIter++)
{
sectionName = hashOuterIter->first;
elementName = hashInnerIter->first;
elementValue= hashInnerIter->second;
// obtain element type
if (obtainElementType(mandatory_format, optional_format,
sectionName, elementName, &elementType) != STATUS_SUCCESS)
{
return status;
}
if (addToElementHash(sectionName, elementName,
elementType->getElementType(), elementValue) != STATUS_SUCCESS)
{
return status;
}
}
}
// Clean up temp hash - there were no New's in it. Can simply clear hash
_temp_String_Hash.cleanHash();
status = STATUS_SUCCESS;
return status;
}
/*
* Adds an element to the element hash, putting it in the correct section, and element in the section,
* and providing it with the correct type (using elemType). The element value is obtained from
* elementValue.
* sectionName - section to put the element in
* elementName - element name in the section to put the element in
* elemType - the type of the element according which the needed type for the element is decided.
* elementValue- the value of the element.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::addToElementHash(const ACE_TString &sectionName,
const ACE_TString &elementName,
const ELEMENT_TYPES_ENUM &elemType,
ACE_TString &elementValue)
{
STATUS status = STATUS_FAILURE;
//Authentication_Param authParam;
LogMasks mask;
time_t timeVal;
URL_Wrapper * mc_data = NULL;
MCList * mc_list = NULL;
ServerData * as_data = NULL;
AuthServerHash * as_hash = NULL;
// check if in FILE section
ACE_TString sectionNameUpper = toUpper(sectionName);
if (sectionNameUpper.compare(FILES) == STATUS_SUCCESS)
{
ACE_TString fullFileName;
fullFileName += absolutePath;
fullFileName += elementValue;
if (addSingleElement<ACE_TString>(sectionName, elementName, fullFileName) != STATUS_SUCCESS)
{
return status;
}
status = STATUS_SUCCESS;
return status;
}
switch(elemType) {
case BOOL_TYPE:
bool cond;
if (validateAndGetElement(elementValue, cond) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid parameter: %s. Must be boolean.\n"),
elementValue.c_str()));
return status;
}
if (addSingleElement<bool>(sectionName, elementName, cond) != STATUS_SUCCESS)
{
return status;
}
break;
case UNSIGNED_INT_TYPE:
unsigned int num;
if (validateAndGetElement(elementValue, num) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid parameter: %s. Must be unsigned int.\n"),
elementValue.c_str()));
return status;
}
if (addSingleElement<unsigned int>(sectionName, elementName, num) != STATUS_SUCCESS)
{
return status;
}
break;
case STRING_TYPE:
if (addSingleElement<ACE_TString>(sectionName, elementName, elementValue) != STATUS_SUCCESS)
{
return status;
}
break;
case PORT_TYPE:
unsigned short port;
if ((validateAndGetElement(elementValue, port) != STATUS_SUCCESS) ||
(validatePort(port) != STATUS_SUCCESS))
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid parameter: %s. Must be unsigned short.\n"),
elementValue.c_str()));
return status;
}
if (addSingleElement<unsigned short>(sectionName, elementName, port) != STATUS_SUCCESS)
{
return status;
}
// try to add INET if possible
/*if (addINET(sectionName, elementName, true) != STATUS_SUCCESS)
{
return status;
}*/
break;
case LOG_LEVELS_TYPE:
unsigned long maskVal;
if (validateAndGetElement(elementValue, mask) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid log level: %s.\n"),
elementValue.c_str()));
return status;
}
maskVal = mask.getMask();
if (addSingleElement<unsigned long>(sectionName, elementName, maskVal) != STATUS_SUCCESS)
{
return status;
}
break;
case TIME_T_TYPE:
unsigned int temp;
if (validateAndGetElement(elementValue, temp) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid time type: %s.\n"),
elementValue.c_str()));
return status;
}
// Loss of information is not expected since the information is supposed to be short.
// But this should be checked.
timeVal = (time_t) temp;
if (addSingleElement<time_t>(sectionName, elementName, timeVal) != STATUS_SUCCESS)
{
return status;
}
break;
case IP_TYPE:
if (validateIP(elementValue) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Invalid host name: %s.\n"),
elementValue.c_str()));
return status;
}
if (addSingleElement<ACE_TString>(sectionName, elementName, elementValue) != STATUS_SUCCESS)
{
return status;
}
// try to add INET if possible
/*if (addINET(sectionName, elementName, false) != STATUS_SUCCESS)
{
return status;
}*/
break;
case MC_LIST_TYPE:
mc_list = getValueChangable<MCList>(sectionName.c_str(), sectionName.c_str());
if (mc_list == NULL)
{
return status;
}
if (mc_list->size() >= MAX_NOTIFICATIONS)
{
ACE_ERROR ((MY_DEBUG ACE_TEXT ("Notification list cannot exceed maximum size of %d entries. Skipping notification entry: %s\n"),
MAX_NOTIFICATIONS , elementValue.c_str()));
}
else
{
mc_data = new URL_Wrapper();
if (mc_data == NULL)
{
return status;
}
if (validateAndGetElement(elementValue, *mc_data) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Skipping invalid notification entry: %s.\n"),
elementValue.c_str()));
delete mc_data;
}
else
{
TRY_ACQUIRE_WRITE_GUARD( ACE_RW_Thread_Mutex ,
lock,
mc_list->guard());
if (lock.locked () == 0)
{
delete mc_data;
ACE_ERROR_RETURN((MY_DEBUG ACE_TEXT ("Unable to acquire write mutex on notification list.\n")), status);
}
mc_list->push_back(mc_data);
}
}
break;
case AUTHERIZED_SERVERS_LIST_TYPE:
as_data = new ServerData();
if (as_data == NULL)
{
return status;
}
if (validateAndGetElement(elementValue, *as_data) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Skipping invalid authorized server entry: %s\n"),
elementValue.c_str()));
delete as_data;
}
else
{
as_hash = getValueChangable<AuthServerHash>(sectionName.c_str(), sectionName.c_str());
if (as_hash == NULL)
{
delete as_data;
return status;
}
// Element value is intentionally inserted here.
// This means that the TESTED host:port are entered as the key in the hash,
// while the value holds the needed data about them, including the relevant INET
TRY_ACQUIRE_WRITE_GUARD( ACE_RW_Thread_Mutex ,
lock,
as_hash->guard());
if (lock.locked () == 0)
{
delete as_data;
ACE_ERROR_RETURN((MY_DEBUG ACE_TEXT ("Unable to acquire write mutex on authorized servers hash.\n")), status);
}
as_hash->insert(make_pair(elementValue, as_data));
}
break;
default:
return status;
break;
}
status = STATUS_SUCCESS;
return status;
}
/*
* Adds an INET type element to the hash according to the given sectionName and elementName.
* isPort indicates if the given element indicates a port or an ip, by which the indication
* of the INET is recognized. It may be that an INET object is not currently possible to add, in which
* case, this is not considered as error (such as in the example that only the port is known)
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
//STATUS Options::addINET(const ACE_TString &sectionName,
// const ACE_TString &elementName,
// bool isPort)
//{
// STATUS status = STATUS_FAILURE;
//
// // Check to see if this is indeed a port and if there is an ip corresponding to it yet
// // if there is, also add an INET object
// ACE_TString addition, otherAddition, IPName, IP2Name,PortName,otherAddition2;
// ACE_TString ipv4 = IPv4;
// ACE_TString ipv6 = IPv6;
// if (isPort)
// {
// /*
// if ((elementName.substr(elementName.length() - ipv4.length())).compare(ipv4) == 0)
// otherAddition = IPv4;
// else if ((elementName.substr(elementName.length() - ipv6.length())).compare(ipv6) == 0)
// otherAddition = IPv6;
// else*/
// addition = PORT;
// otherAddition = IPv4;
// otherAddition2 = IPv6;
// }
// else
// {
// if ((elementName.substr(elementName.length() - ipv4.length())).compare(ipv4) == 0)
// addition = IPv4;
// else if ((elementName.substr(elementName.length() - ipv6.length())).compare(ipv6) == 0)
// addition = IPv6;
// else
// addition = "";
// addition2 = "";
// otherAddition = PORT;
// }
//
// if (elementName.length() - addition.length() < 0)
// {
// return status;
// }
// if ((!addition.is_empty()) && (!otherAddition.is_empty()))
// {
// ACE_TString baseName = elementName.substr(STATUS_SUCCESS, elementName.length() - addition.length());
// ACE_TString otherName = baseName;
// otherName += otherAddition;
// if (_element_hash.hasInner(sectionName, otherName))
// {
// ACE_INET_Addr address;
// IPName = baseName;
// IPName += IPv4;
// IP2Name = baseName;
// IP2Name += IPv6;
// PortName = baseName;
// PortName += PORT;
//
// const unsigned short &port = *getValue<unsigned short>(sectionName.c_str(), PortName.c_str());
// const ACE_TString &ipv4 = *getValue<ACE_TString>(sectionName.c_str(), IPName.c_str());
// const ACE_TString &ipv6 = *getValue<ACE_TString>(sectionName.c_str(), IP2Name.c_str());
// if (address.set(port, ipv4.c_str()) != STATUS_SUCCESS)
// {
// return status;
// }
// if (addSingleElement<ACE_INET_Addr>(sectionName, baseName + INET4, address) != STATUS_SUCCESS)
// {
// return status;
// }
// if (address.set(port, ipv6.c_str()) != STATUS_SUCCESS)
// {
// return status;
// }
// if (addSingleElement<ACE_INET_Addr>(sectionName, baseName + INET6, address) != STATUS_SUCCESS)
// {
// return status;
// }
// }
// }
//
// status = STATUS_SUCCESS;
//
// return status;
//}
/*
* Checks that the format given (mandatory or optional set by isMandatory), fits the inner hash.
* i.e: if the hash holding all the data has all the elements it is required off according to
* the formats (if it does not hold an element that is in the optional format, than its
* default value is added)
* This is done after the inner hash is filled.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::checkFormat(const Format * format, bool isMandatory)
{
STATUS status = STATUS_FAILURE;
if (format == NULL)
{
return status;
}
Format::constOuterIter formatIter;
auto_ptr<Format::constOuterIter> formatIterP_begin(format -> beginOuters());
auto_ptr<Format::constOuterIter> formatIterP_end(format -> endOuters());
if ((formatIterP_begin.get() == NULL) || (formatIterP_end.get() == NULL))
{
return status;
}
formatIter = * formatIterP_begin;
for (; formatIter != *formatIterP_end; formatIter++)
{
if (!_element_hash.hasOuter(formatIter->first))
{
if (isMandatory)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Static config file is missing mandatory section: %s.\n"),
formatIter->first.c_str()));
return status;
}
else
{
_element_hash.addOuter(formatIter->first);
}
}
auto_ptr<Format::constInnerIter> formatInnerIterP_begin(format -> beginInners(formatIter->first));
auto_ptr<Format::constInnerIter> formatInnerIterP_end(format -> endInners(formatIter->first));
if ((formatInnerIterP_begin.get() == NULL) || (formatInnerIterP_end.get() == NULL))
{
return status;
}
Format::constInnerIter formatInnerIter = * formatInnerIterP_begin;
for (; formatInnerIter != *formatInnerIterP_end; formatInnerIter++)
{
if (!_element_hash.hasInner(formatIter->first, formatInnerIter->first))
{
if (isMandatory)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Static config file is missing mandatory element: %s in section: %s.\n"),
formatInnerIter->first.c_str(), formatIter->first.c_str()));
return status;
}
else
{
Element_Type_Enum_and_Default_Val * element =
(Element_Type_Enum_and_Default_Val *) formatInnerIter ->second;
ACE_TString typeVal = element->getTypeVal();
if (addToElementHash(formatIter->first, formatInnerIter->first,
element->getElementType(), typeVal) != STATUS_SUCCESS)
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Default element used for element: %s in section: %s is incompatible with expected type.\n"),
formatInnerIter->first.c_str(), formatIter->first.c_str()));
return status;
}
}
}
}
}
status = STATUS_SUCCESS;
return status;
}
/*
* Reads and parses all external files, and enters them to the element hash according
* their data.
* Returns -1 on failure.
* Otherwise, returns the amount of files that were read (files that were changed).
*/
int Options::read_all_external_files()
{
int ret = 0;
ElementHash::constInnerIter hashInnerIter;
Element_Type_Enum * elementType = NULL;
// Iterate on Files section
auto_ptr<ElementHash::constInnerIter> hashInnerIterP_begin(_element_hash.beginInners(FILES));
auto_ptr<ElementHash::constInnerIter> hashInnerIterP_end(_element_hash.endInners(FILES));
if ((hashInnerIterP_begin.get() == NULL) || (hashInnerIterP_end.get() == NULL))
{
return STATUS_FAILURE;
}
hashInnerIter = * hashInnerIterP_begin;
for (; hashInnerIter != *hashInnerIterP_end; hashInnerIter++)
{
const ACE_TString * fileName = getValue<ACE_TString>(FILES, hashInnerIter->first.c_str());
if (fileName == NULL)
{
return STATUS_FAILURE;
}
ACE_stat fileStats;
ACE_OS::stat(fileName->c_str(), &fileStats);
map< ACE_TString, time_t, ACE_TString_compare_no_toUpper >::const_iterator iter =
_extModifTimeMap.find(*fileName);
// If it is the first time, then the file name is not in the map, and should
// be entered with the proper value.
// If it is, than its last changed date should be checked.
if ((iter == _extModifTimeMap.end()) ||
(_extModifTimeMap[*fileName] != fileStats.st_mtime))
{
if (obtainElementType(_format_static_mandatory, _format_static_optional,
FILES, hashInnerIter->first, &elementType) != STATUS_SUCCESS)
{
return STATUS_FAILURE;
}
List_Parser parser;
vector<ACE_TString> vec;
if (parser.parseItemList(fileName -> c_str(), vec) != PARSER_STATUS_SUCCESS)
{
return STATUS_FAILURE;
}
STATUS tempStat;
if ((tempStat = build_element_hash_ext_from_vector(vec, hashInnerIter->first, elementType)) != STATUS_SUCCESS)
{
if (tempStat == STATUS_LOCK_FAILURE)
{
continue;
}
return STATUS_FAILURE;
}
// If reading the file was successful, than set a new change time.
_extModifTimeMap[*fileName] = fileStats.st_mtime;
++ret;
}
}
return ret;
}
/*
* Adds an element to the element hash according to an external file which was parsed to a vector.
* The vector reference is the vector which holds the parsed information.
* The section name indicates which section the new element is supposed to be in.
* The element type indicates the needed element type
* Note: A single element (in the final hash) is provided per file (usually a list or a hash)
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::build_element_hash_ext_from_vector(const vector<ACE_TString> &vec,
const ACE_TString &sectionName,
Element_Type_Enum * elementType)
{
STATUS status = STATUS_FAILURE;
if (elementType == NULL)
{
return status;
}
// First, delete the previous element that may exist in that location
if (!_element_hash.hasInner(sectionName, sectionName))
{
BaseElement * elem;
switch(elementType->getElementType())
{
case MC_LIST_TYPE:
elem = new Element<MCList>();
if (elem == NULL)
{
return status;
}
break;
case AUTHERIZED_SERVERS_LIST_TYPE:
elem = new Element<AuthServerHash>();
if (elem == NULL)
{
return status;
}
break;
default:
return status;
}
_element_hash.addInner(sectionName, sectionName, elem);
}
else
// If it does exist, than clear its values
// (since every time we read the external files again, we start from scratch).
{
switch(elementType->getElementType())
{
case MC_LIST_TYPE:
{
MCList * mc_list = NULL;
mc_list = getValueChangable<MCList>(sectionName.c_str(), sectionName.c_str());
if (mc_list == NULL)
{
return status;
}
TRY_ACQUIRE_WRITE_GUARD( ACE_RW_Thread_Mutex ,
lock,
mc_list->guard());
if (lock.locked () == 0)
{
ACE_ERROR_RETURN((MY_DEBUG ACE_TEXT ("Unable to acquire write mutex on notification list.\n")), STATUS_LOCK_FAILURE);
}
mc_list->clearList();
}
break;
case AUTHERIZED_SERVERS_LIST_TYPE:
{
AuthServerHash * authHash = NULL;
authHash = getValueChangable<AuthServerHash>(sectionName.c_str(), sectionName.c_str());
if (authHash == NULL)
{
return status;
}
TRY_ACQUIRE_WRITE_GUARD( ACE_RW_Thread_Mutex ,
lock,
authHash->guard());
if (lock.locked () == 0)
{
ACE_ERROR_RETURN((MY_DEBUG ACE_TEXT ("Unable to acquire write mutex on authorized servers hash.\n")), STATUS_LOCK_FAILURE);
}
authHash->clearHash();
}
break;
default:
return status;
}
}
ACE_TString elementName, elementValue;
vector< ACE_TString >::const_iterator iter;
for (iter = vec.begin(); iter != vec.end(); iter++)
{
// The element name does not matter => just leave it blank
elementValue = *iter;
if (addToElementHash(sectionName, elementName,
elementType->getElementType(), elementValue) != STATUS_SUCCESS)
{
return status;
}
}
status = STATUS_SUCCESS;
return status;
}
/*
* Obtains a single elements' type according to the given formats. Puts a pointer to the
* element type in **elementType.
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::obtainElementType(const Format * mandatory_format, const Format * optional_format,
const ACE_TString &sectionName, const ACE_TString &elementName,
Element_Type_Enum ** elementType)
{
STATUS status = STATUS_FAILURE;
if (mandatory_format == NULL ||
optional_format == NULL ||
elementType == NULL)
{
return status;
}
// Check if element is in mandatory first
if (mandatory_format -> hasInner(sectionName, elementName))
{
mandatory_format -> getValue(sectionName, elementName, elementType);
}
// Now check with optional
else
{
if (optional_format -> hasInner(sectionName, elementName))
{
optional_format -> getValue(sectionName, elementName, elementType);
}
// Not in optional or mandatory - considered an error
else
{
ACE_ERROR ((MY_ERROR ACE_TEXT ("Element: %s in section: %s does not appear in accepted formats\n"),
elementName.c_str(), sectionName.c_str()));
return status;
}
}
status = STATUS_SUCCESS;
return status;
}
/*
* Deletes an element from the element hash (deleting the element itself and removing
* it from the canonical element hash).
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::deleteElement(const ACE_TString &sectionName, const ACE_TString &elementName)
{
STATUS status = STATUS_FAILURE;
if (deleteElementContent(sectionName, elementName) != STATUS_SUCCESS)
{
return status;
}
if (_element_hash.deleteElement(sectionName, elementName) != STATUS_SUCCESS)
{
return status;
}
status = STATUS_SUCCESS;
return status;
}
/*
* Deletes an element from the element hash (deleting the element itself but not removing
* it from the canonical element hash).
* Returns STATUS_FAILURE on failure.
* Returns STATUS_SUCCESS on success.
*/
STATUS Options::deleteElementContent(const ACE_TString &sectionName, const ACE_TString &elementName)
{
STATUS status = STATUS_FAILURE;
BaseElement * base = NULL;
if ((_element_hash.getValue(sectionName, elementName, &base) != STATUS_SUCCESS) || (base == NULL))
{
return status;
}
delete base;
status = STATUS_SUCCESS;
return status;
}