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