1 /*
2    Copyright (c) 2005, 2010, 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 /**
26  * Description of config variables, including their min, max, default
27  * values can be printed (--configinfo). This can also be printed
28  * in xml format (--xml).
29  *
30  * Config can be retrieved from only one of the following sources:
31  ** 1) config stored at mgmd (default)
32  ** 2) config stored at a data node (--config_from_node=<data node id>)
33  *** (Note:
34  ***  Node numbers less than 1 give error:
35  ***  "Given value <node id> is not a valid node number."
36  ***  Non-data node numbers give error:
37  ***  "Node <node id> is not a data node.")
38  ** 3) my.cnf (--mycnf=<fullPath/mycnfFileName>)
39  ** 4) config.file (--config_file=<fullPath/configFileName>
40  *
41  * Config variables are displayed from only one of the following
42  * sections of the retrieved config:
43  ** CFG_SECTION_NODE (default, or --nodes)
44  ** CFG_SECTION_CONNECTION (--connections)
45  ** CFG_SECTION_SYSTEM (--system)
46  */
47 
48 /**
49  * Examples:
50  * Get config from mgmd (default):
51  ** Display results from section CFG_SECTION_NODE (default)
52  *** ndb_config --nodes --query=nodeid --type=ndbd --host=local1
53  *** ndb_config  --query=nodeid,host
54  *
55  ** Display results from section CFG_SECTION_SYSTEM
56  *** ndb_config --system --query=ConfigGenerationNumber
57  *
58  ** Display results from section CFG_SECTION_CONNECTION
59  *** ndb_config --connections --query=type
60  *
61  * Get config from eg. node 2, which is a data node:
62  *
63  ** ndb_config --config_from_node=2 --system --query=ConfigGenerationNumber
64  ** ndb_config --config_from_node=2 --connections --query=type
65  ** ndb_config --config_from_node=2 --query=id,NoOfFragmentLogFiles
66  *
67  **  Get config from eg. node 2 and display results for node 2 only:
68  *** ndb_config --config_from_node=2 --query=id,NoOfFragmentLogFiles --nodeid=2
69  */
70 
71 #include <ndb_global.h>
72 #include <ndb_opts.h>
73 
74 #include <NdbOut.hpp>
75 #include <mgmapi.h>
76 #include "../src/mgmapi/mgmapi_configuration.hpp"
77 #include "../src/mgmsrv/ConfigInfo.hpp"
78 #include <NdbAutoPtr.hpp>
79 #include <NdbTCP.h>
80 
81 static int g_verbose = 0;
82 static int try_reconnect = 3;
83 
84 static int g_nodes, g_connections, g_system, g_section;
85 static const char * g_query = 0;
86 
87 static int g_nodeid = 0;
88 static const char * g_type = 0;
89 static const char * g_host = 0;
90 static const char * g_field_delimiter=",";
91 static const char * g_row_delimiter=" ";
92 static const char * g_config_file = 0;
93 static int g_mycnf = 0;
94 static int g_configinfo = 0;
95 static int g_xml = 0;
96 static int g_config_from_node = 0;
97 
98 const char *load_default_groups[]= { "mysql_cluster",0 };
99 
100 typedef ndb_mgm_configuration_iterator Iter;
101 
102 static struct my_option my_long_options[] =
103 {
104   NDB_STD_OPTS("ndb_config"),
105   { "nodes", NDB_OPT_NOSHORT, "Print nodes",
106     (uchar**) &g_nodes, (uchar**) &g_nodes,
107     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
108   { "connections", NDB_OPT_NOSHORT, "Print connections",
109     (uchar**) &g_connections, (uchar**) &g_connections,
110     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
111   { "system", NDB_OPT_NOSHORT, "Print system",
112     (uchar**) &g_system, (uchar**) &g_system,
113     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
114   { "query", 'q', "Query option(s)",
115     (uchar**) &g_query, (uchar**) &g_query,
116     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
117   { "host", NDB_OPT_NOSHORT, "Host",
118     (uchar**) &g_host, (uchar**) &g_host,
119     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
120   { "type", NDB_OPT_NOSHORT, "Type of node/connection",
121     (uchar**) &g_type, (uchar**) &g_type,
122     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
123   { "id", NDB_OPT_NOSHORT, "Nodeid",
124     (uchar**) &g_nodeid, (uchar**) &g_nodeid,
125     0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
126   { "nodeid", NDB_OPT_NOSHORT, "Nodeid",
127     (uchar**) &g_nodeid, (uchar**) &g_nodeid,
128     0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
129   { "fields", 'f', "Field separator",
130     (uchar**) &g_field_delimiter, (uchar**) &g_field_delimiter,
131     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
132   { "rows", 'r', "Row separator",
133     (uchar**) &g_row_delimiter, (uchar**) &g_row_delimiter,
134     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
135   { "config-file", NDB_OPT_NOSHORT, "Path to config.ini",
136     (uchar**) &g_config_file, (uchar**) &g_config_file,
137     0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
138   { "mycnf", NDB_OPT_NOSHORT, "Read config from my.cnf",
139     (uchar**) &g_mycnf, (uchar**) &g_mycnf,
140     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
141   { "configinfo", NDB_OPT_NOSHORT, "Print configinfo",
142     (uchar**) &g_configinfo, (uchar**) &g_configinfo,
143     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
144   { "xml", NDB_OPT_NOSHORT, "Print configinfo in xml format",
145     (uchar**) &g_xml, (uchar**) &g_xml,
146     0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
147   { "config_from_node", NDB_OPT_NOSHORT, "Use current config from node with given nodeid",
148     (uchar**) &g_config_from_node, (uchar**) &g_config_from_node,
149     0, GET_INT, REQUIRED_ARG, INT_MIN, INT_MIN, 0, 0, 0, 0},
150   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
151 };
152 
short_usage_sub(void)153 static void short_usage_sub(void)
154 {
155   ndb_short_usage_sub(NULL);
156 }
157 
usage()158 static void usage()
159 {
160   char desc[] =
161     "This program will retreive config options for a ndb cluster\n";
162   puts(desc);
163   ndb_usage(short_usage_sub, load_default_groups, my_long_options);
164 }
165 
166 /**
167  * Match/Apply framework
168  */
169 struct Match
170 {
171   int m_key;
172   BaseString m_value;
MatchMatch173   Match() {}
174   virtual int eval(const Iter&);
~MatchMatch175   virtual ~Match() {}
176 };
177 
178 struct HostMatch : public Match
179 {
HostMatchHostMatch180   HostMatch() {}
181   virtual int eval(const Iter&);
182 };
183 
184 struct Apply
185 {
ApplyApply186   Apply() {}
ApplyApply187   Apply(int val) { m_key = val;}
188   int m_key;
189   virtual int apply(const Iter&);
~ApplyApply190   virtual ~Apply() {}
191 };
192 
193 struct NodeTypeApply : public Apply
194 {
NodeTypeApplyNodeTypeApply195   NodeTypeApply() {}
196   virtual int apply(const Iter&);
197 };
198 
199 struct ConnectionTypeApply : public Apply
200 {
ConnectionTypeApplyConnectionTypeApply201   ConnectionTypeApply() {}
202   virtual int apply(const Iter&);
203 };
204 
205 static int parse_query(Vector<Apply*>&, int &argc, char**& argv);
206 static int parse_where(Vector<Match*>&, int &argc, char**& argv);
207 static int eval(const Iter&, const Vector<Match*>&);
208 static int apply(const Iter&, const Vector<Apply*>&);
209 static ndb_mgm_configuration* fetch_configuration(int from_node);
210 static ndb_mgm_configuration* load_configuration();
211 
212 
213 int
main(int argc,char ** argv)214 main(int argc, char** argv){
215   NDB_INIT(argv[0]);
216   ndb_opt_set_usage_funcs(short_usage_sub, usage);
217   ndb_load_defaults(NULL,load_default_groups,&argc,&argv);
218   int ho_error;
219   if ((ho_error=handle_options(&argc, &argv, my_long_options,
220 			       ndb_std_get_one_option)))
221     exit(255);
222 
223   if (g_configinfo)
224   {
225     ConfigInfo info;
226     if (g_xml)
227       info.print_xml();
228     else
229       info.print();
230     exit(0);
231   }
232 
233   if ((g_nodes && g_connections) ||
234       (g_system && (g_nodes || g_connections)))
235   {
236     fprintf(stderr,
237 	    "Error: Only one of the section-options: --nodes, --connections, --system is allowed.\n");
238     exit(255);
239   }
240 
241   /* There is no explicit option for the user to set
242    * 'retrieving config from mgmd', but this is the default.
243    * Therefore will not contradict with other sources.
244    */
245 
246   if ((g_config_file && g_mycnf) ||
247       ((g_config_from_node != INT_MIN) && (g_config_file || g_mycnf)))
248   {
249     fprintf(stderr,
250 	    "Error: Config should be retrieved from only one of the following sources:\n");
251     fprintf(stderr,
252             "\tconfig stored at mgmd (default),\n");
253     fprintf(stderr,
254             "\tconfig stored at a data node (--config_from_node=<nodeid>), \n");
255     fprintf(stderr,
256             "\tmy.cnf(--mycnf=<my.cnf file>),\n");
257     fprintf(stderr,
258              "\tconfig.file (--config_file=<config file>).\n");
259     exit(255);
260   }
261 
262   g_section = CFG_SECTION_NODE; //default
263   if (g_connections)
264     g_section = CFG_SECTION_CONNECTION;
265   else if (g_system)
266     g_section = CFG_SECTION_SYSTEM;
267 
268   ndb_mgm_configuration * conf = 0;
269 
270   if (g_config_file || g_mycnf)
271     conf = load_configuration();
272   else
273     conf = fetch_configuration(g_config_from_node);
274 
275   if (conf == 0)
276   {
277     exit(255);
278   }
279 
280   Vector<Apply*> select_list;
281   Vector<Match*> where_clause;
282 
283   if(strcmp(g_row_delimiter, "\\n") == 0)
284     g_row_delimiter = "\n";
285   if(strcmp(g_field_delimiter, "\\n") == 0)
286     g_field_delimiter = "\n";
287 
288   if(parse_query(select_list, argc, argv))
289   {
290     exit(0);
291   }
292 
293   if(parse_where(where_clause, argc, argv))
294   {
295     exit(0);
296   }
297 
298   Iter iter(* conf, g_section);
299   bool prev= false;
300   iter.first();
301   for(iter.first(); iter.valid(); iter.next())
302   {
303     if(eval(iter, where_clause))
304     {
305       if(prev)
306 	printf("%s", g_row_delimiter);
307       prev= true;
308       apply(iter, select_list);
309     }
310   }
311   printf("\n");
312   return 0;
313 }
314 
315 static
316 int
parse_query(Vector<Apply * > & select,int & argc,char ** & argv)317 parse_query(Vector<Apply*>& select, int &argc, char**& argv)
318 {
319   if(g_query)
320   {
321     BaseString q(g_query);
322     Vector<BaseString> list;
323     q.split(list, ",");
324     for(unsigned i = 0; i<list.size(); i++)
325     {
326       const char * str= list[i].c_str();
327       if(g_section == CFG_SECTION_NODE)
328       {
329 	if(native_strcasecmp(str, "id") == 0 || native_strcasecmp(str, "nodeid") == 0)
330 	{
331 	  select.push_back(new Apply(CFG_NODE_ID));
332 	  continue;
333 	}
334 	else if(native_strncasecmp(str, "host", 4) == 0)
335 	{
336 	  select.push_back(new Apply(CFG_NODE_HOST));
337 	  continue;
338 	}
339 	else if(native_strcasecmp(str, "type") == 0)
340 	{
341 	  select.push_back(new NodeTypeApply());
342 	  continue;
343 	}
344       }
345       else if (g_section == CFG_SECTION_CONNECTION)
346       {
347 	if(native_strcasecmp(str, "type") == 0)
348 	{
349 	  select.push_back(new ConnectionTypeApply());
350 	  continue;
351 	}
352       }
353       {
354 	bool found = false;
355 	for(int p = 0; p<ConfigInfo::m_NoOfParams; p++)
356 	{
357 	  if(0)ndbout_c("%s %s",
358 			ConfigInfo::m_ParamInfo[p]._section,
359 			ConfigInfo::m_ParamInfo[p]._fname);
360 	  if((g_section == CFG_SECTION_CONNECTION &&
361               (strcmp(ConfigInfo::m_ParamInfo[p]._section, "TCP") == 0 ||
362                strcmp(ConfigInfo::m_ParamInfo[p]._section, "SCI") == 0 ||
363                strcmp(ConfigInfo::m_ParamInfo[p]._section, "SHM") == 0))
364              ||
365 	     (g_section == CFG_SECTION_NODE &&
366               (strcmp(ConfigInfo::m_ParamInfo[p]._section, "DB") == 0 ||
367                strcmp(ConfigInfo::m_ParamInfo[p]._section, "API") == 0 ||
368                strcmp(ConfigInfo::m_ParamInfo[p]._section, "MGM") == 0))
369              ||
370 	     (g_section == CFG_SECTION_SYSTEM))
371 	  {
372 	    if(native_strcasecmp(ConfigInfo::m_ParamInfo[p]._fname, str) == 0)
373 	    {
374 	      select.push_back(new Apply(ConfigInfo::m_ParamInfo[p]._paramId));
375 	      found = true;
376 	      break;
377 	    }
378 	  }
379 	}
380 	if(!found)
381 	{
382 	  fprintf(stderr, "Unknown query option: %s\n", str);
383 	  return 1;
384 	}
385       }
386     }
387   }
388   return 0;
389 }
390 
391 static
392 int
parse_where(Vector<Match * > & where,int & argc,char ** & argv)393 parse_where(Vector<Match*>& where, int &argc, char**& argv)
394 {
395   Match m;
396   if(g_host)
397   {
398     HostMatch *tmp = new HostMatch;
399     tmp->m_key = CFG_NODE_HOST;
400     tmp->m_value.assfmt("%s", g_host);
401     where.push_back(tmp);
402   }
403 
404   if(g_type)
405   {
406     m.m_key = CFG_TYPE_OF_SECTION;
407     m.m_value.assfmt("%d", ndb_mgm_match_node_type(g_type));
408     where.push_back(new Match(m));
409   }
410 
411   if(g_nodeid)
412   {
413     m.m_key = CFG_NODE_ID;
414     m.m_value.assfmt("%d", g_nodeid);
415     where.push_back(new Match(m));
416   }
417   return 0;
418 }
419 
420 template class Vector<Apply*>;
421 template class Vector<Match*>;
422 
423 static
424 int
eval(const Iter & iter,const Vector<Match * > & where)425 eval(const Iter& iter, const Vector<Match*>& where)
426 {
427   for(unsigned i = 0; i<where.size(); i++)
428   {
429     if(where[i]->eval(iter) == 0)
430       return 0;
431   }
432 
433   return 1;
434 }
435 
436 static
437 int
apply(const Iter & iter,const Vector<Apply * > & list)438 apply(const Iter& iter, const Vector<Apply*>& list)
439 {
440   for(unsigned i = 0; i<list.size(); i++)
441   {
442     list[i]->apply(iter);
443     if(i + 1 != list.size())
444       printf("%s", g_field_delimiter);
445   }
446   return 0;
447 }
448 
449 int
eval(const Iter & iter)450 Match::eval(const Iter& iter)
451 {
452   Uint32 val32;
453   Uint64 val64;
454   const char* valc;
455   if (iter.get(m_key, &val32) == 0)
456   {
457     if(atoi(m_value.c_str()) != (int)val32)
458       return 0;
459   }
460   else if(iter.get(m_key, &val64) == 0)
461   {
462     if(my_strtoll(m_value.c_str(), (char **)NULL, 10) != (long long)val64)
463       return 0;
464   }
465   else if(iter.get(m_key, &valc) == 0)
466   {
467     if(strcmp(m_value.c_str(), valc) != 0)
468       return 0;
469   }
470   else
471   {
472     return 0;
473   }
474   return 1;
475 }
476 
477 int
eval(const Iter & iter)478 HostMatch::eval(const Iter& iter)
479 {
480   const char* valc;
481 
482   if(iter.get(m_key, &valc) == 0)
483   {
484 	  struct hostent *h1, *h2, copy1;
485 	  char *addr1;
486 
487 	  h1 = gethostbyname(m_value.c_str());
488 	  if (h1 == NULL) {
489 		  return 0;
490 	  }
491 
492 	  // gethostbyname returns a pointer to a static structure
493 	  // so we need to copy the results before doing the next call
494 	  memcpy(&copy1, h1, sizeof(struct hostent));
495 	  addr1 = (char *)malloc(copy1.h_length);
496 	  NdbAutoPtr<char> tmp_aptr(addr1);
497 	  memcpy(addr1, h1->h_addr, copy1.h_length);
498 
499 	  h2 = gethostbyname(valc);
500 	  if (h2 == NULL) {
501 		  return 0;
502 	  }
503 
504 	  if (copy1.h_addrtype != h2->h_addrtype) {
505 		  return 0;
506 	  }
507 
508 	  if (copy1.h_length != h2->h_length)
509 	  {
510 		  return 0;
511 	  }
512 
513 	  return 0 ==  memcmp(addr1, h2->h_addr, copy1.h_length);
514   }
515 
516   return 0;
517 }
518 
519 int
apply(const Iter & iter)520 Apply::apply(const Iter& iter)
521 {
522   Uint32 val32;
523   Uint64 val64;
524   const char* valc;
525   if (iter.get(m_key, &val32) == 0)
526   {
527     printf("%u", val32);
528   }
529   else if(iter.get(m_key, &val64) == 0)
530   {
531     printf("%llu", val64);
532   }
533   else if(iter.get(m_key, &valc) == 0)
534   {
535     printf("%s", valc);
536   }
537   return 0;
538 }
539 
540 int
apply(const Iter & iter)541 NodeTypeApply::apply(const Iter& iter)
542 {
543   Uint32 val32;
544   if (iter.get(CFG_TYPE_OF_SECTION, &val32) == 0)
545   {
546     printf("%s", ndb_mgm_get_node_type_alias_string((ndb_mgm_node_type)val32, 0));
547   }
548   return 0;
549 }
550 
551 int
apply(const Iter & iter)552 ConnectionTypeApply::apply(const Iter& iter)
553 {
554   Uint32 val32;
555   if (iter.get(CFG_TYPE_OF_SECTION, &val32) == 0)
556   {
557     switch (val32)
558     {
559     case CONNECTION_TYPE_TCP:
560       printf("tcp");
561       break;
562     case CONNECTION_TYPE_SCI:
563       printf("sci");
564       break;
565     case CONNECTION_TYPE_SHM:
566       printf("shm");
567       break;
568     default:
569       printf("<unknown>");
570       break;
571     }
572   }
573   return 0;
574 }
575 
576 static ndb_mgm_configuration*
fetch_configuration(int from_node)577 fetch_configuration(int from_node)
578 {
579   ndb_mgm_configuration* conf = 0;
580   NdbMgmHandle mgm = ndb_mgm_create_handle();
581   if(mgm == NULL) {
582     fprintf(stderr, "Cannot create handle to management server.\n");
583     return 0;
584   }
585 
586   ndb_mgm_set_error_stream(mgm, stderr);
587 
588   if (ndb_mgm_set_connectstring(mgm, opt_ndb_connectstring))
589   {
590     fprintf(stderr, "* %5d: %s\n",
591 	    ndb_mgm_get_latest_error(mgm),
592 	    ndb_mgm_get_latest_error_msg(mgm));
593     fprintf(stderr,
594 	    "*        %s", ndb_mgm_get_latest_error_desc(mgm));
595     goto noconnect;
596   }
597 
598   if(ndb_mgm_connect(mgm, try_reconnect-1, 5, 1))
599   {
600     fprintf(stderr, "Connect failed");
601     fprintf(stderr, " code: %d, msg: %s\n",
602 	    ndb_mgm_get_latest_error(mgm),
603 	    ndb_mgm_get_latest_error_msg(mgm));
604     goto noconnect;
605   }
606   else if(g_verbose)
607   {
608     fprintf(stderr, "Connected to %s:%d\n",
609 	    ndb_mgm_get_connected_host(mgm),
610 	    ndb_mgm_get_connected_port(mgm));
611   }
612 
613   if (from_node == INT_MIN)
614   {
615     // from_node option is not requested.
616     // Retrieve config from the default src: mgmd
617     conf = ndb_mgm_get_configuration(mgm, 0);
618   }
619   else if (from_node < 1)
620   {
621     fprintf(stderr, "Invalid node number %d is given for --config_from_node.\n", from_node);
622     goto noconnect;
623   }
624   else
625   {
626     // Retrieve config from the given data node
627      conf = ndb_mgm_get_configuration_from_node(mgm, from_node);
628   }
629 
630   if(conf == 0)
631   {
632     fprintf(stderr, "Could not get configuration, ");
633     fprintf(stderr, "error code: %d, error msg: %s\n",
634 	    ndb_mgm_get_latest_error(mgm),
635 	    ndb_mgm_get_latest_error_msg(mgm));
636   }
637   else if(g_verbose)
638   {
639     fprintf(stderr, "Fetched configuration\n");
640   }
641 
642   ndb_mgm_disconnect(mgm);
643 noconnect:
644   ndb_mgm_destroy_handle(&mgm);
645 
646   return conf;
647 }
648 
649 #include "../src/mgmsrv/Config.hpp"
650 #include <EventLogger.hpp>
651 
652 extern EventLogger *g_eventLogger;
653 
654 static ndb_mgm_configuration*
load_configuration()655 load_configuration()
656 {
657   g_eventLogger->removeAllHandlers();
658   g_eventLogger->createConsoleHandler(ndberr);
659   g_eventLogger->setCategory("ndb_config");
660   InitConfigFileParser parser;
661   if (g_config_file)
662   {
663     if (g_verbose)
664       fprintf(stderr, "Using config.ini : %s\n", g_config_file);
665 
666     Config* conf = parser.parseConfig(g_config_file);
667     if (conf)
668       return conf->m_configValues;
669     return 0;
670   }
671 
672   if (g_verbose)
673     fprintf(stderr, "Using my.cnf\n");
674 
675   Config* conf = parser.parse_mycnf();
676   if (conf)
677     return conf->m_configValues;
678 
679   return 0;
680 }
681