1 /*
2    Copyright (c) 2003, 2015, 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 
27 #include <mgmapi.h>
28 #include <ndbd_exit_codes.h>
29 
30 #include <util/BaseString.hpp>
31 #include <util/Vector.hpp>
32 #include <kernel/BlockNumbers.h>
33 
34 /**
35  *  @class CommandInterpreter
36  *  @brief Reads command line in management client
37  *
38  *  For command syntax, see the HELP command.
39  */
40 class CommandInterpreter {
41 public:
42   /**
43    *   Constructor
44    *   @param mgmtSrvr: Management server to use when executing commands
45    */
46   CommandInterpreter(const char *, int verbose);
47   ~CommandInterpreter();
48 
49   /**
50    *   Reads one line from the stream, parse the line to find
51    *   a command and then calls a suitable method which executes
52    *   the command.
53    *
54    *   @return true until quit/bye/exit has been typed
55    */
56   bool execute(const char *line, int try_reconnect = -1,
57                bool interactive = true, int *error = NULL);
58 
59 private:
60   void printError();
61   bool execute_impl(const char *line, bool interactive);
62 
63   /**
64    *   Analyse the command line, after the first token.
65    *
66    *   @param  processId:           DB process id to send command to or -1 if
67    *                                command will be sent to all DB processes.
68    *   @param  allAfterFirstToken:  What the client gave after the
69    *                                first token on the command line
70    *   @return: 0 if analyseAfterFirstToken succeeds, otherwise -1
71    */
72   int  analyseAfterFirstToken(int processId, char* allAfterFirstTokenCstr);
73 
74   int  executeCommand(Vector<BaseString> &command_list,
75                       unsigned command_pos,
76                       int *node_ids, int no_of_nodes);
77   /**
78    *   Parse the block specification part of the LOG* commands,
79    *   things after LOG*: [BLOCK = {ALL|<blockName>+}]
80    *
81    *   @param  allAfterLog: What the client gave after the second token
82    *                        (LOG*) on the command line
83    *   @param  blocks, OUT: ALL or name of all the blocks
84    *   @return: true if correct syntax, otherwise false
85    */
86   bool parseBlockSpecification(const char* allAfterLog,
87 			       Vector<BaseString>& blocks);
88 
89   /**
90    *   A bunch of execute functions: Executes one of the commands
91    *
92    *   @param  processId:   DB process id to send command to
93    *   @param  parameters:  What the client gave after the command name
94    *                        on the command line.
95    *   For example if complete input from user is: "1 LOGLEVEL 22" then the
96    *   parameters argument is the string with everything after LOGLEVEL, in
97    *   this case "22". Each function is responsible to check the parameters
98    *   argument.
99    */
100   int  executeHelp(char* parameters);
101   int  executeShow(char* parameters);
102   int  executePurge(char* parameters);
103   int  executeConnect(char* parameters, bool interactive);
104   int  executeShutdown(char* parameters);
105   void executeClusterLog(char* parameters);
106 
107 public:
108   int  executeStop(int processId, const char* parameters, bool all);
109   int  executeEnterSingleUser(char* parameters);
110   int  executeExitSingleUser(char* parameters);
111   int  executeStart(int processId, const char* parameters, bool all);
112   int  executeRestart(int processId, const char* parameters, bool all);
113   int  executeLogLevel(int processId, const char* parameters, bool all);
114   int  executeError(int processId, const char* parameters, bool all);
115   int  executeLog(int processId, const char* parameters, bool all);
116   int  executeTestOn(int processId, const char* parameters, bool all);
117   int  executeTestOff(int processId, const char* parameters, bool all);
118   int  executeStatus(int processId, const char* parameters, bool all);
119   int  executeEventReporting(int processId, const char* parameters, bool all);
120   int  executeDumpState(int processId, const char* parameters, bool all);
121   int  executeReport(int processId, const char* parameters, bool all);
122   int  executeStartBackup(char * parameters, bool interactive);
123   int  executeAbortBackup(char * parameters);
124   int  executeStop(Vector<BaseString> &command_list, unsigned command_pos,
125                    int *node_ids, int no_of_nodes);
126   int  executeRestart(Vector<BaseString> &command_list, unsigned command_pos,
127                       int *node_ids, int no_of_nodes);
128   int  executeStart(Vector<BaseString> &command_list, unsigned command_pos,
129                     int *node_ids, int no_of_nodes);
130   int executeCreateNodeGroup(char* parameters);
131   int executeDropNodeGroup(char* parameters);
132 public:
133   bool connect(bool interactive);
134   void disconnect(void);
135 
136   /**
137    * A execute function definition
138    */
139 public:
140   typedef int (CommandInterpreter::* ExecuteFunction)(int processId,
141 						       const char * param,
142 						       bool all);
143 
144   struct CommandFunctionPair {
145     const char * command;
146     ExecuteFunction executeFunction;
147   };
148 private:
149   /**
150    *
151    */
152   int  executeForAll(const char * cmd,
153 		     ExecuteFunction fun,
154 		     const char * param);
155 
156   NdbMgmHandle m_mgmsrv;
157   NdbMgmHandle m_mgmsrv2;
158   const char *m_constr;
159   bool m_connected;
160   int m_verbose;
161   int m_try_reconnect;
162   int m_error;
163   struct NdbThread* m_event_thread;
164   NdbMutex *m_print_mutex;
165 };
166 
167 NdbMutex* print_mutex;
168 
169 /*
170  * Facade object for CommandInterpreter
171  */
172 
173 #include "ndb_mgmclient.hpp"
174 
Ndb_mgmclient(const char * host,int verbose)175 Ndb_mgmclient::Ndb_mgmclient(const char *host,int verbose)
176 {
177   m_cmd= new CommandInterpreter(host,verbose);
178 }
~Ndb_mgmclient()179 Ndb_mgmclient::~Ndb_mgmclient()
180 {
181   delete m_cmd;
182 }
execute(const char * line,int try_reconnect,bool interactive,int * error)183 bool Ndb_mgmclient::execute(const char *line, int try_reconnect,
184                             bool interactive, int *error)
185 {
186   return m_cmd->execute(line, try_reconnect, interactive, error);
187 }
188 
189 /*
190  * The CommandInterpreter
191  */
192 
193 #include <mgmapi_debug.h>
194 
195 #include <util/version.h>
196 #include <util/NdbAutoPtr.hpp>
197 #include <util/NdbOut.hpp>
198 
199 #include <portlib/NdbSleep.h>
200 #include <portlib/NdbThread.h>
201 
202 #include <debugger/EventLogger.hpp>
203 #include <signaldata/SetLogLevelOrd.hpp>
204 
205 /*****************************************************************************
206  * HELP
207  *****************************************************************************/
208 static const char* helpText =
209 "---------------------------------------------------------------------------\n"
210 " NDB Cluster -- Management Client -- Help\n"
211 "---------------------------------------------------------------------------\n"
212 "HELP                                   Print help text\n"
213 "HELP COMMAND                           Print detailed help for COMMAND(e.g. SHOW)\n"
214 #ifdef VM_TRACE // DEBUG ONLY
215 "HELP DEBUG                             Help for debug compiled version\n"
216 #endif
217 "SHOW                                   Print information about cluster\n"
218 "CREATE NODEGROUP <id>,<id>...          Add a Nodegroup containing nodes\n"
219 "DROP NODEGROUP <NG>                    Drop nodegroup with id NG\n"
220 "START BACKUP [NOWAIT | WAIT STARTED | WAIT COMPLETED]\n"
221 "START BACKUP [<backup id>] [NOWAIT | WAIT STARTED | WAIT COMPLETED]\n"
222 "START BACKUP [<backup id>] [SNAPSHOTSTART | SNAPSHOTEND] [NOWAIT | WAIT STARTED | WAIT COMPLETED]\n"
223 "                                       Start backup (default WAIT COMPLETED,SNAPSHOTEND)\n"
224 "ABORT BACKUP <backup id>               Abort backup\n"
225 "SHUTDOWN                               Shutdown all processes in cluster\n"
226 "CLUSTERLOG ON [<severity>] ...         Enable Cluster logging\n"
227 "CLUSTERLOG OFF [<severity>] ...        Disable Cluster logging\n"
228 "CLUSTERLOG TOGGLE [<severity>] ...     Toggle severity filter on/off\n"
229 "CLUSTERLOG INFO                        Print cluster log information\n"
230 "<id> START                             Start data node (started with -n)\n"
231 "<id> RESTART [-n] [-i] [-a] [-f]       Restart data or management server node\n"
232 "<id> STOP [-a] [-f]                    Stop data or management server node\n"
233 "ENTER SINGLE USER MODE <id>            Enter single user mode\n"
234 "EXIT SINGLE USER MODE                  Exit single user mode\n"
235 "<id> STATUS                            Print status\n"
236 "<id> CLUSTERLOG {<category>=<level>}+  Set log level for cluster log\n"
237 "PURGE STALE SESSIONS                   Reset reserved nodeid's in the mgmt server\n"
238 "CONNECT [<connectstring>]              Connect to management server (reconnect if already connected)\n"
239 "<id> REPORT <report-type>              Display report for <report-type>\n"
240 "QUIT                                   Quit management client\n"
241 ;
242 
243 static const char* helpTextShow =
244 "---------------------------------------------------------------------------\n"
245 " NDB Cluster -- Management Client -- Help for SHOW command\n"
246 "---------------------------------------------------------------------------\n"
247 "SHOW Print information about cluster\n\n"
248 "SHOW               Print information about cluster.The status reported is from\n"
249 "                   the perspective of the data nodes. API and Management Server nodes\n"
250 "                   are only reported as connected once the data nodes have started.\n"
251 ;
252 
253 static const char* helpTextHelp =
254 "---------------------------------------------------------------------------\n"
255 " NDB Cluster -- Management Client -- Help for HELP command\n"
256 "---------------------------------------------------------------------------\n"
257 "HELP List available commands of NDB Cluster Management Client\n\n"
258 "HELP               List available commands.\n"
259 ;
260 
261 static const char* helpTextBackup =
262 "---------------------------------------------------------------------------\n"
263 " NDB Cluster -- Management Client -- Help for BACKUP command\n"
264 "---------------------------------------------------------------------------\n"
265 "BACKUP  A backup is a snapshot of the database at a given time. \n"
266 "        The backup consists of three main parts:\n\n"
267 "        Metadata: the names and definitions of all database tables. \n"
268 "        Table records: the data actually stored in the database tables \n"
269 "        at the time that the backup was made.\n"
270 "        Transaction log: a sequential record telling how \n"
271 "        and when data was stored in the database.\n\n"
272 "        Backups are stored on each data node in the cluster that \n"
273 "        participates in the backup.\n\n"
274 "        The cluster log records backup related events (such as \n"
275 "        backup started, aborted, finished).\n"
276 ;
277 
278 static const char* helpTextStartBackup =
279 "---------------------------------------------------------------------------\n"
280 " NDB Cluster -- Management Client -- Help for START BACKUP command\n"
281 "---------------------------------------------------------------------------\n"
282 "START BACKUP  Start a cluster backup\n\n"
283 "START BACKUP [<backup id>] [SNAPSHOTSTART | SNAPSHOTEND] [NOWAIT | WAIT STARTED | WAIT COMPLETED]\n"
284 "                   Start a backup for the cluster.\n"
285 "                   Each backup gets an ID number that is reported to the\n"
286 "                   user. This ID number can help you find the backup on the\n"
287 "                   file system, or ABORT BACKUP if you wish to cancel a \n"
288 "                   running backup.\n"
289 "                   You can also start specified backup using START BACKUP <backup id> \n\n"
290 "                   <backup id> \n"
291 "                     Start a specified backup using <backup id> as bakcup ID number.\n"
292 "                   SNAPSHOTSTART \n"
293 "                     Backup snapshot is taken around the time the backup is started.\n"
294 "                   SNAPSHOTEND \n"
295 "                     Backup snapshot is taken around the time the backup is completed.\n"
296 "                   NOWAIT \n"
297 "                     Start a cluster backup and return immediately.\n"
298 "                     The management client will return control directly\n"
299 "                     to the user without waiting for the backup\n"
300 "                     to have started.\n"
301 "                     The status of the backup is recorded in the Cluster log.\n"
302 "                   WAIT STARTED\n"
303 "                     Start a cluster backup and return until the backup has\n"
304 "                     started. The management client will wait for the backup \n"
305 "                     to have started before returning control to the user.\n"
306 "                   WAIT COMPLETED\n"
307 "                     Start a cluster backup and return until the backup has\n"
308 "                     completed. The management client will wait for the backup\n"
309 "                     to complete before returning control to the user.\n"
310 ;
311 
312 static const char* helpTextAbortBackup =
313 "---------------------------------------------------------------------------\n"
314 " NDB Cluster -- Management Client -- Help for ABORT BACKUP command\n"
315 "---------------------------------------------------------------------------\n"
316 "ABORT BACKUP  Abort a cluster backup\n\n"
317 "ABORT BACKUP <backup id>  \n"
318 "                   Abort a backup that is already in progress.\n"
319 "                   The backup id can be seen in the cluster log or in the\n"
320 "                   output of the START BACKUP command.\n"
321 ;
322 
323 static const char* helpTextShutdown =
324 "---------------------------------------------------------------------------\n"
325 " NDB Cluster -- Management Client -- Help for SHUTDOWN command\n"
326 "---------------------------------------------------------------------------\n"
327 "SHUTDOWN  Shutdown the cluster\n\n"
328 "SHUTDOWN           Shutdown the data nodes and management nodes.\n"
329 "                   MySQL Servers and NDBAPI nodes are currently not \n"
330 "                   shut down by issuing this command.\n"
331 ;
332 
333 static const char* helpTextClusterlogOn =
334 "---------------------------------------------------------------------------\n"
335 " NDB Cluster -- Management Client -- Help for CLUSTERLOG ON command\n"
336 "---------------------------------------------------------------------------\n"
337 "CLUSTERLOG ON  Enable Cluster logging\n\n"
338 "CLUSTERLOG ON [<severity>] ... \n"
339 "                   Turn the cluster log on.\n"
340 "                   It tells management server which severity levels\n"
341 "                   messages will be logged.\n\n"
342 "                   <severity> can be any one of the following values:\n"
343 "                   ALERT, CRITICAL, ERROR, WARNING, INFO, DEBUG.\n"
344 ;
345 
346 static const char* helpTextClusterlogOff =
347 "---------------------------------------------------------------------------\n"
348 " NDB Cluster -- Management Client -- Help for CLUSTERLOG OFF command\n"
349 "---------------------------------------------------------------------------\n"
350 "CLUSTERLOG OFF  Disable Cluster logging\n\n"
351 "CLUSTERLOG OFF [<severity>] ...  \n"
352 "                   Turn the cluster log off.\n"
353 "                   It tells management server which serverity\n"
354 "                   levels logging will be disabled.\n\n"
355 "                   <severity> can be any one of the following values:\n"
356 "                   ALERT, CRITICAL, ERROR, WARNING, INFO, DEBUG.\n"
357 ;
358 
359 static const char* helpTextClusterlogToggle =
360 "---------------------------------------------------------------------------\n"
361 " NDB Cluster -- Management Client -- Help for CLUSTERLOG TOGGLE command\n"
362 "---------------------------------------------------------------------------\n"
363 "CLUSTERLOG TOGGLE  Toggle severity filter on/off\n\n"
364 "CLUSTERLOG TOGGLE [<severity>] ...  \n"
365 "                   Toggle serverity filter on/off.\n"
366 "                   If a serverity level is already enabled,then it will\n"
367 "                   be disabled after you use the command,vice versa.\n\n"
368 "                   <severity> can be any one of the following values:\n"
369 "                   ALERT, CRITICAL, ERROR, WARNING, INFO, DEBUG.\n"
370 ;
371 
372 static const char* helpTextClusterlogInfo =
373 "---------------------------------------------------------------------------\n"
374 " NDB Cluster -- Management Client -- Help for CLUSTERLOG INFO command\n"
375 "---------------------------------------------------------------------------\n"
376 "CLUSTERLOG INFO  Print cluster log information\n\n"
377 "CLUSTERLOG INFO    Display which severity levels have been enabled,\n"
378 "                   see HELP CLUSTERLOG for list of the severity levels.\n"
379 ;
380 
381 static const char* helpTextStart =
382 "---------------------------------------------------------------------------\n"
383 " NDB Cluster -- Management Client -- Help for START command\n"
384 "---------------------------------------------------------------------------\n"
385 "START  Start data node (started with -n)\n\n"
386 "<id> START         Start the data node identified by <id>.\n"
387 "                   Only starts data nodes that have not\n"
388 "                   yet joined the cluster. These are nodes\n"
389 "                   launched or restarted with the -n(--nostart)\n"
390 "                   option.\n\n"
391 "                   It does not launch the ndbd process on a remote\n"
392 "                   machine.\n"
393 ;
394 
395 static const char* helpTextRestart =
396 "---------------------------------------------------------------------------\n"
397 " NDB Cluster -- Management Client -- Help for RESTART command\n"
398 "---------------------------------------------------------------------------\n"
399 "RESTART  Restart data or management server node\n\n"
400 "<id> RESTART [-n] [-i] [-a] [-f]\n"
401 "                   Restart the data or management node <id>(or All data nodes).\n\n"
402 "                   -n (--nostart) restarts the node but does not\n"
403 "                   make it join the cluster. Use '<id> START' to\n"
404 "                   join the node to the cluster.\n\n"
405 "                   -i (--initial) perform initial start.\n"
406 "                   This cleans the file system (ndb_<id>_fs)\n"
407 "                   and the node will copy data from another node\n"
408 "                   in the same node group during start up.\n\n"
409 "                   Consult the documentation before using -i.\n\n"
410 "                   INCORRECT USE OF -i WILL CAUSE DATA LOSS!\n\n"
411 "                   -a Aborts the node, not syncing GCP.\n\n"
412 "                   -f Force restart even if that would mean the\n"
413 "                      whole cluster would need to be restarted\n"
414 ;
415 
416 static const char* helpTextStop =
417 "---------------------------------------------------------------------------\n"
418 " NDB Cluster -- Management Client -- Help for STOP command\n"
419 "---------------------------------------------------------------------------\n"
420 "STOP  Stop data or management server node\n\n"
421 "<id> STOP [-a] [-f]\n"
422 "                   Stop the data or management server node <id>.\n\n"
423 "                   ALL STOP will just stop all data nodes.\n\n"
424 "                   If you desire to also shut down management servers,\n"
425 "                   use SHUTDOWN instead.\n\n"
426 "                   -a Aborts the node, not syncing GCP.\n\n"
427 "                   -f Force stop even if that would mean the\n"
428 "                      whole cluster would need to be stopped\n"
429 ;
430 
431 static const char* helpTextEnterSingleUserMode =
432 "---------------------------------------------------------------------------\n"
433 " NDB Cluster -- Management Client -- Help for ENTER SINGLE USER MODE command\n"
434 "---------------------------------------------------------------------------\n"
435 "ENTER SINGLE USER MODE  Enter single user mode\n\n"
436 "ENTER SINGLE USER MODE <id> \n"
437 "                   Enters single-user mode, whereby only the MySQL Server or NDBAPI\n"
438 "                   node identified by <id> is allowed to access the database. \n"
439 ;
440 
441 static const char* helpTextExitSingleUserMode =
442 "---------------------------------------------------------------------------\n"
443 " NDB Cluster -- Management Client -- Help for EXIT SINGLE USER MODE command\n"
444 "---------------------------------------------------------------------------\n"
445 "EXIT SINGLE USER MODE  Exit single user mode\n\n"
446 "EXIT SINGLE USER MODE \n"
447 "                   Exits single-user mode, allowing all SQL nodes \n"
448 "                   (that is, all running mysqld processes) to access the database. \n"
449 ;
450 
451 static const char* helpTextStatus =
452 "---------------------------------------------------------------------------\n"
453 " NDB Cluster -- Management Client -- Help for STATUS command\n"
454 "---------------------------------------------------------------------------\n"
455 "STATUS  Print status\n\n"
456 "<id> STATUS        Displays status information for the data node <id>\n"
457 "                   or for All data nodes. \n\n"
458 "                   e.g.\n"
459 "                      ALL STATUS\n"
460 "                      1 STATUS\n\n"
461 "                   When a node is starting, the start phase will be\n"
462 "                   listed.\n\n"
463 "                   Start Phase   Meaning\n"
464 "                   1             Clear the cluster file system(ndb_<id>_fs). \n"
465 "                                 This stage occurs only when the --initial option \n"
466 "                                 has been specified.\n"
467 "                   2             This stage sets up Cluster connections, establishes \n"
468 "                                 inter-node communications and starts Cluster heartbeats.\n"
469 "                   3             The arbitrator node is elected.\n"
470 "                   4             Initializes a number of internal cluster variables.\n"
471 "                   5             For an initial start or initial node restart,\n"
472 "                                 the redo log files are created.\n"
473 "                   6             If this is an initial start, create internal system tables.\n"
474 "                   7             Update internal variables. \n"
475 "                   8             In a system restart, rebuild all indexes.\n"
476 "                   9             Update internal variables. \n"
477 "                   10            The node can be connected by APIs and can receive events.\n"
478 "                   11            At this point,event delivery is handed over to\n"
479 "                                 the node joining the cluster.\n"
480 "(see manual for more information)\n"
481 ;
482 
483 static const char* helpTextClusterlog =
484 "---------------------------------------------------------------------------\n"
485 " NDB Cluster -- Management Client -- Help for CLUSTERLOG command\n"
486 "---------------------------------------------------------------------------\n"
487 "CLUSTERLOG  Set log level for cluster log\n\n"
488 " <id> CLUSTERLOG {<category>=<level>}+  \n"
489 "                   Logs <category> events with priority less than \n"
490 "                   or equal to <level> in the cluster log.\n\n"
491 "                   <category> can be any one of the following values:\n"
492 "                   STARTUP, SHUTDOWN, STATISTICS, CHECKPOINT, NODERESTART,\n"
493 "                   CONNECTION, ERROR, INFO, CONGESTION, DEBUG, or BACKUP. \n\n"
494 "                   <level> is represented by one of the numbers \n"
495 "                   from 1 to 15 inclusive, where 1 indicates 'most important' \n"
496 "                   and 15 'least important'.\n\n"
497 "                   <severity> can be any one of the following values:\n"
498 "                   ALERT, CRITICAL, ERROR, WARNING, INFO, DEBUG.\n"
499 ;
500 
501 
502 static const char* helpTextPurgeStaleSessions =
503 "---------------------------------------------------------------------------\n"
504 " NDB Cluster -- Management Client -- Help for PURGE STALE SESSIONS command\n"
505 "---------------------------------------------------------------------------\n"
506 "PURGE STALE SESSIONS  Reset reserved nodeid's in the mgmt server\n\n"
507 "PURGE STALE SESSIONS \n"
508 "                   Running this statement forces all reserved \n"
509 "                   node IDs to be checked; any that are not \n"
510 "                   being used by nodes acutally connected to \n"
511 "                   the cluster are then freed.\n\n"
512 "                   This command is not normally needed, but may be\n"
513 "                   required in some situations where failed nodes \n"
514 "                   cannot rejoin the cluster due to failing to\n"
515 "                   allocate a node id.\n"
516 ;
517 
518 static const char* helpTextConnect =
519 "---------------------------------------------------------------------------\n"
520 " NDB Cluster -- Management Client -- Help for CONNECT command\n"
521 "---------------------------------------------------------------------------\n"
522 "CONNECT  Connect to management server (reconnect if already connected)\n\n"
523 "CONNECT [<connectstring>] \n"
524 "                   Connect to management server.\n"
525 "                   The optional parameter connectstring specifies the \n"
526 "                   connect string to user.\n\n"
527 "                   A connect string may be:\n"
528 "                       mgm-server\n"
529 "                       mgm-server:port\n"
530 "                       mgm1:port,mgm2:port\n"
531 "                   With multiple management servers comma separated.\n"
532 "                   The management client with try to connect to the \n"
533 "                   management servers in the order they are listed.\n\n"
534 "                   If no connect string is specified, the default \n"
535 "                   is used. \n"
536 ;
537 
538 static const char* helpTextReport =
539 "---------------------------------------------------------------------------\n"
540 " NDB Cluster -- Management Client -- Help for REPORT command\n"
541 "---------------------------------------------------------------------------\n"
542 "REPORT  Displays a report of type <report-type> for the specified data \n"
543 "        node, or for all data nodes using ALL\n"
544 ;
545 static void helpTextReportFn();
546 static void helpTextReportTypeOptionFn();
547 
548 
549 static const char* helpTextQuit =
550 "---------------------------------------------------------------------------\n"
551 " NDB Cluster -- Management Client -- Help for QUIT command\n"
552 "---------------------------------------------------------------------------\n"
553 "QUIT  Quit management client\n\n"
554 "QUIT               Terminates the management client. \n"
555 ;
556 
557 
558 #ifdef VM_TRACE // DEBUG ONLY
559 static const char* helpTextDebug =
560 "---------------------------------------------------------------------------\n"
561 " NDB Cluster -- Management Client -- Help for Debugging (Internal use only)\n"
562 "---------------------------------------------------------------------------\n"
563 "SHOW PROPERTIES                       Print config properties object\n"
564 "<id> LOGLEVEL {<category>=<level>}+   Set log level\n"
565 #ifdef ERROR_INSERT
566 "<id> ERROR <errorNo>                  Inject error into NDB node\n"
567 #endif
568 "<id> LOG [BLOCK = {ALL|<block>+}]     Set logging on in & out signals\n"
569 "<id> TESTON                           Start signal logging\n"
570 "<id> TESTOFF                          Stop signal logging\n"
571 "<id> DUMP <arg>                       Dump system state to cluster.log\n"
572 "\n"
573 "<id>       = ALL | Any database node id\n"
574 ;
575 #endif
576 
577 struct st_cmd_help {
578   const char *cmd;
579   const char * help;
580   void (* help_fn)();
581 }help_items[]={
582   {"SHOW", helpTextShow, NULL},
583   {"HELP", helpTextHelp, NULL},
584   {"BACKUP", helpTextBackup, NULL},
585   {"START BACKUP", helpTextStartBackup, NULL},
586   {"START BACKUP NOWAIT", helpTextStartBackup, NULL},
587   {"START BACKUP WAIT STARTED", helpTextStartBackup, NULL},
588   {"START BACKUP WAIT", helpTextStartBackup, NULL},
589   {"START BACKUP WAIT COMPLETED", helpTextStartBackup, NULL},
590   {"ABORT BACKUP", helpTextAbortBackup, NULL},
591   {"SHUTDOWN", helpTextShutdown, NULL},
592   {"CLUSTERLOG ON", helpTextClusterlogOn, NULL},
593   {"CLUSTERLOG OFF", helpTextClusterlogOff, NULL},
594   {"CLUSTERLOG TOGGLE", helpTextClusterlogToggle, NULL},
595   {"CLUSTERLOG INFO", helpTextClusterlogInfo, NULL},
596   {"START", helpTextStart, NULL},
597   {"RESTART", helpTextRestart, NULL},
598   {"STOP", helpTextStop, NULL},
599   {"ENTER SINGLE USER MODE", helpTextEnterSingleUserMode, NULL},
600   {"EXIT SINGLE USER MODE", helpTextExitSingleUserMode, NULL},
601   {"STATUS", helpTextStatus, NULL},
602   {"CLUSTERLOG", helpTextClusterlog, NULL},
603   {"PURGE STALE SESSIONS", helpTextPurgeStaleSessions, NULL},
604   {"CONNECT", helpTextConnect, NULL},
605   {"REPORT", helpTextReport, helpTextReportFn},
606   {"QUIT", helpTextQuit, NULL},
607 #ifdef VM_TRACE // DEBUG ONLY
608   {"DEBUG", helpTextDebug, NULL},
609 #endif //VM_TRACE
610   {NULL, NULL, NULL}
611 };
612 
613 static bool
convert(const char * s,int & val)614 convert(const char* s, int& val) {
615 
616   if (s == NULL)
617     return false;
618 
619   if (strlen(s) == 0)
620     return false;
621 
622   errno = 0;
623   char* p;
624   long v = strtol(s, &p, 10);
625   if (errno != 0)
626     return false;
627 
628   if (p != &s[strlen(s)])
629     return false;
630 
631   val = v;
632   return true;
633 }
634 
635 /*
636  * Constructor
637  */
CommandInterpreter(const char * host,int verbose)638 CommandInterpreter::CommandInterpreter(const char *host,int verbose) :
639   m_constr(host),
640   m_connected(false),
641   m_verbose(verbose),
642   m_try_reconnect(0),
643   m_error(-1),
644   m_event_thread(NULL)
645 {
646   m_print_mutex= NdbMutex_Create();
647 }
648 
649 /*
650  * Destructor
651  */
~CommandInterpreter()652 CommandInterpreter::~CommandInterpreter()
653 {
654   disconnect();
655   NdbMutex_Destroy(m_print_mutex);
656 }
657 
658 static bool
emptyString(const char * s)659 emptyString(const char* s)
660 {
661   if (s == NULL) {
662     return true;
663   }
664 
665   for (unsigned int i = 0; i < strlen(s); ++i) {
666     if (! isspace(s[i])) {
667       return false;
668     }
669   }
670 
671   return true;
672 }
673 
674 void
printError()675 CommandInterpreter::printError()
676 {
677   if (m_mgmsrv)
678   {
679     ndbout_c("* %5d: %s",
680              ndb_mgm_get_latest_error(m_mgmsrv),
681              ndb_mgm_get_latest_error_msg(m_mgmsrv));
682     ndbout_c("*        %s", ndb_mgm_get_latest_error_desc(m_mgmsrv));
683     if (ndb_mgm_check_connection(m_mgmsrv))
684     {
685       disconnect();
686     }
687   }
688 }
689 
690 /*
691  * print log event from mgmsrv to console screen
692  */
693 #define make_uint64(a,b) (((Uint64)(a)) + (((Uint64)(b)) << 32))
694 #define Q64(a) make_uint64(event->EVENT.a ## _lo, event->EVENT.a ## _hi)
695 #define R event->source_nodeid
696 #define Q(a) event->EVENT.a
697 #define QVERSION getMajor(Q(version)), getMinor(Q(version)), getBuild(Q(version))
698 #define NDB_LE_(a) NDB_LE_ ## a
699 static void
printLogEvent(struct ndb_logevent * event)700 printLogEvent(struct ndb_logevent* event)
701 {
702   switch (event->type) {
703     /**
704      * NDB_MGM_EVENT_CATEGORY_BACKUP
705      */
706 #undef  EVENT
707 #define EVENT BackupStarted
708   case NDB_LE_BackupStarted:
709       ndbout_c("Node %u: Backup %u started from node %d",
710                R, Q(backup_id), Q(starting_node));
711       break;
712 #undef EVENT
713 #define EVENT BackupStatus
714     case NDB_LE_BackupStatus:
715       if (Q(starting_node))
716         ndbout_c("Node %u: Local backup status: backup %u started from node %u\n"
717                  " #Records: %llu #LogRecords: %llu\n"
718                  " Data: %llu bytes Log: %llu bytes", R,
719                  Q(backup_id),
720                  Q(starting_node),
721                  Q64(n_records),
722                  Q64(n_log_records),
723                  Q64(n_bytes),
724                  Q64(n_log_bytes));
725       else
726         ndbout_c("Node %u: Backup not started", R);
727       break;
728 #undef  EVENT
729 #define EVENT BackupFailedToStart
730     case NDB_LE_BackupFailedToStart:
731       ndbout_c("Node %u: Backup request from %d failed to start. Error: %d",
732                R, Q(starting_node), Q(error));
733       break;
734 #undef  EVENT
735 #define EVENT BackupCompleted
736     case NDB_LE_BackupCompleted:
737       ndbout_c("Node %u: Backup %u started from node %u completed\n"
738                " StartGCP: %u StopGCP: %u\n"
739                " #Records: %u #LogRecords: %u\n"
740                " Data: %u bytes Log: %u bytes", R,
741                Q(backup_id), Q(starting_node),
742                Q(start_gci), Q(stop_gci),
743                Q(n_records), Q(n_log_records),
744                Q(n_bytes),   Q(n_log_bytes));
745       break;
746 #undef  EVENT
747 #define EVENT BackupAborted
748     case NDB_LE_BackupAborted:
749       ndbout_c("Node %u: Backup %u started from %d has been aborted. Error: %d",
750                R, Q(backup_id), Q(starting_node), Q(error));
751       break;
752     /**
753      * NDB_MGM_EVENT_CATEGORY_STARTUP
754      */
755 #undef  EVENT
756 #define EVENT NDBStartStarted
757     case NDB_LE_NDBStartStarted:
758       ndbout_c("Node %u: Start initiated (version %d.%d.%d)",
759                R, QVERSION);
760       break;
761 #undef  EVENT
762 #define EVENT NDBStartCompleted
763     case NDB_LE_NDBStartCompleted:
764       ndbout_c("Node %u: Started (version %d.%d.%d)",
765                R, QVERSION);
766       break;
767 #undef  EVENT
768 #define EVENT NDBStopStarted
769     case NDB_LE_NDBStopStarted:
770       ndbout_c("Node %u: %s shutdown initiated", R,
771                (Q(stoptype) == 1 ? "Cluster" : "Node"));
772       break;
773 #undef  EVENT
774 #define EVENT NDBStopCompleted
775     case NDB_LE_NDBStopCompleted:
776       {
777         BaseString action_str("");
778         BaseString signum_str("");
779         getRestartAction(Q(action), action_str);
780         if (Q(signum))
781           signum_str.appfmt(" Initiated by signal %d.",
782                             Q(signum));
783         ndbout_c("Node %u: Node shutdown completed%s.%s",
784                  R, action_str.c_str(), signum_str.c_str());
785       }
786       break;
787 #undef  EVENT
788 #define EVENT NDBStopForced
789     case NDB_LE_NDBStopForced:
790       {
791         BaseString action_str("");
792         BaseString reason_str("");
793         BaseString sphase_str("");
794         int signum = Q(signum);
795         int error = Q(error);
796         int sphase = Q(sphase);
797         int extra = Q(extra);
798         getRestartAction(Q(action), action_str);
799         if (signum)
800           reason_str.appfmt(" Initiated by signal %d.", signum);
801         if (error)
802         {
803           ndbd_exit_classification cl;
804           ndbd_exit_status st;
805           const char *msg = ndbd_exit_message(error, &cl);
806           const char *cl_msg = ndbd_exit_classification_message(cl, &st);
807           const char *st_msg = ndbd_exit_status_message(st);
808           reason_str.appfmt(" Caused by error %d: \'%s(%s). %s\'.",
809                             error, msg, cl_msg, st_msg);
810           if (extra != 0)
811             reason_str.appfmt(" (extra info %d)", extra);
812         }
813         if (sphase < 255)
814           sphase_str.appfmt(" Occured during startphase %u.", sphase);
815         ndbout_c("Node %u: Forced node shutdown completed%s.%s%s",
816                  R, action_str.c_str(), sphase_str.c_str(),
817                  reason_str.c_str());
818       }
819       break;
820 #undef  EVENT
821 #define EVENT StopAborted
822     case NDB_LE_NDBStopAborted:
823       ndbout_c("Node %u: Node shutdown aborted", R);
824       break;
825     /**
826      * NDB_MGM_EVENT_CATEGORY_STATISTIC
827      */
828 #undef EVENT
829 #define EVENT MemoryUsage
830     case NDB_LE_MemoryUsage:
831     {
832 
833       if (Q(gth) == 0)
834       {
835         // Only print MemoryUsage report for increased/decreased
836         break;
837       }
838 
839       const int percent = Q(pages_total) ? (Q(pages_used)*100)/Q(pages_total) : 0;
840       ndbout_c("Node %u: %s usage %s %d%s(%d %dK pages of total %d)", R,
841                (Q(block) == DBACC ? "Index" : (Q(block) == DBTUP ?"Data":"<unknown>")),
842                (Q(gth) > 0 ? "increased to" : "decreased to"),
843                percent, "%",
844                Q(pages_used), Q(page_size_kb)/1024, Q(pages_total));
845       break;
846     }
847     /**
848      * default nothing to print
849      */
850     default:
851       break;
852   }
853 }
854 
855 //*****************************************************************************
856 //*****************************************************************************
857 
858 struct event_thread_param {
859   NdbMgmHandle *m;
860   NdbMutex **p;
861 };
862 
863 static int do_event_thread = 0;
864 
865 static void*
event_thread_run(void * p)866 event_thread_run(void* p)
867 {
868   DBUG_ENTER("event_thread_run");
869 
870   struct event_thread_param param= *(struct event_thread_param*)p;
871   NdbMgmHandle handle= *(param.m);
872   NdbMutex* printmutex= *(param.p);
873 
874   int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP,
875 		   1, NDB_MGM_EVENT_CATEGORY_STARTUP,
876                    5, NDB_MGM_EVENT_CATEGORY_STATISTIC,
877 		   0 };
878 
879   NdbLogEventHandle log_handle= NULL;
880   struct ndb_logevent log_event;
881 
882   log_handle= ndb_mgm_create_logevent_handle(handle, filter);
883   if (log_handle)
884   {
885     do_event_thread= 1;
886     do {
887       int res= ndb_logevent_get_next(log_handle, &log_event, 2000);
888       if (res > 0)
889       {
890         Guard g(printmutex);
891         printLogEvent(&log_event);
892       }
893       else if (res < 0)
894         break;
895     } while(do_event_thread);
896     ndb_mgm_destroy_logevent_handle(&log_handle);
897   }
898   else
899   {
900     do_event_thread= 0;
901   }
902 
903   DBUG_RETURN(NULL);
904 }
905 
906 bool
connect(bool interactive)907 CommandInterpreter::connect(bool interactive)
908 {
909   DBUG_ENTER("CommandInterpreter::connect");
910 
911   if(m_connected)
912     DBUG_RETURN(m_connected);
913 
914   m_mgmsrv = ndb_mgm_create_handle();
915   if(m_mgmsrv == NULL) {
916     ndbout_c("Can't create handle to management server.");
917     exit(-1);
918   }
919 
920   if (interactive) {
921     m_mgmsrv2 = ndb_mgm_create_handle();
922     if(m_mgmsrv2 == NULL) {
923       ndbout_c("Can't create 2:nd handle to management server.");
924       exit(-1);
925     }
926   }
927 
928   if (ndb_mgm_set_connectstring(m_mgmsrv, m_constr))
929   {
930     printError();
931     exit(-1);
932   }
933 
934   if(ndb_mgm_connect(m_mgmsrv, m_try_reconnect-1, 5, 1))
935     DBUG_RETURN(m_connected); // couldn't connect, always false
936 
937   const char *host= ndb_mgm_get_connected_host(m_mgmsrv);
938   unsigned port= ndb_mgm_get_connected_port(m_mgmsrv);
939   if (interactive) {
940     BaseString constr;
941     constr.assfmt("%s:%d",host,port);
942     if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) &&
943        !ndb_mgm_connect(m_mgmsrv2, m_try_reconnect-1, 5, 1))
944     {
945       DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d",
946                          host, port));
947       assert(m_event_thread == NULL);
948       assert(do_event_thread == 0);
949       do_event_thread= 0;
950       struct event_thread_param p;
951       p.m= &m_mgmsrv2;
952       p.p= &m_print_mutex;
953       m_event_thread = NdbThread_Create(event_thread_run,
954                                         (void**)&p,
955                                         0, // default stack size
956                                         "CommandInterpreted_event_thread",
957                                         NDB_THREAD_PRIO_LOW);
958       if (m_event_thread)
959       {
960         DBUG_PRINT("info",("Thread created ok, waiting for started..."));
961         int iter= 1000; // try for 30 seconds
962         while(do_event_thread == 0 &&
963               iter-- > 0)
964           NdbSleep_MilliSleep(30);
965       }
966       if (m_event_thread == NULL ||
967           do_event_thread == 0 ||
968           do_event_thread == -1)
969       {
970         DBUG_PRINT("info",("Warning, event thread startup failed, "
971                            "degraded printouts as result, errno=%d",
972                            errno));
973         printf("Warning, event thread startup failed, "
974                "degraded printouts as result, errno=%d\n", errno);
975         do_event_thread= 0;
976         if (m_event_thread)
977         {
978           void *res;
979           NdbThread_WaitFor(m_event_thread, &res);
980           NdbThread_Destroy(&m_event_thread);
981         }
982         ndb_mgm_disconnect(m_mgmsrv2);
983       }
984     }
985     else
986     {
987       DBUG_PRINT("warning",
988                  ("Could not do 2:nd connect to mgmtserver for event listening"));
989       DBUG_PRINT("info", ("code: %d, msg: %s",
990                           ndb_mgm_get_latest_error(m_mgmsrv2),
991                           ndb_mgm_get_latest_error_msg(m_mgmsrv2)));
992       printf("Warning, event connect failed, degraded printouts as result\n");
993       printf("code: %d, msg: %s\n",
994              ndb_mgm_get_latest_error(m_mgmsrv2),
995              ndb_mgm_get_latest_error_msg(m_mgmsrv2));
996     }
997   }
998   m_connected= true;
999   DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port));
1000   if (m_verbose)
1001   {
1002     printf("Connected to Management Server at: %s:%d\n",
1003            host, port);
1004   }
1005 
1006   DBUG_RETURN(m_connected);
1007 }
1008 
1009 void
disconnect(void)1010 CommandInterpreter::disconnect(void)
1011 {
1012   DBUG_ENTER("CommandInterpreter::disconnect");
1013 
1014   if (m_event_thread) {
1015     void *res;
1016     do_event_thread= 0;
1017     NdbThread_WaitFor(m_event_thread, &res);
1018     NdbThread_Destroy(&m_event_thread);
1019     m_event_thread= NULL;
1020     ndb_mgm_destroy_handle(&m_mgmsrv2);
1021   }
1022   if (m_connected)
1023   {
1024     ndb_mgm_destroy_handle(&m_mgmsrv);
1025     m_connected= false;
1026   }
1027   DBUG_VOID_RETURN;
1028 }
1029 
1030 //*****************************************************************************
1031 //*****************************************************************************
1032 
1033 bool
execute(const char * _line,int try_reconnect,bool interactive,int * error)1034 CommandInterpreter::execute(const char *_line, int try_reconnect,
1035 			    bool interactive, int *error)
1036 {
1037   if (try_reconnect >= 0)
1038     m_try_reconnect = try_reconnect;
1039   bool result= execute_impl(_line, interactive);
1040   if (error)
1041     *error= m_error;
1042 
1043   return result;
1044 }
1045 
1046 static void
invalid_command(const char * cmd,const char * msg=0)1047 invalid_command(const char *cmd, const char *msg=0)
1048 {
1049   ndbout << "Invalid command: " << cmd << endl;
1050   if(msg)
1051       ndbout << msg << endl;
1052   ndbout << "Type HELP for help." << endl << endl;
1053 }
1054 
1055 
1056 // Utility class for easier checking of args
1057 // given to the commands
1058 class ClusterInfo {
1059   ndb_mgm_cluster_state* m_status;
1060 
1061 public:
ClusterInfo()1062   ClusterInfo() :
1063     m_status(NULL) {};
1064 
~ClusterInfo()1065   ~ClusterInfo() {
1066     if (m_status)
1067       free(m_status);
1068   }
1069 
fetch(NdbMgmHandle handle,bool all_nodes=false)1070   bool fetch(NdbMgmHandle handle, bool all_nodes = false) {
1071 
1072     const ndb_mgm_node_type types[2] = {
1073       NDB_MGM_NODE_TYPE_NDB,
1074       NDB_MGM_NODE_TYPE_UNKNOWN
1075     };
1076     m_status = ndb_mgm_get_status2(handle,
1077                                    !all_nodes ? types : 0);
1078     if (m_status == NULL)
1079     {
1080       ndbout_c("ERROR: couldn't fetch cluster status");
1081       return false;
1082     }
1083     return true;
1084   }
1085 
is_valid_ndb_nodeid(int nodeid) const1086   bool is_valid_ndb_nodeid(int nodeid) const {
1087     // Check valid NDB nodeid
1088     if (nodeid < 1 || nodeid >= MAX_NDB_NODES)
1089     {
1090       ndbout_c("ERROR: illegal nodeid %d!", nodeid);
1091       return false;
1092     }
1093     return true;
1094   }
1095 
is_ndb_node(int nodeid) const1096   bool is_ndb_node(int nodeid) const {
1097 
1098     if (!is_valid_ndb_nodeid(nodeid))
1099       return false;
1100 
1101     bool found = false;
1102     for (int i = 0; i < m_status->no_of_nodes; i++)
1103     {
1104       if (m_status->node_states[i].node_id == nodeid &&
1105           m_status->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB)
1106       {
1107         found = true;
1108         break;
1109       }
1110     }
1111 
1112     if (!found)
1113       ndbout_c("ERROR: node %d is not a NDB node!", nodeid);
1114 
1115     return found;
1116   }
1117 };
1118 
1119 
1120 
1121 static void
split_args(const char * line,Vector<BaseString> & args)1122 split_args(const char* line, Vector<BaseString>& args)
1123 {
1124   // Split the command line on space
1125   BaseString tmp(line);
1126   tmp.split(args);
1127 
1128   // Remove any empty args which come from double
1129   // spaces in the command line
1130   // ie. "hello<space><space>world" becomes ("hello, "", "world")
1131   //
1132   for (unsigned i= 0; i < args.size(); i++)
1133     if (args[i].length() == 0)
1134       args.erase(i--);
1135 }
1136 
1137 
1138 bool
execute_impl(const char * _line,bool interactive)1139 CommandInterpreter::execute_impl(const char *_line, bool interactive)
1140 {
1141   DBUG_ENTER("CommandInterpreter::execute_impl");
1142   DBUG_PRINT("enter",("line='%s'", _line));
1143   m_error= 0;
1144 
1145   if(_line == NULL)
1146   {
1147     // Pressing Ctrl-C on some platforms will cause 'readline' to
1148     // to return NULL, handle it as graceful exit of ndb_mgm
1149     m_error = -1;
1150     DBUG_RETURN(false); // Terminate gracefully
1151   }
1152 
1153   char* line = strdup(_line);
1154   if (line == NULL)
1155   {
1156     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1157     m_error = -1;
1158     DBUG_RETURN(false); // Terminate gracefully
1159   }
1160   NdbAutoPtr<char> ap(line);
1161 
1162   int do_continue;
1163   do {
1164     do_continue= 0;
1165     BaseString::trim(line," \t");
1166     if (line[0] == 0 ||
1167 	line[0] == '#')
1168     {
1169       DBUG_RETURN(true);
1170     }
1171     // for mysql client compatability remove trailing ';'
1172     {
1173       unsigned last= (unsigned)(strlen(line)-1);
1174       if (line[last] == ';')
1175       {
1176 	line[last]= 0;
1177 	do_continue= 1;
1178       }
1179     }
1180   } while (do_continue);
1181 
1182   // if there is anything in the line proceed
1183   Vector<BaseString> command_list;
1184   split_args(line, command_list);
1185 
1186   char* firstToken = strtok(line, " ");
1187   char* allAfterFirstToken = strtok(NULL, "");
1188 
1189   if (native_strcasecmp(firstToken, "HELP") == 0 ||
1190       native_strcasecmp(firstToken, "?") == 0) {
1191     m_error = executeHelp(allAfterFirstToken);
1192     DBUG_RETURN(true);
1193   }
1194   else if (native_strcasecmp(firstToken, "CONNECT") == 0) {
1195     m_error = executeConnect(allAfterFirstToken, interactive);
1196     DBUG_RETURN(true);
1197   }
1198   else if (native_strcasecmp(firstToken, "SLEEP") == 0) {
1199     if (allAfterFirstToken)
1200       NdbSleep_SecSleep(atoi(allAfterFirstToken));
1201     DBUG_RETURN(true);
1202   }
1203   else if((native_strcasecmp(firstToken, "QUIT") == 0 ||
1204 	  native_strcasecmp(firstToken, "EXIT") == 0 ||
1205 	  native_strcasecmp(firstToken, "BYE") == 0) &&
1206 	  allAfterFirstToken == NULL){
1207     DBUG_RETURN(false);
1208   }
1209 
1210   if (!connect(interactive)){
1211     m_error = -1;
1212     DBUG_RETURN(true);
1213   }
1214 
1215   if (ndb_mgm_check_connection(m_mgmsrv))
1216   {
1217     disconnect();
1218     connect(interactive);
1219   }
1220 
1221   if (native_strcasecmp(firstToken, "SHOW") == 0) {
1222     Guard g(m_print_mutex);
1223     m_error = executeShow(allAfterFirstToken);
1224     DBUG_RETURN(true);
1225   }
1226   else if (native_strcasecmp(firstToken, "SHUTDOWN") == 0) {
1227     m_error= executeShutdown(allAfterFirstToken);
1228     DBUG_RETURN(true);
1229   }
1230   else if (native_strcasecmp(firstToken, "CLUSTERLOG") == 0){
1231     executeClusterLog(allAfterFirstToken);
1232     DBUG_RETURN(true);
1233   }
1234   else if(native_strcasecmp(firstToken, "START") == 0 &&
1235 	  allAfterFirstToken != NULL &&
1236 	  native_strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
1237     m_error= executeStartBackup(allAfterFirstToken, interactive);
1238     DBUG_RETURN(true);
1239   }
1240   else if(native_strcasecmp(firstToken, "ABORT") == 0 &&
1241 	  allAfterFirstToken != NULL &&
1242 	  native_strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
1243     m_error = executeAbortBackup(allAfterFirstToken);
1244     DBUG_RETURN(true);
1245   }
1246   else if (native_strcasecmp(firstToken, "PURGE") == 0) {
1247     m_error = executePurge(allAfterFirstToken);
1248     DBUG_RETURN(true);
1249   }
1250   else if(native_strcasecmp(firstToken, "ENTER") == 0 &&
1251 	  allAfterFirstToken != NULL &&
1252 	  allAfterFirstToken != NULL &&
1253 	  native_strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
1254 		  sizeof("SINGLE USER MODE") - 1) == 0){
1255     m_error = executeEnterSingleUser(allAfterFirstToken);
1256     DBUG_RETURN(true);
1257   }
1258   else if(native_strcasecmp(firstToken, "EXIT") == 0 &&
1259 	  allAfterFirstToken != NULL &&
1260 	  native_strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
1261 		  sizeof("SINGLE USER MODE") - 1) == 0){
1262     m_error = executeExitSingleUser(allAfterFirstToken);
1263     DBUG_RETURN(true);
1264   }
1265   else if(native_strcasecmp(firstToken, "CREATE") == 0 &&
1266 	  allAfterFirstToken != NULL &&
1267 	  native_strncasecmp(allAfterFirstToken, "NODEGROUP",
1268                       sizeof("NODEGROUP") - 1) == 0){
1269     m_error = executeCreateNodeGroup(allAfterFirstToken);
1270     DBUG_RETURN(true);
1271   }
1272   else if(native_strcasecmp(firstToken, "DROP") == 0 &&
1273 	  allAfterFirstToken != NULL &&
1274 	  native_strncasecmp(allAfterFirstToken, "NODEGROUP",
1275                       sizeof("NODEGROUP") - 1) == 0){
1276     m_error = executeDropNodeGroup(allAfterFirstToken);
1277     DBUG_RETURN(true);
1278   }
1279   else if (native_strcasecmp(firstToken, "ALL") == 0) {
1280     m_error = analyseAfterFirstToken(-1, allAfterFirstToken);
1281   } else {
1282     /**
1283      * First tokens should be digits, node ID's
1284      */
1285     int node_ids[MAX_NODES];
1286     unsigned pos;
1287     for (pos= 0; pos < command_list.size(); pos++)
1288     {
1289       int node_id;
1290       if (convert(command_list[pos].c_str(), node_id))
1291       {
1292         if (node_id <= 0 || node_id > MAX_NODES) {
1293           ndbout << "Invalid node ID: " << command_list[pos].c_str()
1294                  << "." << endl;
1295           DBUG_RETURN(true);
1296         }
1297         node_ids[pos]= node_id;
1298         continue;
1299       }
1300       break;
1301     }
1302     int no_of_nodes= pos;
1303     if (no_of_nodes == 0)
1304     {
1305       /* No digit found */
1306       invalid_command(_line);
1307       m_error = -1;
1308       DBUG_RETURN(true);
1309     }
1310     if (pos == command_list.size())
1311     {
1312       /* No command found */
1313       invalid_command(_line);
1314       m_error = -1;
1315       DBUG_RETURN(true);
1316     }
1317     if (no_of_nodes == 1)
1318     {
1319       m_error = analyseAfterFirstToken(node_ids[0], allAfterFirstToken);
1320       DBUG_RETURN(true);
1321     }
1322     m_error = executeCommand(command_list, pos, node_ids, no_of_nodes);
1323     DBUG_RETURN(true);
1324   }
1325   DBUG_RETURN(true);
1326 }
1327 
1328 
1329 /**
1330  * List of commands used as second command argument
1331  */
1332 static const CommandInterpreter::CommandFunctionPair commands[] = {
1333   { "START", &CommandInterpreter::executeStart }
1334   ,{ "RESTART", &CommandInterpreter::executeRestart }
1335   ,{ "STOP", &CommandInterpreter::executeStop }
1336   ,{ "STATUS", &CommandInterpreter::executeStatus }
1337   ,{ "LOGLEVEL", &CommandInterpreter::executeLogLevel }
1338   ,{ "CLUSTERLOG", &CommandInterpreter::executeEventReporting }
1339 #ifdef ERROR_INSERT
1340   ,{ "ERROR", &CommandInterpreter::executeError }
1341 #endif
1342   ,{ "LOG", &CommandInterpreter::executeLog }
1343   ,{ "TESTON", &CommandInterpreter::executeTestOn }
1344   ,{ "TESTOFF", &CommandInterpreter::executeTestOff }
1345   ,{ "DUMP", &CommandInterpreter::executeDumpState }
1346   ,{ "REPORT", &CommandInterpreter::executeReport }
1347 };
1348 
1349 
1350 //*****************************************************************************
1351 //*****************************************************************************
1352 int
analyseAfterFirstToken(int processId,char * allAfterFirstToken)1353 CommandInterpreter::analyseAfterFirstToken(int processId,
1354 					   char* allAfterFirstToken) {
1355 
1356   int retval = 0;
1357   if (emptyString(allAfterFirstToken)) {
1358     ndbout << "Expected a command after "
1359 	   << ((processId == -1) ? "ALL." : "node ID.") << endl;
1360     return -1;
1361   }
1362 
1363   char* secondToken = strtok(allAfterFirstToken, " ");
1364   char* allAfterSecondToken = strtok(NULL, "\0");
1365 
1366   const int tmpSize = sizeof(commands)/sizeof(CommandFunctionPair);
1367   ExecuteFunction fun = 0;
1368   const char * command = 0;
1369   for(int i = 0; i<tmpSize; i++){
1370     if(native_strcasecmp(secondToken, commands[i].command) == 0){
1371       fun = commands[i].executeFunction;
1372       command = commands[i].command;
1373       break;
1374     }
1375   }
1376 
1377   if(fun == 0){
1378     invalid_command(secondToken);
1379     return -1;
1380   }
1381 
1382   if(processId == -1){
1383     retval = executeForAll(command, fun, allAfterSecondToken);
1384   } else {
1385     retval = (this->*fun)(processId, allAfterSecondToken, false);
1386   }
1387   ndbout << endl;
1388   return retval;
1389 }
1390 
1391 int
executeCommand(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)1392 CommandInterpreter::executeCommand(Vector<BaseString> &command_list,
1393                                    unsigned command_pos,
1394                                    int *node_ids, int no_of_nodes)
1395 {
1396   const char *cmd= command_list[command_pos].c_str();
1397   int retval = 0;
1398 
1399   if (native_strcasecmp("STOP", cmd) == 0)
1400   {
1401     retval = executeStop(command_list, command_pos+1, node_ids, no_of_nodes);
1402     return retval;
1403   }
1404   if (native_strcasecmp("RESTART", cmd) == 0)
1405   {
1406     retval = executeRestart(command_list, command_pos+1, node_ids, no_of_nodes);
1407     return retval;
1408   }
1409   if (native_strcasecmp("START", cmd) == 0)
1410   {
1411     retval = executeStart(command_list, command_pos+1, node_ids, no_of_nodes);
1412     return retval;
1413   }
1414   ndbout_c("Invalid command: '%s' after multi node id list. "
1415            "Expected STOP, START, or RESTART.", cmd);
1416   return -1;
1417 }
1418 
1419 /**
1420  * Get next nodeid larger than the give node_id. node_id will be
1421  * set to the next node_id in the list. node_id should be set
1422  * to 0 (zero) on the first call.
1423  *
1424  * @param handle the NDB management handle
1425  * @param node_id last node_id retreived, 0 at first call
1426  * @param type type of node to look for
1427  * @return 1 if a node was found, 0 if no more node exist
1428  */
1429 static
1430 int
get_next_nodeid(struct ndb_mgm_cluster_state * cl,int * node_id,enum ndb_mgm_node_type type)1431 get_next_nodeid(struct ndb_mgm_cluster_state *cl,
1432 		int *node_id,
1433 		enum ndb_mgm_node_type type)
1434 {
1435   int i;
1436 
1437   if(cl == NULL)
1438     return 0;
1439 
1440   i=0;
1441   while((i < cl->no_of_nodes)) {
1442     if((*node_id < cl->node_states[i].node_id) &&
1443        (cl->node_states[i].node_type == type)) {
1444 
1445       if(i >= cl->no_of_nodes)
1446 	return 0;
1447 
1448       *node_id = cl->node_states[i].node_id;
1449       return 1;
1450     }
1451     i++;
1452   }
1453 
1454   return 0;
1455 }
1456 
1457 int
executeForAll(const char * cmd,ExecuteFunction fun,const char * allAfterSecondToken)1458 CommandInterpreter::executeForAll(const char * cmd, ExecuteFunction fun,
1459 				  const char * allAfterSecondToken)
1460 {
1461   int nodeId = 0;
1462   int retval = 0;
1463 
1464   if(native_strcasecmp(cmd, "STOP") == 0) {
1465     ndbout_c("Executing STOP on all nodes.");
1466     retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1467   } else if(native_strcasecmp(cmd, "RESTART") == 0) {
1468     retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1469   } else if (native_strcasecmp(cmd, "STATUS") == 0) {
1470     (this->*fun)(nodeId, allAfterSecondToken, true);
1471   } else if (native_strcasecmp(cmd, "REPORT") == 0) {
1472     Guard g(m_print_mutex);
1473     retval = executeReport(nodeId, allAfterSecondToken, true);
1474   } else {
1475     Guard g(m_print_mutex);
1476     struct ndb_mgm_cluster_state *cl= ndb_mgm_get_status(m_mgmsrv);
1477     if(cl == 0){
1478       ndbout_c("Unable get status from management server");
1479       printError();
1480       return -1;
1481     }
1482     NdbAutoPtr<char> ap1((char*)cl);
1483     while(get_next_nodeid(cl, &nodeId, NDB_MGM_NODE_TYPE_NDB))
1484       retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1485   }
1486   return retval;
1487 }
1488 
1489 //*****************************************************************************
1490 //*****************************************************************************
1491 bool
parseBlockSpecification(const char * allAfterLog,Vector<BaseString> & blocks)1492 CommandInterpreter::parseBlockSpecification(const char* allAfterLog,
1493 					    Vector<BaseString>& blocks)
1494 {
1495   // Parse: [BLOCK = {ALL|<blockName>+}]
1496 
1497   if (emptyString(allAfterLog)) {
1498     return true;
1499   }
1500 
1501   // Copy allAfterLog since strtok will modify it
1502   char* newAllAfterLog = strdup(allAfterLog);
1503   if (newAllAfterLog == NULL)
1504   {
1505     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1506     return false; // Error parsing
1507   }
1508 
1509   NdbAutoPtr<char> ap1(newAllAfterLog);
1510   char* firstTokenAfterLog = strtok(newAllAfterLog, " ");
1511   for (unsigned int i = 0; i < strlen(firstTokenAfterLog); ++i) {
1512     firstTokenAfterLog[i] = toupper(firstTokenAfterLog[i]);
1513   }
1514 
1515   if (native_strcasecmp(firstTokenAfterLog, "BLOCK") != 0) {
1516     ndbout << "Unexpected value: " << firstTokenAfterLog
1517 	   << ". Expected BLOCK." << endl;
1518     return false;
1519   }
1520 
1521   char* allAfterFirstToken = strtok(NULL, "\0");
1522   if (emptyString(allAfterFirstToken)) {
1523     ndbout << "Expected =." << endl;
1524     return false;
1525   }
1526 
1527   char* secondTokenAfterLog = strtok(allAfterFirstToken, " ");
1528   if (native_strcasecmp(secondTokenAfterLog, "=") != 0) {
1529     ndbout << "Unexpected value: " << secondTokenAfterLog
1530 	   << ". Expected =." << endl;
1531     return false;
1532   }
1533 
1534   char* blockName = strtok(NULL, " ");
1535   bool all = false;
1536   if (blockName != NULL && (native_strcasecmp(blockName, "ALL") == 0)) {
1537     all = true;
1538   }
1539   while (blockName != NULL) {
1540     blocks.push_back(blockName);
1541     blockName = strtok(NULL, " ");
1542   }
1543 
1544   if (blocks.size() == 0) {
1545     ndbout << "No block specified." << endl;
1546     return false;
1547   }
1548   if (blocks.size() > 1 && all) {
1549     // More than "ALL" specified
1550     ndbout << "Nothing expected after ALL." << endl;
1551     return false;
1552   }
1553 
1554   return true;
1555 }
1556 
1557 
1558 
1559 /*****************************************************************************
1560  * HELP
1561  *****************************************************************************/
1562 int
executeHelp(char * parameters)1563 CommandInterpreter::executeHelp(char* parameters)
1564 {
1565   if (emptyString(parameters)) {
1566     ndbout << helpText;
1567 
1568     ndbout << endl
1569 	   << "<severity> = "
1570 	   << "ALERT | CRITICAL | ERROR | WARNING | INFO | DEBUG"
1571 	   << endl;
1572 
1573     ndbout << "<category> = ";
1574     for(int i = CFG_MIN_LOGLEVEL; i <= CFG_MAX_LOGLEVEL; i++){
1575       const char *str= ndb_mgm_get_event_category_string((ndb_mgm_event_category)i);
1576       if (str) {
1577 	if (i != CFG_MIN_LOGLEVEL)
1578 	  ndbout << " | ";
1579 	ndbout << str;
1580       }
1581     }
1582     ndbout << endl;
1583 
1584     helpTextReportTypeOptionFn();
1585 
1586     ndbout << "<level>    = " << "0 - 15" << endl;
1587     ndbout << "<id>       = " << "ALL | Any database node id" << endl;
1588     ndbout << endl;
1589     ndbout << "For detailed help on COMMAND, use HELP COMMAND." << endl;
1590   } else {
1591     int i = 0;
1592     for (i = 0; help_items[i].cmd != NULL; i++)
1593     {
1594       if (native_strcasecmp(parameters, help_items[i].cmd) == 0)
1595       {
1596         if (help_items[i].help)
1597           ndbout << help_items[i].help;
1598         if (help_items[i].help_fn)
1599           (*help_items[i].help_fn)();
1600         break;
1601       }
1602     }
1603     if (help_items[i].cmd == NULL){
1604       ndbout << "No help for " << parameters << " available" << endl;
1605       return -1;
1606     }
1607   }
1608   return 0;
1609 }
1610 
1611 
1612 /*****************************************************************************
1613  * SHUTDOWN
1614  *****************************************************************************/
1615 
1616 int
executeShutdown(char * parameters)1617 CommandInterpreter::executeShutdown(char* parameters)
1618 {
1619   ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
1620   if(state == NULL) {
1621     ndbout_c("Could not get status");
1622     printError();
1623     return 1;
1624   }
1625   NdbAutoPtr<char> ap1((char*)state);
1626 
1627   int result = 0;
1628   int need_disconnect;
1629   result = ndb_mgm_stop3(m_mgmsrv, -1, 0, 0, &need_disconnect);
1630   if (result < 0) {
1631     ndbout << "Shutdown of NDB Cluster node(s) failed." << endl;
1632     printError();
1633     return result;
1634   }
1635 
1636   ndbout << result << " NDB Cluster node(s) have shutdown." << endl;
1637 
1638   if(need_disconnect) {
1639     ndbout << "Disconnecting to allow management server to shutdown."
1640            << endl;
1641     disconnect();
1642   }
1643   return 0;
1644 }
1645 
1646 /*****************************************************************************
1647  * SHOW
1648  *****************************************************************************/
1649 
1650 
1651 static
status_string(ndb_mgm_node_status status)1652 const char *status_string(ndb_mgm_node_status status)
1653 {
1654   switch(status){
1655   case NDB_MGM_NODE_STATUS_NO_CONTACT:
1656     return "not connected";
1657   case NDB_MGM_NODE_STATUS_NOT_STARTED:
1658     return "not started";
1659   case NDB_MGM_NODE_STATUS_STARTING:
1660     return "starting";
1661   case NDB_MGM_NODE_STATUS_STARTED:
1662     return "started";
1663   case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
1664     return "shutting down";
1665   case NDB_MGM_NODE_STATUS_RESTARTING:
1666     return "restarting";
1667   case NDB_MGM_NODE_STATUS_SINGLEUSER:
1668     return "single user mode";
1669   default:
1670     return "unknown state";
1671   }
1672 }
1673 
1674 static void
print_nodes(ndb_mgm_cluster_state * state,ndb_mgm_configuration_iterator * it,const char * proc_name,int no_proc,ndb_mgm_node_type type,int master_id)1675 print_nodes(ndb_mgm_cluster_state *state, ndb_mgm_configuration_iterator *it,
1676 	    const char *proc_name, int no_proc, ndb_mgm_node_type type,
1677 	    int master_id)
1678 {
1679   int i;
1680   ndbout << "[" << proc_name
1681 	 << "(" << ndb_mgm_get_node_type_string(type) << ")]\t"
1682 	 << no_proc << " node(s)" << endl;
1683   for(i=0; i < state->no_of_nodes; i++) {
1684     struct ndb_mgm_node_state *node_state= &(state->node_states[i]);
1685     if(node_state->node_type == type) {
1686       int node_id= node_state->node_id;
1687       ndbout << "id=" << node_id;
1688       if(node_state->version != 0) {
1689 	const char *hostname= node_state->connect_address;
1690 	if (hostname == 0
1691 	    || strlen(hostname) == 0
1692 	    || native_strcasecmp(hostname,"0.0.0.0") == 0)
1693 	  ndbout << " ";
1694 	else
1695 	  ndbout << "\t@" << hostname;
1696 
1697 	char tmp[100];
1698 	ndbout << "  (" << ndbGetVersionString(node_state->version,
1699                                                node_state->mysql_version,
1700                                                0,
1701                                                tmp, sizeof(tmp));
1702 	if (type == NDB_MGM_NODE_TYPE_NDB) {
1703 	  if (node_state->node_status != NDB_MGM_NODE_STATUS_STARTED) {
1704 	    ndbout << ", " << status_string(node_state->node_status);
1705 	  }
1706 	  if (node_state->node_group >= 0 && node_state->node_group != (int)RNIL) {
1707 	    ndbout << ", Nodegroup: " << node_state->node_group;
1708           }
1709           else if (node_state->node_group == (int)RNIL)
1710           {
1711             ndbout << ", no nodegroup";
1712           }
1713           if (node_state->node_group >= 0 || node_state->node_group == (int)RNIL)
1714 	    if (master_id && node_state->dynamic_id == master_id)
1715 	      ndbout << ", *";
1716         }
1717 	ndbout << ")" << endl;
1718       } else {
1719 	ndb_mgm_first(it);
1720 	if(ndb_mgm_find(it, CFG_NODE_ID, node_id) == 0){
1721 	  const char *config_hostname= 0;
1722 	  ndb_mgm_get_string_parameter(it, CFG_NODE_HOST, &config_hostname);
1723 	  if (config_hostname == 0 || config_hostname[0] == 0)
1724 	    config_hostname= "any host";
1725 	  ndbout_c(" (not connected, accepting connect from %s)",
1726 		   config_hostname);
1727 	}
1728 	else
1729 	{
1730 	  ndbout_c("Unable to find node with id: %d", node_id);
1731 	}
1732       }
1733     }
1734   }
1735   ndbout << endl;
1736 }
1737 
1738 int
executePurge(char * parameters)1739 CommandInterpreter::executePurge(char* parameters)
1740 {
1741   int command_ok= 0;
1742   do {
1743     if (emptyString(parameters))
1744       break;
1745     char* firstToken = strtok(parameters, " ");
1746     char* nextToken = strtok(NULL, " \0");
1747     if (native_strcasecmp(firstToken,"STALE") == 0 &&
1748 	nextToken &&
1749 	native_strcasecmp(nextToken, "SESSIONS") == 0) {
1750       command_ok= 1;
1751       break;
1752     }
1753   } while(0);
1754 
1755   if (!command_ok) {
1756     ndbout_c("Unexpected command, expected: PURGE STALE SESSIONS");
1757     return -1;
1758   }
1759 
1760   char *str;
1761 
1762   if (ndb_mgm_purge_stale_sessions(m_mgmsrv, &str)) {
1763     ndbout_c("Command failed");
1764     return -1;
1765   }
1766   if (str) {
1767     ndbout_c("Purged sessions with node id's: %s", str);
1768     free(str);
1769   }
1770   else
1771   {
1772     ndbout_c("No sessions purged");
1773   }
1774   return 0;
1775 }
1776 
1777 int
executeShow(char * parameters)1778 CommandInterpreter::executeShow(char* parameters)
1779 {
1780   int i;
1781   if (emptyString(parameters)) {
1782     ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
1783     if(state == NULL) {
1784       ndbout_c("Could not get status");
1785       printError();
1786       return -1;
1787     }
1788     NdbAutoPtr<char> ap1((char*)state);
1789 
1790     ndb_mgm_configuration * conf = ndb_mgm_get_configuration(m_mgmsrv,0);
1791     if(conf == 0){
1792       ndbout_c("Could not get configuration");
1793       printError();
1794       return -1;
1795     }
1796 
1797     ndb_mgm_configuration_iterator * it;
1798     it = ndb_mgm_create_configuration_iterator((struct ndb_mgm_configuration *)conf, CFG_SECTION_NODE);
1799 
1800     if(it == 0){
1801       ndbout_c("Unable to create config iterator");
1802       ndb_mgm_destroy_configuration(conf);
1803       return -1;
1804     }
1805     NdbAutoPtr<ndb_mgm_configuration_iterator> ptr(it);
1806 
1807     int
1808       master_id= 0,
1809       ndb_nodes= 0,
1810       api_nodes= 0,
1811       mgm_nodes= 0;
1812 
1813     for(i=0; i < state->no_of_nodes; i++) {
1814       if(state->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB &&
1815 	 state->node_states[i].version != 0){
1816 	master_id= state->node_states[i].dynamic_id;
1817 	break;
1818       }
1819     }
1820 
1821     for(i=0; i < state->no_of_nodes; i++) {
1822       switch(state->node_states[i].node_type) {
1823       case NDB_MGM_NODE_TYPE_API:
1824 	api_nodes++;
1825 	break;
1826       case NDB_MGM_NODE_TYPE_NDB:
1827 	if (state->node_states[i].dynamic_id &&
1828 	    state->node_states[i].dynamic_id < master_id)
1829 	  master_id= state->node_states[i].dynamic_id;
1830 	ndb_nodes++;
1831 	break;
1832       case NDB_MGM_NODE_TYPE_MGM:
1833 	mgm_nodes++;
1834 	break;
1835       case NDB_MGM_NODE_TYPE_UNKNOWN:
1836         ndbout << "Error: Unknown Node Type" << endl;
1837         return -1;
1838       case NDB_MGM_NODE_TYPE_MAX:
1839         break;                                  /* purify: deadcode */
1840       }
1841     }
1842 
1843     ndbout << "Cluster Configuration" << endl
1844 	   << "---------------------" << endl;
1845     print_nodes(state, it, "ndbd",     ndb_nodes, NDB_MGM_NODE_TYPE_NDB, master_id);
1846     print_nodes(state, it, "ndb_mgmd", mgm_nodes, NDB_MGM_NODE_TYPE_MGM, 0);
1847     print_nodes(state, it, "mysqld",   api_nodes, NDB_MGM_NODE_TYPE_API, 0);
1848     ndb_mgm_destroy_configuration(conf);
1849     return 0;
1850   } else {
1851     ndbout << "Invalid argument: '" << parameters << "'" << endl;
1852     return -1;
1853   }
1854   return 0;
1855 }
1856 
1857 int
executeConnect(char * parameters,bool interactive)1858 CommandInterpreter::executeConnect(char* parameters, bool interactive)
1859 {
1860   BaseString *basestring = NULL;
1861 
1862   disconnect();
1863   if (!emptyString(parameters)) {
1864     basestring= new BaseString(parameters);
1865     m_constr= basestring->trim().c_str();
1866   }
1867   if ( connect(interactive) == false ){
1868     return -1;
1869   }
1870   if (basestring != NULL)
1871     delete basestring;
1872 
1873   return 0;
1874 }
1875 
1876 //*****************************************************************************
1877 //*****************************************************************************
1878 void
executeClusterLog(char * parameters)1879 CommandInterpreter::executeClusterLog(char* parameters)
1880 {
1881   DBUG_ENTER("CommandInterpreter::executeClusterLog");
1882   int i;
1883   if (emptyString(parameters))
1884   {
1885     ndbout_c("ERROR: Missing argument(s).");
1886     m_error = -1;
1887     DBUG_VOID_RETURN;
1888   }
1889 
1890   enum ndb_mgm_event_severity severity = NDB_MGM_EVENT_SEVERITY_ALL;
1891 
1892   char * tmpString = strdup(parameters);
1893   if (tmpString == NULL)
1894   {
1895     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1896     m_error = -1;
1897     DBUG_VOID_RETURN;
1898   }
1899 
1900   NdbAutoPtr<char> ap1(tmpString);
1901   char * tmpPtr = 0;
1902   char * item = my_strtok_r(tmpString, " ", &tmpPtr);
1903   int enable;
1904 
1905   ndb_mgm_severity enabled[NDB_MGM_EVENT_SEVERITY_ALL] =
1906     {{NDB_MGM_EVENT_SEVERITY_ON,0},
1907      {NDB_MGM_EVENT_SEVERITY_DEBUG,0},
1908      {NDB_MGM_EVENT_SEVERITY_INFO,0},
1909      {NDB_MGM_EVENT_SEVERITY_WARNING,0},
1910      {NDB_MGM_EVENT_SEVERITY_ERROR,0},
1911      {NDB_MGM_EVENT_SEVERITY_CRITICAL,0},
1912      {NDB_MGM_EVENT_SEVERITY_ALERT,0}};
1913   int returned = ndb_mgm_get_clusterlog_severity_filter(m_mgmsrv,
1914                                                   &enabled[0],
1915                                                   NDB_MGM_EVENT_SEVERITY_ALL);
1916   if(returned != NDB_MGM_EVENT_SEVERITY_ALL) {
1917     ndbout << "Couldn't get status" << endl;
1918     printError();
1919     m_error = -1;
1920     DBUG_VOID_RETURN;
1921   }
1922 
1923   /********************
1924    * CLUSTERLOG INFO
1925    ********************/
1926   if (native_strcasecmp(item, "INFO") == 0) {
1927     DBUG_PRINT("info",("INFO"));
1928     if(enabled[0].value == 0)
1929     {
1930       ndbout << "Cluster logging is disabled." << endl;
1931       m_error = 0;
1932       DBUG_VOID_RETURN;
1933     }
1934 #if 0
1935     for(i = 0; i<DB_MGM_EVENT_SEVERITY_ALL;i++)
1936       printf("enabled[%d] = %d\n", i, enabled[i].value);
1937 #endif
1938     ndbout << "Severities enabled: ";
1939     for(i = 1; i < (int)NDB_MGM_EVENT_SEVERITY_ALL; i++) {
1940       const char *str= ndb_mgm_get_event_severity_string(enabled[i].category);
1941       if (str == 0)
1942       {
1943 	DBUG_ASSERT(false);
1944 	continue;
1945       }
1946       if(enabled[i].value)
1947 	ndbout << BaseString(str).ndb_toupper() << " ";
1948     }
1949     ndbout << endl;
1950     m_error = 0;
1951     DBUG_VOID_RETURN;
1952 
1953   }
1954   else if (native_strcasecmp(item, "FILTER") == 0 ||
1955 	   native_strcasecmp(item, "TOGGLE") == 0)
1956   {
1957     DBUG_PRINT("info",("TOGGLE"));
1958     enable= -1;
1959   }
1960   else if (native_strcasecmp(item, "OFF") == 0)
1961   {
1962     DBUG_PRINT("info",("OFF"));
1963     enable= 0;
1964   } else if (native_strcasecmp(item, "ON") == 0) {
1965     DBUG_PRINT("info",("ON"));
1966     enable= 1;
1967   } else {
1968     ndbout << "Invalid argument." << endl;
1969     m_error = -1;
1970     DBUG_VOID_RETURN;
1971   }
1972 
1973   int res_enable;
1974   item = my_strtok_r(NULL, " ", &tmpPtr);
1975   if (item == NULL) {
1976     res_enable=
1977       ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv,
1978 					     NDB_MGM_EVENT_SEVERITY_ON,
1979 					     enable, NULL);
1980     if (res_enable < 0)
1981     {
1982       ndbout << "Couldn't set filter" << endl;
1983       printError();
1984       m_error = -1;
1985       DBUG_VOID_RETURN;
1986     }
1987     ndbout << "Cluster logging is " << (res_enable ? "enabled.":"disabled") << endl;
1988     m_error = 0;
1989     DBUG_VOID_RETURN;
1990   }
1991 
1992   do {
1993     severity= NDB_MGM_ILLEGAL_EVENT_SEVERITY;
1994     if (native_strcasecmp(item, "ALL") == 0) {
1995       severity = NDB_MGM_EVENT_SEVERITY_ALL;
1996     } else if (native_strcasecmp(item, "ALERT") == 0) {
1997       severity = NDB_MGM_EVENT_SEVERITY_ALERT;
1998     } else if (native_strcasecmp(item, "CRITICAL") == 0) {
1999       severity = NDB_MGM_EVENT_SEVERITY_CRITICAL;
2000     } else if (native_strcasecmp(item, "ERROR") == 0) {
2001       severity = NDB_MGM_EVENT_SEVERITY_ERROR;
2002     } else if (native_strcasecmp(item, "WARNING") == 0) {
2003       severity = NDB_MGM_EVENT_SEVERITY_WARNING;
2004     } else if (native_strcasecmp(item, "INFO") == 0) {
2005       severity = NDB_MGM_EVENT_SEVERITY_INFO;
2006     } else if (native_strcasecmp(item, "DEBUG") == 0) {
2007       severity = NDB_MGM_EVENT_SEVERITY_DEBUG;
2008     } else if (native_strcasecmp(item, "OFF") == 0 ||
2009 	       native_strcasecmp(item, "ON") == 0) {
2010       if (enable < 0) // only makes sense with toggle
2011 	severity = NDB_MGM_EVENT_SEVERITY_ON;
2012     }
2013     if (severity == NDB_MGM_ILLEGAL_EVENT_SEVERITY) {
2014       ndbout << "Invalid severity level: " << item << endl;
2015       m_error = -1;
2016       DBUG_VOID_RETURN;
2017     }
2018 
2019     res_enable= ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv, severity,
2020 						       enable, NULL);
2021     if (res_enable < 0)
2022     {
2023       ndbout << "Couldn't set filter" << endl;
2024       printError();
2025       m_error = -1;
2026       DBUG_VOID_RETURN;
2027     }
2028     ndbout << BaseString(item).ndb_toupper().c_str() << " " << (res_enable ? "enabled":"disabled") << endl;
2029 
2030     item = my_strtok_r(NULL, " ", &tmpPtr);
2031   } while(item != NULL);
2032 
2033   m_error = 0;
2034   DBUG_VOID_RETURN;
2035 }
2036 
2037 //*****************************************************************************
2038 //*****************************************************************************
2039 
2040 int
executeStop(int processId,const char * parameters,bool all)2041 CommandInterpreter::executeStop(int processId, const char *parameters,
2042                                 bool all)
2043 {
2044   Vector<BaseString> command_list;
2045   if (parameters)
2046     split_args(parameters, command_list);
2047 
2048   int retval;
2049   if (all)
2050     retval = executeStop(command_list, 0, 0, 0);
2051   else
2052     retval = executeStop(command_list, 0, &processId, 1);
2053 
2054   return retval;
2055 }
2056 
2057 int
executeStop(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2058 CommandInterpreter::executeStop(Vector<BaseString> &command_list,
2059                                 unsigned command_pos,
2060                                 int *node_ids, int no_of_nodes)
2061 {
2062   int need_disconnect;
2063   int abort= 0;
2064   int retval = 0;
2065   int force = 0;
2066 
2067   for (; command_pos < command_list.size(); command_pos++)
2068   {
2069     const char *item= command_list[command_pos].c_str();
2070     if (native_strcasecmp(item, "-A") == 0)
2071     {
2072       abort= 1;
2073       continue;
2074     }
2075     if (native_strcasecmp(item, "-F") == 0)
2076     {
2077       force = 1;
2078       continue;
2079     }
2080     ndbout_c("Invalid option: %s. Expecting -A or -F after STOP",
2081              item);
2082     return -1;
2083   }
2084 
2085   int result= ndb_mgm_stop4(m_mgmsrv, no_of_nodes, node_ids, abort,
2086                             force, &need_disconnect);
2087   if (result < 0)
2088   {
2089     ndbout_c("Shutdown failed.");
2090     printError();
2091     retval = -1;
2092   }
2093   else
2094   {
2095     if (node_ids == 0)
2096       ndbout_c("NDB Cluster has shutdown.");
2097     else
2098     {
2099       ndbout << "Node";
2100       for (int i= 0; i < no_of_nodes; i++)
2101           ndbout << " " << node_ids[i];
2102       ndbout_c(" has shutdown.");
2103     }
2104   }
2105 
2106   if(need_disconnect)
2107   {
2108     ndbout << "Disconnecting to allow Management Server to shutdown" << endl;
2109     disconnect();
2110   }
2111 
2112   return retval;
2113 }
2114 
2115 int
executeEnterSingleUser(char * parameters)2116 CommandInterpreter::executeEnterSingleUser(char* parameters)
2117 {
2118   strtok(parameters, " ");
2119   struct ndb_mgm_reply reply;
2120   char* id = strtok(NULL, " ");
2121   id = strtok(NULL, " ");
2122   id = strtok(NULL, "\0");
2123   int nodeId = -1;
2124   if(id == 0 || sscanf(id, "%d", &nodeId) != 1){
2125     ndbout_c("Invalid arguments: expected <NodeId>");
2126     ndbout_c("Use SHOW to see what API nodes are configured");
2127     return -1;
2128   }
2129   int result = ndb_mgm_enter_single_user(m_mgmsrv, nodeId, &reply);
2130 
2131   if (result != 0) {
2132     ndbout_c("Entering single user mode for node %d failed", nodeId);
2133     printError();
2134     return -1;
2135   } else {
2136     ndbout_c("Single user mode entered");
2137     ndbout_c("Access is granted for API node %d only.", nodeId);
2138   }
2139   return 0;
2140 }
2141 
2142 int
executeExitSingleUser(char * parameters)2143 CommandInterpreter::executeExitSingleUser(char* parameters)
2144 {
2145   int result = ndb_mgm_exit_single_user(m_mgmsrv, 0);
2146   if (result != 0) {
2147     ndbout_c("Exiting single user mode failed.");
2148     printError();
2149     return -1;
2150   } else {
2151     ndbout_c("Exiting single user mode in progress.");
2152     ndbout_c("Use ALL STATUS or SHOW to see when single user mode has been exited.");
2153     return 0;
2154   }
2155 }
2156 
2157 int
executeStart(int processId,const char * parameters,bool all)2158 CommandInterpreter::executeStart(int processId, const char* parameters,
2159 				 bool all)
2160 {
2161   int result;
2162   int retval = 0;
2163   if(all) {
2164     result = ndb_mgm_start(m_mgmsrv, 0, 0);
2165   } else {
2166     result = ndb_mgm_start(m_mgmsrv, 1, &processId);
2167   }
2168 
2169   if (result <= 0) {
2170     ndbout << "Start failed." << endl;
2171     printError();
2172     retval = -1;
2173   } else
2174     {
2175       if(all)
2176 	ndbout_c("NDB Cluster is being started.");
2177       else
2178 	ndbout_c("Database node %d is being started.", processId);
2179     }
2180   return retval;
2181 }
2182 
2183 int
executeStart(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2184 CommandInterpreter::executeStart(Vector<BaseString> &command_list,
2185                                  unsigned command_pos,
2186                                  int *node_ids, int no_of_nodes)
2187 {
2188   int result;
2189   result= ndb_mgm_start(m_mgmsrv, no_of_nodes, node_ids);
2190 
2191   if (result <= 0) {
2192     ndbout_c("Start failed.");
2193     printError();
2194     return -1;
2195   }
2196   else
2197   {
2198     ndbout << "Node";
2199     for (int i= 0; i < no_of_nodes; i++)
2200       ndbout << " " << node_ids[i];
2201     ndbout_c(" is being started");
2202   }
2203   return 0;
2204 }
2205 
2206 int
executeRestart(int processId,const char * parameters,bool all)2207 CommandInterpreter::executeRestart(int processId, const char* parameters,
2208 				   bool all)
2209 {
2210   Vector<BaseString> command_list;
2211   if (parameters)
2212     split_args(parameters, command_list);
2213 
2214   int retval;
2215   if (all)
2216     retval = executeRestart(command_list, 0, 0, 0);
2217   else
2218     retval = executeRestart(command_list, 0, &processId, 1);
2219 
2220   return retval;
2221 }
2222 
2223 int
executeRestart(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2224 CommandInterpreter::executeRestart(Vector<BaseString> &command_list,
2225                                    unsigned command_pos,
2226                                    int *node_ids, int no_of_nodes)
2227 {
2228   int result;
2229   int retval = 0;
2230   int nostart= 0;
2231   int initialstart= 0;
2232   int abort= 0;
2233   int need_disconnect= 0;
2234   int force = 0;
2235 
2236   for (; command_pos < command_list.size(); command_pos++)
2237   {
2238     const char *item= command_list[command_pos].c_str();
2239     if (native_strcasecmp(item, "-N") == 0)
2240     {
2241       nostart= 1;
2242       continue;
2243     }
2244     if (native_strcasecmp(item, "-I") == 0)
2245     {
2246       initialstart= 1;
2247       continue;
2248     }
2249     if (native_strcasecmp(item, "-A") == 0)
2250     {
2251       abort= 1;
2252       continue;
2253     }
2254     if (native_strcasecmp(item, "-F") == 0)
2255     {
2256       force = 1;
2257       continue;
2258     }
2259     ndbout_c("Invalid option: %s. Expecting -A,-N,-I or -F after RESTART",
2260              item);
2261     return -1;
2262   }
2263 
2264   struct ndb_mgm_cluster_state *cl = ndb_mgm_get_status(m_mgmsrv);
2265   if(cl == NULL)
2266   {
2267     ndbout_c("Could not get status");
2268     printError();
2269     return -1;
2270   }
2271   NdbAutoPtr<char> ap1((char*)cl);
2272 
2273   // We allow 'all restart' in single user mode
2274   if(node_ids != 0) {
2275     for (int i = 0; i<cl->no_of_nodes; i++) {
2276       if((cl->node_states+i)->node_status == NDB_MGM_NODE_STATUS_SINGLEUSER)
2277       {
2278         ndbout_c("Cannot restart nodes: single user mode");
2279         return -1;
2280       }
2281     }
2282   }
2283 
2284   if (node_ids == 0) {
2285     ndbout_c("Executing RESTART on all nodes.");
2286     ndbout_c("Starting shutdown. This may take a while. Please wait...");
2287   }
2288 
2289   for (int i= 0; i < no_of_nodes; i++)
2290   {
2291     int j = 0;
2292     while((j < cl->no_of_nodes) && cl->node_states[j].node_id != node_ids[i])
2293       j++;
2294 
2295     if(cl->node_states[j].node_id != node_ids[i])
2296     {
2297       ndbout << node_ids[i] << ": Node not found" << endl;
2298       return -1;
2299     }
2300 
2301     if(cl->node_states[j].node_type == NDB_MGM_NODE_TYPE_MGM)
2302     {
2303       ndbout << "Shutting down MGM node"
2304 	     << " " << node_ids[i] << " for restart" << endl;
2305     }
2306   }
2307 
2308   result= ndb_mgm_restart4(m_mgmsrv, no_of_nodes, node_ids,
2309                            initialstart, nostart, abort, force,
2310                            &need_disconnect);
2311 
2312   if (result <= 0) {
2313     ndbout_c("Restart failed.");
2314     printError();
2315     retval = -1;
2316   }
2317   else
2318   {
2319     if (node_ids == 0)
2320       ndbout_c("All DB nodes are being restarted.");
2321     else
2322     {
2323       ndbout << "Node";
2324       for (int i= 0; i < no_of_nodes; i++)
2325         ndbout << " " << node_ids[i];
2326       ndbout_c(" is being restarted");
2327     }
2328     if(need_disconnect)
2329       disconnect();
2330   }
2331   return retval;
2332 }
2333 
2334 /**
2335  * print status of one node
2336  */
2337 static
2338 void
print_status(const ndb_mgm_node_state * state)2339 print_status(const ndb_mgm_node_state * state)
2340 {
2341   Uint32 version = state->version;
2342   if (state->node_type != NDB_MGM_NODE_TYPE_NDB)
2343   {
2344     if (version != 0)
2345     {
2346       ndbout << "Node " << state->node_id <<": connected" ;
2347       ndbout_c(" (Version %d.%d.%d)",
2348                getMajor(version) ,
2349                getMinor(version),
2350                getBuild(version));
2351 
2352     }
2353     else
2354     {
2355       ndbout << "Node " << state->node_id << ": not connected" << endl;
2356     }
2357     return;
2358   }
2359 
2360   ndbout << "Node " << state->node_id
2361          << ": " << status_string(state->node_status);
2362   switch(state->node_status){
2363   case NDB_MGM_NODE_STATUS_STARTING:
2364     ndbout << " (Last completed phase " << state->start_phase << ")";
2365     break;
2366   case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
2367     ndbout << " (Last completed phase " << state->start_phase << ")";
2368     break;
2369   default:
2370     break;
2371   }
2372 
2373   if(state->node_status != NDB_MGM_NODE_STATUS_NO_CONTACT)
2374   {
2375     char tmp[100];
2376     ndbout_c(" (%s)", ndbGetVersionString(version,
2377                                           state->mysql_version, 0,
2378 					  tmp, sizeof(tmp)));
2379   }
2380   else
2381   {
2382     ndbout << endl;
2383   }
2384 }
2385 
2386 int
executeStatus(int processId,const char * parameters,bool all)2387 CommandInterpreter::executeStatus(int processId,
2388 				  const char* parameters, bool all)
2389 {
2390   if (! emptyString(parameters)) {
2391     ndbout_c("No parameters expected to this command.");
2392     return -1;
2393   }
2394 
2395   ndb_mgm_node_type types[2] = {
2396     NDB_MGM_NODE_TYPE_NDB,
2397     NDB_MGM_NODE_TYPE_UNKNOWN
2398   };
2399   struct ndb_mgm_cluster_state *cl;
2400   cl = ndb_mgm_get_status2(m_mgmsrv, all ? types : 0);
2401   if(cl == NULL)
2402   {
2403     ndbout_c("Can't get status of node %d.", processId);
2404     printError();
2405     return -1;
2406   }
2407   NdbAutoPtr<char> ap1((char*)cl);
2408 
2409   if (all)
2410   {
2411     for (int i = 0; i<cl->no_of_nodes; i++)
2412       print_status(cl->node_states+i);
2413     return 0;
2414   }
2415   else
2416   {
2417     for (int i = 0; i<cl->no_of_nodes; i++)
2418     {
2419       if (cl->node_states[i].node_id == processId)
2420       {
2421         print_status(cl->node_states + i);
2422         return 0;
2423       }
2424     }
2425     ndbout << processId << ": Node not found" << endl;
2426     return -1;
2427   }
2428   return 0;
2429 } //
2430 
2431 int
executeDumpState(int processId,const char * parameters,bool all)2432 CommandInterpreter::executeDumpState(int processId, const char* parameters,
2433 				     bool all)
2434 {
2435   if(emptyString(parameters))
2436   {
2437     ndbout_c("ERROR: Expected argument!");
2438     return -1;
2439   }
2440 
2441   int params[25];
2442   int num_params = 0;
2443   const size_t max_params = sizeof(params)/sizeof(params[0]);
2444 
2445   Vector<BaseString> args;
2446   split_args(parameters, args);
2447 
2448   if (args.size() > max_params)
2449   {
2450     ndbout_c("ERROR: Too many arguments, max %d allowed", (int)max_params);
2451     return -1;
2452   }
2453 
2454   for (unsigned i = 0; i < args.size(); i++)
2455   {
2456     const char* arg = args[i].c_str();
2457 
2458     if (my_strtoll(arg, NULL, 0) < 0 ||
2459         my_strtoll(arg, NULL, 0) > 0xffffffff)
2460     {
2461       ndbout_c("ERROR: Illegal value '%s' in argument to signal.\n"
2462                "(Value must be between 0 and 0xffffffff.)", arg);
2463       return -1;
2464     }
2465     assert(num_params < (int)max_params);
2466     params[num_params] = (int)my_strtoll(arg, NULL, 0);
2467     num_params++;
2468   }
2469 
2470   ndbout << "Sending dump signal with data:" << endl;
2471   for (int i = 0; i < num_params; i++)
2472   {
2473     ndbout.setHexFormat(1) << params[i] << " ";
2474     if (!((i+1) & 0x3)) ndbout << endl;
2475   }
2476   ndbout << endl;
2477 
2478   struct ndb_mgm_reply reply;
2479   return ndb_mgm_dump_state(m_mgmsrv, processId, params, num_params, &reply);
2480 }
2481 
2482 static void
report_memoryusage(const ndb_logevent & event)2483 report_memoryusage(const ndb_logevent& event)
2484 {
2485   const ndb_logevent_MemoryUsage& usage = event.MemoryUsage;
2486   const Uint32 block = usage.block;
2487   const Uint32 total = usage.pages_total;
2488   const Uint32 used = usage.pages_used;
2489   assert(event.type == NDB_LE_MemoryUsage);
2490 
2491   ndbout_c("Node %u: %s usage is %d%%(%d %dK pages of total %d)",
2492            event.source_nodeid,
2493            (block == DBACC ? "Index" : (block == DBTUP ? "Data" : "<unknown>")),
2494            (total ? (used * 100 / total) : 0),
2495            used,
2496            usage.page_size_kb/1024,
2497            total);
2498 }
2499 
2500 
2501 static void
report_backupstatus(const ndb_logevent & event)2502 report_backupstatus(const ndb_logevent& event)
2503 {
2504   const ndb_logevent_BackupStatus& status = event.BackupStatus;
2505   assert(event.type == NDB_LE_BackupStatus);
2506 
2507 
2508   if (status.starting_node)
2509     ndbout_c("Node %u: Local backup status: backup %u started from node %u\n"
2510              " #Records: %llu #LogRecords: %llu\n"
2511              " Data: %llu bytes Log: %llu bytes",
2512              event.source_nodeid,
2513              status.backup_id,
2514              refToNode(status.starting_node),
2515              make_uint64(status.n_records_lo, status.n_records_hi),
2516              make_uint64(status.n_log_records_lo, status.n_log_records_hi),
2517              make_uint64(status.n_bytes_lo, status.n_bytes_hi),
2518              make_uint64(status.n_log_bytes_lo, status.n_log_bytes_hi));
2519   else
2520     ndbout_c("Node %u: Backup not started",
2521              event.source_nodeid);
2522 }
2523 
2524 static
2525 void
report_events(const ndb_logevent & event)2526 report_events(const ndb_logevent& event)
2527 {
2528   Uint32 threshold = 0;
2529   Logger::LoggerLevel severity = Logger::LL_WARNING;
2530   LogLevel::EventCategory cat= LogLevel::llInvalid;
2531   EventLogger::EventTextFunction textF;
2532 
2533   const EventReport * real_event = (const EventReport*)event.SavedEvent.data;
2534   Uint32 type = real_event->getEventType();
2535 
2536   if (EventLoggerBase::event_lookup(type,cat,threshold,severity,textF))
2537     return;
2538 
2539   char out[1024];
2540   Uint32 pos = 0;
2541   if (event.source_nodeid != 0)
2542   {
2543     BaseString::snprintf(out, sizeof(out), "Node %u: ", event.source_nodeid);
2544     pos= (Uint32)strlen(out);
2545   }
2546   textF(out+pos, sizeof(out)-pos, event.SavedEvent.data, event.SavedEvent.len);
2547 
2548   char timestamp_str[64];
2549   Logger::format_timestamp(event.SavedEvent.time, timestamp_str, sizeof(timestamp_str));
2550 
2551   ndbout_c("%s %s", timestamp_str, out);
2552 }
2553 
2554 static int
sort_log(const void * _a,const void * _b)2555 sort_log(const void *_a, const void *_b)
2556 {
2557   const ndb_logevent * a = (const ndb_logevent*)_a;
2558   const ndb_logevent * b = (const ndb_logevent*)_b;
2559 
2560   if (a->source_nodeid == b->source_nodeid)
2561   {
2562     return a->SavedEvent.seq - b->SavedEvent.seq;
2563   }
2564 
2565   if (a->SavedEvent.time < b->SavedEvent.time)
2566     return -1;
2567   if (a->SavedEvent.time > b->SavedEvent.time)
2568     return 1;
2569 
2570   if (a->SavedEvent.seq < b->SavedEvent.seq)
2571     return -1;
2572   if (a->SavedEvent.seq > b->SavedEvent.seq)
2573     return 1;
2574 
2575   return (a->source_nodeid - b->source_nodeid);
2576 }
2577 
2578 static const
2579 struct st_report_cmd {
2580   const char *name;
2581   const char *help;
2582   Ndb_logevent_type type;
2583   void (*print_event_fn)(const ndb_logevent&);
2584   int (* sort_fn)(const void *_a, const void *_b);
2585 } report_cmds[] = {
2586 
2587   { "BackupStatus",
2588     "Report backup status of respective node",
2589     NDB_LE_BackupStatus,
2590     report_backupstatus, 0 },
2591 
2592   { "MemoryUsage",
2593     "Report memory usage of respective node",
2594     NDB_LE_MemoryUsage,
2595     report_memoryusage, 0 },
2596 
2597   { "EventLog",
2598     "Report events in datanodes circular event log buffer",
2599     NDB_LE_SavedEvent,
2600     report_events, sort_log },
2601 
2602   { 0, 0, NDB_LE_ILLEGAL_TYPE, 0, 0 }
2603 };
2604 
2605 
2606 int
executeReport(int nodeid,const char * parameters,bool all)2607 CommandInterpreter::executeReport(int nodeid, const char* parameters,
2608                                   bool all)
2609 {
2610   if (emptyString(parameters))
2611   {
2612     ndbout_c("ERROR: missing report type specifier!");
2613     return -1;
2614   }
2615 
2616   Vector<BaseString> args;
2617   split_args(parameters, args);
2618 
2619   const st_report_cmd* report_cmd = report_cmds;
2620   for (; report_cmd->name; report_cmd++)
2621   {
2622     if (native_strncasecmp(report_cmd->name, args[0].c_str(),
2623                     args[0].length()) == 0)
2624       break;
2625   }
2626 
2627   if (!report_cmd->name)
2628   {
2629     ndbout_c("ERROR: '%s' - report type specifier unknown!", args[0].c_str());
2630     return -1;
2631   }
2632 
2633   if (!all)
2634   {
2635     ClusterInfo info;
2636     if (!info.fetch(m_mgmsrv))
2637     {
2638       printError();
2639       return -1;
2640     }
2641 
2642     // Check that given nodeid is a NDB node
2643     if (!info.is_ndb_node(nodeid))
2644       return -1;
2645   }
2646 
2647   struct ndb_mgm_events* events =
2648     ndb_mgm_dump_events(m_mgmsrv, report_cmd->type,
2649                         all ? 0 : 1, &nodeid);
2650   if (!events)
2651   {
2652     ndbout_c("ERROR: failed to fetch report!");
2653     printError();
2654     return -1;
2655   }
2656 
2657   if (report_cmd->sort_fn)
2658   {
2659     qsort(events->events, events->no_of_events,
2660           sizeof(events->events[0]), report_cmd->sort_fn);
2661   }
2662 
2663   for (int i = 0; i < events->no_of_events; i++)
2664   {
2665     const ndb_logevent& event = events->events[i];
2666     report_cmd->print_event_fn(event);
2667   }
2668 
2669   free(events);
2670   return 0;
2671 }
2672 
2673 
2674 static void
helpTextReportFn()2675 helpTextReportFn()
2676 {
2677   ndbout_c("  <report-type> =");
2678   const st_report_cmd* report_cmd = report_cmds;
2679   for (; report_cmd->name; report_cmd++)
2680     ndbout_c("    %s\t- %s", report_cmd->name, report_cmd->help);
2681 }
2682 
2683 static void
helpTextReportTypeOptionFn()2684 helpTextReportTypeOptionFn()
2685 {
2686   ndbout << "<report-type> = ";
2687   const st_report_cmd* report_cmd = report_cmds;
2688   for (; report_cmd->name; report_cmd++){
2689     if (report_cmd != report_cmds)
2690       ndbout << " | ";
2691     ndbout << BaseString(report_cmd->name).ndb_toupper().c_str();
2692   }
2693   ndbout << endl;
2694 }
2695 
2696 //*****************************************************************************
2697 //*****************************************************************************
2698 
2699 int
executeLogLevel(int processId,const char * parameters,bool all)2700 CommandInterpreter::executeLogLevel(int processId, const char* parameters,
2701 				    bool all)
2702 {
2703   (void) all;
2704   if (emptyString(parameters)) {
2705     ndbout << "Expected argument" << endl;
2706     return -1;
2707   }
2708   BaseString tmp(parameters);
2709   Vector<BaseString> spec;
2710   tmp.split(spec, "=");
2711   if(spec.size() != 2){
2712     ndbout << "Invalid loglevel specification: " << parameters << endl;
2713     return -1;
2714   }
2715 
2716   spec[0].trim().ndb_toupper();
2717   int category = ndb_mgm_match_event_category(spec[0].c_str());
2718   if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
2719     category = atoi(spec[0].c_str());
2720     if(category < NDB_MGM_MIN_EVENT_CATEGORY ||
2721        category > NDB_MGM_MAX_EVENT_CATEGORY){
2722       ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
2723       return -1;
2724     }
2725   }
2726 
2727   int level = atoi(spec[1].c_str());
2728   if(level < 0 || level > 15){
2729     ndbout << "Invalid level: " << spec[1].c_str() << endl;
2730     return -1;
2731   }
2732 
2733   ndbout << "Executing LOGLEVEL on node " << processId << flush;
2734 
2735   struct ndb_mgm_reply reply;
2736   int result;
2737   result = ndb_mgm_set_loglevel_node(m_mgmsrv,
2738 				     processId,
2739 				     (ndb_mgm_event_category)category,
2740 				     level,
2741 				     &reply);
2742 
2743   if (result < 0) {
2744     ndbout_c(" failed.");
2745     printError();
2746     return -1;
2747   } else {
2748     ndbout_c(" OK!");
2749   }
2750   return 0;
2751 }
2752 
2753 //*****************************************************************************
2754 //*****************************************************************************
executeError(int processId,const char * parameters,bool)2755 int CommandInterpreter::executeError(int processId,
2756 				      const char* parameters, bool /* all */)
2757 {
2758   if (emptyString(parameters))
2759   {
2760     ndbout_c("ERROR: Missing error number.");
2761     return -1;
2762   }
2763 
2764   Vector<BaseString> args;
2765   split_args(parameters, args);
2766 
2767   if (args.size() >= 2)
2768   {
2769     ndbout << "ERROR: Too many arguments." << endl;
2770     return -1;
2771   }
2772 
2773   int errorNo;
2774   if (! convert(args[0].c_str(), errorNo)) {
2775     ndbout << "ERROR: Expected an integer." << endl;
2776     return -1;
2777   }
2778 
2779   return ndb_mgm_insert_error(m_mgmsrv, processId, errorNo, NULL);
2780 }
2781 
2782 //*****************************************************************************
2783 //*****************************************************************************
2784 
2785 int
executeLog(int processId,const char * parameters,bool all)2786 CommandInterpreter::executeLog(int processId,
2787 			       const char* parameters, bool all)
2788 {
2789   struct ndb_mgm_reply reply;
2790   Vector<BaseString> blocks;
2791   if (! parseBlockSpecification(parameters, blocks)) {
2792     return -1;
2793   }
2794 
2795   BaseString block_names;
2796   for (unsigned i = 0; i<blocks.size(); i++)
2797     block_names.appfmt("%s|", blocks[i].c_str());
2798 
2799   int result = ndb_mgm_log_signals(m_mgmsrv,
2800 				   processId,
2801 				   NDB_MGM_SIGNAL_LOG_MODE_INOUT,
2802 				   block_names.c_str(),
2803 				   &reply);
2804   if (result != 0) {
2805     ndbout_c("Execute LOG on node %d failed.", processId);
2806     printError();
2807     return -1;
2808   }
2809   return 0;
2810 }
2811 
2812 
2813 //*****************************************************************************
2814 //*****************************************************************************
2815 int
executeTestOn(int processId,const char * parameters,bool)2816 CommandInterpreter::executeTestOn(int processId,
2817 				  const char* parameters, bool /*all*/)
2818 {
2819   if (! emptyString(parameters)) {
2820     ndbout << "No parameters expected to this command." << endl;
2821     return -1;
2822   }
2823   struct ndb_mgm_reply reply;
2824   int result = ndb_mgm_start_signallog(m_mgmsrv, processId, &reply);
2825   if (result != 0) {
2826     ndbout_c("Execute TESTON failed.");
2827     printError();
2828     return -1;
2829   }
2830   return 0;
2831 }
2832 
2833 //*****************************************************************************
2834 //*****************************************************************************
2835 int
executeTestOff(int processId,const char * parameters,bool)2836 CommandInterpreter::executeTestOff(int processId,
2837 				   const char* parameters, bool /*all*/)
2838 {
2839   if (! emptyString(parameters)) {
2840     ndbout << "No parameters expected to this command." << endl;
2841     return -1;
2842   }
2843   struct ndb_mgm_reply reply;
2844   int result = ndb_mgm_stop_signallog(m_mgmsrv, processId, &reply);
2845   if (result != 0) {
2846     ndbout_c("Execute TESTOFF failed.");
2847     printError();
2848     return -1;
2849   }
2850   return 0;
2851 }
2852 
2853 
2854 //*****************************************************************************
2855 //*****************************************************************************
2856 
2857 int
executeEventReporting(int processId,const char * parameters,bool all)2858 CommandInterpreter::executeEventReporting(int processId,
2859 					  const char* parameters,
2860 					  bool all)
2861 {
2862   int retval = 0;
2863   if (emptyString(parameters)) {
2864     ndbout << "Expected argument" << endl;
2865     return -1;
2866   }
2867 
2868   Vector<BaseString> specs;
2869   split_args(parameters, specs);
2870 
2871   for (int i=0; i < (int) specs.size(); i++)
2872   {
2873     Vector<BaseString> spec;
2874     specs[i].split(spec, "=");
2875     if(spec.size() != 2){
2876       ndbout << "Invalid loglevel specification: " << specs[i] << endl;
2877       continue;
2878     }
2879 
2880     spec[0].trim().ndb_toupper();
2881     int category = ndb_mgm_match_event_category(spec[0].c_str());
2882     if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
2883       if(!convert(spec[0].c_str(), category) ||
2884 	 category < NDB_MGM_MIN_EVENT_CATEGORY ||
2885 	 category > NDB_MGM_MAX_EVENT_CATEGORY){
2886 	ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
2887 	continue;
2888       }
2889     }
2890 
2891     int level;
2892     if (!convert(spec[1].c_str(),level))
2893     {
2894       ndbout << "Invalid level: " << spec[1].c_str() << endl;
2895       continue;
2896     }
2897 
2898     ndbout << "Executing CLUSTERLOG " << spec[0] << "=" << spec[1]
2899 	   << " on node " << processId << flush;
2900 
2901     struct ndb_mgm_reply reply;
2902     int result;
2903     result = ndb_mgm_set_loglevel_clusterlog(m_mgmsrv,
2904 					     processId,
2905 					     (ndb_mgm_event_category)category,
2906 					     level,
2907 					     &reply);
2908 
2909     if (result != 0) {
2910       ndbout_c(" failed.");
2911       printError();
2912       retval = -1;
2913     } else {
2914       ndbout_c(" OK!");
2915     }
2916   }
2917   return retval;
2918 }
2919 
2920 
2921 /*****************************************************************************
2922  * Backup
2923  *****************************************************************************/
2924 int
executeStartBackup(char * parameters,bool interactive)2925 CommandInterpreter::executeStartBackup(char* parameters, bool interactive)
2926 {
2927   struct ndb_mgm_reply reply;
2928   unsigned int backupId;
2929   unsigned int input_backupId = 0;
2930   unsigned long long int tmp_backupId = 0;
2931 
2932   Vector<BaseString> args;
2933   if (parameters)
2934     split_args(parameters, args);
2935 
2936   for (unsigned i= 0; i < args.size(); i++)
2937     args[i].ndb_toupper();
2938 
2939   int sz= args.size();
2940 
2941   int result;
2942   int flags = 2;
2943   //1,snapshot at start time. 0 snapshot at end time
2944   unsigned int backuppoint = 0;
2945   bool b_log = false;
2946   bool b_nowait = false;
2947   bool b_wait_completed = false;
2948   bool b_wait_started = false;
2949 
2950   /*
2951    All the commands list as follow:
2952    start backup <backupid> nowait | start backup <backupid> snapshotstart/snapshotend nowati | start backup <backupid> nowait snapshotstart/snapshotend
2953    start backup <backupid> | start backup <backupid> wait completed | start backup <backupid> snapshotstart/snapshotend
2954    start backup <backupid> snapshotstart/snapshotend wait completed | start backup <backupid> wait completed snapshotstart/snapshotend
2955    start backup <backupid> wait started | start backup <backupid> snapshotstart/snapshotend wait started
2956    start backup <backupid> wait started snapshotstart/snapshotend
2957   */
2958   for (int i= 1; i < sz; i++)
2959   {
2960     if (i == 1 && sscanf(args[1].c_str(), "%llu", &tmp_backupId) == 1) {
2961       char out[1024];
2962       BaseString::snprintf(out, sizeof(out), "%u: ", MAX_BACKUPS);
2963       // to detect wraparound due to overflow, check if number of digits in
2964       // input backup ID <= number of digits in max backup ID
2965       if (tmp_backupId > 0 && tmp_backupId < MAX_BACKUPS && args[1].length() <= strlen(out)) {
2966         input_backupId = static_cast<unsigned>(tmp_backupId);
2967         continue;
2968       } else {
2969         BaseString::snprintf(out, sizeof(out), "Backup ID out of range [1 - %u]", MAX_BACKUPS-1);
2970         invalid_command(parameters, out);
2971         return -1;
2972       }
2973     }
2974 
2975     if (args[i] == "SNAPSHOTEND") {
2976       if (b_log ==true) {
2977         invalid_command(parameters);
2978         return -1;
2979       }
2980       b_log = true;
2981       backuppoint = 0;
2982       continue;
2983     }
2984     if (args[i] == "SNAPSHOTSTART") {
2985       if (b_log ==true) {
2986         invalid_command(parameters);
2987         return -1;
2988       }
2989       b_log = true;
2990       backuppoint = 1;
2991       continue;
2992     }
2993     if (args[i] == "NOWAIT") {
2994       if (b_nowait == true || b_wait_completed == true || b_wait_started ==true) {
2995         invalid_command(parameters);
2996         return -1;
2997       }
2998       b_nowait = true;
2999       flags = 0;
3000       continue;
3001     }
3002     if (args[i] == "WAIT") {
3003       if (b_nowait == true || b_wait_completed == true || b_wait_started ==true) {
3004         invalid_command(parameters);
3005         return -1;
3006       }
3007       if (i+1 < sz) {
3008         if (args[i+1] == "COMPLETED") {
3009           b_wait_completed = true;
3010           flags = 2;
3011           i++;
3012         }
3013         else if (args[i+1] == "STARTED") {
3014           b_wait_started = true;
3015           flags = 1;
3016           i++;
3017         }
3018         else {
3019           invalid_command(parameters);
3020           return -1;
3021         }
3022       }
3023       else {
3024         invalid_command(parameters);
3025         return -1;
3026       }
3027       continue;
3028     }
3029     invalid_command(parameters);
3030     return -1;
3031   }
3032 
3033   //print message
3034   if (flags == 2)
3035     ndbout_c("Waiting for completed, this may take several minutes");
3036   if (flags == 1)
3037     ndbout_c("Waiting for started, this may take several minutes");
3038 
3039   NdbLogEventHandle log_handle= NULL;
3040   struct ndb_logevent log_event;
3041   if (flags > 0 && !interactive)
3042   {
3043     int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP, 0, 0 };
3044     log_handle = ndb_mgm_create_logevent_handle(m_mgmsrv, filter);
3045     if (!log_handle)
3046     {
3047       ndbout << "Initializing start of backup failed" << endl;
3048       printError();
3049       return -1;
3050     }
3051   }
3052 
3053   //start backup N | start backup snapshotstart/snapshotend
3054   if (input_backupId > 0 || b_log == true)
3055     result = ndb_mgm_start_backup3(m_mgmsrv, flags, &backupId, &reply, input_backupId, backuppoint);
3056   //start backup
3057   else
3058     result = ndb_mgm_start_backup(m_mgmsrv, flags, &backupId, &reply);
3059 
3060   if (result != 0) {
3061     ndbout << "Backup failed" << endl;
3062     printError();
3063 
3064     if (log_handle)
3065       ndb_mgm_destroy_logevent_handle(&log_handle);
3066     return result;
3067   }
3068 
3069   /**
3070    * If interactive, event listner thread is already running
3071    */
3072   if (log_handle && !interactive)
3073   {
3074     int count = 0;
3075     int retry = 0;
3076     int res;
3077     do {
3078       if ((res= ndb_logevent_get_next(log_handle, &log_event, 60000)) > 0)
3079       {
3080         int print = 0;
3081         switch (log_event.type) {
3082           case NDB_LE_BackupStarted:
3083             if (log_event.BackupStarted.backup_id == backupId)
3084               print = 1;
3085             break;
3086           case NDB_LE_BackupCompleted:
3087             if (log_event.BackupCompleted.backup_id == backupId)
3088               print = 1;
3089             break;
3090           case NDB_LE_BackupAborted:
3091             if (log_event.BackupAborted.backup_id == backupId)
3092               print = 1;
3093             break;
3094           default:
3095             break;
3096         }
3097         if (print)
3098         {
3099           Guard g(m_print_mutex);
3100           printLogEvent(&log_event);
3101           count++;
3102           // for WAIT STARTED, exit after printing "Backup started" logevent
3103           if(flags == 1 && log_event.type == NDB_LE_BackupStarted)
3104           {
3105             ndb_mgm_destroy_logevent_handle(&log_handle);
3106             return 0;
3107           }
3108         }
3109       }
3110       else
3111       {
3112         retry++;
3113       }
3114     } while(res >= 0 && count < 2 && retry < 3);
3115 
3116     if (retry >= 3)
3117       ndbout << "get backup event failed for " << retry << " times" << endl;
3118 
3119     ndb_mgm_destroy_logevent_handle(&log_handle);
3120   }
3121 
3122   return 0;
3123 }
3124 
3125 int
executeAbortBackup(char * parameters)3126 CommandInterpreter::executeAbortBackup(char* parameters)
3127 {
3128   unsigned int bid = 0;
3129   unsigned long long int tmp_bid = 0;
3130   struct ndb_mgm_reply reply;
3131   if (emptyString(parameters))
3132     goto executeAbortBackupError1;
3133 
3134   {
3135     strtok(parameters, " ");
3136     char* id = strtok(NULL, "\0");
3137     if(id == 0 || sscanf(id, "%llu", &tmp_bid) != 1)
3138       goto executeAbortBackupError1;
3139 
3140     // to detect wraparound due to overflow, check if number of digits in
3141     // input backup ID > number of digits in max backup ID
3142     char out[1024];
3143     BaseString::snprintf(out, sizeof(out), "%u", MAX_BACKUPS);
3144     if(tmp_bid <= 0 || tmp_bid >= MAX_BACKUPS || strlen(id) > strlen(out))
3145       goto executeAbortBackupError2;
3146     else
3147       bid = static_cast<unsigned>(tmp_bid);
3148   }
3149   {
3150     int result= ndb_mgm_abort_backup(m_mgmsrv, bid, &reply);
3151     if (result != 0) {
3152       ndbout << "Abort of backup " << bid << " failed" << endl;
3153       printError();
3154       return -1;
3155     } else {
3156       ndbout << "Abort of backup " << bid << " ordered" << endl;
3157     }
3158   }
3159   return 0;
3160  executeAbortBackupError1:
3161   ndbout << "Invalid arguments: expected <BackupId>" << endl;
3162   return -1;
3163  executeAbortBackupError2:
3164   ndbout << "Invalid arguments: <BackupId> out of range [1-" << MAX_BACKUPS-1 << "]" << endl;
3165   return -1;
3166 }
3167 
3168 int
executeCreateNodeGroup(char * parameters)3169 CommandInterpreter::executeCreateNodeGroup(char* parameters)
3170 {
3171   char *id= strchr(parameters, ' ');
3172   if (emptyString(id))
3173     goto err;
3174 
3175   {
3176     Vector<int> nodes;
3177     BaseString args(id);
3178     Vector<BaseString> nodelist;
3179     args.split(nodelist, ",");
3180 
3181     for (Uint32 i = 0; i<nodelist.size(); i++)
3182     {
3183       nodes.push_back(atoi(nodelist[i].c_str()));
3184     }
3185     nodes.push_back(0);
3186 
3187     int ng;
3188     struct ndb_mgm_reply reply;
3189     const int result= ndb_mgm_create_nodegroup(m_mgmsrv, nodes.getBase(),
3190                                                &ng, &reply);
3191     if (result != 0) {
3192       printError();
3193       return -1;
3194     } else {
3195       ndbout << "Nodegroup " << ng << " created" << endl;
3196     }
3197 
3198   }
3199 
3200   return 0;
3201 err:
3202   ndbout << "Invalid arguments: expected <id>,<id>..." << endl;
3203   return -1;
3204 }
3205 
3206 int
executeDropNodeGroup(char * parameters)3207 CommandInterpreter::executeDropNodeGroup(char* parameters)
3208 {
3209   int ng = -1;
3210   if (emptyString(parameters))
3211     goto err;
3212 
3213   {
3214     char* id = strchr(parameters, ' ');
3215     if(id == 0 || sscanf(id, "%d", &ng) != 1)
3216       goto err;
3217   }
3218 
3219   {
3220     struct ndb_mgm_reply reply;
3221     const int result= ndb_mgm_drop_nodegroup(m_mgmsrv, ng, &reply);
3222     if (result != 0) {
3223       printError();
3224       return -1;
3225     } else {
3226       ndbout << "Drop Node Group " << ng << " done" << endl;
3227     }
3228   }
3229   return 0;
3230 err:
3231   ndbout << "Invalid arguments: expected <NG>" << endl;
3232   return -1;
3233 }
3234