1 /*
2    Copyright (c) 2003, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 #include <ndb_global.h>
26 #include <ndb_opts.h>
27 
28 #include "MgmtSrvr.hpp"
29 #include "EventLogger.hpp"
30 #include "Config.hpp"
31 
32 #include <version.h>
33 #include <kernel_types.h>
34 #include <portlib/ndb_daemon.h>
35 #include <NdbConfig.h>
36 #include <NdbSleep.h>
37 #include <portlib/NdbDir.hpp>
38 #include <ndb_version.h>
39 #include <mgmapi_config_parameters.h>
40 #include <NdbAutoPtr.hpp>
41 #include <ndb_mgmclient.hpp>
42 
43 #include <EventLogger.hpp>
44 extern EventLogger * g_eventLogger;
45 
46 #if defined VM_TRACE || defined ERROR_INSERT
47 extern int g_errorInsert;
48 #endif
49 
50 const char *load_default_groups[]= { "mysql_cluster","ndb_mgmd",0 };
51 
52 // copied from mysql.cc to get readline
53 extern "C" {
54 #if defined(_WIN32)
55 #include <conio.h>
56 #elif !defined(__NETWARE__)
57 #include <readline.h>
58 extern "C" int add_history(const char *command); /* From readline directory */
59 #define HAVE_READLINE
60 #endif
61 }
62 
63 static int
read_and_execute(Ndb_mgmclient * com,const char * prompt,int _try_reconnect)64 read_and_execute(Ndb_mgmclient* com, const char * prompt, int _try_reconnect)
65 {
66   static char *line_read = (char *)NULL;
67 
68   /* If the buffer has already been allocated, return the memory
69      to the free pool. */
70   if (line_read)
71   {
72     free (line_read);
73     line_read = (char *)NULL;
74   }
75 #ifdef HAVE_READLINE
76   /* Get a line from the user. */
77   line_read = readline (prompt);
78   /* If the line has any text in it, save it on the history. */
79   if (line_read && *line_read)
80     add_history (line_read);
81 #else
82   static char linebuffer[254];
83   fputs(prompt, stdout);
84   linebuffer[sizeof(linebuffer)-1]=0;
85   line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin);
86   if (line_read == linebuffer) {
87     char *q=linebuffer;
88     while (*q > 31) q++;
89     *q=0;
90     line_read= strdup(linebuffer);
91   }
92 #endif
93   return com->execute(line_read,_try_reconnect);
94 }
95 
96 /* Global variables */
97 bool g_StopServer= false;
98 bool g_RestartServer= false;
99 static MgmtSrvr* mgm;
100 static MgmtSrvr::MgmtOpts opts;
101 static const char* opt_logname = "MgmtSrvr";
102 static const char* opt_nowait_nodes = 0;
103 
104 static struct my_option my_long_options[] =
105 {
106   NDB_STD_OPTS("ndb_mgmd"),
107   { "config-file", 'f', "Specify cluster configuration file",
108     (uchar**) &opts.config_filename, (uchar**) &opts.config_filename, 0,
109     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
110   { "print-full-config", 'P', "Print full config and exit",
111     (uchar**) &opts.print_full_config, (uchar**) &opts.print_full_config, 0,
112     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
113   { "daemon", 'd', "Run ndb_mgmd in daemon mode (default)",
114     (uchar**) &opts.daemon, (uchar**) &opts.daemon, 0,
115     GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
116   { "interactive", NDB_OPT_NOSHORT,
117     "Run interactive. Not supported but provided for testing purposes",
118     (uchar**) &opts.interactive, (uchar**) &opts.interactive, 0,
119     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
120   { "no-nodeid-checks", NDB_OPT_NOSHORT,
121     "Do not provide any node id checks",
122     (uchar**) &opts.no_nodeid_checks, (uchar**) &opts.no_nodeid_checks, 0,
123     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
124   { "nodaemon", NDB_OPT_NOSHORT,
125     "Don't run as daemon, but don't read from stdin",
126     (uchar**) &opts.non_interactive, (uchar**) &opts.non_interactive, 0,
127     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
128   { "mycnf", NDB_OPT_NOSHORT,
129     "Read cluster config from my.cnf",
130     (uchar**) &opts.mycnf, (uchar**) &opts.mycnf, 0,
131     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
132   { "bind-address", NDB_OPT_NOSHORT,
133     "Local bind address",
134     (uchar**) &opts.bind_address, (uchar**) &opts.bind_address, 0,
135     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
136   { "configdir", NDB_OPT_NOSHORT,
137     "Directory for the binary configuration files (alias for --config-dir)",
138     (uchar**) &opts.configdir, (uchar**) &opts.configdir, 0,
139     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
140   { "config-dir", NDB_OPT_NOSHORT,
141     "Directory for the binary configuration files",
142     (uchar**) &opts.configdir, (uchar**) &opts.configdir, 0,
143     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
144   { "config-cache", NDB_OPT_NOSHORT,
145     "Enable configuration cache and change management",
146     (uchar**) &opts.config_cache, (uchar**) &opts.config_cache, 0,
147     GET_BOOL, NO_ARG, 1, 0, 1, 0, 0, 0 },
148   { "verbose", 'v',
149     "Write more log messages",
150     (uchar**) &opts.verbose, (uchar**) &opts.verbose, 0,
151     GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
152   { "reload", NDB_OPT_NOSHORT,
153     "Reload config from config.ini or my.cnf if it has changed on startup",
154     (uchar**) &opts.reload, (uchar**) &opts.reload, 0,
155     GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
156   { "initial", NDB_OPT_NOSHORT,
157     "Delete all binary config files and start from config.ini or my.cnf",
158     (uchar**) &opts.initial, (uchar**) &opts.initial, 0,
159     GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 },
160   { "log-name", NDB_OPT_NOSHORT,
161     "Name to use when logging messages for this node",
162     (uchar**) &opt_logname, (uchar**) &opt_logname, 0,
163     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
164   { "nowait-nodes", NDB_OPT_NOSHORT,
165     "Nodes that will not be waited for during start",
166     (uchar**) &opt_nowait_nodes, (uchar**) &opt_nowait_nodes, 0,
167     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
168 #if defined VM_TRACE || defined ERROR_INSERT
169   { "error-insert", NDB_OPT_NOSHORT,
170     "Start with error insert variable set",
171     (uchar**) &g_errorInsert, (uchar**) &g_errorInsert, 0,
172     GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
173 #endif
174   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
175 };
176 
short_usage_sub(void)177 static void short_usage_sub(void)
178 {
179   ndb_short_usage_sub(NULL);
180   ndb_service_print_options("ndb_mgmd");
181 }
182 
usage()183 static void usage()
184 {
185   ndb_usage(short_usage_sub, load_default_groups, my_long_options);
186 }
187 
188 static char **defaults_argv;
189 
mgmd_exit(int result)190 static void mgmd_exit(int result)
191 {
192   g_eventLogger->close();
193 
194   /* Free memory allocated by 'load_defaults' */
195   ndb_free_defaults(defaults_argv);
196 
197   ndb_end(opt_ndb_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
198 
199   ndb_daemon_exit(result);
200 }
201 
202 #include "../common/util/parse_mask.hpp"
203 
mgmd_main(int argc,char ** argv)204 static int mgmd_main(int argc, char** argv)
205 {
206   NDB_INIT(argv[0]);
207 
208   printf("MySQL Cluster Management Server %s\n", NDB_VERSION_STRING);
209 
210   ndb_opt_set_usage_funcs(short_usage_sub, usage);
211 
212   ndb_load_defaults(NULL, load_default_groups,&argc,&argv);
213   defaults_argv= argv; /* Must be freed by 'free_defaults' */
214 
215   int ho_error;
216 #ifndef NDEBUG
217   opt_debug= IF_WIN("d:t:i:F:o,c:\\ndb_mgmd.trace",
218                     "d:t:i:F:o,/tmp/ndb_mgmd.trace");
219 #endif
220 
221   if ((ho_error=handle_options(&argc, &argv, my_long_options,
222                                ndb_std_get_one_option)))
223     mgmd_exit(ho_error);
224 
225   if (opts.interactive ||
226       opts.non_interactive ||
227       opts.print_full_config) {
228     opts.daemon= 0;
229   }
230 
231   if (opts.mycnf && opts.config_filename)
232   {
233     fprintf(stderr, "ERROR: Both --mycnf and -f is not supported\n");
234     mgmd_exit(1);
235   }
236 
237   if (opt_nowait_nodes)
238   {
239     int res = parse_mask(opt_nowait_nodes, opts.nowait_nodes);
240     if(res == -2 || (res > 0 && opts.nowait_nodes.get(0)))
241     {
242       fprintf(stderr, "ERROR: Invalid nodeid specified in nowait-nodes: '%s'\n",
243               opt_nowait_nodes);
244       mgmd_exit(1);
245     }
246     else if (res < 0)
247     {
248       fprintf(stderr, "ERROR: Unable to parse nowait-nodes argument: '%s'\n",
249               opt_nowait_nodes);
250       mgmd_exit(1);
251     }
252   }
253 
254   /* Setup use of event logger */
255   g_eventLogger->setCategory(opt_logname);
256 
257   /* Output to console initially */
258   g_eventLogger->createConsoleHandler();
259 
260 #ifdef _WIN32
261   /* Output to Windows event log */
262   g_eventLogger->createEventLogHandler("MySQL Cluster Management Server");
263 #endif
264 
265   if (opts.verbose)
266     g_eventLogger->enable(Logger::LL_ALL); // --verbose turns on everything
267 
268   /**
269      Install signal handler for SIGPIPE
270      Done in TransporterFacade as well.. what about Configretriever?
271    */
272 #if !defined NDB_WIN32
273   signal(SIGPIPE, SIG_IGN);
274 #endif
275 
276   while (!g_StopServer)
277   {
278     mgm= new MgmtSrvr(opts);
279     if (mgm == NULL) {
280       g_eventLogger->critical("Out of memory, couldn't create MgmtSrvr");
281       mgmd_exit(1);
282     }
283 
284     /* Init mgm, load or fetch config */
285     if (!mgm->init()) {
286       delete mgm;
287       mgmd_exit(1);
288     }
289 
290     if (NdbDir::chdir(NdbConfig_get_path(NULL)) != 0)
291     {
292       g_eventLogger->warning("Cannot change directory to '%s', error: %d",
293                              NdbConfig_get_path(NULL), errno);
294       // Ignore error
295     }
296 
297     if (opts.daemon)
298     {
299       NodeId localNodeId= mgm->getOwnNodeId();
300       if (localNodeId == 0) {
301         g_eventLogger->error("Couldn't get own node id");
302         delete mgm;
303         mgmd_exit(1);
304       }
305 
306       char *lockfile= NdbConfig_PidFileName(localNodeId);
307       char *logfile=  NdbConfig_StdoutFileName(localNodeId);
308       if (ndb_daemonize(lockfile, logfile))
309       {
310         g_eventLogger->error("Couldn't start as daemon, error: '%s'",
311                              ndb_daemon_error);
312         mgmd_exit(1);
313       }
314     }
315 
316     /* Start mgm services */
317     if (!mgm->start()) {
318       delete mgm;
319       mgmd_exit(1);
320     }
321 
322     if (opts.interactive) {
323       int port= mgm->getPort();
324       BaseString con_str;
325       if(opts.bind_address)
326         con_str.appfmt("host=%s:%d", opts.bind_address, port);
327       else
328         con_str.appfmt("localhost:%d", port);
329       Ndb_mgmclient com(con_str.c_str(), 1);
330       while(!g_StopServer){
331         if (!read_and_execute(&com, "ndb_mgm> ", 1))
332           g_StopServer = true;
333       }
334     }
335     else
336     {
337       g_eventLogger->info("MySQL Cluster Management Server %s started",
338                           NDB_VERSION_STRING);
339 
340       while (!g_StopServer)
341         NdbSleep_MilliSleep(500);
342     }
343 
344     g_eventLogger->info("Shutting down server...");
345     delete mgm;
346     g_eventLogger->info("Shutdown complete");
347 
348     if(g_RestartServer){
349       g_eventLogger->info("Restarting server...");
350       g_RestartServer= g_StopServer= false;
351     }
352   }
353 
354   mgmd_exit(0);
355   return 0;
356 }
357 
358 
mgmd_stop(void)359 static void mgmd_stop(void)
360 {
361   g_StopServer= true;
362 }
363 
364 
main(int argc,char ** argv)365 int main(int argc, char** argv)
366 {
367   return ndb_daemon_init(argc, argv, mgmd_main, mgmd_stop,
368                          "ndb_mgmd", "MySQL Cluster Management Server");
369 }
370