summaryrefslogtreecommitdiff
path: root/ndb/src/mgmsrv/InitConfigFileParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ndb/src/mgmsrv/InitConfigFileParser.cpp')
-rw-r--r--ndb/src/mgmsrv/InitConfigFileParser.cpp601
1 files changed, 601 insertions, 0 deletions
diff --git a/ndb/src/mgmsrv/InitConfigFileParser.cpp b/ndb/src/mgmsrv/InitConfigFileParser.cpp
new file mode 100644
index 00000000000..fdfe7823fc2
--- /dev/null
+++ b/ndb/src/mgmsrv/InitConfigFileParser.cpp
@@ -0,0 +1,601 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <ndb_global.h>
+
+#include "InitConfigFileParser.hpp"
+#include "Config.hpp"
+#include "MgmtErrorReporter.hpp"
+#include <NdbOut.hpp>
+#include "ConfigInfo.hpp"
+
+const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file
+static void trim(char *);
+
+static void require(bool v) { if(!v) abort();}
+
+//****************************************************************************
+// Ctor / Dtor
+//****************************************************************************
+InitConfigFileParser::InitConfigFileParser(){
+ m_info = new ConfigInfo();
+}
+
+InitConfigFileParser::~InitConfigFileParser() {
+ delete m_info;
+}
+
+//****************************************************************************
+// Read Config File
+//****************************************************************************
+InitConfigFileParser::Context::Context(const ConfigInfo * info)
+ : m_userProperties(true), m_configValues(1000, 20) {
+
+ m_config = new Properties(true);
+ m_defaults = new Properties(true);
+}
+
+InitConfigFileParser::Context::~Context(){
+ if(m_config != 0)
+ delete m_config;
+
+ if(m_defaults != 0)
+ delete m_defaults;
+}
+
+Config *
+InitConfigFileParser::parseConfig(const char * filename) {
+ FILE * file = fopen(filename, "r");
+ if(file == 0){
+ ndbout << "Error opening file: " << filename << endl;
+ return 0;
+ }
+
+ Config * ret = parseConfig(file);
+ fclose(file);
+ return ret;
+}
+
+Config *
+InitConfigFileParser::parseConfig(FILE * file) {
+
+ char line[MAX_LINE_LENGTH];
+
+ Context ctx(m_info);
+ ctx.m_lineno = 0;
+ ctx.m_currentSection = 0;
+
+ /*************
+ * Open file *
+ *************/
+ if (file == NULL) {
+ return 0;
+ }
+
+ /***********************
+ * While lines to read *
+ ***********************/
+ while (fgets(line, MAX_LINE_LENGTH, file)) {
+ ctx.m_lineno++;
+
+ trim(line);
+
+ if (isEmptyLine(line)) // Skip if line is empty or comment
+ continue;
+
+ // End with NULL instead of newline
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ /********************************
+ * 1. Parse new default section *
+ ********************************/
+ if (char* section = parseDefaultSectionHeader(line)) {
+ if(!storeSection(ctx)){
+ free(section);
+ ctx.reportError("Could not store previous default section "
+ "of configuration file.");
+ return 0;
+ }
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section); free(section);
+ ctx.type = InitConfigFileParser::DefaultSection;
+ ctx.m_sectionLineno = ctx.m_lineno;
+ ctx.m_currentSection = new Properties(true);
+ ctx.m_userDefaults = NULL;
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ continue;
+ }
+
+ /************************
+ * 2. Parse new section *
+ ************************/
+ if (char* section = parseSectionHeader(line)) {
+ if(!storeSection(ctx)){
+ free(section);
+ ctx.reportError("Could not store previous section "
+ "of configuration file.");
+ return 0;
+ }
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section);
+ free(section);
+ ctx.type = InitConfigFileParser::Section;
+ ctx.m_sectionLineno = ctx.m_lineno;
+ ctx.m_currentSection = new Properties(true);
+ ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ continue;
+ }
+
+ /****************************
+ * 3. Parse name-value pair *
+ ****************************/
+ if (!parseNameValuePair(ctx, line)) {
+ ctx.reportError("Could not parse name-value pair in config file.");
+ return 0;
+ }
+ }
+
+ if (ferror(file)){
+ ctx.reportError("Failure in reading");
+ return 0;
+ }
+
+ if(!storeSection(ctx)) {
+ ctx.reportError("Could not store section of configuration file.");
+ return 0;
+ }
+ for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){
+ ctx.type = InitConfigFileParser::Undefined;
+ ctx.m_currentSection = 0;
+ ctx.m_userDefaults = 0;
+ ctx.m_currentInfo = 0;
+ ctx.m_systemDefaults = 0;
+
+ Vector<ConfigInfo::ConfigRuleSection> tmp;
+ if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx,
+ ConfigInfo::m_ConfigRules[i].m_ruleData))
+ return 0;
+
+ for(size_t j = 0; j<tmp.size(); j++){
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), tmp[j].m_sectionType.c_str());
+ ctx.type = InitConfigFileParser::Section;
+ ctx.m_currentSection = tmp[j].m_sectionData;
+ ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
+ require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
+ require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
+ if(!storeSection(ctx))
+ return 0;
+ }
+ }
+
+ Uint32 nConnections = 0;
+ Uint32 nComputers = 0;
+ Uint32 nNodes = 0;
+ Uint32 nExtConnections = 0;
+ const char * system = "?";
+ ctx.m_userProperties.get("NoOfConnections", &nConnections);
+ ctx.m_userProperties.get("NoOfComputers", &nComputers);
+ ctx.m_userProperties.get("NoOfNodes", &nNodes);
+ ctx.m_userProperties.get("ExtNoOfConnections", &nExtConnections);
+ ctx.m_userProperties.get("ExtSystem", &system);
+ ctx.m_config->put("NoOfConnections", nConnections);
+ ctx.m_config->put("NoOfComputers", nComputers);
+ ctx.m_config->put("NoOfNodes", nNodes);
+
+ char tmpLine[MAX_LINE_LENGTH];
+ BaseString::snprintf(tmpLine, MAX_LINE_LENGTH, "EXTERNAL SYSTEM_");
+ strncat(tmpLine, system, MAX_LINE_LENGTH);
+ strncat(tmpLine, ":NoOfConnections", MAX_LINE_LENGTH);
+ ctx.m_config->put(tmpLine, nExtConnections);
+
+ Config * ret = new Config();
+ ret->m_configValues = (struct ndb_mgm_configuration*)ctx.m_configValues.getConfigValues();
+ ret->m_oldConfig = ctx.m_config; ctx.m_config = 0;
+ return ret;
+}
+
+//****************************************************************************
+// Parse Name-Value Pair
+//****************************************************************************
+
+bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line) {
+
+ char tmpLine[MAX_LINE_LENGTH];
+ char fname[MAX_LINE_LENGTH], rest[MAX_LINE_LENGTH];
+ char* t;
+ const char *separator_list[]= {":", "=", 0};
+ const char *separator= 0;
+
+ if (ctx.m_currentSection == NULL){
+ ctx.reportError("Value specified outside section");
+ return false;
+ }
+
+ strncpy(tmpLine, line, MAX_LINE_LENGTH);
+
+ // *************************************
+ // Check if a separator exists in line
+ // *************************************
+ for(int i= 0; separator_list[i] != 0; i++) {
+ if(strchr(tmpLine, separator_list[i][0])) {
+ separator= separator_list[i];
+ break;
+ }
+ }
+
+ if (separator == 0) {
+ ctx.reportError("Parse error");
+ return false;
+ }
+
+ // *******************************************
+ // Get pointer to substring before separator
+ // *******************************************
+ t = strtok(tmpLine, separator);
+
+ // *****************************************
+ // Count number of tokens before separator
+ // *****************************************
+ if (sscanf(t, "%120s%120s", fname, rest) != 1) {
+ ctx.reportError("Multiple names before \'%c\'", separator[0]);
+ return false;
+ }
+ if (!ctx.m_currentInfo->contains(fname)) {
+ ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname);
+ return false;
+ }
+ ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname);
+ if (status == ConfigInfo::NOTIMPLEMENTED) {
+ ctx.reportWarning("[%s] %s not yet implemented", ctx.fname, fname);
+ }
+ if (status == ConfigInfo::DEPRICATED) {
+ const char * desc = m_info->getDescription(ctx.m_currentInfo, fname);
+ if(desc){
+ ctx.reportWarning("[%s] %s is depricated, use %s instead",
+ ctx.fname, fname, desc);
+ } else {
+ ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname);
+ }
+ }
+
+ // ******************************************
+ // Get pointer to substring after separator
+ // ******************************************
+ t = strtok(NULL, "\0");
+ if (t == NULL) {
+ ctx.reportError("No value for parameter");
+ return false;
+ }
+
+ // ******************************************
+ // Remove prefix and postfix spaces and tabs
+ // *******************************************
+ trim(t);
+
+ // ***********************
+ // Store name-value pair
+ // ***********************
+ return storeNameValuePair(ctx, fname, t);
+}
+
+
+//****************************************************************************
+// STORE NAME-VALUE pair in properties section
+//****************************************************************************
+
+bool
+InitConfigFileParser::storeNameValuePair(Context& ctx,
+ const char* fname,
+ const char* value) {
+
+ const char * pname = fname;
+
+ if (ctx.m_currentSection->contains(pname)) {
+ ctx.reportError("[%s] Parameter %s specified twice", ctx.fname, fname);
+ return false;
+ }
+
+ // ***********************
+ // Store name-value pair
+ // ***********************
+
+ const ConfigInfo::Type type = m_info->getType(ctx.m_currentInfo, fname);
+ switch(type){
+ case ConfigInfo::BOOL: {
+ bool value_bool;
+ if (!convertStringToBool(value, value_bool)) {
+ ctx.reportError("Illegal boolean value for parameter %s", fname);
+ return false;
+ }
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, value_bool));
+ break;
+ }
+ case ConfigInfo::INT:
+ case ConfigInfo::INT64:{
+ Uint64 value_int;
+ if (!convertStringToUint64(value, value_int)) {
+ ctx.reportError("Illegal integer value for parameter %s", fname);
+ return false;
+ }
+ if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) {
+ ctx.reportError("Illegal value %s for parameter %s.\n"
+ "Legal values are between %Lu and %Lu", value, fname,
+ m_info->getMin(ctx.m_currentInfo, fname),
+ m_info->getMax(ctx.m_currentInfo, fname));
+ return false;
+ }
+ if(type == ConfigInfo::INT){
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, (Uint32)value_int));
+ } else {
+ MGM_REQUIRE(ctx.m_currentSection->put64(pname, value_int));
+ }
+ break;
+ }
+ case ConfigInfo::STRING:
+ MGM_REQUIRE(ctx.m_currentSection->put(pname, value));
+ break;
+ case ConfigInfo::SECTION:
+ abort();
+ }
+ return true;
+}
+
+//****************************************************************************
+// Is Empty Line
+//****************************************************************************
+
+bool InitConfigFileParser::isEmptyLine(const char* line) const {
+ int i;
+
+ // Check if it is a comment line
+ if (line[0] == '#') return true;
+
+ // Check if it is a line with only spaces
+ for (i = 0; i < MAX_LINE_LENGTH && line[i] != '\n' && line[i] != '\0'; i++) {
+ if (line[i] != ' ' && line[i] != '\t') return false;
+ }
+ return true;
+}
+
+//****************************************************************************
+// Convert String to Int
+//****************************************************************************
+bool InitConfigFileParser::convertStringToUint64(const char* s,
+ Uint64& val,
+ Uint32 log10base) {
+ if (s == NULL)
+ return false;
+ if (strlen(s) == 0)
+ return false;
+
+ errno = 0;
+ char* p;
+ long long v = strtoll(s, &p, log10base);
+ if (errno != 0)
+ return false;
+
+ long mul = 0;
+ if (p != &s[strlen(s)]){
+ char * tmp = strdup(p);
+ trim(tmp);
+ switch(tmp[0]){
+ case 'k':
+ case 'K':
+ mul = 10;
+ break;
+ case 'M':
+ mul = 20;
+ break;
+ case 'G':
+ mul = 30;
+ break;
+ default:
+ free(tmp);
+ return false;
+ }
+ free(tmp);
+ }
+
+ val = (v << mul);
+ return true;
+}
+
+bool InitConfigFileParser::convertStringToBool(const char* s, bool& val) {
+ if (s == NULL) return false;
+ if (strlen(s) == 0) return false;
+
+ if (!strcmp(s, "Y") || !strcmp(s, "y") ||
+ !strcmp(s, "Yes") || !strcmp(s, "YES") || !strcmp(s, "yes") ||
+ !strcmp(s, "True") || !strcmp(s, "TRUE") || !strcmp(s, "true") ||
+ !strcmp(s, "1")) {
+ val = true;
+ return true;
+ }
+
+ if (!strcmp(s, "N") || !strcmp(s, "n") ||
+ !strcmp(s, "No") || !strcmp(s, "NO") || !strcmp(s, "no") ||
+ !strcmp(s, "False") || !strcmp(s, "FALSE") || !strcmp(s, "false") ||
+ !strcmp(s, "0")) {
+ val = false;
+ return true;
+ }
+
+ return false; // Failure to convert
+}
+
+//****************************************************************************
+// Parse Section Header
+//****************************************************************************
+static void
+trim(char * str){
+ int len = strlen(str);
+ for(len--;
+ (str[len] == '\r' || str[len] == '\n' ||
+ str[len] == ' ' || str[len] == '\t') &&
+ len > 0;
+ len--)
+ str[len] = 0;
+
+ int pos = 0;
+ while(str[pos] == ' ' || str[pos] == '\t')
+ pos++;
+
+ if(str[pos] == '\"' && str[len] == '\"') {
+ pos++;
+ str[len] = 0;
+ len--;
+ }
+
+ memmove(str, &str[pos], len - pos + 2);
+}
+
+char*
+InitConfigFileParser::parseSectionHeader(const char* line) const {
+ char * tmp = strdup(line);
+
+ if(tmp[0] != '['){
+ free(tmp);
+ return NULL;
+ }
+
+ if(tmp[strlen(tmp)-1] != ']'){
+ free(tmp);
+ return NULL;
+ }
+ tmp[strlen(tmp)-1] = 0;
+
+ tmp[0] = ' ';
+ trim(tmp);
+
+ // Get the correct header name if an alias
+ {
+ const char *tmp_alias= m_info->getAlias(tmp);
+ if (tmp_alias) {
+ free(tmp);
+ tmp= strdup(tmp_alias);
+ }
+ }
+
+ // Lookup token among sections
+ if(!m_info->isSection(tmp)) {
+ free(tmp);
+ return NULL;
+ }
+ if(m_info->getInfo(tmp)) return tmp;
+
+ free(tmp);
+ return NULL;
+}
+
+//****************************************************************************
+// Parse Default Section Header
+//****************************************************************************
+
+char*
+InitConfigFileParser::parseDefaultSectionHeader(const char* line) const {
+ static char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH];
+
+ int no = sscanf(line, "[%120[A-Z_a-z] %120[A-Z_a-z]]", token1, token2);
+
+ // Not correct no of tokens
+ if (no != 2) return NULL;
+
+ // Not correct keyword at end
+ if (!strcasecmp(token2, "DEFAULT") == 0) return NULL;
+
+ const char *token1_alias= m_info->getAlias(token1);
+ if (token1_alias == 0)
+ token1_alias= token1;
+
+ if(m_info->getInfo(token1_alias)){
+ return strdup(token1_alias);
+ }
+
+ // Did not find section
+ return NULL;
+}
+
+const Properties *
+InitConfigFileParser::getSection(const char * name, const Properties * src){
+ const Properties * p;
+ if(src && src->get(name, &p))
+ return p;
+
+ return 0;
+}
+
+//****************************************************************************
+// STORE section
+//****************************************************************************
+bool
+InitConfigFileParser::storeSection(Context& ctx){
+ if(ctx.m_currentSection == NULL)
+ return true;
+ for(int i = strlen(ctx.fname) - 1; i>=0; i--){
+ ctx.fname[i] = toupper(ctx.fname[i]);
+ }
+ BaseString::snprintf(ctx.pname, sizeof(ctx.pname), ctx.fname);
+ char buf[255];
+ if(ctx.type == InitConfigFileParser::Section)
+ BaseString::snprintf(buf, sizeof(buf), "%s", ctx.fname);
+ if(ctx.type == InitConfigFileParser::DefaultSection)
+ BaseString::snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname);
+ BaseString::snprintf(ctx.fname, sizeof(ctx.fname), buf);
+ if(ctx.type == InitConfigFileParser::Section){
+ for(int i = 0; i<m_info->m_NoOfRules; i++){
+ const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i];
+ if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){
+ if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){
+ return false;
+ }
+ }
+ }
+ }
+ if(ctx.type == InitConfigFileParser::DefaultSection)
+ require(ctx.m_defaults->put(ctx.pname, ctx.m_currentSection));
+ if(ctx.type == InitConfigFileParser::Section)
+ require(ctx.m_config->put(ctx.pname, ctx.m_currentSection));
+ delete ctx.m_currentSection; ctx.m_currentSection = NULL;
+ return true;
+}
+
+void
+InitConfigFileParser::Context::reportError(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << "Error line " << m_lineno << ": " << buf << endl;
+ va_end(ap);
+
+ //m_currentSection->print();
+}
+
+void
+InitConfigFileParser::Context::reportWarning(const char * fmt, ...){
+ va_list ap;
+ char buf[1000];
+
+ va_start(ap, fmt);
+ if (fmt != 0)
+ BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
+ ndbout << "Warning line " << m_lineno << ": " << buf << endl;
+ va_end(ap);
+}