mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-04 06:03:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1172 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1172 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
						|
* Copyright 2010 Kestrel Signal Processing, Inc.
 | 
						|
* Copyright 2011, 2012 Range Networks, Inc.
 | 
						|
*
 | 
						|
*
 | 
						|
* This software is distributed under the terms of the GNU Affero Public License.
 | 
						|
* See the COPYING file in the main directory for details.
 | 
						|
*
 | 
						|
* This use of this software may be subject to additional restrictions.
 | 
						|
* See the LEGAL file in the main directory for details.
 | 
						|
 | 
						|
	This program is free software: you can redistribute it and/or modify
 | 
						|
	it under the terms of the GNU Affero General Public License as published by
 | 
						|
	the Free Software Foundation, either version 3 of the License, or
 | 
						|
	(at your option) any later version.
 | 
						|
 | 
						|
	This program is distributed in the hope that it will be useful,
 | 
						|
	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
	GNU Affero General Public License for more details.
 | 
						|
 | 
						|
	You should have received a copy of the GNU Affero General Public License
 | 
						|
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
#include "Configuration.h"
 | 
						|
#include "Logger.h"
 | 
						|
#include <fstream>
 | 
						|
#include <iostream>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#ifdef DEBUG_CONFIG
 | 
						|
#define	debugLogEarly gLogEarly
 | 
						|
#else
 | 
						|
#define	debugLogEarly(x,y,z)
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
using namespace std;
 | 
						|
 | 
						|
char gCmdName[20] = {0}; // Use a char* to avoid avoid static initialization of string, and race at startup.
 | 
						|
 | 
						|
static const char* createConfigTable = {
 | 
						|
	"CREATE TABLE IF NOT EXISTS CONFIG ("
 | 
						|
		"KEYSTRING TEXT UNIQUE NOT NULL, "
 | 
						|
		"VALUESTRING TEXT, "
 | 
						|
		"STATIC INTEGER DEFAULT 0, "
 | 
						|
		"OPTIONAL INTEGER DEFAULT 0, "
 | 
						|
		"COMMENTS TEXT DEFAULT ''"
 | 
						|
	")"
 | 
						|
};
 | 
						|
 | 
						|
static std::string replaceAll(const std::string input, const std::string search, const std::string replace)
 | 
						|
{
 | 
						|
	std::string output = input;
 | 
						|
	size_t index = 0;
 | 
						|
 | 
						|
	while (true) {
 | 
						|
		index = output.find(search, index);
 | 
						|
		if (index == std::string::npos) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		output.replace(index, replace.length(), replace);
 | 
						|
		index += replace.length();
 | 
						|
	}
 | 
						|
 | 
						|
	return output;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
float ConfigurationRecord::floatNumber() const
 | 
						|
{
 | 
						|
	float val;
 | 
						|
	sscanf(mValue.c_str(),"%f",&val);
 | 
						|
	return val;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName, ConfigurationKeyMap wSchema)
 | 
						|
{
 | 
						|
	gLogEarly(LOG_INFO, "opening configuration table from path %s", filename);
 | 
						|
	// Connect to the database.
 | 
						|
	int rc = sqlite3_open(filename,&mDB);
 | 
						|
	// (pat) When I used malloc here, sqlite3 sporadically crashes.
 | 
						|
	if (wCmdName) {
 | 
						|
		strncpy(gCmdName,wCmdName,18);
 | 
						|
		gCmdName[18] = 0;
 | 
						|
		strcat(gCmdName,":");
 | 
						|
	}
 | 
						|
	if (rc) {
 | 
						|
		gLogEarly(LOG_EMERG, "cannot open configuration database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
 | 
						|
		sqlite3_close(mDB);
 | 
						|
		mDB = NULL;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	// Create the table, if needed.
 | 
						|
	if (!sqlite3_command(mDB,createConfigTable)) {
 | 
						|
		gLogEarly(LOG_EMERG, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
 | 
						|
	}
 | 
						|
 | 
						|
	// Build CommonLibs schema
 | 
						|
	ConfigurationKey *tmp;
 | 
						|
	tmp = new ConfigurationKey("Log.Alarms.Max","20",
 | 
						|
		"alarms",
 | 
						|
		ConfigurationKey::CUSTOMER,
 | 
						|
		ConfigurationKey::VALRANGE,
 | 
						|
		"10:20",// educated guess
 | 
						|
		false,
 | 
						|
		"Maximum number of alarms to remember inside the application."
 | 
						|
	);
 | 
						|
	mSchema[tmp->getName()] = *tmp;
 | 
						|
	delete tmp;
 | 
						|
 | 
						|
	tmp = new ConfigurationKey("Log.File","",
 | 
						|
		"",
 | 
						|
		ConfigurationKey::DEVELOPER,
 | 
						|
		ConfigurationKey::FILEPATH_OPT,// audited
 | 
						|
		"",
 | 
						|
		false,
 | 
						|
		"Path to use for textfile based logging.  "
 | 
						|
			"By default, this feature is disabled.  "
 | 
						|
			"To enable, specify an absolute path to the file you wish to use, eg: /tmp/my-debug.log.  "
 | 
						|
			"To disable again, execute \"unconfig Log.File\"."
 | 
						|
	);
 | 
						|
	mSchema[tmp->getName()] = *tmp;
 | 
						|
	delete tmp;
 | 
						|
 | 
						|
	tmp = new ConfigurationKey("Log.Level","NOTICE",
 | 
						|
		"",
 | 
						|
		ConfigurationKey::CUSTOMER,
 | 
						|
		ConfigurationKey::CHOICE,
 | 
						|
		"EMERG|EMERGENCY - report serious faults associated with service failure or hardware damage,"
 | 
						|
			"ALERT|ALERT - report likely service disruption caused by misconfiguration or poor connectivity,"
 | 
						|
			"CRIT|CRITICAL - report anomalous events that are likely to degrade service,"
 | 
						|
			"ERR|ERROR - report internal errors of the software that may result in degradation of service in unusual circumstances,"
 | 
						|
			"WARNING|WARNING - report anomalous events that may indicate a degradation of normal service,"
 | 
						|
			"NOTICE|NOTICE - report anomalous events that probably do not affect service but may be of interest to network operators,"
 | 
						|
			"INFO|INFORMATION - report normal events,"
 | 
						|
			"DEBUG|DEBUG - only for use by developers and will degrade system performance",
 | 
						|
		false,
 | 
						|
		"Default logging level when no other level is defined for a file."
 | 
						|
	);
 | 
						|
	mSchema[tmp->getName()] = *tmp;
 | 
						|
	delete tmp;
 | 
						|
 | 
						|
	// Add application specific schema
 | 
						|
	mSchema.insert(wSchema.begin(), wSchema.end());
 | 
						|
 | 
						|
	// Init the cross checking callback to something predictable
 | 
						|
	mCrossCheck = NULL;
 | 
						|
}
 | 
						|
 | 
						|
string ConfigurationTable::getDefaultSQL(const std::string& program, const std::string& version)
 | 
						|
{
 | 
						|
	stringstream ss;
 | 
						|
	ConfigurationKeyMap::iterator mp;
 | 
						|
 | 
						|
	ss << "--" << endl;
 | 
						|
	ss << "-- This file was generated using: " << program << " --gensql" << endl;
 | 
						|
	ss << "-- binary version: " << version << endl;
 | 
						|
	ss << "--" << endl;
 | 
						|
	ss << "-- Future changes should not be put in this file directly but" << endl;
 | 
						|
	ss << "-- rather in the program's ConfigurationKey schema." << endl;
 | 
						|
	ss << "--" << endl;
 | 
						|
	ss << "PRAGMA foreign_keys=OFF;" << endl;
 | 
						|
	ss << "BEGIN TRANSACTION;" << endl;
 | 
						|
	ss << "CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');" << endl;
 | 
						|
 | 
						|
	mp = mSchema.begin();
 | 
						|
	while (mp != mSchema.end()) {
 | 
						|
		ss << "INSERT INTO \"CONFIG\" VALUES(";
 | 
						|
			// name
 | 
						|
			ss << "'" << mp->first << "',";
 | 
						|
			// default
 | 
						|
			ss << "'" << mp->second.getDefaultValue() << "',";
 | 
						|
			// static
 | 
						|
			if (mp->second.isStatic()) {
 | 
						|
				ss << "1";
 | 
						|
			} else {
 | 
						|
				ss << "0";
 | 
						|
			}
 | 
						|
			ss << ",";
 | 
						|
			// optional
 | 
						|
			ss << "0,";
 | 
						|
			// description
 | 
						|
			ss << "'";
 | 
						|
			if (mp->second.getType() == ConfigurationKey::BOOLEAN) {
 | 
						|
				ss << "1=enabled, 0=disabled - ";
 | 
						|
			}
 | 
						|
			ss << mp->second.getDescription();
 | 
						|
			if (mp->second.isStatic()) {
 | 
						|
				ss << "  Static.";
 | 
						|
			}
 | 
						|
			ss << "'";
 | 
						|
		ss << ");" << endl;
 | 
						|
		mp++;
 | 
						|
	}
 | 
						|
 | 
						|
	ss << "COMMIT;" << endl;
 | 
						|
	ss << endl;
 | 
						|
 | 
						|
	return ss.str();
 | 
						|
}
 | 
						|
 | 
						|
string ConfigurationTable::getTeX(const std::string& program, const std::string& version)
 | 
						|
{
 | 
						|
	stringstream ss;
 | 
						|
	ConfigurationKeyMap::iterator mp;
 | 
						|
 | 
						|
	ss << "% START AUTO-GENERATED CONTENT" << endl;
 | 
						|
	ss << "% -- these sections were generated using: " << program << " --gentex" << endl;
 | 
						|
	ss << "% -- binary version: " << version << endl;
 | 
						|
 | 
						|
	ss << "\\subsection{Customer Site Parameters}" << endl;
 | 
						|
	ss << "These parameters must be changed to fit your site." << endl;
 | 
						|
	ss << "\\begin{itemize}" << endl;
 | 
						|
	mp = mSchema.begin();
 | 
						|
	while (mp != mSchema.end()) {
 | 
						|
		if (mp->second.getVisibility() == ConfigurationKey::CUSTOMERSITE) {
 | 
						|
			ss << "	\\item ";
 | 
						|
				// name
 | 
						|
				ss << mp->first << " -- ";
 | 
						|
				// description
 | 
						|
				ss << mp->second.getDescription();
 | 
						|
			ss << endl;
 | 
						|
		}
 | 
						|
		mp++;
 | 
						|
	}
 | 
						|
	ss << "\\end{itemize}" << endl;
 | 
						|
	ss << endl;
 | 
						|
 | 
						|
	ss << "\\subsection{Customer Tuneable Parameters}" << endl;
 | 
						|
	ss << "These parameters can be changed to optimize your site." << endl;
 | 
						|
	ss << "\\begin{itemize}" << endl;
 | 
						|
	mp = mSchema.begin();
 | 
						|
	while (mp != mSchema.end()) {
 | 
						|
		if (mp->second.getVisibility() != ConfigurationKey::CUSTOMERSITE &&
 | 
						|
			(
 | 
						|
				mp->second.getVisibility() == ConfigurationKey::CUSTOMER ||
 | 
						|
				mp->second.getVisibility() == ConfigurationKey::CUSTOMERTUNE ||
 | 
						|
				mp->second.getVisibility() == ConfigurationKey::CUSTOMERWARN
 | 
						|
			)) {
 | 
						|
			ss << "	\\item ";
 | 
						|
				// name
 | 
						|
				ss << mp->first << " -- ";
 | 
						|
				// description
 | 
						|
				ss << mp->second.getDescription();
 | 
						|
			ss << endl;
 | 
						|
		}
 | 
						|
		mp++;
 | 
						|
	}
 | 
						|
	ss << "\\end{itemize}" << endl;
 | 
						|
	ss << endl;
 | 
						|
 | 
						|
	ss << "\\subsection{Developer/Factory Parameters}" << endl;
 | 
						|
	ss << "These parameters should only be changed by when developing new code." << endl;
 | 
						|
	ss << "\\begin{itemize}" << endl;
 | 
						|
	mp = mSchema.begin();
 | 
						|
	while (mp != mSchema.end()) {
 | 
						|
		if (mp->second.getVisibility() == ConfigurationKey::FACTORY ||
 | 
						|
			mp->second.getVisibility() == ConfigurationKey::DEVELOPER) {
 | 
						|
			ss << "	\\item ";
 | 
						|
				// name
 | 
						|
				ss << mp->first << " -- ";
 | 
						|
				// description
 | 
						|
				ss << mp->second.getDescription();
 | 
						|
			ss << endl;
 | 
						|
		}
 | 
						|
		mp++;
 | 
						|
	}
 | 
						|
	ss << "\\end{itemize}" << endl;
 | 
						|
	ss << "% END AUTO-GENERATED CONTENT" << endl;
 | 
						|
	ss << endl;
 | 
						|
 | 
						|
	string tmp = replaceAll(ss.str(), "^", "\\^");
 | 
						|
	return replaceAll(tmp, "_", "\\_");
 | 
						|
}
 | 
						|
 | 
						|
bool ConfigurationTable::defines(const string& key)
 | 
						|
{
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		return lookup(key).defined();
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
bool ConfigurationTable::keyDefinedInSchema(const std::string& name)
 | 
						|
{
 | 
						|
	return mSchema.find(name) == mSchema.end() ? false : true;
 | 
						|
}
 | 
						|
 | 
						|
bool ConfigurationTable::isValidValue(const std::string& name, const std::string& val) {
 | 
						|
	bool ret = false;
 | 
						|
 | 
						|
	ConfigurationKey key = mSchema[name];
 | 
						|
 | 
						|
	switch (key.getType()) {
 | 
						|
		case ConfigurationKey::BOOLEAN: {
 | 
						|
			if (val == "1" || val == "0") {
 | 
						|
				ret = true;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::CHOICE_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::CHOICE: {
 | 
						|
			int startPos = -1;
 | 
						|
			uint endPos = 0;
 | 
						|
 | 
						|
			std::string tmp = key.getValidValues();
 | 
						|
 | 
						|
			do {
 | 
						|
				startPos++;
 | 
						|
				if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
 | 
						|
					if (val == tmp.substr(startPos, endPos-startPos)) {
 | 
						|
						ret = true;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if (val == tmp.substr(startPos, tmp.find(',', startPos)-startPos)) {
 | 
						|
						ret = true;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
			} while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::CIDR_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::CIDR: {
 | 
						|
			uint delimiter;
 | 
						|
			std::string ip;
 | 
						|
			int cidr = -1;
 | 
						|
 | 
						|
			delimiter = val.find('/');
 | 
						|
			if (delimiter != std::string::npos) {
 | 
						|
				ip = val.substr(0, delimiter);
 | 
						|
				std::stringstream(val.substr(delimiter+1)) >> cidr;
 | 
						|
				if (ConfigurationKey::isValidIP(ip) && 0 <= cidr && cidr <= 32) {
 | 
						|
					ret = true;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::FILEPATH_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::FILEPATH: {
 | 
						|
			regex_t r;
 | 
						|
			const char* expression = "^[a-zA-Z0-9/_.-]+$";
 | 
						|
			int result = regcomp(&r, expression, REG_EXTENDED);
 | 
						|
			if (result) {
 | 
						|
				char msg[256];
 | 
						|
				regerror(result,&r,msg,255);
 | 
						|
				break;//abort();
 | 
						|
			}
 | 
						|
			if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
 | 
						|
				ret = true;
 | 
						|
			}
 | 
						|
			regfree(&r);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::IPADDRESS_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::IPADDRESS: {
 | 
						|
			ret = ConfigurationKey::isValidIP(val);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::IPANDPORT: {
 | 
						|
			uint delimiter;
 | 
						|
			std::string ip;
 | 
						|
			int port = -1;
 | 
						|
 | 
						|
			delimiter = val.find(':');
 | 
						|
			if (delimiter != std::string::npos) {
 | 
						|
				ip = val.substr(0, delimiter);
 | 
						|
				std::stringstream(val.substr(delimiter+1)) >> port;
 | 
						|
				if (ConfigurationKey::isValidIP(ip) && 1 <= port && port <= 65535) {
 | 
						|
					ret = true;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::MIPADDRESS_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::MIPADDRESS: {
 | 
						|
			int startPos = -1;
 | 
						|
			uint endPos = 0;
 | 
						|
 | 
						|
			do {
 | 
						|
				startPos++;
 | 
						|
				endPos = val.find(' ', startPos);
 | 
						|
				if (ConfigurationKey::isValidIP(val.substr(startPos, endPos-startPos))) {
 | 
						|
					ret = true;
 | 
						|
				} else {
 | 
						|
					ret = false;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
			} while ((startPos = endPos) != (int)std::string::npos);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::PORT_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::PORT: {
 | 
						|
			int intVal;
 | 
						|
 | 
						|
			std::stringstream(val) >> intVal;
 | 
						|
 | 
						|
			if (1 <= intVal && intVal <= 65535) {
 | 
						|
				ret = true;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::REGEX_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::REGEX: {
 | 
						|
			regex_t r;
 | 
						|
			const char* expression = val.c_str();
 | 
						|
			int result = regcomp(&r, expression, REG_EXTENDED);
 | 
						|
			if (result) {
 | 
						|
				char msg[256];
 | 
						|
				regerror(result,&r,msg,255);
 | 
						|
			} else {
 | 
						|
				ret = true;
 | 
						|
			}
 | 
						|
			regfree(&r);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::STRING_OPT: {
 | 
						|
			if (val.length() == 0) {
 | 
						|
				ret = true;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		case ConfigurationKey::STRING: {
 | 
						|
			regex_t r;
 | 
						|
			const char* expression = key.getValidValues().c_str();
 | 
						|
			int result = regcomp(&r, expression, REG_EXTENDED);
 | 
						|
			if (result) {
 | 
						|
				char msg[256];
 | 
						|
				regerror(result,&r,msg,255);
 | 
						|
				break;//abort();
 | 
						|
			}
 | 
						|
			if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
 | 
						|
				ret = true;
 | 
						|
			}
 | 
						|
			regfree(&r);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		case ConfigurationKey::VALRANGE: {
 | 
						|
			regex_t r;
 | 
						|
			int result;
 | 
						|
			if (key.getValidValues().find('.') != std::string::npos) {
 | 
						|
				result = regcomp(&r, "^[0-9.-]+$", REG_EXTENDED);
 | 
						|
			} else {
 | 
						|
				result = regcomp(&r, "^[0-9-]+$", REG_EXTENDED);
 | 
						|
			}
 | 
						|
			if (result) {
 | 
						|
				char msg[256];
 | 
						|
				regerror(result,&r,msg,255);
 | 
						|
				break;//abort();
 | 
						|
			}
 | 
						|
			if (regexec(&r, val.c_str(), 0, NULL, 0)!=0) {
 | 
						|
				ret = false;
 | 
						|
			} else if (key.getValidValues().find('.') != std::string::npos) {
 | 
						|
				ret = ConfigurationKey::isInValRange<float>(key, val, false);
 | 
						|
			} else {
 | 
						|
				ret = ConfigurationKey::isInValRange<int>(key, val, true);
 | 
						|
			}
 | 
						|
 | 
						|
			regfree(&r);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
ConfigurationKeyMap ConfigurationTable::getSimilarKeys(const std::string& snippet) {
 | 
						|
	ConfigurationKeyMap tmp;
 | 
						|
 | 
						|
	ConfigurationKeyMap::const_iterator mp = mSchema.begin();
 | 
						|
	while (mp != mSchema.end()) {
 | 
						|
		if (mp->first.find(snippet) != std::string::npos) {
 | 
						|
			tmp[mp->first] = mp->second;
 | 
						|
		}
 | 
						|
		mp++;
 | 
						|
	}
 | 
						|
 | 
						|
	return tmp;
 | 
						|
}
 | 
						|
 | 
						|
const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
 | 
						|
{
 | 
						|
	assert(mDB);
 | 
						|
	checkCacheAge();
 | 
						|
	// We assume the caller holds mLock.
 | 
						|
	// So it is OK to return a reference into the cache.
 | 
						|
 | 
						|
	// Check the cache.
 | 
						|
	// This is cheap.
 | 
						|
	ConfigurationMap::const_iterator where = mCache.find(key);
 | 
						|
	if (where!=mCache.end()) {
 | 
						|
		if (where->second.defined()) return where->second;
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
 | 
						|
	// Check the database.
 | 
						|
	// This is more expensive.
 | 
						|
	char *value = NULL;
 | 
						|
	sqlite3_single_lookup(mDB,"CONFIG",
 | 
						|
			"KEYSTRING",key.c_str(),"VALUESTRING",value);
 | 
						|
 | 
						|
	// value found, cache the result
 | 
						|
	if (value) {
 | 
						|
		mCache[key] = ConfigurationRecord(value);
 | 
						|
	// key definition found, cache the default
 | 
						|
	} else if (keyDefinedInSchema(key)) {
 | 
						|
		mCache[key] = ConfigurationRecord(mSchema[key].getDefaultValue());
 | 
						|
	// total miss, cache the error
 | 
						|
	} else {
 | 
						|
		mCache[key] = ConfigurationRecord(false);
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
 | 
						|
	free(value);
 | 
						|
 | 
						|
	// Leave mLock locked.  The caller holds it still.
 | 
						|
	return mCache[key];
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool ConfigurationTable::isStatic(const string& key)
 | 
						|
{
 | 
						|
	if (keyDefinedInSchema(key)) {
 | 
						|
		return mSchema[key].isStatic();
 | 
						|
	} else {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
string ConfigurationTable::getStr(const string& key)
 | 
						|
{
 | 
						|
	// We need the lock because rec is a reference into the cache.
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		return lookup(key).value();
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool ConfigurationTable::getBool(const string& key)
 | 
						|
{
 | 
						|
	try {
 | 
						|
		return getNum(key) != 0;
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
long ConfigurationTable::getNum(const string& key)
 | 
						|
{
 | 
						|
	// We need the lock because rec is a reference into the cache.
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		return lookup(key).number();
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
float ConfigurationTable::getFloat(const string& key)
 | 
						|
{
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		return lookup(key).floatNumber();
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key)
 | 
						|
{
 | 
						|
	// Look up the string.
 | 
						|
	char *line=NULL;
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		const ConfigurationRecord& rec = lookup(key);
 | 
						|
		line = strdup(rec.value().c_str());
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
 | 
						|
	assert(line);
 | 
						|
	char *lp = line;
 | 
						|
	
 | 
						|
	// Parse the string.
 | 
						|
	std::vector<string> retVal;
 | 
						|
	while (lp) {
 | 
						|
		while (*lp==' ') lp++;
 | 
						|
		if (*lp == '\0') break;
 | 
						|
		char *tp = strsep(&lp," ");
 | 
						|
		if (!tp) break;
 | 
						|
		retVal.push_back(tp);
 | 
						|
	}
 | 
						|
	free(line);
 | 
						|
	return retVal;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
std::vector<unsigned> ConfigurationTable::getVector(const string& key)
 | 
						|
{
 | 
						|
	// Look up the string.
 | 
						|
	char *line=NULL;
 | 
						|
	try {
 | 
						|
		ScopedLock lock(mLock);
 | 
						|
		const ConfigurationRecord& rec = lookup(key);
 | 
						|
		line = strdup(rec.value().c_str());
 | 
						|
	} catch (ConfigurationTableKeyNotFound) {
 | 
						|
		// Raise an alert and re-throw the exception.
 | 
						|
		debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
 | 
						|
		throw ConfigurationTableKeyNotFound(key);
 | 
						|
	}
 | 
						|
 | 
						|
	assert(line);
 | 
						|
	char *lp = line;
 | 
						|
 | 
						|
	// Parse the string.
 | 
						|
	std::vector<unsigned> retVal;
 | 
						|
	while (lp) {
 | 
						|
		// Watch for multiple or trailing spaces.
 | 
						|
		while (*lp==' ') lp++;
 | 
						|
		if (*lp=='\0') break;
 | 
						|
		retVal.push_back(strtol(lp,NULL,0));
 | 
						|
		strsep(&lp," ");
 | 
						|
	}
 | 
						|
	free(line);
 | 
						|
	return retVal;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool ConfigurationTable::remove(const string& key)
 | 
						|
{
 | 
						|
	assert(mDB);
 | 
						|
 | 
						|
	ScopedLock lock(mLock);
 | 
						|
	// Clear the cache entry and the database.
 | 
						|
	ConfigurationMap::iterator where = mCache.find(key);
 | 
						|
	if (where!=mCache.end()) mCache.erase(where);
 | 
						|
	// Really remove it.
 | 
						|
	string cmd = "DELETE FROM CONFIG WHERE KEYSTRING=='"+key+"'";
 | 
						|
	return sqlite3_command(mDB,cmd.c_str());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void ConfigurationTable::find(const string& pat, ostream& os) const
 | 
						|
{
 | 
						|
	// Prepare the statement.
 | 
						|
	string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG WHERE KEYSTRING LIKE \"%" + pat + "%\"";
 | 
						|
	sqlite3_stmt *stmt;
 | 
						|
	if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return;
 | 
						|
	// Read the result.
 | 
						|
	int src = sqlite3_run_query(mDB,stmt);
 | 
						|
	while (src==SQLITE_ROW) {
 | 
						|
		const char* value = (const char*)sqlite3_column_text(stmt,1);
 | 
						|
		os << sqlite3_column_text(stmt,0) << " ";
 | 
						|
		int len = 0;
 | 
						|
		if (value) {
 | 
						|
			len = strlen(value);
 | 
						|
		}
 | 
						|
		if (len && value) os << value << endl;
 | 
						|
		else os << "(disabled)" << endl;
 | 
						|
		src = sqlite3_run_query(mDB,stmt);
 | 
						|
	}
 | 
						|
	sqlite3_finalize(stmt);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
ConfigurationRecordMap ConfigurationTable::getAllPairs() const
 | 
						|
{
 | 
						|
	ConfigurationRecordMap tmp;
 | 
						|
 | 
						|
	// Prepare the statement.
 | 
						|
	string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG";
 | 
						|
	sqlite3_stmt *stmt;
 | 
						|
	if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return tmp;
 | 
						|
	// Read the result.
 | 
						|
	int src = sqlite3_run_query(mDB,stmt);
 | 
						|
	while (src==SQLITE_ROW) {
 | 
						|
		const char* key = (const char*)sqlite3_column_text(stmt,0);
 | 
						|
		const char* value = (const char*)sqlite3_column_text(stmt,1);
 | 
						|
		if (key && value) {
 | 
						|
			tmp[string(key)] = ConfigurationRecord(value);
 | 
						|
		} else if (key && !value) {
 | 
						|
			tmp[string(key)] = ConfigurationRecord(false);
 | 
						|
		}
 | 
						|
		src = sqlite3_run_query(mDB,stmt);
 | 
						|
	}
 | 
						|
	sqlite3_finalize(stmt);
 | 
						|
 | 
						|
	return tmp;
 | 
						|
}
 | 
						|
 | 
						|
bool ConfigurationTable::set(const string& key, const string& value)
 | 
						|
{
 | 
						|
	assert(mDB);
 | 
						|
	ScopedLock lock(mLock);
 | 
						|
	string cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",\"" + value + "\",1)";
 | 
						|
	bool success = sqlite3_command(mDB,cmd.c_str());
 | 
						|
	// Cache the result.
 | 
						|
	if (success) mCache[key] = ConfigurationRecord(value);
 | 
						|
	return success;
 | 
						|
}
 | 
						|
 | 
						|
bool ConfigurationTable::set(const string& key, long value)
 | 
						|
{
 | 
						|
	char buffer[30];
 | 
						|
	sprintf(buffer,"%ld",value);
 | 
						|
	return set(key,buffer);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool ConfigurationTable::set(const string& key)
 | 
						|
{
 | 
						|
	assert(mDB);
 | 
						|
	ScopedLock lock(mLock);
 | 
						|
	string cmd = "INSERT OR REPLACE INTO CONFIG (KEYSTRING,VALUESTRING,OPTIONAL) VALUES (\"" + key + "\",NULL,1)";
 | 
						|
	bool success = sqlite3_command(mDB,cmd.c_str());
 | 
						|
	if (success) mCache[key] = ConfigurationRecord(true);
 | 
						|
	return success;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ConfigurationTable::checkCacheAge()
 | 
						|
{
 | 
						|
	// mLock is set by caller 
 | 
						|
	static time_t timeOfLastPurge = 0;
 | 
						|
	time_t now = time(NULL);
 | 
						|
	// purge every 3 seconds
 | 
						|
	// purge period cannot be configuration parameter
 | 
						|
	if (now - timeOfLastPurge < 3) return;
 | 
						|
	timeOfLastPurge = now;
 | 
						|
	// this is purge() without the lock
 | 
						|
	ConfigurationMap::iterator mp = mCache.begin();
 | 
						|
	while (mp != mCache.end()) {
 | 
						|
		ConfigurationMap::iterator prev = mp;
 | 
						|
		mp++;
 | 
						|
		mCache.erase(prev);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ConfigurationTable::purge()
 | 
						|
{
 | 
						|
	ScopedLock lock(mLock);
 | 
						|
	ConfigurationMap::iterator mp = mCache.begin();
 | 
						|
	while (mp != mCache.end()) {
 | 
						|
		ConfigurationMap::iterator prev = mp;
 | 
						|
		mp++;
 | 
						|
		mCache.erase(prev);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ConfigurationTable::setUpdateHook(void(*func)(void *,int ,char const *,char const *,sqlite3_int64))
 | 
						|
{
 | 
						|
	assert(mDB);
 | 
						|
	sqlite3_update_hook(mDB,func,NULL);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ConfigurationTable::setCrossCheckHook(vector<string> (*wCrossCheck)(const string&))
 | 
						|
{
 | 
						|
	mCrossCheck = wCrossCheck;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
vector<string> ConfigurationTable::crossCheck(const string& key) {
 | 
						|
	vector<string> ret;
 | 
						|
 | 
						|
	if (mCrossCheck != NULL) {
 | 
						|
		ret = mCrossCheck(key);
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void HashString::computeHash()
 | 
						|
{
 | 
						|
	// FIXME -- Someone needs to review this hash function.
 | 
						|
	const char* cstr = c_str();
 | 
						|
	mHash = 0;
 | 
						|
	for (unsigned i=0; i<size(); i++) {
 | 
						|
		mHash = mHash ^ (mHash >> 32);
 | 
						|
		mHash = mHash*127 + cstr[i];
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void SimpleKeyValue::addItem(const char* pair_orig)
 | 
						|
{
 | 
						|
	char *pair = strdup(pair_orig);
 | 
						|
	char *key = pair;
 | 
						|
	char *mark = strchr(pair,'=');
 | 
						|
	if (!mark) return;
 | 
						|
	*mark = '\0';
 | 
						|
	char *value = mark+1;
 | 
						|
	mMap[key] = value;
 | 
						|
	free(pair);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
const char* SimpleKeyValue::get(const char* key) const
 | 
						|
{
 | 
						|
	HashStringMap::const_iterator p = mMap.find(key);
 | 
						|
	if (p==mMap.end()) return NULL;
 | 
						|
	return p->second.c_str();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void SimpleKeyValue::addItems(const char* pairs_orig)
 | 
						|
{
 | 
						|
	char *pairs = strdup(pairs_orig);
 | 
						|
	char *thisPair;
 | 
						|
	while ((thisPair=strsep(&pairs," "))!=NULL) {
 | 
						|
		addItem(thisPair);
 | 
						|
	}
 | 
						|
	free(pairs);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool ConfigurationKey::isValidIP(const std::string& ip) {
 | 
						|
	struct sockaddr_in sa;
 | 
						|
	return inet_pton(AF_INET, ip.c_str(), &(sa.sin_addr)) != 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void ConfigurationKey::getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping) {
 | 
						|
	uint delimiter;
 | 
						|
	int startPos;
 | 
						|
	uint endPos;
 | 
						|
 | 
						|
	std::string tmp = key.getValidValues();
 | 
						|
	stepping = "1";
 | 
						|
 | 
						|
	// grab steps if they're defined
 | 
						|
	startPos = tmp.find('(');
 | 
						|
	if (startPos != (int)std::string::npos) {
 | 
						|
		endPos = tmp.find(')');
 | 
						|
		stepping = tmp.substr(startPos+1, endPos-startPos-1);
 | 
						|
		tmp = tmp.substr(0, startPos);
 | 
						|
	}
 | 
						|
	startPos = 0;
 | 
						|
 | 
						|
	delimiter = tmp.find(':', startPos);
 | 
						|
	min = tmp.substr(startPos, delimiter-startPos);
 | 
						|
	max = tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
template<class T> bool ConfigurationKey::isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger) {
 | 
						|
	bool ret = false;
 | 
						|
 | 
						|
	T convVal;
 | 
						|
	T min;
 | 
						|
	T max;
 | 
						|
	T steps;
 | 
						|
	std::string strMin;
 | 
						|
	std::string strMax;
 | 
						|
	std::string strSteps;
 | 
						|
 | 
						|
	std::stringstream(val) >> convVal;
 | 
						|
 | 
						|
	ConfigurationKey::getMinMaxStepping(key, strMin, strMax, strSteps);
 | 
						|
	std::stringstream(strMin) >> min;
 | 
						|
	std::stringstream(strMax) >> max;
 | 
						|
	std::stringstream(strSteps) >> steps;
 | 
						|
 | 
						|
	// TODO : only ranges checked, steps not enforced
 | 
						|
	if (isInteger) {
 | 
						|
		if (val.find('.') == std::string::npos && min <= convVal && convVal <= max) {
 | 
						|
			ret = true;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if (min <= convVal && convVal <= max) {
 | 
						|
			ret = true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
const std::string ConfigurationKey::getARFCNsString() {
 | 
						|
	stringstream ss;
 | 
						|
	int i;
 | 
						|
	float downlink;
 | 
						|
	float uplink;
 | 
						|
 | 
						|
	// 128:251 GSM850
 | 
						|
	downlink = 869.2;
 | 
						|
	uplink = 824.2;
 | 
						|
	for (i = 128; i <= 251; i++) {
 | 
						|
		ss << i << "|GSM850 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
 | 
						|
		downlink += 0.2;
 | 
						|
		uplink += 0.2;
 | 
						|
	}
 | 
						|
 | 
						|
	// 1:124 PGSM900
 | 
						|
	downlink = 935.2;
 | 
						|
	uplink = 890.2;
 | 
						|
	for (i = 1; i <= 124; i++) {
 | 
						|
		ss << i << "|PGSM900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
 | 
						|
		downlink += 0.2;
 | 
						|
		uplink += 0.2;
 | 
						|
	}
 | 
						|
 | 
						|
	// 512:885 DCS1800
 | 
						|
	downlink = 1805.2;
 | 
						|
	uplink = 1710.2;
 | 
						|
	for (i = 512; i <= 885; i++) {
 | 
						|
		ss << i << "|DCS1800 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
 | 
						|
		downlink += 0.2;
 | 
						|
		uplink += 0.2;
 | 
						|
	}
 | 
						|
 | 
						|
	// 512:810 PCS1900
 | 
						|
	downlink = 1930.2;
 | 
						|
	uplink = 1850.2;
 | 
						|
	for (i = 512; i <= 810; i++) {
 | 
						|
		ss << i << "|PCS1900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
 | 
						|
		downlink += 0.2;
 | 
						|
		uplink += 0.2;
 | 
						|
	}
 | 
						|
 | 
						|
	ss << endl;
 | 
						|
 | 
						|
	return ss.str();
 | 
						|
}
 | 
						|
 | 
						|
const std::string ConfigurationKey::visibilityLevelToString(const ConfigurationKey::VisibilityLevel& visibility) {
 | 
						|
	std::string ret = "UNKNOWN ERROR";
 | 
						|
 | 
						|
	switch (visibility) {
 | 
						|
		case ConfigurationKey::CUSTOMER:
 | 
						|
			ret = "customer - can be freely changed by the customer without any detriment to their system";
 | 
						|
			break;
 | 
						|
		case ConfigurationKey::CUSTOMERSITE:
 | 
						|
			ret = "customer site - these values are different for each BTS and should not be left default";
 | 
						|
			break;
 | 
						|
		case ConfigurationKey::CUSTOMERTUNE:
 | 
						|
			ret = "customer tune - should only be changed to tune an installation to better suit the physical environment or MS usage pattern";
 | 
						|
			break;
 | 
						|
		case ConfigurationKey::CUSTOMERWARN:
 | 
						|
			ret = "customer warn - a warning will be presented and confirmation required before changing this sensitive setting";
 | 
						|
			break;
 | 
						|
		case ConfigurationKey::DEVELOPER:
 | 
						|
			ret = "developer - should only be changed by developers to debug/optimize the implementation";
 | 
						|
			break;
 | 
						|
		case ConfigurationKey::FACTORY:
 | 
						|
			ret = "factory - set once at the factory, should never be changed";
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
const std::string ConfigurationKey::typeToString(const ConfigurationKey::Type& type) {
 | 
						|
	std::string ret = "UNKNOWN ERROR";
 | 
						|
 | 
						|
	switch (type) {
 | 
						|
		case BOOLEAN:
 | 
						|
			ret = "boolean";
 | 
						|
			break;
 | 
						|
		case CHOICE_OPT:
 | 
						|
			ret = "multiple choice (optional)";
 | 
						|
			break;
 | 
						|
		case CHOICE:
 | 
						|
			ret = "multiple choice";
 | 
						|
			break;
 | 
						|
		case CIDR_OPT:
 | 
						|
			ret = "CIDR notation (optional)";
 | 
						|
			break;
 | 
						|
		case CIDR:
 | 
						|
			ret = "CIDR notation";
 | 
						|
			break;
 | 
						|
		case FILEPATH_OPT:
 | 
						|
			ret = "file path (optional)";
 | 
						|
			break;
 | 
						|
		case FILEPATH:
 | 
						|
			ret = "file path";
 | 
						|
			break;
 | 
						|
		case IPADDRESS_OPT:
 | 
						|
			ret = "IP address (optional)";
 | 
						|
			break;
 | 
						|
		case IPADDRESS:
 | 
						|
			ret = "IP address";
 | 
						|
			break;
 | 
						|
		case IPANDPORT:
 | 
						|
			ret = "IP address and port";
 | 
						|
			break;
 | 
						|
		case MIPADDRESS_OPT:
 | 
						|
			ret = "space-separated list of IP addresses (optional)";
 | 
						|
			break;
 | 
						|
		case MIPADDRESS:
 | 
						|
			ret = "space-separated list of IP addresses";
 | 
						|
			break;
 | 
						|
		case PORT_OPT:
 | 
						|
			ret = "IP port (optional)";
 | 
						|
			break;
 | 
						|
		case PORT:
 | 
						|
			ret = "IP port";
 | 
						|
			break;
 | 
						|
		case REGEX_OPT:
 | 
						|
			ret = "regular expression (optional)";
 | 
						|
			break;
 | 
						|
		case REGEX:
 | 
						|
			ret = "regular expression";
 | 
						|
			break;
 | 
						|
		case STRING_OPT:
 | 
						|
			ret = "string (optional)";
 | 
						|
			break;
 | 
						|
		case STRING:
 | 
						|
			ret = "string";
 | 
						|
			break;
 | 
						|
		case VALRANGE:
 | 
						|
			ret = "value range";
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void ConfigurationKey::printKey(const ConfigurationKey &key, const std::string& currentValue, ostream& os) {
 | 
						|
	os << key.getName() << " ";
 | 
						|
	if (!currentValue.length()) {
 | 
						|
		os << "(disabled)";
 | 
						|
	} else {
 | 
						|
		os << currentValue;
 | 
						|
	}
 | 
						|
	if (currentValue.compare(key.getDefaultValue()) == 0) {
 | 
						|
		os << "     [default]";
 | 
						|
	}
 | 
						|
	os << endl;
 | 
						|
}
 | 
						|
 | 
						|
void ConfigurationKey::printDescription(const ConfigurationKey &key, ostream& os) {
 | 
						|
	std::string tmp;
 | 
						|
 | 
						|
	os << " - description:      " << key.getDescription() << std::endl;
 | 
						|
	if (key.getUnits().length()) {
 | 
						|
		os << " - units:            " << key.getUnits() << std::endl;
 | 
						|
	}
 | 
						|
	os << " - type:             " << ConfigurationKey::typeToString(key.getType()) << std::endl;
 | 
						|
	if (key.getDefaultValue().length()) {
 | 
						|
		os << " - default value:    " << key.getDefaultValue() << std::endl;
 | 
						|
	}
 | 
						|
	os << " - visibility level: " << ConfigurationKey::visibilityLevelToString(key.getVisibility()) << std::endl;
 | 
						|
	os << " - static:           " << key.isStatic() << std::endl;
 | 
						|
 | 
						|
	tmp = key.getValidValues();
 | 
						|
	if (key.getType() == ConfigurationKey::VALRANGE) {
 | 
						|
		int startPos = tmp.find('(');
 | 
						|
		uint delimiter = 0;
 | 
						|
		if (startPos != (int)std::string::npos) {
 | 
						|
			tmp = tmp.substr(0, startPos);
 | 
						|
		}
 | 
						|
		startPos = -1;
 | 
						|
 | 
						|
		do {
 | 
						|
			startPos++;
 | 
						|
			delimiter = tmp.find(':', startPos);
 | 
						|
			os << " - valid values:     " << "from " << tmp.substr(startPos, delimiter-startPos) << " to "
 | 
						|
				<< tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1) << std::endl;
 | 
						|
 | 
						|
		} while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
 | 
						|
 | 
						|
	} else if (key.getType() == ConfigurationKey::CHOICE) {
 | 
						|
		int startPos = -1;
 | 
						|
		uint endPos = 0;
 | 
						|
 | 
						|
		do {
 | 
						|
			startPos++;
 | 
						|
			if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
 | 
						|
				os << " - valid values:     " << tmp.substr(startPos, endPos-startPos);
 | 
						|
				os << " = " << tmp.substr(endPos+1, tmp.find(',', endPos)-endPos-1) << std::endl;
 | 
						|
			} else {
 | 
						|
				os << " - valid values:     " << tmp.substr(startPos, tmp.find(',', startPos)-startPos) << std::endl;
 | 
						|
			}
 | 
						|
 | 
						|
		} while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
 | 
						|
 | 
						|
	} else if (key.getType() == ConfigurationKey::BOOLEAN) {
 | 
						|
		os << " - valid values:     0 = disabled" << std::endl;
 | 
						|
		os << " - valid values:     1 = enabled" << std::endl;
 | 
						|
 | 
						|
	} else if (key.getType() == ConfigurationKey::STRING) {
 | 
						|
		os << " - valid val regex:  " << tmp << std::endl;
 | 
						|
 | 
						|
	} else if (key.getValidValues().length()) {
 | 
						|
		os << " - raw valid values: " << tmp << std::endl;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// vim: ts=4 sw=4
 |