1 /*
2 Copyright (c) 2003, 2021, Oracle and/or its affiliates.
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(unsigned 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 = my_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 = (int)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 (!native_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 = (int)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 #ifdef HAVE_MY_DEFAULT_H
656 #include <my_default.h>
657 #endif
658
659 static int order = 1;
660 static
661 my_bool
parse_mycnf_opt(int,const struct my_option * opt,char * value)662 parse_mycnf_opt(int, const struct my_option * opt, char * value)
663 {
664 long *app_type= (long*) &opt->app_type;
665 if(opt->comment)
666 (*app_type)++;
667 else
668 *app_type = order++;
669 return 0;
670 }
671
672 bool
store_in_properties(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name)673 InitConfigFileParser::store_in_properties(Vector<struct my_option>& options,
674 InitConfigFileParser::Context& ctx,
675 const char * name)
676 {
677 for(unsigned i = 0; i<options.size(); i++)
678 {
679 if (options[i].app_type == 0)
680 {
681 // Option not found in in my.cnf
682 continue;
683 }
684
685 const char* section = options[i].comment;
686 if (!section)
687 {
688 // Option which is not to be saved, like "ndbd", "ndbapi", "mysqld" etc.
689 continue;
690 }
691
692 if (strcmp(section, name) == 0)
693 {
694 const char* value = NULL;
695 char buf[32];
696 switch(options[i].var_type){
697 case GET_INT:
698 case GET_UINT:
699 BaseString::snprintf(buf, sizeof(buf), "%u",
700 *(Uint32*)options[i].value);
701 value = buf;
702 break;
703 case GET_ULL:
704 BaseString::snprintf(buf, sizeof(buf), "%llu",
705 *(Uint64*)options[i].value);
706 value = buf;
707 break;
708 case GET_STR:
709 value = *(char**)options[i].value;
710 break;
711 default:
712 abort();
713 }
714
715 const char* fname = options[i].name;
716 if (!storeNameValuePair(ctx, fname, value))
717 return false;
718 }
719 }
720 return true;
721 }
722
723 bool
handle_mycnf_defaults(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name)724 InitConfigFileParser::handle_mycnf_defaults(Vector<struct my_option>& options,
725 InitConfigFileParser::Context& ctx,
726 const char * name)
727 {
728 strcpy(ctx.fname, name);
729 ctx.type = InitConfigFileParser::DefaultSection;
730 ctx.m_currentSection = new Properties(true);
731 ctx.m_userDefaults = NULL;
732 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
733 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0);
734 if(store_in_properties(options, ctx, name))
735 return storeSection(ctx);
736 return false;
737 }
738
739 static
740 int
load_defaults(Vector<struct my_option> & options,const char * groups[])741 load_defaults(Vector<struct my_option>& options, const char* groups[])
742 {
743 int argc = 1;
744 const char * argv[] = { "ndb_mgmd", 0, 0, 0, 0 };
745 BaseString file;
746 BaseString extra_file;
747 BaseString group_suffix;
748
749 const char *save_file = my_defaults_file;
750 #if MYSQL_VERSION_ID >= 50508
751 const
752 #endif
753 char *save_extra_file = my_defaults_extra_file;
754 const char *save_group_suffix = my_defaults_group_suffix;
755
756 if (my_defaults_file)
757 {
758 file.assfmt("--defaults-file=%s", my_defaults_file);
759 argv[argc++] = file.c_str();
760 }
761
762 if (my_defaults_extra_file)
763 {
764 extra_file.assfmt("--defaults-extra-file=%s", my_defaults_extra_file);
765 argv[argc++] = extra_file.c_str();
766 }
767
768 if (my_defaults_group_suffix)
769 {
770 group_suffix.assfmt("--defaults-group-suffix=%s",
771 my_defaults_group_suffix);
772 argv[argc++] = group_suffix.c_str();
773 }
774
775 char ** tmp = (char**)argv;
776 int ret = load_defaults("my", groups, &argc, &tmp);
777
778 my_defaults_file = save_file;
779 my_defaults_extra_file = save_extra_file;
780 my_defaults_group_suffix = save_group_suffix;
781
782 if (ret == 0)
783 {
784 return handle_options(&argc, &tmp, options.getBase(), parse_mycnf_opt);
785 }
786
787 return ret;
788 }
789
790 bool
load_mycnf_groups(Vector<struct my_option> & options,InitConfigFileParser::Context & ctx,const char * name,const char * groups[])791 InitConfigFileParser::load_mycnf_groups(Vector<struct my_option> & options,
792 InitConfigFileParser::Context& ctx,
793 const char * name,
794 const char *groups[])
795 {
796 unsigned i;
797 Vector<struct my_option> copy;
798 for(i = 0; i<options.size(); i++)
799 {
800 if(options[i].comment && strcmp(options[i].comment, name) == 0)
801 {
802 options[i].app_type = 0;
803 copy.push_back(options[i]);
804 }
805 }
806
807 struct my_option end;
808 memset(&end, 0, sizeof(end));
809 copy.push_back(end);
810
811 if (load_defaults(copy, groups))
812 return false;
813
814 return store_in_properties(copy, ctx, name);
815 }
816
817 Config *
parse_mycnf()818 InitConfigFileParser::parse_mycnf()
819 {
820 Config * res = 0;
821 Vector<struct my_option> options;
822 for(int i = 0 ; i < ConfigInfo::m_NoOfParams ; ++ i)
823 {
824 {
825 struct my_option opt;
826 memset(&opt, 0, sizeof(opt));
827 const ConfigInfo::ParamInfo& param = ConfigInfo::m_ParamInfo[i];
828 switch(param._type){
829 case ConfigInfo::CI_BOOL:
830 opt.value = (uchar **)malloc(sizeof(int));
831 opt.var_type = GET_INT;
832 break;
833 case ConfigInfo::CI_INT:
834 opt.value = (uchar**)malloc(sizeof(uint));
835 opt.var_type = GET_UINT;
836 break;
837 case ConfigInfo::CI_INT64:
838 opt.value = (uchar**)malloc(sizeof(Uint64));
839 opt.var_type = GET_ULL;
840 break;
841 case ConfigInfo::CI_ENUM:
842 case ConfigInfo::CI_STRING:
843 case ConfigInfo::CI_BITMASK:
844 opt.value = (uchar**)malloc(sizeof(char *));
845 opt.var_type = GET_STR;
846 break;
847 default:
848 continue;
849 }
850 opt.name = param._fname;
851 opt.id = 256;
852 opt.app_type = 0;
853 opt.arg_type = REQUIRED_ARG;
854 opt.comment = param._section;
855 options.push_back(opt);
856 }
857 }
858
859 struct my_option *ndbd, *ndb_mgmd, *mysqld, *api;
860
861 /**
862 * Add ndbd, ndb_mgmd, api/mysqld
863 */
864 Uint32 idx = options.size();
865 {
866 struct my_option opt;
867 memset(&opt, 0, sizeof(opt));
868 opt.name = "ndbd";
869 opt.id = 256;
870 opt.value = (uchar**)malloc(sizeof(char*));
871 opt.var_type = GET_STR;
872 opt.arg_type = REQUIRED_ARG;
873 options.push_back(opt);
874
875 opt.name = "ndb_mgmd";
876 opt.id = 256;
877 opt.value = (uchar**)malloc(sizeof(char*));
878 opt.var_type = GET_STR;
879 opt.arg_type = REQUIRED_ARG;
880 options.push_back(opt);
881
882 opt.name = "mysqld";
883 opt.id = 256;
884 opt.value = (uchar**)malloc(sizeof(char*));
885 opt.var_type = GET_STR;
886 opt.arg_type = REQUIRED_ARG;
887 options.push_back(opt);
888
889 opt.name = "ndbapi";
890 opt.id = 256;
891 opt.value = (uchar**)malloc(sizeof(char*));
892 opt.var_type = GET_STR;
893 opt.arg_type = REQUIRED_ARG;
894 options.push_back(opt);
895
896 memset(&opt, 0, sizeof(opt));
897 options.push_back(opt);
898
899 ndbd = &options[idx];
900 ndb_mgmd = &options[idx+1];
901 mysqld = &options[idx+2];
902 api = &options[idx+3];
903 }
904
905 Context ctx(m_info);
906 const char *groups[]= { "cluster_config", 0 };
907 if (load_defaults(options, groups))
908 goto end;
909
910 ctx.m_lineno = 0;
911 if(!handle_mycnf_defaults(options, ctx, "DB"))
912 goto end;
913 if(!handle_mycnf_defaults(options, ctx, "API"))
914 goto end;
915 if(!handle_mycnf_defaults(options, ctx, "MGM"))
916 goto end;
917 if(!handle_mycnf_defaults(options, ctx, "TCP"))
918 goto end;
919 if(!handle_mycnf_defaults(options, ctx, "SHM"))
920 goto end;
921 if(!handle_mycnf_defaults(options, ctx, "SCI"))
922 goto end;
923
924 {
925 struct sect { struct my_option* src; const char * name; } sections[] =
926 {
927 { ndb_mgmd, "MGM" },
928 { ndbd, "DB" },
929 { mysqld, "API" },
930 { api, "API" }
931 };
932
933 for(unsigned i = 0; i + 1 < NDB_ARRAY_SIZE(sections) ; i++)
934 {
935 for(unsigned j = i + 1; j < NDB_ARRAY_SIZE(sections) ; j++)
936 {
937 if (sections[j].src->app_type < sections[i].src->app_type)
938 {
939 sect swap = sections[i];
940 sections[i] = sections[j];
941 sections[j] = swap;
942 }
943 }
944 }
945
946 ctx.type = InitConfigFileParser::Section;
947 ctx.m_sectionLineno = ctx.m_lineno;
948 for(unsigned i = 0; i < NDB_ARRAY_SIZE(sections) ; i++)
949 {
950 if (sections[i].src->app_type)
951 {
952 strcpy(ctx.fname, sections[i].name);
953 BaseString str(*(char**)sections[i].src->value);
954 Vector<BaseString> list;
955 str.split(list, ",");
956
957 const char * defaults_groups[] = { 0, 0, 0 };
958 for(unsigned j = 0; j<list.size(); j++)
959 {
960 // Remove leading and trailing spaces from hostname
961 list[j].trim();
962
963 BaseString group_idx;
964 BaseString group_host;
965 group_idx.assfmt("%s.%s.%d", groups[0],
966 sections[i].src->name, j + 1);
967 group_host.assfmt("%s.%s.%s", groups[0],
968 sections[i].src->name, list[j].c_str());
969 defaults_groups[0] = group_idx.c_str();
970 if(list[j].length())
971 defaults_groups[1] = group_host.c_str();
972 else
973 defaults_groups[1] = 0;
974
975 ctx.m_currentSection = new Properties(true);
976 ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults);
977 require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0);
978 require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname))!= 0);
979 if(!load_mycnf_groups(options, ctx, sections[i].name,
980 defaults_groups))
981 goto end;
982
983 // The [cluster_config] section in my.cnf specifies the hostname,
984 // but it can also be specified a second time in the nodes section
985 // make sure they match if specified in both places, else
986 // save the value from cluster_config section
987 //
988 // Example:
989 // [cluster_config]
990 // ndbd=hostname1
991 // ^^^^^^^^^
992 // [cluster_config.ndbd.1]
993 // HostName=hostname1
994 // ^^^^^^^^^
995 //
996 if (ctx.m_currentSection->contains("HostName"))
997 {
998 // HostName specified a second time, check that it matches
999 const char* host_name;
1000 require(ctx.m_currentSection->get("HostName", &host_name));
1001 if (strcmp(host_name, list[j].c_str()))
1002 {
1003 ctx.reportError("Illegal value 'HostName=%s' specified for "
1004 "%s, previously set to '%s'",
1005 host_name, group_idx.c_str(),
1006 list[j].c_str());
1007 goto end;
1008 }
1009 }
1010 else
1011 {
1012 require(ctx.m_currentSection->put("HostName", list[j].c_str()));
1013 }
1014
1015 if(!storeSection(ctx))
1016 goto end;
1017 }
1018 }
1019 }
1020 }
1021
1022 res = run_config_rules(ctx);
1023
1024 end:
1025 for(int i = 0; options[i].name; i++)
1026 free(options[i].value);
1027
1028 return res;
1029 }
1030
1031 template class Vector<struct my_option>;
1032
1033 /*
1034 See include/my_getopt.h for the declaration of struct my_option
1035 */
1036