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