1 /*
2 Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <ndb_global.h>
26 #include <ndb_version.h>
27
28 #include "InitConfigFileParser.hpp"
29 #include "Config.hpp"
30 #include <NdbOut.hpp>
31 #include "ConfigInfo.hpp"
32 #include "EventLogger.hpp"
33 #include <m_string.h>
34 #include <util/SparseBitmask.hpp>
35 #include "../common/util/parse_mask.hpp"
36
37 extern EventLogger *g_eventLogger;
38
39 const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file
40 static void trim(char *);
41
42 //****************************************************************************
43 // Ctor / Dtor
44 //****************************************************************************
InitConfigFileParser()45 InitConfigFileParser::InitConfigFileParser()
46 {
47 m_info = new ConfigInfo();
48 }
49
~InitConfigFileParser()50 InitConfigFileParser::~InitConfigFileParser() {
51 delete m_info;
52 }
53
54 //****************************************************************************
55 // Read Config File
56 //****************************************************************************
Context(const ConfigInfo * info)57 InitConfigFileParser::Context::Context(const ConfigInfo * info)
58 : m_userProperties(true), m_configValues(1000, 20) {
59
60 m_config = new Properties(true);
61 m_defaults = new Properties(true);
62 }
63
~Context()64 InitConfigFileParser::Context::~Context(){
65 if(m_config != 0)
66 delete m_config;
67
68 if(m_defaults != 0)
69 delete m_defaults;
70 }
71
72 Config *
parseConfig(const char * filename)73 InitConfigFileParser::parseConfig(const char * filename) {
74 FILE * file = fopen(filename, "r");
75 if(file == 0){
76 g_eventLogger->error("Error opening '%s', error: %d, %s", filename, errno, strerror(errno));
77 return 0;
78 }
79
80 Config * ret = parseConfig(file);
81 fclose(file);
82 return ret;
83 }
84
85 Config *
parseConfig(FILE * file)86 InitConfigFileParser::parseConfig(FILE * file) {
87
88 char line[MAX_LINE_LENGTH];
89
90 Context ctx(m_info);
91 ctx.m_lineno = 0;
92 ctx.m_currentSection = 0;
93
94 /*************
95 * Open file *
96 *************/
97 if (file == NULL) {
98 return 0;
99 }
100
101 /***********************
102 * While lines to read *
103 ***********************/
104 while (fgets(line, MAX_LINE_LENGTH, file)) {
105 ctx.m_lineno++;
106
107 trim(line);
108
109 if (isEmptyLine(line)) // Skip if line is empty or comment
110 continue;
111
112 // End with NULL instead of newline
113 if (line[strlen(line)-1] == '\n')
114 line[strlen(line)-1] = '\0';
115
116 /********************************
117 * 1. Parse new default section *
118 ********************************/
119 if (char* section = parseDefaultSectionHeader(line)) {
120 if(!storeSection(ctx)){
121 free(section);
122 ctx.reportError("Could not store previous default section "
123 "of configuration file.");
124 return 0;
125 }
126 BaseString::snprintf(ctx.fname, sizeof(ctx.fname), "%s", section);
127 free(section);
128 ctx.type = InitConfigFileParser::DefaultSection;
129 ctx.m_sectionLineno = ctx.m_lineno;
130 ctx.m_currentSection = new Properties(true);
131 ctx.m_userDefaults = NULL;
132 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
133 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
134 continue;
135 }
136
137 /************************
138 * 2. Parse new section *
139 ************************/
140 if (char* section = parseSectionHeader(line)) {
141 if(!storeSection(ctx)){
142 free(section);
143 ctx.reportError("Could not store previous section "
144 "of configuration file.");
145 return 0;
146 }
147 BaseString::snprintf(ctx.fname, sizeof(ctx.fname), "%s", section);
148 free(section);
149 ctx.type = InitConfigFileParser::Section;
150 ctx.m_sectionLineno = ctx.m_lineno;
151 ctx.m_currentSection = new Properties(true);
152 ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
153 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
154 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
155 continue;
156 }
157
158 /****************************
159 * 3. Parse name-value pair *
160 ****************************/
161 if (!parseNameValuePair(ctx, line)) {
162 ctx.reportError("Could not parse name-value pair in config file.");
163 return 0;
164 }
165 }
166
167 if (ferror(file)){
168 ctx.reportError("Failure in reading");
169 return 0;
170 }
171
172 if(!storeSection(ctx)) {
173 ctx.reportError("Could not store section of configuration file.");
174 return 0;
175 }
176
177 return run_config_rules(ctx);
178 }
179
180 Config*
run_config_rules(Context & ctx)181 InitConfigFileParser::run_config_rules(Context& ctx)
182 {
183 for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){
184 ctx.type = InitConfigFileParser::Undefined;
185 ctx.m_info = m_info;
186 ctx.m_currentSection = 0;
187 ctx.m_userDefaults = 0;
188 ctx.m_currentInfo = 0;
189 ctx.m_systemDefaults = 0;
190
191 Vector<ConfigInfo::ConfigRuleSection> tmp;
192 if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx,
193 ConfigInfo::m_ConfigRules[i].m_ruleData))
194 return 0;
195
196 for(size_t j = 0; j<tmp.size(); j++){
197 BaseString::snprintf(ctx.fname, sizeof(ctx.fname),
198 "%s", tmp[j].m_sectionType.c_str());
199 ctx.type = InitConfigFileParser::Section;
200 ctx.m_currentSection = tmp[j].m_sectionData;
201 ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
202 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
203 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
204 if(!storeSection(ctx))
205 return 0;
206 }
207 }
208
209 Uint32 nConnections = 0;
210 Uint32 nComputers = 0;
211 Uint32 nNodes = 0;
212 Uint32 nExtConnections = 0;
213 const char * system = "?";
214 ctx.m_userProperties.get("NoOfConnections", &nConnections);
215 ctx.m_userProperties.get("NoOfComputers", &nComputers);
216 ctx.m_userProperties.get("NoOfNodes", &nNodes);
217 ctx.m_userProperties.get("ExtNoOfConnections", &nExtConnections);
218 ctx.m_userProperties.get("ExtSystem", &system);
219 ctx.m_config->put("NoOfConnections", nConnections);
220 ctx.m_config->put("NoOfComputers", nComputers);
221 ctx.m_config->put("NoOfNodes", nNodes);
222
223 char tmpLine[MAX_LINE_LENGTH];
224 BaseString::snprintf(tmpLine, MAX_LINE_LENGTH,
225 "EXTERNAL SYSTEM_%s:NoOfConnections", system);
226 ctx.m_config->put(tmpLine, nExtConnections);
227
228 return new Config(ctx.m_configValues.getConfigValues());
229 }
230
231 //****************************************************************************
232 // Parse Name-Value Pair
233 //****************************************************************************
234
parseNameValuePair(Context & ctx,const char * line)235 bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line)
236 {
237 if (ctx.m_currentSection == NULL){
238 ctx.reportError("Value specified outside section");
239 return false;
240 }
241
242 // *************************************
243 // Split string at first occurrence of
244 // '=' or ':'
245 // *************************************
246
247 Vector<BaseString> tmp_string_split;
248 if (BaseString(line).split(tmp_string_split,
249 "=:", 2) != 2)
250 {
251 ctx.reportError("Parse error");
252 return false;
253 }
254
255 // *************************************
256 // Remove all after #
257 // *************************************
258
259 Vector<BaseString> tmp_string_split2;
260 tmp_string_split[1].split(tmp_string_split2,
261 "#", 2);
262 tmp_string_split[1]=tmp_string_split2[0];
263
264 // *************************************
265 // Remove leading and trailing chars
266 // *************************************
267 {
268 for (int i = 0; i < 2; i++)
269 tmp_string_split[i].trim("\r\n \t");
270 }
271
272 return storeNameValuePair(ctx,
273 tmp_string_split[0].c_str(), // fname
274 tmp_string_split[1].c_str()); // value
275 }
276
277
278 bool
storeNameValuePair(Context & ctx,const char * fname,const char * value)279 InitConfigFileParser::storeNameValuePair(Context& ctx,
280 const char* fname,
281 const char* value)
282 {
283
284 if (ctx.m_currentSection->contains(fname))
285 {
286 ctx.reportError("[%s] Parameter %s specified twice", ctx.fname, fname);
287 return false;
288 }
289
290 if (!ctx.m_currentInfo->contains(fname))
291 {
292 ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname);
293 return false;
294 }
295
296 ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname);
297 if (status == ConfigInfo::CI_NOTIMPLEMENTED) {
298 ctx.reportWarning("[%s] %s not yet implemented", ctx.fname, fname);
299 }
300 if (status == ConfigInfo::CI_DEPRECATED) {
301 const char * desc = m_info->getDescription(ctx.m_currentInfo, fname);
302 if(desc && desc[0]){
303 ctx.reportWarning("[%s] %s is deprecated, use %s instead",
304 ctx.fname, fname, desc);
305 } else if (desc == 0){
306 ctx.reportWarning("[%s] %s is deprecated", ctx.fname, fname);
307 }
308 }
309
310 const ConfigInfo::Type type = m_info->getType(ctx.m_currentInfo, fname);
311 switch(type){
312 case ConfigInfo::CI_BOOL: {
313 bool value_bool;
314 if (!convertStringToBool(value, value_bool)) {
315 ctx.reportError("Illegal boolean value for parameter %s", fname);
316 return false;
317 }
318 require(ctx.m_currentSection->put(fname, value_bool));
319 break;
320 }
321 case ConfigInfo::CI_INT:
322 case ConfigInfo::CI_INT64:{
323 Uint64 value_int;
324 if (!convertStringToUint64(value, value_int)) {
325 ctx.reportError("Illegal integer value for parameter %s", fname);
326 return false;
327 }
328 if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) {
329 ctx.reportError("Illegal value %s for parameter %s.\n"
330 "Legal values are between %llu and %llu", value, fname,
331 m_info->getMin(ctx.m_currentInfo, fname),
332 m_info->getMax(ctx.m_currentInfo, fname));
333 return false;
334 }
335 if(type == ConfigInfo::CI_INT){
336 require(ctx.m_currentSection->put(fname, (Uint32)value_int));
337 } else {
338 require(ctx.m_currentSection->put64(fname, value_int));
339 }
340 break;
341 }
342 case ConfigInfo::CI_STRING:
343 require(ctx.m_currentSection->put(fname, value));
344 break;
345
346 case ConfigInfo::CI_ENUM:{
347 Uint32 value_int;
348 if (!m_info->verify_enum(ctx.m_currentInfo, fname, value, value_int)) {
349 BaseString values;
350 m_info->get_enum_values(ctx.m_currentInfo, fname, values);
351 ctx.reportError("Illegal value '%s' for parameter %s. "
352 "Legal values are: '%s'", value, fname,
353 values.c_str());
354 return false;
355 }
356 require(ctx.m_currentSection->put(fname, value_int));
357 break;
358 }
359
360 case ConfigInfo::CI_BITMASK:{
361 if (strlen(value) <= 0)
362 {
363 ctx.reportError("Illegal value '%s' for parameter %s. "
364 "Error: Zero length string",
365 value, fname);
366 return false;
367 }
368 Uint64 max = m_info->getMax(ctx.m_currentInfo, fname);
369 SparseBitmask mask((unsigned)max);
370 int res = parse_mask(value, mask);
371 if (res < 0)
372 {
373 BaseString desc("Unknown error.");
374 switch(res)
375 {
376 case -1:
377 desc.assign("Invalid syntax for bitmask");
378 break;
379 case -2:
380 desc.assfmt("Too large id used in bitmask, max is %llu", max);
381 break;
382 default:
383 break;
384 }
385
386 ctx.reportError("Illegal value '%s' for parameter %s. Error: %s",
387 value, fname, desc.c_str());
388 return false;
389 }
390 require(ctx.m_currentSection->put(fname, value));
391 break;
392 }
393
394 case ConfigInfo::CI_SECTION:
395 abort();
396 }
397 return true;
398 }
399
400 //****************************************************************************
401 // Is Empty Line
402 //****************************************************************************
403
isEmptyLine(const char * line) const404 bool InitConfigFileParser::isEmptyLine(const char* line) const {
405 int i;
406
407 // Check if it is a comment line
408 if (line[0] == '#') return true;
409
410 // Check if it is a line with only spaces
411 for (i = 0; i < MAX_LINE_LENGTH && line[i] != '\n' && line[i] != '\0'; i++) {
412 if (line[i] != ' ' && line[i] != '\t') return false;
413 }
414 return true;
415 }
416
417 //****************************************************************************
418 // Convert String to Int
419 //****************************************************************************
convertStringToUint64(const char * s,Uint64 & val,Uint32 log10base)420 bool InitConfigFileParser::convertStringToUint64(const char* s,
421 Uint64& val,
422 Uint32 log10base) {
423 if (s == NULL)
424 return false;
425 if (strlen(s) == 0)
426 return false;
427
428 errno = 0;
429 char* p;
430 Int64 v = strtoll(s, &p, log10base);
431 if (errno != 0)
432 return false;
433
434 long mul = 0;
435 if (p != &s[strlen(s)]){
436 char * tmp = strdup(p);
437 trim(tmp);
438 switch(tmp[0]){
439 case 'k':
440 case 'K':
441 mul = 10;
442 break;
443 case 'M':
444 mul = 20;
445 break;
446 case 'G':
447 mul = 30;
448 break;
449 default:
450 free(tmp);
451 return false;
452 }
453 free(tmp);
454 }
455
456 val = (v << mul);
457 return true;
458 }
459
convertStringToBool(const char * s,bool & val)460 bool InitConfigFileParser::convertStringToBool(const char* s, bool& val) {
461 if (s == NULL) return false;
462 if (strlen(s) == 0) return false;
463
464 if (!strcmp(s, "Y") || !strcmp(s, "y") ||
465 !strcmp(s, "Yes") || !strcmp(s, "YES") || !strcmp(s, "yes") ||
466 !strcmp(s, "True") || !strcmp(s, "TRUE") || !strcmp(s, "true") ||
467 !strcmp(s, "1")) {
468 val = true;
469 return true;
470 }
471
472 if (!strcmp(s, "N") || !strcmp(s, "n") ||
473 !strcmp(s, "No") || !strcmp(s, "NO") || !strcmp(s, "no") ||
474 !strcmp(s, "False") || !strcmp(s, "FALSE") || !strcmp(s, "false") ||
475 !strcmp(s, "0")) {
476 val = false;
477 return true;
478 }
479
480 return false; // Failure to convert
481 }
482
483 //****************************************************************************
484 // Parse Section Header
485 //****************************************************************************
486 static void
trim(char * str)487 trim(char * str){
488 int len = strlen(str);
489 for(len--;
490 (str[len] == '\r' || str[len] == '\n' ||
491 str[len] == ' ' || str[len] == '\t') &&
492 len > 0;
493 len--)
494 str[len] = 0;
495
496 int pos = 0;
497 while(str[pos] == ' ' || str[pos] == '\t')
498 pos++;
499
500 if(str[pos] == '\"' && str[len] == '\"') {
501 pos++;
502 str[len] = 0;
503 len--;
504 }
505
506 memmove(str, &str[pos], len - pos + 2);
507 }
508
509 char*
parseSectionHeader(const char * line) const510 InitConfigFileParser::parseSectionHeader(const char* line) const {
511 char * tmp = strdup(line);
512
513 if(tmp[0] != '['){
514 free(tmp);
515 return NULL;
516 }
517
518 if(tmp[strlen(tmp)-1] != ']'){
519 free(tmp);
520 return NULL;
521 }
522 tmp[strlen(tmp)-1] = 0;
523
524 tmp[0] = ' ';
525 trim(tmp);
526
527 // Get the correct header name if an alias
528 {
529 const char *tmp_alias= m_info->getAlias(tmp);
530 if (tmp_alias) {
531 free(tmp);
532 tmp= strdup(tmp_alias);
533 }
534 }
535
536 // Lookup token among sections
537 if(!m_info->isSection(tmp)) {
538 free(tmp);
539 return NULL;
540 }
541 if(m_info->getInfo(tmp)) return tmp;
542
543 free(tmp);
544 return NULL;
545 }
546
547 //****************************************************************************
548 // Parse Default Section Header
549 //****************************************************************************
550
551 char*
parseDefaultSectionHeader(const char * line) const552 InitConfigFileParser::parseDefaultSectionHeader(const char* line) const {
553 static char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH];
554
555 int no = sscanf(line, "[%120[A-Z_a-z] %120[A-Z_a-z]]", token1, token2);
556
557 // Not correct no of tokens
558 if (no != 2) return NULL;
559
560 // Not correct keyword at end
561 if (!strcasecmp(token2, "DEFAULT") == 0) return NULL;
562
563 const char *token1_alias= m_info->getAlias(token1);
564 if (token1_alias == 0)
565 token1_alias= token1;
566
567 if(m_info->getInfo(token1_alias)){
568 return strdup(token1_alias);
569 }
570
571 // Did not find section
572 return NULL;
573 }
574
575 const Properties *
getSection(const char * name,const Properties * src)576 InitConfigFileParser::getSection(const char * name, const Properties * src){
577 const Properties * p;
578 if(src && src->get(name, &p))
579 return p;
580
581 return 0;
582 }
583
584 //****************************************************************************
585 // STORE section
586 //****************************************************************************
587 bool
storeSection(Context & ctx)588 InitConfigFileParser::storeSection(Context& ctx){
589 if(ctx.m_currentSection == NULL)
590 return true;
591 for(int i = strlen(ctx.fname) - 1; i>=0; i--){
592 ctx.fname[i] = toupper(ctx.fname[i]);
593 }
594 BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "%s", ctx.fname);
595
596 char buf[255];
597 if(ctx.type == InitConfigFileParser::Section)
598 BaseString::snprintf(buf, sizeof(buf), "%s", ctx.fname);
599 if(ctx.type == InitConfigFileParser::DefaultSection)
600 BaseString::snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname);
601 BaseString::snprintf(ctx.fname, sizeof(ctx.fname), "%s", buf);
602
603 if(ctx.type == InitConfigFileParser::Section){
604 for(int i = 0; i<m_info->m_NoOfRules; i++){
605 const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i];
606 if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){
607 if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){
608 return false;
609 }
610 }
611 }
612 }
613 if(ctx.type == InitConfigFileParser::DefaultSection &&
614 !ctx.m_defaults->put(ctx.pname, ctx.m_currentSection))
615 {
616 ctx.reportError("Duplicate default section not allowed");
617 return false;
618 }
619 if(ctx.type == InitConfigFileParser::Section)
620 require(ctx.m_config->put(ctx.pname, ctx.m_currentSection));
621 delete ctx.m_currentSection; ctx.m_currentSection = NULL;
622 return true;
623 }
624
625 void
reportError(const char * fmt,...)626 InitConfigFileParser::Context::reportError(const char * fmt, ...){
627 va_list ap;
628 char buf[1000];
629
630 va_start(ap, fmt);
631 if (fmt != 0)
632 BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
633 va_end(ap);
634 g_eventLogger->error("at line %d: %s",
635 m_lineno, buf);
636
637 //m_currentSection->print();
638 }
639
640 void
reportWarning(const char * fmt,...)641 InitConfigFileParser::Context::reportWarning(const char * fmt, ...){
642 va_list ap;
643 char buf[1000];
644
645 va_start(ap, fmt);
646 if (fmt != 0)
647 BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap);
648 va_end(ap);
649 g_eventLogger->warning("at line %d: %s",
650 m_lineno, buf);
651 }
652
653 #include <my_sys.h>
654 #include <my_getopt.h>
655
656 static int order = 1;
657 static
658 my_bool
parse_mycnf_opt(int,const struct my_option * opt,char * value)659 parse_mycnf_opt(int, const struct my_option * opt, char * value)
660 {
661 long *app_type= (long*) &opt->app_type;
662 if(opt->comment)
663 (*app_type)++;
664 else
665 *app_type = order++;
666 return 0;
667 }
668
669 bool
store_in_properties(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name)670 InitConfigFileParser::store_in_properties(Vector<struct my_option>& options,
671 InitConfigFileParser::Context& ctx,
672 const char * name)
673 {
674 for(unsigned i = 0; i<options.size(); i++)
675 {
676 if (options[i].app_type == 0)
677 {
678 // Option not found in in my.cnf
679 continue;
680 }
681
682 const char* section = options[i].comment;
683 if (!section)
684 {
685 // Option which is not to be saved, like "ndbd", "ndbapi", "mysqld" etc.
686 continue;
687 }
688
689 if (strcmp(section, name) == 0)
690 {
691 const char* value = NULL;
692 char buf[32];
693 switch(options[i].var_type){
694 case GET_INT:
695 case GET_UINT:
696 BaseString::snprintf(buf, sizeof(buf), "%u",
697 *(Uint32*)options[i].value);
698 value = buf;
699 break;
700 case GET_ULL:
701 BaseString::snprintf(buf, sizeof(buf), "%llu",
702 *(Uint64*)options[i].value);
703 value = buf;
704 break;
705 case GET_STR:
706 value = *(char**)options[i].value;
707 break;
708 default:
709 abort();
710 }
711
712 const char* fname = options[i].name;
713 if (!storeNameValuePair(ctx, fname, value))
714 return false;
715 }
716 }
717 return true;
718 }
719
720 bool
handle_mycnf_defaults(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name)721 InitConfigFileParser::handle_mycnf_defaults(Vector<struct my_option>& options,
722 InitConfigFileParser::Context& ctx,
723 const char * name)
724 {
725 strcpy(ctx.fname, name);
726 ctx.type = InitConfigFileParser::DefaultSection;
727 ctx.m_currentSection = new Properties(true);
728 ctx.m_userDefaults = NULL;
729 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
730 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
731 if(store_in_properties(options, ctx, name))
732 return storeSection(ctx);
733 return false;
734 }
735
736 static
737 int
load_defaults(Vector<struct my_option> & options,const char * groups[])738 load_defaults(Vector<struct my_option>& options, const char* groups[])
739 {
740 int argc = 1;
741 const char * argv[] = { "ndb_mgmd", 0, 0, 0, 0 };
742 BaseString file;
743 BaseString extra_file;
744 BaseString group_suffix;
745
746 const char *save_file = my_defaults_file;
747 #if MYSQL_VERSION_ID >= 50508
748 const
749 #endif
750 char *save_extra_file = my_defaults_extra_file;
751 const char *save_group_suffix = my_defaults_group_suffix;
752
753 if (my_defaults_file)
754 {
755 file.assfmt("--defaults-file=%s", my_defaults_file);
756 argv[argc++] = file.c_str();
757 }
758
759 if (my_defaults_extra_file)
760 {
761 extra_file.assfmt("--defaults-extra-file=%s", my_defaults_extra_file);
762 argv[argc++] = extra_file.c_str();
763 }
764
765 if (my_defaults_group_suffix)
766 {
767 group_suffix.assfmt("--defaults-group-suffix=%s",
768 my_defaults_group_suffix);
769 argv[argc++] = group_suffix.c_str();
770 }
771
772 char ** tmp = (char**)argv;
773 int ret = load_defaults("my", groups, &argc, &tmp);
774
775 my_defaults_file = save_file;
776 my_defaults_extra_file = save_extra_file;
777 my_defaults_group_suffix = save_group_suffix;
778
779 if (ret == 0)
780 {
781 return handle_options(&argc, &tmp, options.getBase(), parse_mycnf_opt);
782 }
783
784 return ret;
785 }
786
787 bool
load_mycnf_groups(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name,const char * groups[])788 InitConfigFileParser::load_mycnf_groups(Vector<struct my_option> & options,
789 InitConfigFileParser::Context& ctx,
790 const char * name,
791 const char *groups[])
792 {
793 unsigned i;
794 Vector<struct my_option> copy;
795 for(i = 0; i<options.size(); i++)
796 {
797 if(options[i].comment && strcmp(options[i].comment, name) == 0)
798 {
799 options[i].app_type = 0;
800 copy.push_back(options[i]);
801 }
802 }
803
804 struct my_option end;
805 memset(&end, 0, sizeof(end));
806 copy.push_back(end);
807
808 if (load_defaults(copy, groups))
809 return false;
810
811 return store_in_properties(copy, ctx, name);
812 }
813
814 Config *
parse_mycnf()815 InitConfigFileParser::parse_mycnf()
816 {
817 int i;
818 Config * res = 0;
819 Vector<struct my_option> options;
820 for(i = 0; i<ConfigInfo::m_NoOfParams; i++)
821 {
822 {
823 struct my_option opt;
824 memset(&opt, 0, sizeof(opt));
825 const ConfigInfo::ParamInfo& param = ConfigInfo::m_ParamInfo[i];
826 switch(param._type){
827 case ConfigInfo::CI_BOOL:
828 opt.value = (uchar **)malloc(sizeof(int));
829 opt.var_type = GET_INT;
830 break;
831 case ConfigInfo::CI_INT:
832 opt.value = (uchar**)malloc(sizeof(uint));
833 opt.var_type = GET_UINT;
834 break;
835 case ConfigInfo::CI_INT64:
836 opt.value = (uchar**)malloc(sizeof(Uint64));
837 opt.var_type = GET_ULL;
838 break;
839 case ConfigInfo::CI_ENUM:
840 case ConfigInfo::CI_STRING:
841 case ConfigInfo::CI_BITMASK:
842 opt.value = (uchar**)malloc(sizeof(char *));
843 opt.var_type = GET_STR;
844 break;
845 default:
846 continue;
847 }
848 opt.name = param._fname;
849 opt.id = 256;
850 opt.app_type = 0;
851 opt.arg_type = REQUIRED_ARG;
852 opt.comment = param._section;
853 options.push_back(opt);
854 }
855 }
856
857 struct my_option *ndbd, *ndb_mgmd, *mysqld, *api;
858
859 /**
860 * Add ndbd, ndb_mgmd, api/mysqld
861 */
862 Uint32 idx = options.size();
863 {
864 struct my_option opt;
865 memset(&opt, 0, sizeof(opt));
866 opt.name = "ndbd";
867 opt.id = 256;
868 opt.value = (uchar**)malloc(sizeof(char*));
869 opt.var_type = GET_STR;
870 opt.arg_type = REQUIRED_ARG;
871 options.push_back(opt);
872
873 opt.name = "ndb_mgmd";
874 opt.id = 256;
875 opt.value = (uchar**)malloc(sizeof(char*));
876 opt.var_type = GET_STR;
877 opt.arg_type = REQUIRED_ARG;
878 options.push_back(opt);
879
880 opt.name = "mysqld";
881 opt.id = 256;
882 opt.value = (uchar**)malloc(sizeof(char*));
883 opt.var_type = GET_STR;
884 opt.arg_type = REQUIRED_ARG;
885 options.push_back(opt);
886
887 opt.name = "ndbapi";
888 opt.id = 256;
889 opt.value = (uchar**)malloc(sizeof(char*));
890 opt.var_type = GET_STR;
891 opt.arg_type = REQUIRED_ARG;
892 options.push_back(opt);
893
894 memset(&opt, 0, sizeof(opt));
895 options.push_back(opt);
896
897 ndbd = &options[idx];
898 ndb_mgmd = &options[idx+1];
899 mysqld = &options[idx+2];
900 api = &options[idx+3];
901 }
902
903 Context ctx(m_info);
904 const char *groups[]= { "cluster_config", 0 };
905 if (load_defaults(options, groups))
906 goto end;
907
908 ctx.m_lineno = 0;
909 if(!handle_mycnf_defaults(options, ctx, "DB"))
910 goto end;
911 if(!handle_mycnf_defaults(options, ctx, "API"))
912 goto end;
913 if(!handle_mycnf_defaults(options, ctx, "MGM"))
914 goto end;
915 if(!handle_mycnf_defaults(options, ctx, "TCP"))
916 goto end;
917 if(!handle_mycnf_defaults(options, ctx, "SHM"))
918 goto end;
919 if(!handle_mycnf_defaults(options, ctx, "SCI"))
920 goto end;
921
922 {
923 struct sect { struct my_option* src; const char * name; } sections[] =
924 {
925 { ndb_mgmd, "MGM" }
926 ,{ ndbd, "DB" }
927 ,{ mysqld, "API" }
928 ,{ api, "API" }
929 ,{ 0, 0 }, { 0, 0 }
930 };
931
932 for(i = 0; sections[i].src; i++)
933 {
934 for(int j = i + 1; sections[j].src; j++)
935 {
936 if (sections[j].src->app_type < sections[i].src->app_type)
937 {
938 sect swap = sections[i];
939 sections[i] = sections[j];
940 sections[j] = swap;
941 }
942 }
943 }
944
945 ctx.type = InitConfigFileParser::Section;
946 ctx.m_sectionLineno = ctx.m_lineno;
947 for(i = 0; sections[i].src; i++)
948 {
949 if (sections[i].src->app_type)
950 {
951 strcpy(ctx.fname, sections[i].name);
952 BaseString str(*(char**)sections[i].src->value);
953 Vector<BaseString> list;
954 str.split(list, ",");
955
956 const char * defaults_groups[] = { 0, 0, 0 };
957 for(unsigned j = 0; j<list.size(); j++)
958 {
959 // Remove leading and trailing spaces from hostname
960 list[j].trim();
961
962 BaseString group_idx;
963 BaseString group_host;
964 group_idx.assfmt("%s.%s.%d", groups[0],
965 sections[i].src->name, j + 1);
966 group_host.assfmt("%s.%s.%s", groups[0],
967 sections[i].src->name, list[j].c_str());
968 defaults_groups[0] = group_idx.c_str();
969 if(list[j].length())
970 defaults_groups[1] = group_host.c_str();
971 else
972 defaults_groups[1] = 0;
973
974 ctx.m_currentSection = new Properties(true);
975 ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
976 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
977 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname))!= 0);
978 if(!load_mycnf_groups(options, ctx, sections[i].name,
979 defaults_groups))
980 goto end;
981
982 // The [cluster_config] section in my.cnf specifies the hostname,
983 // but it can also be specified a second time in the nodes section
984 // make sure they match if specified in both places, else
985 // save the value from cluster_config section
986 //
987 // Example:
988 // [cluster_config]
989 // ndbd=hostname1
990 // ^^^^^^^^^
991 // [cluster_config.ndbd.1]
992 // HostName=hostname1
993 // ^^^^^^^^^
994 //
995 if (ctx.m_currentSection->contains("HostName"))
996 {
997 // HostName specified a second time, check that it matches
998 const char* host_name;
999 require(ctx.m_currentSection->get("HostName", &host_name));
1000 if (strcmp(host_name, list[j].c_str()))
1001 {
1002 ctx.reportError("Illegal value 'HostName=%s' specified for "
1003 "%s, previously set to '%s'",
1004 host_name, group_idx.c_str(),
1005 list[j].c_str());
1006 goto end;
1007 }
1008 }
1009 else
1010 {
1011 require(ctx.m_currentSection->put("HostName", list[j].c_str()));
1012 }
1013
1014 if(!storeSection(ctx))
1015 goto end;
1016 }
1017 }
1018 }
1019 }
1020
1021 res = run_config_rules(ctx);
1022
1023 end:
1024 for(i = 0; options[i].name; i++)
1025 free(options[i].value);
1026
1027 return res;
1028 }
1029
1030 template class Vector<struct my_option>;
1031
1032 /*
1033 See include/my_getopt.h for the declaration of struct my_option
1034 */
1035