1 /*
2    Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #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 
547 static const char* helpTextQuit =
548 "---------------------------------------------------------------------------\n"
549 " NDB Cluster -- Management Client -- Help for QUIT command\n"
550 "---------------------------------------------------------------------------\n"
551 "QUIT  Quit management client\n\n"
552 "QUIT               Terminates the management client. \n"
553 ;
554 
555 
556 #ifdef VM_TRACE // DEBUG ONLY
557 static const char* helpTextDebug =
558 "---------------------------------------------------------------------------\n"
559 " NDB Cluster -- Management Client -- Help for Debugging (Internal use only)\n"
560 "---------------------------------------------------------------------------\n"
561 "SHOW PROPERTIES                       Print config properties object\n"
562 "<id> LOGLEVEL {<category>=<level>}+   Set log level\n"
563 #ifdef ERROR_INSERT
564 "<id> ERROR <errorNo>                  Inject error into NDB node\n"
565 #endif
566 "<id> LOG [BLOCK = {ALL|<block>+}]     Set logging on in & out signals\n"
567 "<id> TESTON                           Start signal logging\n"
568 "<id> TESTOFF                          Stop signal logging\n"
569 "<id> DUMP <arg>                       Dump system state to cluster.log\n"
570 "\n"
571 "<id>       = ALL | Any database node id\n"
572 ;
573 #endif
574 
575 struct st_cmd_help {
576   const char *cmd;
577   const char * help;
578   void (* help_fn)();
579 }help_items[]={
580   {"SHOW", helpTextShow, NULL},
581   {"HELP", helpTextHelp, NULL},
582   {"BACKUP", helpTextBackup, NULL},
583   {"START BACKUP", helpTextStartBackup, NULL},
584   {"START BACKUP NOWAIT", helpTextStartBackup, NULL},
585   {"START BACKUP WAIT STARTED", helpTextStartBackup, NULL},
586   {"START BACKUP WAIT", helpTextStartBackup, NULL},
587   {"START BACKUP WAIT COMPLETED", helpTextStartBackup, NULL},
588   {"ABORT BACKUP", helpTextAbortBackup, NULL},
589   {"SHUTDOWN", helpTextShutdown, NULL},
590   {"CLUSTERLOG ON", helpTextClusterlogOn, NULL},
591   {"CLUSTERLOG OFF", helpTextClusterlogOff, NULL},
592   {"CLUSTERLOG TOGGLE", helpTextClusterlogToggle, NULL},
593   {"CLUSTERLOG INFO", helpTextClusterlogInfo, NULL},
594   {"START", helpTextStart, NULL},
595   {"RESTART", helpTextRestart, NULL},
596   {"STOP", helpTextStop, NULL},
597   {"ENTER SINGLE USER MODE", helpTextEnterSingleUserMode, NULL},
598   {"EXIT SINGLE USER MODE", helpTextExitSingleUserMode, NULL},
599   {"STATUS", helpTextStatus, NULL},
600   {"CLUSTERLOG", helpTextClusterlog, NULL},
601   {"PURGE STALE SESSIONS", helpTextPurgeStaleSessions, NULL},
602   {"CONNECT", helpTextConnect, NULL},
603   {"REPORT", helpTextReport, helpTextReportFn},
604   {"QUIT", helpTextQuit, NULL},
605 #ifdef VM_TRACE // DEBUG ONLY
606   {"DEBUG", helpTextDebug, NULL},
607 #endif //VM_TRACE
608   {NULL, NULL, NULL}
609 };
610 
611 static bool
convert(const char * s,int & val)612 convert(const char* s, int& val) {
613 
614   if (s == NULL)
615     return false;
616 
617   if (strlen(s) == 0)
618     return false;
619 
620   errno = 0;
621   char* p;
622   long v = strtol(s, &p, 10);
623   if (errno != 0)
624     return false;
625 
626   if (p != &s[strlen(s)])
627     return false;
628 
629   val = v;
630   return true;
631 }
632 
633 /*
634  * Constructor
635  */
CommandInterpreter(const char * host,int verbose)636 CommandInterpreter::CommandInterpreter(const char *host,int verbose) :
637   m_constr(host),
638   m_connected(false),
639   m_verbose(verbose),
640   m_try_reconnect(0),
641   m_error(-1),
642   m_event_thread(NULL)
643 {
644   m_print_mutex= NdbMutex_Create();
645 }
646 
647 /*
648  * Destructor
649  */
~CommandInterpreter()650 CommandInterpreter::~CommandInterpreter()
651 {
652   disconnect();
653   NdbMutex_Destroy(m_print_mutex);
654 }
655 
656 static bool
emptyString(const char * s)657 emptyString(const char* s)
658 {
659   if (s == NULL) {
660     return true;
661   }
662 
663   for (unsigned int i = 0; i < strlen(s); ++i) {
664     if (! isspace(s[i])) {
665       return false;
666     }
667   }
668 
669   return true;
670 }
671 
672 void
printError()673 CommandInterpreter::printError()
674 {
675   if (m_mgmsrv)
676   {
677     ndbout_c("* %5d: %s",
678              ndb_mgm_get_latest_error(m_mgmsrv),
679              ndb_mgm_get_latest_error_msg(m_mgmsrv));
680     ndbout_c("*        %s", ndb_mgm_get_latest_error_desc(m_mgmsrv));
681     if (ndb_mgm_check_connection(m_mgmsrv))
682     {
683       disconnect();
684     }
685   }
686 }
687 
688 /*
689  * print log event from mgmsrv to console screen
690  */
691 #define make_uint64(a,b) (((Uint64)(a)) + (((Uint64)(b)) << 32))
692 #define Q64(a) make_uint64(event->EVENT.a ## _lo, event->EVENT.a ## _hi)
693 #define R event->source_nodeid
694 #define Q(a) event->EVENT.a
695 #define QVERSION getMajor(Q(version)), getMinor(Q(version)), getBuild(Q(version))
696 #define NDB_LE_(a) NDB_LE_ ## a
697 static void
printLogEvent(struct ndb_logevent * event)698 printLogEvent(struct ndb_logevent* event)
699 {
700   switch (event->type) {
701     /**
702      * NDB_MGM_EVENT_CATEGORY_BACKUP
703      */
704 #undef  EVENT
705 #define EVENT BackupStarted
706   case NDB_LE_BackupStarted:
707       ndbout_c("Node %u: Backup %u started from node %d",
708                R, Q(backup_id), Q(starting_node));
709       break;
710 #undef EVENT
711 #define EVENT BackupStatus
712     case NDB_LE_BackupStatus:
713       if (Q(starting_node))
714         ndbout_c("Node %u: Local backup status: backup %u started from node %u\n"
715                  " #Records: %llu #LogRecords: %llu\n"
716                  " Data: %llu bytes Log: %llu bytes", R,
717                  Q(backup_id),
718                  Q(starting_node),
719                  Q64(n_records),
720                  Q64(n_log_records),
721                  Q64(n_bytes),
722                  Q64(n_log_bytes));
723       else
724         ndbout_c("Node %u: Backup not started", R);
725       break;
726 #undef  EVENT
727 #define EVENT BackupFailedToStart
728     case NDB_LE_BackupFailedToStart:
729       ndbout_c("Node %u: Backup request from %d failed to start. Error: %d",
730                R, Q(starting_node), Q(error));
731       break;
732 #undef  EVENT
733 #define EVENT BackupCompleted
734     case NDB_LE_BackupCompleted:
735       ndbout_c("Node %u: Backup %u started from node %u completed\n"
736                " StartGCP: %u StopGCP: %u\n"
737                " #Records: %u #LogRecords: %u\n"
738                " Data: %u bytes Log: %u bytes", R,
739                Q(backup_id), Q(starting_node),
740                Q(start_gci), Q(stop_gci),
741                Q(n_records), Q(n_log_records),
742                Q(n_bytes),   Q(n_log_bytes));
743       break;
744 #undef  EVENT
745 #define EVENT BackupAborted
746     case NDB_LE_BackupAborted:
747       ndbout_c("Node %u: Backup %u started from %d has been aborted. Error: %d",
748                R, Q(backup_id), Q(starting_node), Q(error));
749       break;
750     /**
751      * NDB_MGM_EVENT_CATEGORY_STARTUP
752      */
753 #undef  EVENT
754 #define EVENT NDBStartStarted
755     case NDB_LE_NDBStartStarted:
756       ndbout_c("Node %u: Start initiated (version %d.%d.%d)",
757                R, QVERSION);
758       break;
759 #undef  EVENT
760 #define EVENT NDBStartCompleted
761     case NDB_LE_NDBStartCompleted:
762       ndbout_c("Node %u: Started (version %d.%d.%d)",
763                R, QVERSION);
764       break;
765 #undef  EVENT
766 #define EVENT NDBStopStarted
767     case NDB_LE_NDBStopStarted:
768       ndbout_c("Node %u: %s shutdown initiated", R,
769                (Q(stoptype) == 1 ? "Cluster" : "Node"));
770       break;
771 #undef  EVENT
772 #define EVENT NDBStopCompleted
773     case NDB_LE_NDBStopCompleted:
774       {
775         BaseString action_str("");
776         BaseString signum_str("");
777         getRestartAction(Q(action), action_str);
778         if (Q(signum))
779           signum_str.appfmt(" Initiated by signal %d.",
780                             Q(signum));
781         ndbout_c("Node %u: Node shutdown completed%s.%s",
782                  R, action_str.c_str(), signum_str.c_str());
783       }
784       break;
785 #undef  EVENT
786 #define EVENT NDBStopForced
787     case NDB_LE_NDBStopForced:
788       {
789         BaseString action_str("");
790         BaseString reason_str("");
791         BaseString sphase_str("");
792         int signum = Q(signum);
793         int error = Q(error);
794         int sphase = Q(sphase);
795         int extra = Q(extra);
796         getRestartAction(Q(action), action_str);
797         if (signum)
798           reason_str.appfmt(" Initiated by signal %d.", signum);
799         if (error)
800         {
801           ndbd_exit_classification cl;
802           ndbd_exit_status st;
803           const char *msg = ndbd_exit_message(error, &cl);
804           const char *cl_msg = ndbd_exit_classification_message(cl, &st);
805           const char *st_msg = ndbd_exit_status_message(st);
806           reason_str.appfmt(" Caused by error %d: \'%s(%s). %s\'.",
807                             error, msg, cl_msg, st_msg);
808           if (extra != 0)
809             reason_str.appfmt(" (extra info %d)", extra);
810         }
811         if (sphase < 255)
812           sphase_str.appfmt(" Occured during startphase %u.", sphase);
813         ndbout_c("Node %u: Forced node shutdown completed%s.%s%s",
814                  R, action_str.c_str(), sphase_str.c_str(),
815                  reason_str.c_str());
816       }
817       break;
818 #undef  EVENT
819 #define EVENT StopAborted
820     case NDB_LE_NDBStopAborted:
821       ndbout_c("Node %u: Node shutdown aborted", R);
822       break;
823     /**
824      * NDB_MGM_EVENT_CATEGORY_STATISTIC
825      */
826 #undef EVENT
827 #define EVENT MemoryUsage
828     case NDB_LE_MemoryUsage:
829     {
830 
831       if (Q(gth) == 0)
832       {
833         // Only print MemoryUsage report for increased/decreased
834         break;
835       }
836 
837       const int percent = Q(pages_total) ? (Q(pages_used)*100)/Q(pages_total) : 0;
838       ndbout_c("Node %u: %s usage %s %d%s(%d %dK pages of total %d)", R,
839                (Q(block) == DBACC ? "Index" : (Q(block) == DBTUP ?"Data":"<unknown>")),
840                (Q(gth) > 0 ? "increased to" : "decreased to"),
841                percent, "%",
842                Q(pages_used), Q(page_size_kb)/1024, Q(pages_total));
843       break;
844     }
845     /**
846      * default nothing to print
847      */
848     default:
849       break;
850   }
851 }
852 
853 //*****************************************************************************
854 //*****************************************************************************
855 
856 struct event_thread_param {
857   NdbMgmHandle *m;
858   NdbMutex **p;
859 };
860 
861 static int do_event_thread = 0;
862 
863 static void*
event_thread_run(void * p)864 event_thread_run(void* p)
865 {
866   DBUG_ENTER("event_thread_run");
867 
868   struct event_thread_param param= *(struct event_thread_param*)p;
869   NdbMgmHandle handle= *(param.m);
870   NdbMutex* printmutex= *(param.p);
871 
872   int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP,
873 		   1, NDB_MGM_EVENT_CATEGORY_STARTUP,
874                    5, NDB_MGM_EVENT_CATEGORY_STATISTIC,
875 		   0 };
876 
877   NdbLogEventHandle log_handle= NULL;
878   struct ndb_logevent log_event;
879 
880   log_handle= ndb_mgm_create_logevent_handle(handle, filter);
881   if (log_handle)
882   {
883     do_event_thread= 1;
884     do {
885       int res= ndb_logevent_get_next(log_handle, &log_event, 2000);
886       if (res > 0)
887       {
888         Guard g(printmutex);
889         printLogEvent(&log_event);
890       }
891       else if (res < 0)
892         break;
893     } while(do_event_thread);
894     ndb_mgm_destroy_logevent_handle(&log_handle);
895   }
896   else
897   {
898     do_event_thread= 0;
899   }
900 
901   DBUG_RETURN(NULL);
902 }
903 
904 bool
connect(bool interactive)905 CommandInterpreter::connect(bool interactive)
906 {
907   DBUG_ENTER("CommandInterpreter::connect");
908 
909   if(m_connected)
910     DBUG_RETURN(m_connected);
911 
912   m_mgmsrv = ndb_mgm_create_handle();
913   if(m_mgmsrv == NULL) {
914     ndbout_c("Can't create handle to management server.");
915     exit(-1);
916   }
917 
918   if (interactive) {
919     m_mgmsrv2 = ndb_mgm_create_handle();
920     if(m_mgmsrv2 == NULL) {
921       ndbout_c("Can't create 2:nd handle to management server.");
922       exit(-1);
923     }
924   }
925 
926   if (ndb_mgm_set_connectstring(m_mgmsrv, m_constr))
927   {
928     printError();
929     exit(-1);
930   }
931 
932   if(ndb_mgm_connect(m_mgmsrv, m_try_reconnect-1, 5, 1))
933     DBUG_RETURN(m_connected); // couldn't connect, always false
934 
935   const char *host= ndb_mgm_get_connected_host(m_mgmsrv);
936   unsigned port= ndb_mgm_get_connected_port(m_mgmsrv);
937   if (interactive) {
938     BaseString constr;
939     constr.assfmt("%s:%d",host,port);
940     if(!ndb_mgm_set_connectstring(m_mgmsrv2, constr.c_str()) &&
941        !ndb_mgm_connect(m_mgmsrv2, m_try_reconnect-1, 5, 1))
942     {
943       DBUG_PRINT("info",("2:ndb connected to Management Server ok at: %s:%d",
944                          host, port));
945       assert(m_event_thread == NULL);
946       assert(do_event_thread == 0);
947       do_event_thread= 0;
948       struct event_thread_param p;
949       p.m= &m_mgmsrv2;
950       p.p= &m_print_mutex;
951       m_event_thread = NdbThread_Create(event_thread_run,
952                                         (void**)&p,
953                                         0, // default stack size
954                                         "CommandInterpreted_event_thread",
955                                         NDB_THREAD_PRIO_LOW);
956       if (m_event_thread)
957       {
958         DBUG_PRINT("info",("Thread created ok, waiting for started..."));
959         int iter= 1000; // try for 30 seconds
960         while(do_event_thread == 0 &&
961               iter-- > 0)
962           NdbSleep_MilliSleep(30);
963       }
964       if (m_event_thread == NULL ||
965           do_event_thread == 0 ||
966           do_event_thread == -1)
967       {
968         DBUG_PRINT("info",("Warning, event thread startup failed, "
969                            "degraded printouts as result, errno=%d",
970                            errno));
971         printf("Warning, event thread startup failed, "
972                "degraded printouts as result, errno=%d\n", errno);
973         do_event_thread= 0;
974         if (m_event_thread)
975         {
976           void *res;
977           NdbThread_WaitFor(m_event_thread, &res);
978           NdbThread_Destroy(&m_event_thread);
979         }
980         ndb_mgm_disconnect(m_mgmsrv2);
981       }
982     }
983     else
984     {
985       DBUG_PRINT("warning",
986                  ("Could not do 2:nd connect to mgmtserver for event listening"));
987       DBUG_PRINT("info", ("code: %d, msg: %s",
988                           ndb_mgm_get_latest_error(m_mgmsrv2),
989                           ndb_mgm_get_latest_error_msg(m_mgmsrv2)));
990       printf("Warning, event connect failed, degraded printouts as result\n");
991       printf("code: %d, msg: %s\n",
992              ndb_mgm_get_latest_error(m_mgmsrv2),
993              ndb_mgm_get_latest_error_msg(m_mgmsrv2));
994     }
995   }
996   m_connected= true;
997   DBUG_PRINT("info",("Connected to Management Server at: %s:%d", host, port));
998   if (m_verbose)
999   {
1000     printf("Connected to Management Server at: %s:%d\n",
1001            host, port);
1002   }
1003 
1004   DBUG_RETURN(m_connected);
1005 }
1006 
1007 void
disconnect(void)1008 CommandInterpreter::disconnect(void)
1009 {
1010   DBUG_ENTER("CommandInterpreter::disconnect");
1011 
1012   if (m_event_thread) {
1013     void *res;
1014     do_event_thread= 0;
1015     NdbThread_WaitFor(m_event_thread, &res);
1016     NdbThread_Destroy(&m_event_thread);
1017     m_event_thread= NULL;
1018     ndb_mgm_destroy_handle(&m_mgmsrv2);
1019   }
1020   if (m_connected)
1021   {
1022     ndb_mgm_destroy_handle(&m_mgmsrv);
1023     m_connected= false;
1024   }
1025   DBUG_VOID_RETURN;
1026 }
1027 
1028 //*****************************************************************************
1029 //*****************************************************************************
1030 
1031 bool
execute(const char * _line,int try_reconnect,bool interactive,int * error)1032 CommandInterpreter::execute(const char *_line, int try_reconnect,
1033 			    bool interactive, int *error)
1034 {
1035   if (try_reconnect >= 0)
1036     m_try_reconnect = try_reconnect;
1037   bool result= execute_impl(_line, interactive);
1038   if (error)
1039     *error= m_error;
1040 
1041   return result;
1042 }
1043 
1044 static void
invalid_command(const char * cmd)1045 invalid_command(const char *cmd)
1046 {
1047   ndbout << "Invalid command: " << cmd << endl;
1048   ndbout << "Type HELP for help." << endl << endl;
1049 }
1050 
1051 
1052 // Utility class for easier checking of args
1053 // given to the commands
1054 class ClusterInfo {
1055   ndb_mgm_cluster_state* m_status;
1056 
1057 public:
ClusterInfo()1058   ClusterInfo() :
1059     m_status(NULL) {};
1060 
~ClusterInfo()1061   ~ClusterInfo() {
1062     if (m_status)
1063       free(m_status);
1064   }
1065 
fetch(NdbMgmHandle handle,bool all_nodes=false)1066   bool fetch(NdbMgmHandle handle, bool all_nodes = false) {
1067 
1068     const ndb_mgm_node_type types[2] = {
1069       NDB_MGM_NODE_TYPE_NDB,
1070       NDB_MGM_NODE_TYPE_UNKNOWN
1071     };
1072     m_status = ndb_mgm_get_status2(handle,
1073                                    !all_nodes ? types : 0);
1074     if (m_status == NULL)
1075     {
1076       ndbout_c("ERROR: couldn't fetch cluster status");
1077       return false;
1078     }
1079     return true;
1080   }
1081 
is_valid_ndb_nodeid(int nodeid) const1082   bool is_valid_ndb_nodeid(int nodeid) const {
1083     // Check valid NDB nodeid
1084     if (nodeid < 1 || nodeid >= MAX_NDB_NODES)
1085     {
1086       ndbout_c("ERROR: illegal nodeid %d!", nodeid);
1087       return false;
1088     }
1089     return true;
1090   }
1091 
is_ndb_node(int nodeid) const1092   bool is_ndb_node(int nodeid) const {
1093 
1094     if (!is_valid_ndb_nodeid(nodeid))
1095       return false;
1096 
1097     bool found = false;
1098     for (int i = 0; i < m_status->no_of_nodes; i++)
1099     {
1100       if (m_status->node_states[i].node_id == nodeid &&
1101           m_status->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB)
1102         found = true;
1103     }
1104 
1105     if (!found)
1106       ndbout_c("ERROR: node %d is not a NDB node!", nodeid);
1107 
1108     return found;
1109   }
1110 };
1111 
1112 
1113 
1114 static void
split_args(const char * line,Vector<BaseString> & args)1115 split_args(const char* line, Vector<BaseString>& args)
1116 {
1117   // Split the command line on space
1118   BaseString tmp(line);
1119   tmp.split(args);
1120 
1121   // Remove any empty args which come from double
1122   // spaces in the command line
1123   // ie. "hello<space><space>world" becomes ("hello, "", "world")
1124   //
1125   for (unsigned i= 0; i < args.size(); i++)
1126     if (args[i].length() == 0)
1127       args.erase(i--);
1128 }
1129 
1130 
1131 bool
execute_impl(const char * _line,bool interactive)1132 CommandInterpreter::execute_impl(const char *_line, bool interactive)
1133 {
1134   DBUG_ENTER("CommandInterpreter::execute_impl");
1135   DBUG_PRINT("enter",("line='%s'", _line));
1136   m_error= 0;
1137 
1138   if(_line == NULL) {
1139     ndbout_c("ERROR: Internal error at %s:%d.", __FILE__, __LINE__);
1140     m_error = -1;
1141     DBUG_RETURN(false); // Terminate gracefully
1142   }
1143 
1144   char* line = strdup(_line);
1145   if (line == NULL)
1146   {
1147     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1148     m_error = -1;
1149     DBUG_RETURN(false); // Terminate gracefully
1150   }
1151   NdbAutoPtr<char> ap(line);
1152 
1153   int do_continue;
1154   do {
1155     do_continue= 0;
1156     BaseString::trim(line," \t");
1157     if (line[0] == 0 ||
1158 	line[0] == '#')
1159     {
1160       DBUG_RETURN(true);
1161     }
1162     // for mysql client compatability remove trailing ';'
1163     {
1164       unsigned last= strlen(line)-1;
1165       if (line[last] == ';')
1166       {
1167 	line[last]= 0;
1168 	do_continue= 1;
1169       }
1170     }
1171   } while (do_continue);
1172 
1173   // if there is anything in the line proceed
1174   Vector<BaseString> command_list;
1175   split_args(line, command_list);
1176 
1177   char* firstToken = strtok(line, " ");
1178   char* allAfterFirstToken = strtok(NULL, "");
1179 
1180   if (strcasecmp(firstToken, "HELP") == 0 ||
1181       strcasecmp(firstToken, "?") == 0) {
1182     m_error = executeHelp(allAfterFirstToken);
1183     DBUG_RETURN(true);
1184   }
1185   else if (strcasecmp(firstToken, "CONNECT") == 0) {
1186     m_error = executeConnect(allAfterFirstToken, interactive);
1187     DBUG_RETURN(true);
1188   }
1189   else if (strcasecmp(firstToken, "SLEEP") == 0) {
1190     if (allAfterFirstToken)
1191       NdbSleep_SecSleep(atoi(allAfterFirstToken));
1192     DBUG_RETURN(true);
1193   }
1194   else if((strcasecmp(firstToken, "QUIT") == 0 ||
1195 	  strcasecmp(firstToken, "EXIT") == 0 ||
1196 	  strcasecmp(firstToken, "BYE") == 0) &&
1197 	  allAfterFirstToken == NULL){
1198     DBUG_RETURN(false);
1199   }
1200 
1201   if (!connect(interactive)){
1202     m_error = -1;
1203     DBUG_RETURN(true);
1204   }
1205 
1206   if (ndb_mgm_check_connection(m_mgmsrv))
1207   {
1208     disconnect();
1209     connect(interactive);
1210   }
1211 
1212   if (strcasecmp(firstToken, "SHOW") == 0) {
1213     Guard g(m_print_mutex);
1214     m_error = executeShow(allAfterFirstToken);
1215     DBUG_RETURN(true);
1216   }
1217   else if (strcasecmp(firstToken, "SHUTDOWN") == 0) {
1218     m_error= executeShutdown(allAfterFirstToken);
1219     DBUG_RETURN(true);
1220   }
1221   else if (strcasecmp(firstToken, "CLUSTERLOG") == 0){
1222     executeClusterLog(allAfterFirstToken);
1223     DBUG_RETURN(true);
1224   }
1225   else if(strcasecmp(firstToken, "START") == 0 &&
1226 	  allAfterFirstToken != NULL &&
1227 	  strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
1228     m_error= executeStartBackup(allAfterFirstToken, interactive);
1229     DBUG_RETURN(true);
1230   }
1231   else if(strcasecmp(firstToken, "ABORT") == 0 &&
1232 	  allAfterFirstToken != NULL &&
1233 	  strncasecmp(allAfterFirstToken, "BACKUP", sizeof("BACKUP") - 1) == 0){
1234     m_error = executeAbortBackup(allAfterFirstToken);
1235     DBUG_RETURN(true);
1236   }
1237   else if (strcasecmp(firstToken, "PURGE") == 0) {
1238     m_error = executePurge(allAfterFirstToken);
1239     DBUG_RETURN(true);
1240   }
1241   else if(strcasecmp(firstToken, "ENTER") == 0 &&
1242 	  allAfterFirstToken != NULL &&
1243 	  allAfterFirstToken != NULL &&
1244 	  strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
1245 		  sizeof("SINGLE USER MODE") - 1) == 0){
1246     m_error = executeEnterSingleUser(allAfterFirstToken);
1247     DBUG_RETURN(true);
1248   }
1249   else if(strcasecmp(firstToken, "EXIT") == 0 &&
1250 	  allAfterFirstToken != NULL &&
1251 	  strncasecmp(allAfterFirstToken, "SINGLE USER MODE ",
1252 		  sizeof("SINGLE USER MODE") - 1) == 0){
1253     m_error = executeExitSingleUser(allAfterFirstToken);
1254     DBUG_RETURN(true);
1255   }
1256   else if(strcasecmp(firstToken, "CREATE") == 0 &&
1257 	  allAfterFirstToken != NULL &&
1258 	  strncasecmp(allAfterFirstToken, "NODEGROUP",
1259                       sizeof("NODEGROUP") - 1) == 0){
1260     m_error = executeCreateNodeGroup(allAfterFirstToken);
1261     DBUG_RETURN(true);
1262   }
1263   else if(strcasecmp(firstToken, "DROP") == 0 &&
1264 	  allAfterFirstToken != NULL &&
1265 	  strncasecmp(allAfterFirstToken, "NODEGROUP",
1266                       sizeof("NODEGROUP") - 1) == 0){
1267     m_error = executeDropNodeGroup(allAfterFirstToken);
1268     DBUG_RETURN(true);
1269   }
1270   else if (strcasecmp(firstToken, "ALL") == 0) {
1271     m_error = analyseAfterFirstToken(-1, allAfterFirstToken);
1272   } else {
1273     /**
1274      * First tokens should be digits, node ID's
1275      */
1276     int node_ids[MAX_NODES];
1277     unsigned pos;
1278     for (pos= 0; pos < command_list.size(); pos++)
1279     {
1280       int node_id;
1281       if (convert(command_list[pos].c_str(), node_id))
1282       {
1283         if (node_id <= 0 || node_id > MAX_NODES) {
1284           ndbout << "Invalid node ID: " << command_list[pos].c_str()
1285                  << "." << endl;
1286           DBUG_RETURN(true);
1287         }
1288         node_ids[pos]= node_id;
1289         continue;
1290       }
1291       break;
1292     }
1293     int no_of_nodes= pos;
1294     if (no_of_nodes == 0)
1295     {
1296       /* No digit found */
1297       invalid_command(_line);
1298       m_error = -1;
1299       DBUG_RETURN(true);
1300     }
1301     if (pos == command_list.size())
1302     {
1303       /* No command found */
1304       invalid_command(_line);
1305       m_error = -1;
1306       DBUG_RETURN(true);
1307     }
1308     if (no_of_nodes == 1)
1309     {
1310       m_error = analyseAfterFirstToken(node_ids[0], allAfterFirstToken);
1311       DBUG_RETURN(true);
1312     }
1313     m_error = executeCommand(command_list, pos, node_ids, no_of_nodes);
1314     DBUG_RETURN(true);
1315   }
1316   DBUG_RETURN(true);
1317 }
1318 
1319 
1320 /**
1321  * List of commands used as second command argument
1322  */
1323 static const CommandInterpreter::CommandFunctionPair commands[] = {
1324   { "START", &CommandInterpreter::executeStart }
1325   ,{ "RESTART", &CommandInterpreter::executeRestart }
1326   ,{ "STOP", &CommandInterpreter::executeStop }
1327   ,{ "STATUS", &CommandInterpreter::executeStatus }
1328   ,{ "LOGLEVEL", &CommandInterpreter::executeLogLevel }
1329   ,{ "CLUSTERLOG", &CommandInterpreter::executeEventReporting }
1330 #ifdef ERROR_INSERT
1331   ,{ "ERROR", &CommandInterpreter::executeError }
1332 #endif
1333   ,{ "LOG", &CommandInterpreter::executeLog }
1334   ,{ "TESTON", &CommandInterpreter::executeTestOn }
1335   ,{ "TESTOFF", &CommandInterpreter::executeTestOff }
1336   ,{ "DUMP", &CommandInterpreter::executeDumpState }
1337   ,{ "REPORT", &CommandInterpreter::executeReport }
1338 };
1339 
1340 
1341 //*****************************************************************************
1342 //*****************************************************************************
1343 int
analyseAfterFirstToken(int processId,char * allAfterFirstToken)1344 CommandInterpreter::analyseAfterFirstToken(int processId,
1345 					   char* allAfterFirstToken) {
1346 
1347   int retval = 0;
1348   if (emptyString(allAfterFirstToken)) {
1349     ndbout << "Expected a command after "
1350 	   << ((processId == -1) ? "ALL." : "node ID.") << endl;
1351     return -1;
1352   }
1353 
1354   char* secondToken = strtok(allAfterFirstToken, " ");
1355   char* allAfterSecondToken = strtok(NULL, "\0");
1356 
1357   const int tmpSize = sizeof(commands)/sizeof(CommandFunctionPair);
1358   ExecuteFunction fun = 0;
1359   const char * command = 0;
1360   for(int i = 0; i<tmpSize; i++){
1361     if(strcasecmp(secondToken, commands[i].command) == 0){
1362       fun = commands[i].executeFunction;
1363       command = commands[i].command;
1364       break;
1365     }
1366   }
1367 
1368   if(fun == 0){
1369     invalid_command(secondToken);
1370     return -1;
1371   }
1372 
1373   if(processId == -1){
1374     retval = executeForAll(command, fun, allAfterSecondToken);
1375   } else {
1376     retval = (this->*fun)(processId, allAfterSecondToken, false);
1377   }
1378   ndbout << endl;
1379   return retval;
1380 }
1381 
1382 int
executeCommand(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)1383 CommandInterpreter::executeCommand(Vector<BaseString> &command_list,
1384                                    unsigned command_pos,
1385                                    int *node_ids, int no_of_nodes)
1386 {
1387   const char *cmd= command_list[command_pos].c_str();
1388   int retval = 0;
1389 
1390   if (strcasecmp("STOP", cmd) == 0)
1391   {
1392     retval = executeStop(command_list, command_pos+1, node_ids, no_of_nodes);
1393     return retval;
1394   }
1395   if (strcasecmp("RESTART", cmd) == 0)
1396   {
1397     retval = executeRestart(command_list, command_pos+1, node_ids, no_of_nodes);
1398     return retval;
1399   }
1400   if (strcasecmp("START", cmd) == 0)
1401   {
1402     retval = executeStart(command_list, command_pos+1, node_ids, no_of_nodes);
1403     return retval;
1404   }
1405   ndbout_c("Invalid command: '%s' after multi node id list. "
1406            "Expected STOP, START, or RESTART.", cmd);
1407   return -1;
1408 }
1409 
1410 /**
1411  * Get next nodeid larger than the give node_id. node_id will be
1412  * set to the next node_id in the list. node_id should be set
1413  * to 0 (zero) on the first call.
1414  *
1415  * @param handle the NDB management handle
1416  * @param node_id last node_id retreived, 0 at first call
1417  * @param type type of node to look for
1418  * @return 1 if a node was found, 0 if no more node exist
1419  */
1420 static
1421 int
get_next_nodeid(struct ndb_mgm_cluster_state * cl,int * node_id,enum ndb_mgm_node_type type)1422 get_next_nodeid(struct ndb_mgm_cluster_state *cl,
1423 		int *node_id,
1424 		enum ndb_mgm_node_type type)
1425 {
1426   int i;
1427 
1428   if(cl == NULL)
1429     return 0;
1430 
1431   i=0;
1432   while((i < cl->no_of_nodes)) {
1433     if((*node_id < cl->node_states[i].node_id) &&
1434        (cl->node_states[i].node_type == type)) {
1435 
1436       if(i >= cl->no_of_nodes)
1437 	return 0;
1438 
1439       *node_id = cl->node_states[i].node_id;
1440       return 1;
1441     }
1442     i++;
1443   }
1444 
1445   return 0;
1446 }
1447 
1448 int
executeForAll(const char * cmd,ExecuteFunction fun,const char * allAfterSecondToken)1449 CommandInterpreter::executeForAll(const char * cmd, ExecuteFunction fun,
1450 				  const char * allAfterSecondToken)
1451 {
1452   int nodeId = 0;
1453   int retval = 0;
1454 
1455   if(strcasecmp(cmd, "STOP") == 0) {
1456     ndbout_c("Executing STOP on all nodes.");
1457     retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1458   } else if(strcasecmp(cmd, "RESTART") == 0) {
1459     retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1460   } else if (strcasecmp(cmd, "STATUS") == 0) {
1461     (this->*fun)(nodeId, allAfterSecondToken, true);
1462   } else if (strcasecmp(cmd, "REPORT") == 0) {
1463     Guard g(m_print_mutex);
1464     retval = executeReport(nodeId, allAfterSecondToken, true);
1465   } else {
1466     Guard g(m_print_mutex);
1467     struct ndb_mgm_cluster_state *cl= ndb_mgm_get_status(m_mgmsrv);
1468     if(cl == 0){
1469       ndbout_c("Unable get status from management server");
1470       printError();
1471       return -1;
1472     }
1473     NdbAutoPtr<char> ap1((char*)cl);
1474     while(get_next_nodeid(cl, &nodeId, NDB_MGM_NODE_TYPE_NDB))
1475       retval = (this->*fun)(nodeId, allAfterSecondToken, true);
1476   }
1477   return retval;
1478 }
1479 
1480 //*****************************************************************************
1481 //*****************************************************************************
1482 bool
parseBlockSpecification(const char * allAfterLog,Vector<BaseString> & blocks)1483 CommandInterpreter::parseBlockSpecification(const char* allAfterLog,
1484 					    Vector<BaseString>& blocks)
1485 {
1486   // Parse: [BLOCK = {ALL|<blockName>+}]
1487 
1488   if (emptyString(allAfterLog)) {
1489     return true;
1490   }
1491 
1492   // Copy allAfterLog since strtok will modify it
1493   char* newAllAfterLog = strdup(allAfterLog);
1494   if (newAllAfterLog == NULL)
1495   {
1496     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1497     return false; // Error parsing
1498   }
1499 
1500   NdbAutoPtr<char> ap1(newAllAfterLog);
1501   char* firstTokenAfterLog = strtok(newAllAfterLog, " ");
1502   for (unsigned int i = 0; i < strlen(firstTokenAfterLog); ++i) {
1503     firstTokenAfterLog[i] = toupper(firstTokenAfterLog[i]);
1504   }
1505 
1506   if (strcasecmp(firstTokenAfterLog, "BLOCK") != 0) {
1507     ndbout << "Unexpected value: " << firstTokenAfterLog
1508 	   << ". Expected BLOCK." << endl;
1509     return false;
1510   }
1511 
1512   char* allAfterFirstToken = strtok(NULL, "\0");
1513   if (emptyString(allAfterFirstToken)) {
1514     ndbout << "Expected =." << endl;
1515     return false;
1516   }
1517 
1518   char* secondTokenAfterLog = strtok(allAfterFirstToken, " ");
1519   if (strcasecmp(secondTokenAfterLog, "=") != 0) {
1520     ndbout << "Unexpected value: " << secondTokenAfterLog
1521 	   << ". Expected =." << endl;
1522     return false;
1523   }
1524 
1525   char* blockName = strtok(NULL, " ");
1526   bool all = false;
1527   if (blockName != NULL && (strcasecmp(blockName, "ALL") == 0)) {
1528     all = true;
1529   }
1530   while (blockName != NULL) {
1531     blocks.push_back(blockName);
1532     blockName = strtok(NULL, " ");
1533   }
1534 
1535   if (blocks.size() == 0) {
1536     ndbout << "No block specified." << endl;
1537     return false;
1538   }
1539   if (blocks.size() > 1 && all) {
1540     // More than "ALL" specified
1541     ndbout << "Nothing expected after ALL." << endl;
1542     return false;
1543   }
1544 
1545   return true;
1546 }
1547 
1548 
1549 
1550 /*****************************************************************************
1551  * HELP
1552  *****************************************************************************/
1553 int
executeHelp(char * parameters)1554 CommandInterpreter::executeHelp(char* parameters)
1555 {
1556   if (emptyString(parameters)) {
1557     ndbout << helpText;
1558 
1559     ndbout << endl
1560 	   << "<severity> = "
1561 	   << "ALERT | CRITICAL | ERROR | WARNING | INFO | DEBUG"
1562 	   << endl;
1563 
1564     ndbout << "<category> = ";
1565     for(int i = CFG_MIN_LOGLEVEL; i <= CFG_MAX_LOGLEVEL; i++){
1566       const char *str= ndb_mgm_get_event_category_string((ndb_mgm_event_category)i);
1567       if (str) {
1568 	if (i != CFG_MIN_LOGLEVEL)
1569 	  ndbout << " | ";
1570 	ndbout << str;
1571       }
1572     }
1573     ndbout << endl;
1574 
1575     ndbout << "<level>    = " << "0 - 15" << endl;
1576     ndbout << "<id>       = " << "ALL | Any database node id" << endl;
1577     ndbout << endl;
1578     ndbout << "For detailed help on COMMAND, use HELP COMMAND." << endl;
1579   } else {
1580     int i = 0;
1581     for (i = 0; help_items[i].cmd != NULL; i++)
1582     {
1583       if (strcasecmp(parameters, help_items[i].cmd) == 0)
1584       {
1585         if (help_items[i].help)
1586           ndbout << help_items[i].help;
1587         if (help_items[i].help_fn)
1588           (*help_items[i].help_fn)();
1589         break;
1590       }
1591     }
1592     if (help_items[i].cmd == NULL){
1593       ndbout << "No help for " << parameters << " available" << endl;
1594       return -1;
1595     }
1596   }
1597   return 0;
1598 }
1599 
1600 
1601 /*****************************************************************************
1602  * SHUTDOWN
1603  *****************************************************************************/
1604 
1605 int
executeShutdown(char * parameters)1606 CommandInterpreter::executeShutdown(char* parameters)
1607 {
1608   ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
1609   if(state == NULL) {
1610     ndbout_c("Could not get status");
1611     printError();
1612     return 1;
1613   }
1614   NdbAutoPtr<char> ap1((char*)state);
1615 
1616   int result = 0;
1617   int need_disconnect;
1618   result = ndb_mgm_stop3(m_mgmsrv, -1, 0, 0, &need_disconnect);
1619   if (result < 0) {
1620     ndbout << "Shutdown of NDB Cluster node(s) failed." << endl;
1621     printError();
1622     return result;
1623   }
1624 
1625   ndbout << result << " NDB Cluster node(s) have shutdown." << endl;
1626 
1627   if(need_disconnect) {
1628     ndbout << "Disconnecting to allow management server to shutdown."
1629            << endl;
1630     disconnect();
1631   }
1632   return 0;
1633 }
1634 
1635 /*****************************************************************************
1636  * SHOW
1637  *****************************************************************************/
1638 
1639 
1640 static
status_string(ndb_mgm_node_status status)1641 const char *status_string(ndb_mgm_node_status status)
1642 {
1643   switch(status){
1644   case NDB_MGM_NODE_STATUS_NO_CONTACT:
1645     return "not connected";
1646   case NDB_MGM_NODE_STATUS_NOT_STARTED:
1647     return "not started";
1648   case NDB_MGM_NODE_STATUS_STARTING:
1649     return "starting";
1650   case NDB_MGM_NODE_STATUS_STARTED:
1651     return "started";
1652   case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
1653     return "shutting down";
1654   case NDB_MGM_NODE_STATUS_RESTARTING:
1655     return "restarting";
1656   case NDB_MGM_NODE_STATUS_SINGLEUSER:
1657     return "single user mode";
1658   default:
1659     return "unknown state";
1660   }
1661 }
1662 
1663 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)1664 print_nodes(ndb_mgm_cluster_state *state, ndb_mgm_configuration_iterator *it,
1665 	    const char *proc_name, int no_proc, ndb_mgm_node_type type,
1666 	    int master_id)
1667 {
1668   int i;
1669   ndbout << "[" << proc_name
1670 	 << "(" << ndb_mgm_get_node_type_string(type) << ")]\t"
1671 	 << no_proc << " node(s)" << endl;
1672   for(i=0; i < state->no_of_nodes; i++) {
1673     struct ndb_mgm_node_state *node_state= &(state->node_states[i]);
1674     if(node_state->node_type == type) {
1675       int node_id= node_state->node_id;
1676       ndbout << "id=" << node_id;
1677       if(node_state->version != 0) {
1678 	const char *hostname= node_state->connect_address;
1679 	if (hostname == 0
1680 	    || strlen(hostname) == 0
1681 	    || strcasecmp(hostname,"0.0.0.0") == 0)
1682 	  ndbout << " ";
1683 	else
1684 	  ndbout << "\t@" << hostname;
1685 
1686 	char tmp[100];
1687 	ndbout << "  (" << ndbGetVersionString(node_state->version,
1688                                                node_state->mysql_version,
1689                                                0,
1690                                                tmp, sizeof(tmp));
1691 	if (type == NDB_MGM_NODE_TYPE_NDB) {
1692 	  if (node_state->node_status != NDB_MGM_NODE_STATUS_STARTED) {
1693 	    ndbout << ", " << status_string(node_state->node_status);
1694 	  }
1695 	  if (node_state->node_group >= 0 && node_state->node_group != (int)RNIL) {
1696 	    ndbout << ", Nodegroup: " << node_state->node_group;
1697           }
1698           else if (node_state->node_group == (int)RNIL)
1699           {
1700             ndbout << ", no nodegroup";
1701           }
1702           if (node_state->node_group >= 0 || node_state->node_group == (int)RNIL)
1703 	    if (master_id && node_state->dynamic_id == master_id)
1704 	      ndbout << ", Master";
1705         }
1706 	ndbout << ")" << endl;
1707       } else {
1708 	ndb_mgm_first(it);
1709 	if(ndb_mgm_find(it, CFG_NODE_ID, node_id) == 0){
1710 	  const char *config_hostname= 0;
1711 	  ndb_mgm_get_string_parameter(it, CFG_NODE_HOST, &config_hostname);
1712 	  if (config_hostname == 0 || config_hostname[0] == 0)
1713 	    config_hostname= "any host";
1714 	  ndbout_c(" (not connected, accepting connect from %s)",
1715 		   config_hostname);
1716 	}
1717 	else
1718 	{
1719 	  ndbout_c("Unable to find node with id: %d", node_id);
1720 	}
1721       }
1722     }
1723   }
1724   ndbout << endl;
1725 }
1726 
1727 int
executePurge(char * parameters)1728 CommandInterpreter::executePurge(char* parameters)
1729 {
1730   int command_ok= 0;
1731   do {
1732     if (emptyString(parameters))
1733       break;
1734     char* firstToken = strtok(parameters, " ");
1735     char* nextToken = strtok(NULL, " \0");
1736     if (strcasecmp(firstToken,"STALE") == 0 &&
1737 	nextToken &&
1738 	strcasecmp(nextToken, "SESSIONS") == 0) {
1739       command_ok= 1;
1740       break;
1741     }
1742   } while(0);
1743 
1744   if (!command_ok) {
1745     ndbout_c("Unexpected command, expected: PURGE STALE SESSIONS");
1746     return -1;
1747   }
1748 
1749   char *str;
1750 
1751   if (ndb_mgm_purge_stale_sessions(m_mgmsrv, &str)) {
1752     ndbout_c("Command failed");
1753     return -1;
1754   }
1755   if (str) {
1756     ndbout_c("Purged sessions with node id's: %s", str);
1757     free(str);
1758   }
1759   else
1760   {
1761     ndbout_c("No sessions purged");
1762   }
1763   return 0;
1764 }
1765 
1766 int
executeShow(char * parameters)1767 CommandInterpreter::executeShow(char* parameters)
1768 {
1769   int i;
1770   if (emptyString(parameters)) {
1771     ndb_mgm_cluster_state *state = ndb_mgm_get_status(m_mgmsrv);
1772     if(state == NULL) {
1773       ndbout_c("Could not get status");
1774       printError();
1775       return -1;
1776     }
1777     NdbAutoPtr<char> ap1((char*)state);
1778 
1779     ndb_mgm_configuration * conf = ndb_mgm_get_configuration(m_mgmsrv,0);
1780     if(conf == 0){
1781       ndbout_c("Could not get configuration");
1782       printError();
1783       return -1;
1784     }
1785 
1786     ndb_mgm_configuration_iterator * it;
1787     it = ndb_mgm_create_configuration_iterator((struct ndb_mgm_configuration *)conf, CFG_SECTION_NODE);
1788 
1789     if(it == 0){
1790       ndbout_c("Unable to create config iterator");
1791       ndb_mgm_destroy_configuration(conf);
1792       return -1;
1793     }
1794     NdbAutoPtr<ndb_mgm_configuration_iterator> ptr(it);
1795 
1796     int
1797       master_id= 0,
1798       ndb_nodes= 0,
1799       api_nodes= 0,
1800       mgm_nodes= 0;
1801 
1802     for(i=0; i < state->no_of_nodes; i++) {
1803       if(state->node_states[i].node_type == NDB_MGM_NODE_TYPE_NDB &&
1804 	 state->node_states[i].version != 0){
1805 	master_id= state->node_states[i].dynamic_id;
1806 	break;
1807       }
1808     }
1809 
1810     for(i=0; i < state->no_of_nodes; i++) {
1811       switch(state->node_states[i].node_type) {
1812       case NDB_MGM_NODE_TYPE_API:
1813 	api_nodes++;
1814 	break;
1815       case NDB_MGM_NODE_TYPE_NDB:
1816 	if (state->node_states[i].dynamic_id &&
1817 	    state->node_states[i].dynamic_id < master_id)
1818 	  master_id= state->node_states[i].dynamic_id;
1819 	ndb_nodes++;
1820 	break;
1821       case NDB_MGM_NODE_TYPE_MGM:
1822 	mgm_nodes++;
1823 	break;
1824       case NDB_MGM_NODE_TYPE_UNKNOWN:
1825         ndbout << "Error: Unknown Node Type" << endl;
1826         return -1;
1827       case NDB_MGM_NODE_TYPE_MAX:
1828         break;                                  /* purify: deadcode */
1829       }
1830     }
1831 
1832     ndbout << "Cluster Configuration" << endl
1833 	   << "---------------------" << endl;
1834     print_nodes(state, it, "ndbd",     ndb_nodes, NDB_MGM_NODE_TYPE_NDB, master_id);
1835     print_nodes(state, it, "ndb_mgmd", mgm_nodes, NDB_MGM_NODE_TYPE_MGM, 0);
1836     print_nodes(state, it, "mysqld",   api_nodes, NDB_MGM_NODE_TYPE_API, 0);
1837     ndb_mgm_destroy_configuration(conf);
1838     return 0;
1839   } else {
1840     ndbout << "Invalid argument: '" << parameters << "'" << endl;
1841     return -1;
1842   }
1843   return 0;
1844 }
1845 
1846 int
executeConnect(char * parameters,bool interactive)1847 CommandInterpreter::executeConnect(char* parameters, bool interactive)
1848 {
1849   BaseString *basestring = NULL;
1850 
1851   disconnect();
1852   if (!emptyString(parameters)) {
1853     basestring= new BaseString(parameters);
1854     m_constr= basestring->trim().c_str();
1855   }
1856   if ( connect(interactive) == false ){
1857     return -1;
1858   }
1859   if (basestring != NULL)
1860     delete basestring;
1861 
1862   return 0;
1863 }
1864 
1865 //*****************************************************************************
1866 //*****************************************************************************
1867 void
executeClusterLog(char * parameters)1868 CommandInterpreter::executeClusterLog(char* parameters)
1869 {
1870   DBUG_ENTER("CommandInterpreter::executeClusterLog");
1871   int i;
1872   if (emptyString(parameters))
1873   {
1874     ndbout_c("ERROR: Missing argument(s).");
1875     m_error = -1;
1876     DBUG_VOID_RETURN;
1877   }
1878 
1879   enum ndb_mgm_event_severity severity = NDB_MGM_EVENT_SEVERITY_ALL;
1880 
1881   char * tmpString = strdup(parameters);
1882   if (tmpString == NULL)
1883   {
1884     ndbout_c("ERROR: Memory allocation error at %s:%d.", __FILE__, __LINE__);
1885     m_error = -1;
1886     DBUG_VOID_RETURN;
1887   }
1888 
1889   NdbAutoPtr<char> ap1(tmpString);
1890   char * tmpPtr = 0;
1891   char * item = strtok_r(tmpString, " ", &tmpPtr);
1892   int enable;
1893 
1894   ndb_mgm_severity enabled[NDB_MGM_EVENT_SEVERITY_ALL] =
1895     {{NDB_MGM_EVENT_SEVERITY_ON,0},
1896      {NDB_MGM_EVENT_SEVERITY_DEBUG,0},
1897      {NDB_MGM_EVENT_SEVERITY_INFO,0},
1898      {NDB_MGM_EVENT_SEVERITY_WARNING,0},
1899      {NDB_MGM_EVENT_SEVERITY_ERROR,0},
1900      {NDB_MGM_EVENT_SEVERITY_CRITICAL,0},
1901      {NDB_MGM_EVENT_SEVERITY_ALERT,0}};
1902   ndb_mgm_get_clusterlog_severity_filter(m_mgmsrv, &enabled[0], NDB_MGM_EVENT_SEVERITY_ALL);
1903   if(enabled == NULL) {
1904     ndbout << "Couldn't get status" << endl;
1905     printError();
1906     m_error = -1;
1907     DBUG_VOID_RETURN;
1908   }
1909 
1910   /********************
1911    * CLUSTERLOG INFO
1912    ********************/
1913   if (strcasecmp(item, "INFO") == 0) {
1914     DBUG_PRINT("info",("INFO"));
1915     if(enabled[0].value == 0)
1916     {
1917       ndbout << "Cluster logging is disabled." << endl;
1918       m_error = 0;
1919       DBUG_VOID_RETURN;
1920     }
1921 #if 0
1922     for(i = 0; i<DB_MGM_EVENT_SEVERITY_ALL;i++)
1923       printf("enabled[%d] = %d\n", i, enabled[i].value);
1924 #endif
1925     ndbout << "Severities enabled: ";
1926     for(i = 1; i < (int)NDB_MGM_EVENT_SEVERITY_ALL; i++) {
1927       const char *str= ndb_mgm_get_event_severity_string(enabled[i].category);
1928       if (str == 0)
1929       {
1930 	DBUG_ASSERT(false);
1931 	continue;
1932       }
1933       if(enabled[i].value)
1934 	ndbout << BaseString(str).ndb_toupper() << " ";
1935     }
1936     ndbout << endl;
1937     m_error = 0;
1938     DBUG_VOID_RETURN;
1939 
1940   }
1941   else if (strcasecmp(item, "FILTER") == 0 ||
1942 	   strcasecmp(item, "TOGGLE") == 0)
1943   {
1944     DBUG_PRINT("info",("TOGGLE"));
1945     enable= -1;
1946   }
1947   else if (strcasecmp(item, "OFF") == 0)
1948   {
1949     DBUG_PRINT("info",("OFF"));
1950     enable= 0;
1951   } else if (strcasecmp(item, "ON") == 0) {
1952     DBUG_PRINT("info",("ON"));
1953     enable= 1;
1954   } else {
1955     ndbout << "Invalid argument." << endl;
1956     m_error = -1;
1957     DBUG_VOID_RETURN;
1958   }
1959 
1960   int res_enable;
1961   item = strtok_r(NULL, " ", &tmpPtr);
1962   if (item == NULL) {
1963     res_enable=
1964       ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv,
1965 					     NDB_MGM_EVENT_SEVERITY_ON,
1966 					     enable, NULL);
1967     if (res_enable < 0)
1968     {
1969       ndbout << "Couldn't set filter" << endl;
1970       printError();
1971       m_error = -1;
1972       DBUG_VOID_RETURN;
1973     }
1974     ndbout << "Cluster logging is " << (res_enable ? "enabled.":"disabled") << endl;
1975     m_error = 0;
1976     DBUG_VOID_RETURN;
1977   }
1978 
1979   do {
1980     severity= NDB_MGM_ILLEGAL_EVENT_SEVERITY;
1981     if (strcasecmp(item, "ALL") == 0) {
1982       severity = NDB_MGM_EVENT_SEVERITY_ALL;
1983     } else if (strcasecmp(item, "ALERT") == 0) {
1984       severity = NDB_MGM_EVENT_SEVERITY_ALERT;
1985     } else if (strcasecmp(item, "CRITICAL") == 0) {
1986       severity = NDB_MGM_EVENT_SEVERITY_CRITICAL;
1987     } else if (strcasecmp(item, "ERROR") == 0) {
1988       severity = NDB_MGM_EVENT_SEVERITY_ERROR;
1989     } else if (strcasecmp(item, "WARNING") == 0) {
1990       severity = NDB_MGM_EVENT_SEVERITY_WARNING;
1991     } else if (strcasecmp(item, "INFO") == 0) {
1992       severity = NDB_MGM_EVENT_SEVERITY_INFO;
1993     } else if (strcasecmp(item, "DEBUG") == 0) {
1994       severity = NDB_MGM_EVENT_SEVERITY_DEBUG;
1995     } else if (strcasecmp(item, "OFF") == 0 ||
1996 	       strcasecmp(item, "ON") == 0) {
1997       if (enable < 0) // only makes sense with toggle
1998 	severity = NDB_MGM_EVENT_SEVERITY_ON;
1999     }
2000     if (severity == NDB_MGM_ILLEGAL_EVENT_SEVERITY) {
2001       ndbout << "Invalid severity level: " << item << endl;
2002       m_error = -1;
2003       DBUG_VOID_RETURN;
2004     }
2005 
2006     res_enable= ndb_mgm_set_clusterlog_severity_filter(m_mgmsrv, severity,
2007 						       enable, NULL);
2008     if (res_enable < 0)
2009     {
2010       ndbout << "Couldn't set filter" << endl;
2011       printError();
2012       m_error = -1;
2013       DBUG_VOID_RETURN;
2014     }
2015     ndbout << BaseString(item).ndb_toupper().c_str() << " " << (res_enable ? "enabled":"disabled") << endl;
2016 
2017     item = strtok_r(NULL, " ", &tmpPtr);
2018   } while(item != NULL);
2019 
2020   m_error = 0;
2021   DBUG_VOID_RETURN;
2022 }
2023 
2024 //*****************************************************************************
2025 //*****************************************************************************
2026 
2027 int
executeStop(int processId,const char * parameters,bool all)2028 CommandInterpreter::executeStop(int processId, const char *parameters,
2029                                 bool all)
2030 {
2031   Vector<BaseString> command_list;
2032   if (parameters)
2033     split_args(parameters, command_list);
2034 
2035   int retval;
2036   if (all)
2037     retval = executeStop(command_list, 0, 0, 0);
2038   else
2039     retval = executeStop(command_list, 0, &processId, 1);
2040 
2041   return retval;
2042 }
2043 
2044 int
executeStop(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2045 CommandInterpreter::executeStop(Vector<BaseString> &command_list,
2046                                 unsigned command_pos,
2047                                 int *node_ids, int no_of_nodes)
2048 {
2049   int need_disconnect;
2050   int abort= 0;
2051   int retval = 0;
2052   int force = 0;
2053 
2054   for (; command_pos < command_list.size(); command_pos++)
2055   {
2056     const char *item= command_list[command_pos].c_str();
2057     if (strcasecmp(item, "-A") == 0)
2058     {
2059       abort= 1;
2060       continue;
2061     }
2062     if (strcasecmp(item, "-F") == 0)
2063     {
2064       force = 1;
2065       continue;
2066     }
2067     ndbout_c("Invalid option: %s. Expecting -A or -F after STOP",
2068              item);
2069     return -1;
2070   }
2071 
2072   int result= ndb_mgm_stop4(m_mgmsrv, no_of_nodes, node_ids, abort,
2073                             force, &need_disconnect);
2074   if (result < 0)
2075   {
2076     ndbout_c("Shutdown failed.");
2077     printError();
2078     retval = -1;
2079   }
2080   else
2081   {
2082     if (node_ids == 0)
2083       ndbout_c("NDB Cluster has shutdown.");
2084     else
2085     {
2086       ndbout << "Node";
2087       for (int i= 0; i < no_of_nodes; i++)
2088           ndbout << " " << node_ids[i];
2089       ndbout_c(" has shutdown.");
2090     }
2091   }
2092 
2093   if(need_disconnect)
2094   {
2095     ndbout << "Disconnecting to allow Management Server to shutdown" << endl;
2096     disconnect();
2097   }
2098 
2099   return retval;
2100 }
2101 
2102 int
executeEnterSingleUser(char * parameters)2103 CommandInterpreter::executeEnterSingleUser(char* parameters)
2104 {
2105   strtok(parameters, " ");
2106   struct ndb_mgm_reply reply;
2107   char* id = strtok(NULL, " ");
2108   id = strtok(NULL, " ");
2109   id = strtok(NULL, "\0");
2110   int nodeId = -1;
2111   if(id == 0 || sscanf(id, "%d", &nodeId) != 1){
2112     ndbout_c("Invalid arguments: expected <NodeId>");
2113     ndbout_c("Use SHOW to see what API nodes are configured");
2114     return -1;
2115   }
2116   int result = ndb_mgm_enter_single_user(m_mgmsrv, nodeId, &reply);
2117 
2118   if (result != 0) {
2119     ndbout_c("Entering single user mode for node %d failed", nodeId);
2120     printError();
2121     return -1;
2122   } else {
2123     ndbout_c("Single user mode entered");
2124     ndbout_c("Access is granted for API node %d only.", nodeId);
2125   }
2126   return 0;
2127 }
2128 
2129 int
executeExitSingleUser(char * parameters)2130 CommandInterpreter::executeExitSingleUser(char* parameters)
2131 {
2132   int result = ndb_mgm_exit_single_user(m_mgmsrv, 0);
2133   if (result != 0) {
2134     ndbout_c("Exiting single user mode failed.");
2135     printError();
2136     return -1;
2137   } else {
2138     ndbout_c("Exiting single user mode in progress.");
2139     ndbout_c("Use ALL STATUS or SHOW to see when single user mode has been exited.");
2140     return 0;
2141   }
2142 }
2143 
2144 int
executeStart(int processId,const char * parameters,bool all)2145 CommandInterpreter::executeStart(int processId, const char* parameters,
2146 				 bool all)
2147 {
2148   int result;
2149   int retval = 0;
2150   if(all) {
2151     result = ndb_mgm_start(m_mgmsrv, 0, 0);
2152   } else {
2153     result = ndb_mgm_start(m_mgmsrv, 1, &processId);
2154   }
2155 
2156   if (result <= 0) {
2157     ndbout << "Start failed." << endl;
2158     printError();
2159     retval = -1;
2160   } else
2161     {
2162       if(all)
2163 	ndbout_c("NDB Cluster is being started.");
2164       else
2165 	ndbout_c("Database node %d is being started.", processId);
2166     }
2167   return retval;
2168 }
2169 
2170 int
executeStart(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2171 CommandInterpreter::executeStart(Vector<BaseString> &command_list,
2172                                  unsigned command_pos,
2173                                  int *node_ids, int no_of_nodes)
2174 {
2175   int result;
2176   result= ndb_mgm_start(m_mgmsrv, no_of_nodes, node_ids);
2177 
2178   if (result <= 0) {
2179     ndbout_c("Start failed.");
2180     printError();
2181     return -1;
2182   }
2183   else
2184   {
2185     ndbout << "Node";
2186     for (int i= 0; i < no_of_nodes; i++)
2187       ndbout << " " << node_ids[i];
2188     ndbout_c(" is being started");
2189   }
2190   return 0;
2191 }
2192 
2193 int
executeRestart(int processId,const char * parameters,bool all)2194 CommandInterpreter::executeRestart(int processId, const char* parameters,
2195 				   bool all)
2196 {
2197   Vector<BaseString> command_list;
2198   if (parameters)
2199     split_args(parameters, command_list);
2200 
2201   int retval;
2202   if (all)
2203     retval = executeRestart(command_list, 0, 0, 0);
2204   else
2205     retval = executeRestart(command_list, 0, &processId, 1);
2206 
2207   return retval;
2208 }
2209 
2210 int
executeRestart(Vector<BaseString> & command_list,unsigned command_pos,int * node_ids,int no_of_nodes)2211 CommandInterpreter::executeRestart(Vector<BaseString> &command_list,
2212                                    unsigned command_pos,
2213                                    int *node_ids, int no_of_nodes)
2214 {
2215   int result;
2216   int retval = 0;
2217   int nostart= 0;
2218   int initialstart= 0;
2219   int abort= 0;
2220   int need_disconnect= 0;
2221   int force = 0;
2222 
2223   for (; command_pos < command_list.size(); command_pos++)
2224   {
2225     const char *item= command_list[command_pos].c_str();
2226     if (strcasecmp(item, "-N") == 0)
2227     {
2228       nostart= 1;
2229       continue;
2230     }
2231     if (strcasecmp(item, "-I") == 0)
2232     {
2233       initialstart= 1;
2234       continue;
2235     }
2236     if (strcasecmp(item, "-A") == 0)
2237     {
2238       abort= 1;
2239       continue;
2240     }
2241     if (strcasecmp(item, "-F") == 0)
2242     {
2243       force = 1;
2244       continue;
2245     }
2246     ndbout_c("Invalid option: %s. Expecting -A,-N,-I or -F after RESTART",
2247              item);
2248     return -1;
2249   }
2250 
2251   struct ndb_mgm_cluster_state *cl = ndb_mgm_get_status(m_mgmsrv);
2252   if(cl == NULL)
2253   {
2254     ndbout_c("Could not get status");
2255     printError();
2256     return -1;
2257   }
2258   NdbAutoPtr<char> ap1((char*)cl);
2259 
2260   // We allow 'all restart' in single user mode
2261   if(node_ids != 0) {
2262     for (int i = 0; i<cl->no_of_nodes; i++) {
2263       if((cl->node_states+i)->node_status == NDB_MGM_NODE_STATUS_SINGLEUSER)
2264       {
2265         ndbout_c("Cannot restart nodes: single user mode");
2266         return -1;
2267       }
2268     }
2269   }
2270 
2271   if (node_ids == 0) {
2272     ndbout_c("Executing RESTART on all nodes.");
2273     ndbout_c("Starting shutdown. This may take a while. Please wait...");
2274   }
2275 
2276   for (int i= 0; i < no_of_nodes; i++)
2277   {
2278     int j = 0;
2279     while((j < cl->no_of_nodes) && cl->node_states[j].node_id != node_ids[i])
2280       j++;
2281 
2282     if(cl->node_states[j].node_id != node_ids[i])
2283     {
2284       ndbout << node_ids[i] << ": Node not found" << endl;
2285       return -1;
2286     }
2287 
2288     if(cl->node_states[j].node_type == NDB_MGM_NODE_TYPE_MGM)
2289     {
2290       ndbout << "Shutting down MGM node"
2291 	     << " " << node_ids[i] << " for restart" << endl;
2292     }
2293   }
2294 
2295   result= ndb_mgm_restart4(m_mgmsrv, no_of_nodes, node_ids,
2296                            initialstart, nostart, abort, force,
2297                            &need_disconnect);
2298 
2299   if (result <= 0) {
2300     ndbout_c("Restart failed.");
2301     printError();
2302     retval = -1;
2303   }
2304   else
2305   {
2306     if (node_ids == 0)
2307       ndbout_c("All DB nodes are being restarted.");
2308     else
2309     {
2310       ndbout << "Node";
2311       for (int i= 0; i < no_of_nodes; i++)
2312         ndbout << " " << node_ids[i];
2313       ndbout_c(" is being restarted");
2314     }
2315     if(need_disconnect)
2316       disconnect();
2317   }
2318   return retval;
2319 }
2320 
2321 /**
2322  * print status of one node
2323  */
2324 static
2325 void
print_status(const ndb_mgm_node_state * state)2326 print_status(const ndb_mgm_node_state * state)
2327 {
2328   Uint32 version = state->version;
2329   if (state->node_type != NDB_MGM_NODE_TYPE_NDB)
2330   {
2331     if (version != 0)
2332     {
2333       ndbout << "Node " << state->node_id <<": connected" ;
2334       ndbout_c(" (Version %d.%d.%d)",
2335                getMajor(version) ,
2336                getMinor(version),
2337                getBuild(version));
2338 
2339     }
2340     else
2341     {
2342       ndbout << "Node " << state->node_id << ": not connected" << endl;
2343     }
2344     return;
2345   }
2346 
2347   ndbout << "Node " << state->node_id
2348          << ": " << status_string(state->node_status);
2349   switch(state->node_status){
2350   case NDB_MGM_NODE_STATUS_STARTING:
2351     ndbout << " (Last completed phase " << state->start_phase << ")";
2352     break;
2353   case NDB_MGM_NODE_STATUS_SHUTTING_DOWN:
2354     ndbout << " (Last completed phase " << state->start_phase << ")";
2355     break;
2356   default:
2357     break;
2358   }
2359 
2360   if(state->node_status != NDB_MGM_NODE_STATUS_NO_CONTACT)
2361   {
2362     char tmp[100];
2363     ndbout_c(" (%s)", ndbGetVersionString(version,
2364                                           state->mysql_version, 0,
2365 					  tmp, sizeof(tmp)));
2366   }
2367   else
2368   {
2369     ndbout << endl;
2370   }
2371 }
2372 
2373 int
executeStatus(int processId,const char * parameters,bool all)2374 CommandInterpreter::executeStatus(int processId,
2375 				  const char* parameters, bool all)
2376 {
2377   if (! emptyString(parameters)) {
2378     ndbout_c("No parameters expected to this command.");
2379     return -1;
2380   }
2381 
2382   ndb_mgm_node_type types[2] = {
2383     NDB_MGM_NODE_TYPE_NDB,
2384     NDB_MGM_NODE_TYPE_UNKNOWN
2385   };
2386   struct ndb_mgm_cluster_state *cl;
2387   cl = ndb_mgm_get_status2(m_mgmsrv, all ? types : 0);
2388   if(cl == NULL)
2389   {
2390     ndbout_c("Can't get status of node %d.", processId);
2391     printError();
2392     return -1;
2393   }
2394   NdbAutoPtr<char> ap1((char*)cl);
2395 
2396   if (all)
2397   {
2398     for (int i = 0; i<cl->no_of_nodes; i++)
2399       print_status(cl->node_states+i);
2400     return 0;
2401   }
2402   else
2403   {
2404     for (int i = 0; i<cl->no_of_nodes; i++)
2405     {
2406       if (cl->node_states[i].node_id == processId)
2407       {
2408         print_status(cl->node_states + i);
2409         return 0;
2410       }
2411     }
2412     ndbout << processId << ": Node not found" << endl;
2413     return -1;
2414   }
2415   return 0;
2416 } //
2417 
2418 int
executeDumpState(int processId,const char * parameters,bool all)2419 CommandInterpreter::executeDumpState(int processId, const char* parameters,
2420 				     bool all)
2421 {
2422   if(emptyString(parameters))
2423   {
2424     ndbout_c("ERROR: Expected argument!");
2425     return -1;
2426   }
2427 
2428   int params[25];
2429   int num_params = 0;
2430   const size_t max_params = sizeof(params)/sizeof(params[0]);
2431 
2432   Vector<BaseString> args;
2433   split_args(parameters, args);
2434 
2435   if (args.size() > max_params)
2436   {
2437     ndbout_c("ERROR: Too many arguments, max %d allowed", (int)max_params);
2438     return -1;
2439   }
2440 
2441   for (size_t i = 0; i < args.size(); i++)
2442   {
2443     const char* arg = args[i].c_str();
2444 
2445     if (strtoll(arg, NULL, 0) < 0 ||
2446         strtoll(arg, NULL, 0) > 0xffffffff)
2447     {
2448       ndbout_c("ERROR: Illegal value '%s' in argument to signal.\n"
2449                "(Value must be between 0 and 0xffffffff.)", arg);
2450       return -1;
2451     }
2452     assert(num_params < (int)max_params);
2453     params[num_params] = (int)strtoll(arg, NULL, 0);
2454     num_params++;
2455   }
2456 
2457   ndbout << "Sending dump signal with data:" << endl;
2458   for (int i = 0; i < num_params; i++)
2459   {
2460     ndbout.setHexFormat(1) << params[i] << " ";
2461     if (!((i+1) & 0x3)) ndbout << endl;
2462   }
2463   ndbout << endl;
2464 
2465   struct ndb_mgm_reply reply;
2466   return ndb_mgm_dump_state(m_mgmsrv, processId, params, num_params, &reply);
2467 }
2468 
2469 static void
report_memoryusage(const ndb_logevent & event)2470 report_memoryusage(const ndb_logevent& event)
2471 {
2472   const ndb_logevent_MemoryUsage& usage = event.MemoryUsage;
2473   const Uint32 block = usage.block;
2474   const Uint32 total = usage.pages_total;
2475   const Uint32 used = usage.pages_used;
2476   assert(event.type == NDB_LE_MemoryUsage);
2477 
2478   ndbout_c("Node %u: %s usage is %d%%(%d %dK pages of total %d)",
2479            event.source_nodeid,
2480            (block == DBACC ? "Index" : (block == DBTUP ? "Data" : "<unknown>")),
2481            (total ? (used * 100 / total) : 0),
2482            used,
2483            usage.page_size_kb/1024,
2484            total);
2485 }
2486 
2487 
2488 static void
report_backupstatus(const ndb_logevent & event)2489 report_backupstatus(const ndb_logevent& event)
2490 {
2491   const ndb_logevent_BackupStatus& status = event.BackupStatus;
2492   assert(event.type == NDB_LE_BackupStatus);
2493 
2494 
2495   if (status.starting_node)
2496     ndbout_c("Node %u: Local backup status: backup %u started from node %u\n"
2497              " #Records: %llu #LogRecords: %llu\n"
2498              " Data: %llu bytes Log: %llu bytes",
2499              event.source_nodeid,
2500              status.backup_id,
2501              refToNode(status.starting_node),
2502              make_uint64(status.n_records_lo, status.n_records_hi),
2503              make_uint64(status.n_log_records_lo, status.n_log_records_hi),
2504              make_uint64(status.n_bytes_lo, status.n_bytes_hi),
2505              make_uint64(status.n_log_bytes_lo, status.n_log_bytes_hi));
2506   else
2507     ndbout_c("Node %u: Backup not started",
2508              event.source_nodeid);
2509 }
2510 
2511 static
2512 void
report_events(const ndb_logevent & event)2513 report_events(const ndb_logevent& event)
2514 {
2515   Uint32 threshold = 0;
2516   Logger::LoggerLevel severity = Logger::LL_WARNING;
2517   LogLevel::EventCategory cat= LogLevel::llInvalid;
2518   EventLogger::EventTextFunction textF;
2519 
2520   const EventReport * real_event = (const EventReport*)event.SavedEvent.data;
2521   Uint32 type = real_event->getEventType();
2522 
2523   if (EventLoggerBase::event_lookup(type,cat,threshold,severity,textF))
2524     return;
2525 
2526   char out[1024];
2527   Uint32 pos = 0;
2528   if (event.source_nodeid != 0)
2529   {
2530     BaseString::snprintf(out, sizeof(out), "Node %u: ", event.source_nodeid);
2531     pos= (Uint32)strlen(out);
2532   }
2533   textF(out+pos, sizeof(out)-pos, event.SavedEvent.data, event.SavedEvent.len);
2534 
2535   time_t t = event.SavedEvent.time;
2536   struct tm * tm_now = localtime(&t);
2537   ndbout_c("%d-%.2d-%.2d %.2d:%.2d:%.2d %s",
2538            tm_now->tm_year + 1900,
2539            tm_now->tm_mon + 1, //month is [0,11]. +1 -> [1,12]
2540            tm_now->tm_mday,
2541            tm_now->tm_hour,
2542            tm_now->tm_min,
2543            tm_now->tm_sec,
2544            out);
2545 }
2546 
2547 static int
sort_log(const void * _a,const void * _b)2548 sort_log(const void *_a, const void *_b)
2549 {
2550   const ndb_logevent * a = (const ndb_logevent*)_a;
2551   const ndb_logevent * b = (const ndb_logevent*)_b;
2552 
2553   if (a->source_nodeid == b->source_nodeid)
2554   {
2555     return a->SavedEvent.seq - b->SavedEvent.seq;
2556   }
2557 
2558   if (a->SavedEvent.time < b->SavedEvent.time)
2559     return -1;
2560   if (a->SavedEvent.time > b->SavedEvent.time)
2561     return 1;
2562 
2563   if (a->SavedEvent.seq < b->SavedEvent.seq)
2564     return -1;
2565   if (a->SavedEvent.seq > b->SavedEvent.seq)
2566     return 1;
2567 
2568   return (a->source_nodeid - b->source_nodeid);
2569 }
2570 
2571 static const
2572 struct st_report_cmd {
2573   const char *name;
2574   const char *help;
2575   Ndb_logevent_type type;
2576   void (*print_event_fn)(const ndb_logevent&);
2577   int (* sort_fn)(const void *_a, const void *_b);
2578 } report_cmds[] = {
2579 
2580   { "BackupStatus",
2581     "Report backup status of respective node",
2582     NDB_LE_BackupStatus,
2583     report_backupstatus, 0 },
2584 
2585   { "MemoryUsage",
2586     "Report memory usage of respective node",
2587     NDB_LE_MemoryUsage,
2588     report_memoryusage, 0 },
2589 
2590   { "EventLog",
2591     "Report events in datanodes circular event log buffer",
2592     NDB_LE_SavedEvent,
2593     report_events, sort_log },
2594 
2595   { 0, 0, NDB_LE_ILLEGAL_TYPE, 0, 0 }
2596 };
2597 
2598 
2599 int
executeReport(int nodeid,const char * parameters,bool all)2600 CommandInterpreter::executeReport(int nodeid, const char* parameters,
2601                                   bool all)
2602 {
2603   if (emptyString(parameters))
2604   {
2605     ndbout_c("ERROR: missing report type specifier!");
2606     return -1;
2607   }
2608 
2609   Vector<BaseString> args;
2610   split_args(parameters, args);
2611 
2612   const st_report_cmd* report_cmd = report_cmds;
2613   for (; report_cmd->name; report_cmd++)
2614   {
2615     if (strncasecmp(report_cmd->name, args[0].c_str(),
2616                     args[0].length()) == 0)
2617       break;
2618   }
2619 
2620   if (!report_cmd->name)
2621   {
2622     ndbout_c("ERROR: '%s' - report type specifier unknown!", args[0].c_str());
2623     return -1;
2624   }
2625 
2626   if (!all)
2627   {
2628     ClusterInfo info;
2629     if (!info.fetch(m_mgmsrv))
2630     {
2631       printError();
2632       return -1;
2633     }
2634 
2635     // Check that given nodeid is a NDB node
2636     if (!info.is_ndb_node(nodeid))
2637       return -1;
2638   }
2639 
2640   struct ndb_mgm_events* events =
2641     ndb_mgm_dump_events(m_mgmsrv, report_cmd->type,
2642                         all ? 0 : 1, &nodeid);
2643   if (!events)
2644   {
2645     ndbout_c("ERROR: failed to fetch report!");
2646     printError();
2647     return -1;
2648   }
2649 
2650   if (report_cmd->sort_fn)
2651   {
2652     qsort(events->events, events->no_of_events,
2653           sizeof(events->events[0]), report_cmd->sort_fn);
2654   }
2655 
2656   for (int i = 0; i < events->no_of_events; i++)
2657   {
2658     const ndb_logevent& event = events->events[i];
2659     report_cmd->print_event_fn(event);
2660   }
2661 
2662   free(events);
2663   return 0;
2664 }
2665 
2666 
2667 static void
helpTextReportFn()2668 helpTextReportFn()
2669 {
2670   ndbout_c("  <report-type> =");
2671   const st_report_cmd* report_cmd = report_cmds;
2672   for (; report_cmd->name; report_cmd++)
2673     ndbout_c("    %s\t- %s", report_cmd->name, report_cmd->help);
2674 }
2675 
2676 
2677 //*****************************************************************************
2678 //*****************************************************************************
2679 
2680 int
executeLogLevel(int processId,const char * parameters,bool all)2681 CommandInterpreter::executeLogLevel(int processId, const char* parameters,
2682 				    bool all)
2683 {
2684   (void) all;
2685   if (emptyString(parameters)) {
2686     ndbout << "Expected argument" << endl;
2687     return -1;
2688   }
2689   BaseString tmp(parameters);
2690   Vector<BaseString> spec;
2691   tmp.split(spec, "=");
2692   if(spec.size() != 2){
2693     ndbout << "Invalid loglevel specification: " << parameters << endl;
2694     return -1;
2695   }
2696 
2697   spec[0].trim().ndb_toupper();
2698   int category = ndb_mgm_match_event_category(spec[0].c_str());
2699   if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
2700     category = atoi(spec[0].c_str());
2701     if(category < NDB_MGM_MIN_EVENT_CATEGORY ||
2702        category > NDB_MGM_MAX_EVENT_CATEGORY){
2703       ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
2704       return -1;
2705     }
2706   }
2707 
2708   int level = atoi(spec[1].c_str());
2709   if(level < 0 || level > 15){
2710     ndbout << "Invalid level: " << spec[1].c_str() << endl;
2711     return -1;
2712   }
2713 
2714   ndbout << "Executing LOGLEVEL on node " << processId << flush;
2715 
2716   struct ndb_mgm_reply reply;
2717   int result;
2718   result = ndb_mgm_set_loglevel_node(m_mgmsrv,
2719 				     processId,
2720 				     (ndb_mgm_event_category)category,
2721 				     level,
2722 				     &reply);
2723 
2724   if (result < 0) {
2725     ndbout_c(" failed.");
2726     printError();
2727     return -1;
2728   } else {
2729     ndbout_c(" OK!");
2730   }
2731   return 0;
2732 }
2733 
2734 //*****************************************************************************
2735 //*****************************************************************************
executeError(int processId,const char * parameters,bool)2736 int CommandInterpreter::executeError(int processId,
2737 				      const char* parameters, bool /* all */)
2738 {
2739   if (emptyString(parameters))
2740   {
2741     ndbout_c("ERROR: Missing error number.");
2742     return -1;
2743   }
2744 
2745   Vector<BaseString> args;
2746   split_args(parameters, args);
2747 
2748   if (args.size() >= 2)
2749   {
2750     ndbout << "ERROR: Too many arguments." << endl;
2751     return -1;
2752   }
2753 
2754   int errorNo;
2755   if (! convert(args[0].c_str(), errorNo)) {
2756     ndbout << "ERROR: Expected an integer." << endl;
2757     return -1;
2758   }
2759 
2760   return ndb_mgm_insert_error(m_mgmsrv, processId, errorNo, NULL);
2761 }
2762 
2763 //*****************************************************************************
2764 //*****************************************************************************
2765 
2766 int
executeLog(int processId,const char * parameters,bool all)2767 CommandInterpreter::executeLog(int processId,
2768 			       const char* parameters, bool all)
2769 {
2770   struct ndb_mgm_reply reply;
2771   Vector<BaseString> blocks;
2772   if (! parseBlockSpecification(parameters, blocks)) {
2773     return -1;
2774   }
2775 
2776   BaseString block_names;
2777   for (unsigned i = 0; i<blocks.size(); i++)
2778     block_names.appfmt("%s|", blocks[i].c_str());
2779 
2780   int result = ndb_mgm_log_signals(m_mgmsrv,
2781 				   processId,
2782 				   NDB_MGM_SIGNAL_LOG_MODE_INOUT,
2783 				   block_names.c_str(),
2784 				   &reply);
2785   if (result != 0) {
2786     ndbout_c("Execute LOG on node %d failed.", processId);
2787     printError();
2788     return -1;
2789   }
2790   return 0;
2791 }
2792 
2793 
2794 //*****************************************************************************
2795 //*****************************************************************************
2796 int
executeTestOn(int processId,const char * parameters,bool)2797 CommandInterpreter::executeTestOn(int processId,
2798 				  const char* parameters, bool /*all*/)
2799 {
2800   if (! emptyString(parameters)) {
2801     ndbout << "No parameters expected to this command." << endl;
2802     return -1;
2803   }
2804   struct ndb_mgm_reply reply;
2805   int result = ndb_mgm_start_signallog(m_mgmsrv, processId, &reply);
2806   if (result != 0) {
2807     ndbout_c("Execute TESTON failed.");
2808     printError();
2809     return -1;
2810   }
2811   return 0;
2812 }
2813 
2814 //*****************************************************************************
2815 //*****************************************************************************
2816 int
executeTestOff(int processId,const char * parameters,bool)2817 CommandInterpreter::executeTestOff(int processId,
2818 				   const char* parameters, bool /*all*/)
2819 {
2820   if (! emptyString(parameters)) {
2821     ndbout << "No parameters expected to this command." << endl;
2822     return -1;
2823   }
2824   struct ndb_mgm_reply reply;
2825   int result = ndb_mgm_stop_signallog(m_mgmsrv, processId, &reply);
2826   if (result != 0) {
2827     ndbout_c("Execute TESTOFF failed.");
2828     printError();
2829     return -1;
2830   }
2831   return 0;
2832 }
2833 
2834 
2835 //*****************************************************************************
2836 //*****************************************************************************
2837 
2838 int
executeEventReporting(int processId,const char * parameters,bool all)2839 CommandInterpreter::executeEventReporting(int processId,
2840 					  const char* parameters,
2841 					  bool all)
2842 {
2843   int retval = 0;
2844   if (emptyString(parameters)) {
2845     ndbout << "Expected argument" << endl;
2846     return -1;
2847   }
2848 
2849   Vector<BaseString> specs;
2850   split_args(parameters, specs);
2851 
2852   for (int i=0; i < (int) specs.size(); i++)
2853   {
2854     Vector<BaseString> spec;
2855     specs[i].split(spec, "=");
2856     if(spec.size() != 2){
2857       ndbout << "Invalid loglevel specification: " << specs[i] << endl;
2858       continue;
2859     }
2860 
2861     spec[0].trim().ndb_toupper();
2862     int category = ndb_mgm_match_event_category(spec[0].c_str());
2863     if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){
2864       if(!convert(spec[0].c_str(), category) ||
2865 	 category < NDB_MGM_MIN_EVENT_CATEGORY ||
2866 	 category > NDB_MGM_MAX_EVENT_CATEGORY){
2867 	ndbout << "Unknown category: \"" << spec[0].c_str() << "\"" << endl;
2868 	continue;
2869       }
2870     }
2871 
2872     int level;
2873     if (!convert(spec[1].c_str(),level))
2874     {
2875       ndbout << "Invalid level: " << spec[1].c_str() << endl;
2876       continue;
2877     }
2878 
2879     ndbout << "Executing CLUSTERLOG " << spec[0] << "=" << spec[1]
2880 	   << " on node " << processId << flush;
2881 
2882     struct ndb_mgm_reply reply;
2883     int result;
2884     result = ndb_mgm_set_loglevel_clusterlog(m_mgmsrv,
2885 					     processId,
2886 					     (ndb_mgm_event_category)category,
2887 					     level,
2888 					     &reply);
2889 
2890     if (result != 0) {
2891       ndbout_c(" failed.");
2892       printError();
2893       retval = -1;
2894     } else {
2895       ndbout_c(" OK!");
2896     }
2897   }
2898   return retval;
2899 }
2900 
2901 
2902 /*****************************************************************************
2903  * Backup
2904  *****************************************************************************/
2905 int
executeStartBackup(char * parameters,bool interactive)2906 CommandInterpreter::executeStartBackup(char* parameters, bool interactive)
2907 {
2908   struct ndb_mgm_reply reply;
2909   unsigned int backupId;
2910   unsigned int input_backupId = 0;
2911 
2912   Vector<BaseString> args;
2913   if (parameters)
2914     split_args(parameters, args);
2915 
2916   for (unsigned i= 0; i < args.size(); i++)
2917     args[i].ndb_toupper();
2918 
2919   int sz= args.size();
2920 
2921   int result;
2922   int flags = 2;
2923   //1,snapshot at start time. 0 snapshot at end time
2924   unsigned int backuppoint = 0;
2925   bool b_log = false;
2926   bool b_nowait = false;
2927   bool b_wait_completed = false;
2928   bool b_wait_started = false;
2929 
2930   /*
2931    All the commands list as follow:
2932    start backup <backupid> nowait | start backup <backupid> snapshotstart/snapshotend nowati | start backup <backupid> nowait snapshotstart/snapshotend
2933    start backup <backupid> | start backup <backupid> wait completed | start backup <backupid> snapshotstart/snapshotend
2934    start backup <backupid> snapshotstart/snapshotend wait completed | start backup <backupid> wait completed snapshotstart/snapshotend
2935    start backup <backupid> wait started | start backup <backupid> snapshotstart/snapshotend wait started
2936    start backup <backupid> wait started snapshotstart/snapshotend
2937   */
2938   for (int i= 1; i < sz; i++)
2939   {
2940     if (i == 1 && sscanf(args[1].c_str(), "%u", &input_backupId) == 1) {
2941       if (input_backupId > 0 && input_backupId < MAX_BACKUPS)
2942         continue;
2943       else {
2944         invalid_command(parameters);
2945         return -1;
2946       }
2947     }
2948 
2949     if (args[i] == "SNAPSHOTEND") {
2950       if (b_log ==true) {
2951         invalid_command(parameters);
2952         return -1;
2953       }
2954       b_log = true;
2955       backuppoint = 0;
2956       continue;
2957     }
2958     if (args[i] == "SNAPSHOTSTART") {
2959       if (b_log ==true) {
2960         invalid_command(parameters);
2961         return -1;
2962       }
2963       b_log = true;
2964       backuppoint = 1;
2965       continue;
2966     }
2967     if (args[i] == "NOWAIT") {
2968       if (b_nowait == true || b_wait_completed == true || b_wait_started ==true) {
2969         invalid_command(parameters);
2970         return -1;
2971       }
2972       b_nowait = true;
2973       flags = 0;
2974       continue;
2975     }
2976     if (args[i] == "WAIT") {
2977       if (b_nowait == true || b_wait_completed == true || b_wait_started ==true) {
2978         invalid_command(parameters);
2979         return -1;
2980       }
2981       if (i+1 < sz) {
2982         if (args[i+1] == "COMPLETED") {
2983           b_wait_completed = true;
2984           flags = 2;
2985           i++;
2986         }
2987         else if (args[i+1] == "STARTED") {
2988           b_wait_started = true;
2989           flags = 1;
2990           i++;
2991         }
2992         else {
2993           invalid_command(parameters);
2994           return -1;
2995         }
2996       }
2997       else {
2998         invalid_command(parameters);
2999         return -1;
3000       }
3001       continue;
3002     }
3003     invalid_command(parameters);
3004     return -1;
3005   }
3006 
3007   //print message
3008   if (flags == 2)
3009     ndbout_c("Waiting for completed, this may take several minutes");
3010   if (flags == 1)
3011     ndbout_c("Waiting for started, this may take several minutes");
3012 
3013   NdbLogEventHandle log_handle= NULL;
3014   struct ndb_logevent log_event;
3015   if (flags > 0 && !interactive)
3016   {
3017     int filter[] = { 15, NDB_MGM_EVENT_CATEGORY_BACKUP, 0, 0 };
3018     log_handle = ndb_mgm_create_logevent_handle(m_mgmsrv, filter);
3019     if (!log_handle)
3020     {
3021       ndbout << "Initializing start of backup failed" << endl;
3022       printError();
3023       return -1;
3024     }
3025   }
3026 
3027   //start backup N | start backup snapshotstart/snapshotend
3028   if (input_backupId > 0 || b_log == true)
3029     result = ndb_mgm_start_backup3(m_mgmsrv, flags, &backupId, &reply, input_backupId, backuppoint);
3030   //start backup
3031   else
3032     result = ndb_mgm_start_backup(m_mgmsrv, flags, &backupId, &reply);
3033 
3034   if (result != 0) {
3035     ndbout << "Backup failed" << endl;
3036     printError();
3037 
3038     if (log_handle)
3039       ndb_mgm_destroy_logevent_handle(&log_handle);
3040     return result;
3041   }
3042 
3043   /**
3044    * If interactive, event listner thread is already running
3045    */
3046   if (log_handle && !interactive)
3047   {
3048     int count = 0;
3049     int retry = 0;
3050     int res;
3051     do {
3052       if ((res= ndb_logevent_get_next(log_handle, &log_event, 60000)) > 0)
3053       {
3054         int print = 0;
3055         switch (log_event.type) {
3056           case NDB_LE_BackupStarted:
3057             if (log_event.BackupStarted.backup_id == backupId)
3058               print = 1;
3059             break;
3060           case NDB_LE_BackupCompleted:
3061             if (log_event.BackupCompleted.backup_id == backupId)
3062               print = 1;
3063             break;
3064           case NDB_LE_BackupAborted:
3065             if (log_event.BackupAborted.backup_id == backupId)
3066               print = 1;
3067             break;
3068           default:
3069             break;
3070         }
3071         if (print)
3072         {
3073           Guard g(m_print_mutex);
3074           printLogEvent(&log_event);
3075           count++;
3076         }
3077       }
3078       else
3079       {
3080         retry++;
3081       }
3082     } while(res >= 0 && count < 2 && retry < 3);
3083 
3084     if (retry >= 3)
3085       ndbout << "get backup event failed for " << retry << " times" << endl;
3086 
3087     ndb_mgm_destroy_logevent_handle(&log_handle);
3088   }
3089 
3090   return 0;
3091 }
3092 
3093 int
executeAbortBackup(char * parameters)3094 CommandInterpreter::executeAbortBackup(char* parameters)
3095 {
3096   int bid = -1;
3097   struct ndb_mgm_reply reply;
3098   if (emptyString(parameters))
3099     goto executeAbortBackupError1;
3100 
3101   {
3102     strtok(parameters, " ");
3103     char* id = strtok(NULL, "\0");
3104     if(id == 0 || sscanf(id, "%d", &bid) != 1)
3105       goto executeAbortBackupError1;
3106   }
3107   {
3108     int result= ndb_mgm_abort_backup(m_mgmsrv, bid, &reply);
3109     if (result != 0) {
3110       ndbout << "Abort of backup " << bid << " failed" << endl;
3111       printError();
3112       return -1;
3113     } else {
3114       ndbout << "Abort of backup " << bid << " ordered" << endl;
3115     }
3116   }
3117   return 0;
3118  executeAbortBackupError1:
3119   ndbout << "Invalid arguments: expected <BackupId>" << endl;
3120   return -1;
3121 }
3122 
3123 int
executeCreateNodeGroup(char * parameters)3124 CommandInterpreter::executeCreateNodeGroup(char* parameters)
3125 {
3126   int result;
3127   int ng;
3128   struct ndb_mgm_reply reply;
3129   char *id= strchr(parameters, ' ');
3130   if (emptyString(id))
3131     goto err;
3132 
3133   {
3134     Vector<int> nodes;
3135     BaseString args(id);
3136     Vector<BaseString> nodelist;
3137     args.split(nodelist, ",");
3138 
3139     for (Uint32 i = 0; i<nodelist.size(); i++)
3140     {
3141       nodes.push_back(atoi(nodelist[i].c_str()));
3142     }
3143     nodes.push_back(0);
3144 
3145     result= ndb_mgm_create_nodegroup(m_mgmsrv, nodes.getBase(), &ng, &reply);
3146 
3147     if (result != 0) {
3148       printError();
3149       return -1;
3150     } else {
3151       ndbout << "Nodegroup " << ng << " created" << endl;
3152     }
3153 
3154   }
3155 
3156   return 0;
3157 err:
3158   ndbout << "Invalid arguments: expected <id>,<id>..." << endl;
3159   return -1;
3160 }
3161 
3162 int
executeDropNodeGroup(char * parameters)3163 CommandInterpreter::executeDropNodeGroup(char* parameters)
3164 {
3165   int ng = -1;
3166   struct ndb_mgm_reply reply;
3167   if (emptyString(parameters))
3168     goto err;
3169 
3170   {
3171     char* id = strchr(parameters, ' ');
3172     if(id == 0 || sscanf(id, "%d", &ng) != 1)
3173       goto err;
3174   }
3175 
3176   {
3177     int result= ndb_mgm_drop_nodegroup(m_mgmsrv, ng, &reply);
3178     if (result != 0) {
3179       printError();
3180       return -1;
3181     } else {
3182       ndbout << "Drop Node Group " << ng << " done" << endl;
3183     }
3184   }
3185   return 0;
3186 err:
3187   ndbout << "Invalid arguments: expected <NG>" << endl;
3188   return -1;
3189 }
3190