1
/* Copyright (C) 2003 MySQL AB
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16
#include <ndb_global.h>
18
#include "InitConfigFileParser.hpp"
20
#include "MgmtErrorReporter.hpp"
22
#include "ConfigInfo.hpp"
25
const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file
26
static void trim(char *);
28
static void require(bool v) { if(!v) abort();}
30
//****************************************************************************
32
//****************************************************************************
33
InitConfigFileParser::InitConfigFileParser(FILE * out)
35
m_info = new ConfigInfo();
36
m_errstream = out ? out : stdout;
39
InitConfigFileParser::~InitConfigFileParser() {
43
//****************************************************************************
45
//****************************************************************************
46
InitConfigFileParser::Context::Context(const ConfigInfo * info, FILE * out)
47
: m_userProperties(true), m_configValues(1000, 20) {
49
m_config = new Properties(true);
50
m_defaults = new Properties(true);
54
InitConfigFileParser::Context::~Context(){
63
InitConfigFileParser::parseConfig(const char * filename) {
64
FILE * file = fopen(filename, "r");
66
fprintf(m_errstream, "Error opening file: %s\n", filename);
70
Config * ret = parseConfig(file);
76
InitConfigFileParser::parseConfig(FILE * file) {
78
char line[MAX_LINE_LENGTH];
80
Context ctx(m_info, m_errstream);
82
ctx.m_currentSection = 0;
91
/***********************
92
* While lines to read *
93
***********************/
94
while (fgets(line, MAX_LINE_LENGTH, file)) {
99
if (isEmptyLine(line)) // Skip if line is empty or comment
102
// End with NULL instead of newline
103
if (line[strlen(line)-1] == '\n')
104
line[strlen(line)-1] = '\0';
106
/********************************
107
* 1. Parse new default section *
108
********************************/
109
if (char* section = parseDefaultSectionHeader(line)) {
110
if(!storeSection(ctx)){
112
ctx.reportError("Could not store previous default section "
113
"of configuration file.");
116
BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section); free(section);
117
ctx.type = InitConfigFileParser::DefaultSection;
118
ctx.m_sectionLineno = ctx.m_lineno;
119
ctx.m_currentSection = new Properties(true);
120
ctx.m_userDefaults = NULL;
121
require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
122
require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
126
/************************
127
* 2. Parse new section *
128
************************/
129
if (char* section = parseSectionHeader(line)) {
130
if(!storeSection(ctx)){
132
ctx.reportError("Could not store previous section "
133
"of configuration file.");
136
BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section);
138
ctx.type = InitConfigFileParser::Section;
139
ctx.m_sectionLineno = ctx.m_lineno;
140
ctx.m_currentSection = new Properties(true);
141
ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
142
require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
143
require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
147
/****************************
148
* 3. Parse name-value pair *
149
****************************/
150
if (!parseNameValuePair(ctx, line)) {
151
ctx.reportError("Could not parse name-value pair in config file.");
157
ctx.reportError("Failure in reading");
161
if(!storeSection(ctx)) {
162
ctx.reportError("Could not store section of configuration file.");
166
return run_config_rules(ctx);
170
InitConfigFileParser::run_config_rules(Context& ctx)
172
for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){
173
ctx.type = InitConfigFileParser::Undefined;
174
ctx.m_currentSection = 0;
175
ctx.m_userDefaults = 0;
176
ctx.m_currentInfo = 0;
177
ctx.m_systemDefaults = 0;
179
Vector<ConfigInfo::ConfigRuleSection> tmp;
180
if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx,
181
ConfigInfo::m_ConfigRules[i].m_ruleData))
184
for(size_t j = 0; j<tmp.size(); j++){
185
BaseString::snprintf(ctx.fname, sizeof(ctx.fname), tmp[j].m_sectionType.c_str());
186
ctx.type = InitConfigFileParser::Section;
187
ctx.m_currentSection = tmp[j].m_sectionData;
188
ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
189
require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
190
require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
191
if(!storeSection(ctx))
196
Uint32 nConnections = 0;
197
Uint32 nComputers = 0;
199
Uint32 nExtConnections = 0;
200
const char * system = "?";
201
ctx.m_userProperties.get("NoOfConnections", &nConnections);
202
ctx.m_userProperties.get("NoOfComputers", &nComputers);
203
ctx.m_userProperties.get("NoOfNodes", &nNodes);
204
ctx.m_userProperties.get("ExtNoOfConnections", &nExtConnections);
205
ctx.m_userProperties.get("ExtSystem", &system);
206
ctx.m_config->put("NoOfConnections", nConnections);
207
ctx.m_config->put("NoOfComputers", nComputers);
208
ctx.m_config->put("NoOfNodes", nNodes);
210
char tmpLine[MAX_LINE_LENGTH];
211
BaseString::snprintf(tmpLine, MAX_LINE_LENGTH,
212
"EXTERNAL SYSTEM_%s:NoOfConnections", system);
213
ctx.m_config->put(tmpLine, nExtConnections);
215
Config * ret = new Config();
216
ret->m_configValues = (struct ndb_mgm_configuration*)ctx.m_configValues.getConfigValues();
217
ret->m_oldConfig = ctx.m_config; ctx.m_config = 0;
221
//****************************************************************************
222
// Parse Name-Value Pair
223
//****************************************************************************
225
bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line)
227
if (ctx.m_currentSection == NULL){
228
ctx.reportError("Value specified outside section");
232
// *************************************
233
// Split string at first occurrence of
235
// *************************************
237
Vector<BaseString> tmp_string_split;
238
if (BaseString(line).split(tmp_string_split,
241
ctx.reportError("Parse error");
245
// *************************************
246
// Remove all after #
247
// *************************************
249
Vector<BaseString> tmp_string_split2;
250
tmp_string_split[1].split(tmp_string_split2,
252
tmp_string_split[1]=tmp_string_split2[0];
254
// *************************************
255
// Remove leading and trailing chars
256
// *************************************
258
for (int i = 0; i < 2; i++)
259
tmp_string_split[i].trim("\r\n \t");
262
// *************************************
263
// First in split is fname
264
// *************************************
266
const char *fname= tmp_string_split[0].c_str();
268
if (!ctx.m_currentInfo->contains(fname)) {
269
ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname);
272
ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname);
273
if (status == ConfigInfo::CI_NOTIMPLEMENTED) {
274
ctx.reportWarning("[%s] %s not yet implemented", ctx.fname, fname);
276
if (status == ConfigInfo::CI_DEPRICATED) {
277
const char * desc = m_info->getDescription(ctx.m_currentInfo, fname);
279
ctx.reportWarning("[%s] %s is depricated, use %s instead",
280
ctx.fname, fname, desc);
281
} else if (desc == 0){
282
ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname);
286
// ***********************
287
// Store name-value pair
288
// ***********************
290
return storeNameValuePair(ctx, fname, tmp_string_split[1].c_str());
294
//****************************************************************************
295
// STORE NAME-VALUE pair in properties section
296
//****************************************************************************
299
InitConfigFileParser::storeNameValuePair(Context& ctx,
303
const char * pname = fname;
305
if (ctx.m_currentSection->contains(pname)) {
306
ctx.reportError("[%s] Parameter %s specified twice", ctx.fname, fname);
310
// ***********************
311
// Store name-value pair
312
// ***********************
314
const ConfigInfo::Type type = m_info->getType(ctx.m_currentInfo, fname);
316
case ConfigInfo::CI_BOOL: {
318
if (!convertStringToBool(value, value_bool)) {
319
ctx.reportError("Illegal boolean value for parameter %s", fname);
322
MGM_REQUIRE(ctx.m_currentSection->put(pname, value_bool));
325
case ConfigInfo::CI_INT:
326
case ConfigInfo::CI_INT64:{
328
if (!convertStringToUint64(value, value_int)) {
329
ctx.reportError("Illegal integer value for parameter %s", fname);
332
if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) {
333
ctx.reportError("Illegal value %s for parameter %s.\n"
334
"Legal values are between %Lu and %Lu", value, fname,
335
m_info->getMin(ctx.m_currentInfo, fname),
336
m_info->getMax(ctx.m_currentInfo, fname));
339
if(type == ConfigInfo::CI_INT){
340
MGM_REQUIRE(ctx.m_currentSection->put(pname, (Uint32)value_int));
342
MGM_REQUIRE(ctx.m_currentSection->put64(pname, value_int));
346
case ConfigInfo::CI_STRING:
347
MGM_REQUIRE(ctx.m_currentSection->put(pname, value));
349
case ConfigInfo::CI_SECTION:
355
//****************************************************************************
357
//****************************************************************************
359
bool InitConfigFileParser::isEmptyLine(const char* line) const {
362
// Check if it is a comment line
363
if (line[0] == '#') return true;
365
// Check if it is a line with only spaces
366
for (i = 0; i < MAX_LINE_LENGTH && line[i] != '\n' && line[i] != '\0'; i++) {
367
if (line[i] != ' ' && line[i] != '\t') return false;
372
//****************************************************************************
373
// Convert String to Int
374
//****************************************************************************
375
bool InitConfigFileParser::convertStringToUint64(const char* s,
385
Int64 v = strtoll(s, &p, log10base);
390
if (p != &s[strlen(s)]){
391
char * tmp = strdup(p);
415
bool InitConfigFileParser::convertStringToBool(const char* s, bool& val) {
416
if (s == NULL) return false;
417
if (strlen(s) == 0) return false;
419
if (!strcmp(s, "Y") || !strcmp(s, "y") ||
420
!strcmp(s, "Yes") || !strcmp(s, "YES") || !strcmp(s, "yes") ||
421
!strcmp(s, "True") || !strcmp(s, "TRUE") || !strcmp(s, "true") ||
427
if (!strcmp(s, "N") || !strcmp(s, "n") ||
428
!strcmp(s, "No") || !strcmp(s, "NO") || !strcmp(s, "no") ||
429
!strcmp(s, "False") || !strcmp(s, "FALSE") || !strcmp(s, "false") ||
435
return false; // Failure to convert
438
//****************************************************************************
439
// Parse Section Header
440
//****************************************************************************
443
int len = strlen(str);
445
(str[len] == '\r' || str[len] == '\n' ||
446
str[len] == ' ' || str[len] == '\t') &&
452
while(str[pos] == ' ' || str[pos] == '\t')
455
if(str[pos] == '\"' && str[len] == '\"') {
461
memmove(str, &str[pos], len - pos + 2);
465
InitConfigFileParser::parseSectionHeader(const char* line) const {
466
char * tmp = strdup(line);
473
if(tmp[strlen(tmp)-1] != ']'){
477
tmp[strlen(tmp)-1] = 0;
482
// Get the correct header name if an alias
484
const char *tmp_alias= m_info->getAlias(tmp);
487
tmp= strdup(tmp_alias);
491
// Lookup token among sections
492
if(!m_info->isSection(tmp)) {
496
if(m_info->getInfo(tmp)) return tmp;
502
//****************************************************************************
503
// Parse Default Section Header
504
//****************************************************************************
507
InitConfigFileParser::parseDefaultSectionHeader(const char* line) const {
508
static char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH];
510
int no = sscanf(line, "[%120[A-Z_a-z] %120[A-Z_a-z]]", token1, token2);
512
// Not correct no of tokens
513
if (no != 2) return NULL;
515
// Not correct keyword at end
516
if (!strcasecmp(token2, "DEFAULT") == 0) return NULL;
518
const char *token1_alias= m_info->getAlias(token1);
519
if (token1_alias == 0)
520
token1_alias= token1;
522
if(m_info->getInfo(token1_alias)){
523
return strdup(token1_alias);
526
// Did not find section
531
InitConfigFileParser::getSection(const char * name, const Properties * src){
532
const Properties * p;
533
if(src && src->get(name, &p))
539
//****************************************************************************
541
//****************************************************************************
543
InitConfigFileParser::storeSection(Context& ctx){
544
if(ctx.m_currentSection == NULL)
546
for(int i = strlen(ctx.fname) - 1; i>=0; i--){
547
ctx.fname[i] = toupper(ctx.fname[i]);
549
BaseString::snprintf(ctx.pname, sizeof(ctx.pname), ctx.fname);
551
if(ctx.type == InitConfigFileParser::Section)
552
BaseString::snprintf(buf, sizeof(buf), "%s", ctx.fname);
553
if(ctx.type == InitConfigFileParser::DefaultSection)
554
BaseString::snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname);
555
BaseString::snprintf(ctx.fname, sizeof(ctx.fname), buf);
556
if(ctx.type == InitConfigFileParser::Section){
557
for(int i = 0; i<m_info->m_NoOfRules; i++){
558
const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i];
559
if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){
560
if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){
566
if(ctx.type == InitConfigFileParser::DefaultSection &&
567
!ctx.m_defaults->put(ctx.pname, ctx.m_currentSection))
569
ctx.reportError("Duplicate default section not allowed");
572
if(ctx.type == InitConfigFileParser::Section)
573
require(ctx.m_config->put(ctx.pname, ctx.m_currentSection));
574
delete ctx.m_currentSection; ctx.m_currentSection = NULL;
579
InitConfigFileParser::Context::reportError(const char * fmt, ...){
585
BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
587
fprintf(m_errstream, "Error line %d: %s\n",
590
//m_currentSection->print();
594
InitConfigFileParser::Context::reportWarning(const char * fmt, ...){
600
BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
602
fprintf(m_errstream, "Warning line %d: %s\n",
607
#include <my_getopt.h>
609
static int order = 1;
612
parse_mycnf_opt(int, const struct my_option * opt, char * value)
614
long *app_type= (long*) &opt->app_type;
623
InitConfigFileParser::store_in_properties(Vector<struct my_option>& options,
624
InitConfigFileParser::Context& ctx,
627
for(unsigned i = 0; i<options.size(); i++)
629
if(options[i].comment &&
630
options[i].app_type &&
631
strcmp(options[i].comment, name) == 0)
634
switch(options[i].var_type){
636
value_int = *(Uint32*)options[i].value;
639
value_int = *(Uint64*)options[i].value;
642
ctx.m_currentSection->put(options[i].name, *(char**)options[i].value);
648
const char * fname = options[i].name;
649
if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) {
650
ctx.reportError("Illegal value %lld for parameter %s.\n"
651
"Legal values are between %Lu and %Lu",
653
m_info->getMin(ctx.m_currentInfo, fname),
654
m_info->getMax(ctx.m_currentInfo, fname));
658
ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname);
659
if (status == ConfigInfo::CI_DEPRICATED) {
660
const char * desc = m_info->getDescription(ctx.m_currentInfo, fname);
662
ctx.reportWarning("[%s] %s is depricated, use %s instead",
663
ctx.fname, fname, desc);
664
} else if (desc == 0){
665
ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname);
669
if (options[i].var_type == GET_INT)
670
ctx.m_currentSection->put(options[i].name, (Uint32)value_int);
672
ctx.m_currentSection->put64(options[i].name, value_int);
679
InitConfigFileParser::handle_mycnf_defaults(Vector<struct my_option>& options,
680
InitConfigFileParser::Context& ctx,
683
strcpy(ctx.fname, name);
684
ctx.type = InitConfigFileParser::DefaultSection;
685
ctx.m_currentSection = new Properties(true);
686
ctx.m_userDefaults = NULL;
687
require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
688
require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
689
if(store_in_properties(options, ctx, name))
690
return storeSection(ctx);
696
load_defaults(Vector<struct my_option>& options, const char* groups[])
699
const char * argv[] = { "ndb_mgmd", 0, 0, 0, 0 };
701
BaseString extra_file;
702
BaseString group_suffix;
704
const char *save_file = my_defaults_file;
705
char *save_extra_file = my_defaults_extra_file;
706
const char *save_group_suffix = my_defaults_group_suffix;
708
if (my_defaults_file)
710
file.assfmt("--defaults-file=%s", my_defaults_file);
711
argv[argc++] = file.c_str();
714
if (my_defaults_extra_file)
716
extra_file.assfmt("--defaults-extra-file=%s", my_defaults_extra_file);
717
argv[argc++] = extra_file.c_str();
720
if (my_defaults_group_suffix)
722
group_suffix.assfmt("--defaults-group-suffix=%s",
723
my_defaults_group_suffix);
724
argv[argc++] = group_suffix.c_str();
727
char ** tmp = (char**)argv;
728
int ret = load_defaults("my", groups, &argc, &tmp);
730
my_defaults_file = save_file;
731
my_defaults_extra_file = save_extra_file;
732
my_defaults_group_suffix = save_group_suffix;
736
return handle_options(&argc, &tmp, options.getBase(), parse_mycnf_opt);
743
InitConfigFileParser::load_mycnf_groups(Vector<struct my_option> & options,
744
InitConfigFileParser::Context& ctx,
746
const char *groups[])
749
Vector<struct my_option> copy;
750
for(i = 0; i<options.size(); i++)
752
if(options[i].comment && strcmp(options[i].comment, name) == 0)
754
options[i].app_type = 0;
755
copy.push_back(options[i]);
759
struct my_option end;
760
bzero(&end, sizeof(end));
763
if (load_defaults(copy, groups))
766
return store_in_properties(copy, ctx, name);
770
InitConfigFileParser::parse_mycnf()
774
Vector<struct my_option> options;
775
for(i = 0; i<ConfigInfo::m_NoOfParams; i++)
778
struct my_option opt;
779
bzero(&opt, sizeof(opt));
780
const ConfigInfo::ParamInfo& param = ConfigInfo::m_ParamInfo[i];
782
case ConfigInfo::CI_BOOL:
783
opt.value = (uchar **)malloc(sizeof(int));
784
opt.var_type = GET_INT;
786
case ConfigInfo::CI_INT:
787
opt.value = (uchar**)malloc(sizeof(int));
788
opt.var_type = GET_INT;
790
case ConfigInfo::CI_INT64:
791
opt.value = (uchar**)malloc(sizeof(Int64));
792
opt.var_type = GET_LL;
794
case ConfigInfo::CI_STRING:
795
opt.value = (uchar**)malloc(sizeof(char *));
796
opt.var_type = GET_STR;
801
opt.name = param._fname;
804
opt.arg_type = REQUIRED_ARG;
805
opt.comment = param._section;
806
options.push_back(opt);
810
struct my_option *ndbd, *ndb_mgmd, *mysqld, *api;
813
* Add ndbd, ndb_mgmd, api/mysqld
815
Uint32 idx = options.size();
817
struct my_option opt;
818
bzero(&opt, sizeof(opt));
821
opt.value = (uchar**)malloc(sizeof(char*));
822
opt.var_type = GET_STR;
823
opt.arg_type = REQUIRED_ARG;
824
options.push_back(opt);
826
opt.name = "ndb_mgmd";
828
opt.value = (uchar**)malloc(sizeof(char*));
829
opt.var_type = GET_STR;
830
opt.arg_type = REQUIRED_ARG;
831
options.push_back(opt);
835
opt.value = (uchar**)malloc(sizeof(char*));
836
opt.var_type = GET_STR;
837
opt.arg_type = REQUIRED_ARG;
838
options.push_back(opt);
842
opt.value = (uchar**)malloc(sizeof(char*));
843
opt.var_type = GET_STR;
844
opt.arg_type = REQUIRED_ARG;
845
options.push_back(opt);
847
bzero(&opt, sizeof(opt));
848
options.push_back(opt);
850
ndbd = &options[idx];
851
ndb_mgmd = &options[idx+1];
852
mysqld = &options[idx+2];
853
api = &options[idx+3];
856
Context ctx(m_info, m_errstream);
857
const char *groups[]= { "cluster_config", 0 };
858
if (load_defaults(options, groups))
862
if(!handle_mycnf_defaults(options, ctx, "DB"))
864
if(!handle_mycnf_defaults(options, ctx, "API"))
866
if(!handle_mycnf_defaults(options, ctx, "MGM"))
868
if(!handle_mycnf_defaults(options, ctx, "TCP"))
870
if(!handle_mycnf_defaults(options, ctx, "SHM"))
872
if(!handle_mycnf_defaults(options, ctx, "SCI"))
876
struct sect { struct my_option* src; const char * name; } sections[] =
885
for(i = 0; sections[i].src; i++)
887
for(int j = i + 1; sections[j].src; j++)
889
if (sections[j].src->app_type < sections[i].src->app_type)
891
sect swap = sections[i];
892
sections[i] = sections[j];
898
ctx.type = InitConfigFileParser::Section;
899
ctx.m_sectionLineno = ctx.m_lineno;
900
for(i = 0; sections[i].src; i++)
902
if (sections[i].src->app_type)
904
strcpy(ctx.fname, sections[i].name);
905
BaseString str(*(char**)sections[i].src->value);
906
Vector<BaseString> list;
907
str.split(list, ",");
909
const char * defaults_groups[] = { 0, 0, 0 };
910
for(unsigned j = 0; j<list.size(); j++)
912
BaseString group_idx;
913
BaseString group_host;
914
group_idx.assfmt("%s.%s.%d", groups[0],
915
sections[i].src->name, j + 1);
916
group_host.assfmt("%s.%s.%s", groups[0],
917
sections[i].src->name, list[j].c_str());
918
defaults_groups[0] = group_idx.c_str();
920
defaults_groups[1] = group_host.c_str();
922
defaults_groups[1] = 0;
924
ctx.m_currentSection = new Properties(true);
925
ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
926
require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
927
require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname))!= 0);
928
ctx.m_currentSection->put("HostName", list[j].c_str());
929
if(!load_mycnf_groups(options, ctx, sections[i].name,
933
if(!storeSection(ctx))
940
res = run_config_rules(ctx);
943
for(i = 0; options[i].name; i++)
944
free(options[i].value);
949
template class Vector<struct my_option>;
952
See include/my_getopt.h for the declaration of struct my_option