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