diff options
Diffstat (limited to 'storage/ndb/src/mgmsrv')
22 files changed, 13885 insertions, 0 deletions
diff --git a/storage/ndb/src/mgmsrv/Config.cpp b/storage/ndb/src/mgmsrv/Config.cpp new file mode 100644 index 00000000000..6ff5fb789f0 --- /dev/null +++ b/storage/ndb/src/mgmsrv/Config.cpp @@ -0,0 +1,181 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "Config.hpp" +#include <ctype.h> +#include <string.h> +#include "MgmtErrorReporter.hpp" +#include <Properties.hpp> + +//***************************************************************************** +// Ctor / Dtor +//***************************************************************************** + +Config::Config() { + m_oldConfig = 0; + m_configValues = 0; +} + +Config::~Config() { + if(m_configValues != 0){ + free(m_configValues); + } + + if(m_oldConfig != 0) + delete m_oldConfig; +} + +/*****************************************************************************/ + +void +Config::printAllNameValuePairs(NdbOut &out, + const Properties *prop, + const char* s) const { + Properties::Iterator it(prop); + const Properties * section = m_info.getInfo(s); + for (const char* n = it.first(); n != NULL; n = it.next()) { + Uint32 int_value; + const char* str_value; + Uint64 int_64; + + if(!section->contains(n)) + continue; + if (m_info.getStatus(section, n) == ConfigInfo::CI_INTERNAL) + continue; + if (m_info.getStatus(section, n) == ConfigInfo::CI_DEPRICATED) + continue; + if (m_info.getStatus(section, n) == ConfigInfo::CI_NOTIMPLEMENTED) + continue; + + out << n << ": "; + + switch (m_info.getType(section, n)) { + case ConfigInfo::CI_INT: + MGM_REQUIRE(prop->get(n, &int_value)); + out << int_value; + break; + + case ConfigInfo::CI_INT64: + MGM_REQUIRE(prop->get(n, &int_64)); + out << int_64; + break; + + case ConfigInfo::CI_BOOL: + MGM_REQUIRE(prop->get(n, &int_value)); + if (int_value) { + out << "Y"; + } else { + out << "N"; + } + break; + case ConfigInfo::CI_STRING: + MGM_REQUIRE(prop->get(n, &str_value)); + out << str_value; + break; + case ConfigInfo::CI_SECTION: + out << "SECTION"; + break; + } + out << endl; + } +} + +/*****************************************************************************/ + +void Config::printConfigFile(NdbOut &out) const { +#if 0 + Uint32 noOfNodes, noOfConnections, noOfComputers; + MGM_REQUIRE(get("NoOfNodes", &noOfNodes)); + MGM_REQUIRE(get("NoOfConnections", &noOfConnections)); + MGM_REQUIRE(get("NoOfComputers", &noOfComputers)); + + out << + "######################################################################" << + endl << + "#" << endl << + "# NDB Cluster System configuration" << endl << + "#" << endl << + "######################################################################" << + endl << + "# No of nodes (DB, API or MGM): " << noOfNodes << endl << + "# No of connections: " << noOfConnections << endl << + "######################################################################" << + endl; + + /************************** + * Print COMPUTER configs * + **************************/ + const char * name; + Properties::Iterator it(this); + for(name = it.first(); name != NULL; name = it.next()){ + if(strncasecmp("Computer_", name, 9) == 0){ + + const Properties *prop; + out << endl << "[COMPUTER]" << endl; + MGM_REQUIRE(get(name, &prop)); + printAllNameValuePairs(out, prop, "COMPUTER"); + + out << endl << + "###################################################################" << + endl; + + } else if(strncasecmp("Node_", name, 5) == 0){ + /********************** + * Print NODE configs * + **********************/ + const Properties *prop; + const char *s; + + MGM_REQUIRE(get(name, &prop)); + MGM_REQUIRE(prop->get("Type", &s)); + out << endl << "[" << s << "]" << endl; + printAllNameValuePairs(out, prop, s); + + out << endl << + "###################################################################" << + endl; + } else if(strncasecmp("Connection_", name, 11) == 0){ + /**************************** + * Print CONNECTION configs * + ****************************/ + const Properties *prop; + const char *s; + + MGM_REQUIRE(get(name, &prop)); + MGM_REQUIRE(prop->get("Type", &s)); + out << endl << "[" << s << "]" << endl; + printAllNameValuePairs(out, prop, s); + + out << endl << + "###################################################################" << + endl; + } else if(strncasecmp("SYSTEM", name, strlen("SYSTEM")) == 0) { + /************************ + * Print SYSTEM configs * + ************************/ + const Properties *prop; + + MGM_REQUIRE(get(name, &prop)); + out << endl << "[SYSTEM]" << endl; + printAllNameValuePairs(out, prop, "SYSTEM"); + + out << endl << + "###################################################################" << + endl; + } + } +#endif +} diff --git a/storage/ndb/src/mgmsrv/Config.hpp b/storage/ndb/src/mgmsrv/Config.hpp new file mode 100644 index 00000000000..05a48ad91f0 --- /dev/null +++ b/storage/ndb/src/mgmsrv/Config.hpp @@ -0,0 +1,82 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef Config_H +#define Config_H + +#include <LogLevel.hpp> + +#include <kernel_types.h> + +#include <NdbOut.hpp> +#include <ndb_limits.h> +#include <Properties.hpp> +#include <ConfigInfo.hpp> + +class ConfigInfo; + +/** + * @class Config + * @brief Cluster Configuration (corresponds to initial configuration file) + * + * Contains all cluster configuration parameters. + * + * The information includes all configurable parameters for a NDB cluster: + * - DB, API and MGM nodes with all their properties, + * - Connections between nodes and computers the nodes will execute on. + * + * The following categories (sections) of configuration parameters exists: + * - COMPUTER, DB, MGM, API, TCP, SCI, SHM + * + */ + +class Config { +public: + /** + * Constructor which loads the object with an Properties object + */ + Config(); + virtual ~Config(); + + /** + * Prints the configuration in configfile format + */ + void printConfigFile(NdbOut &out = ndbout) const; + void printConfigFile(OutputStream &out) const { + NdbOut ndb(out); + printConfigFile(ndb); + } + + /** + * Info + */ + const ConfigInfo * getConfigInfo() const { return &m_info;} +private: + ConfigInfo m_info; + + void printAllNameValuePairs(NdbOut &out, + const Properties *prop, + const char* section) const; + + /** + * Information about parameters (min, max values etc) + */ +public: + Properties * m_oldConfig; + struct ndb_mgm_configuration * m_configValues; +}; + +#endif // Config_H diff --git a/storage/ndb/src/mgmsrv/ConfigInfo.cpp b/storage/ndb/src/mgmsrv/ConfigInfo.cpp new file mode 100644 index 00000000000..def349cf744 --- /dev/null +++ b/storage/ndb/src/mgmsrv/ConfigInfo.cpp @@ -0,0 +1,3727 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <ndb_opt_defaults.h> + +#include <NdbTCP.h> +#include "ConfigInfo.hpp" +#include <mgmapi_config_parameters.h> +#include <ndb_limits.h> +#include "InitConfigFileParser.hpp" +#include <m_string.h> + +extern my_bool opt_ndb_shm; +extern my_bool opt_core; + +#define MAX_LINE_LENGTH 255 +#define KEY_INTERNAL 0 +#define MAX_INT_RNIL 0xfffffeff +#define MAX_PORT_NO 65535 + +#define _STR_VALUE(x) #x +#define STR_VALUE(x) _STR_VALUE(x) + +/**************************************************************************** + * Section names + ****************************************************************************/ + +#define DB_TOKEN_PRINT "ndbd(DB)" +#define MGM_TOKEN_PRINT "ndb_mgmd(MGM)" +#define API_TOKEN_PRINT "mysqld(API)" + +#define DB_TOKEN "DB" +#define MGM_TOKEN "MGM" +#define API_TOKEN "API" + +const ConfigInfo::AliasPair +ConfigInfo::m_sectionNameAliases[]={ + {API_TOKEN, "MYSQLD"}, + {DB_TOKEN, "NDBD"}, + {MGM_TOKEN, "NDB_MGMD"}, + {0, 0} +}; + +const char* +ConfigInfo::m_sectionNames[]={ + "SYSTEM", + "COMPUTER", + + DB_TOKEN, + MGM_TOKEN, + API_TOKEN, + + "TCP", + "SCI", + "SHM" +}; +const int ConfigInfo::m_noOfSectionNames = +sizeof(m_sectionNames)/sizeof(char*); + + +/**************************************************************************** + * Section Rules declarations + ****************************************************************************/ +static bool transformComputer(InitConfigFileParser::Context & ctx, const char *); +static bool transformSystem(InitConfigFileParser::Context & ctx, const char *); +static bool transformNode(InitConfigFileParser::Context & ctx, const char *); +static bool checkConnectionSupport(InitConfigFileParser::Context & ctx, const char *); +static bool transformConnection(InitConfigFileParser::Context & ctx, const char *); +static bool applyDefaultValues(InitConfigFileParser::Context & ctx, const char *); +static bool checkMandatory(InitConfigFileParser::Context & ctx, const char *); +static bool fixPortNumber(InitConfigFileParser::Context & ctx, const char *); +static bool fixShmKey(InitConfigFileParser::Context & ctx, const char *); +static bool checkDbConstraints(InitConfigFileParser::Context & ctx, const char *); +static bool checkConnectionConstraints(InitConfigFileParser::Context &, const char *); +static bool checkTCPConstraints(InitConfigFileParser::Context &, const char *); +static bool fixNodeHostname(InitConfigFileParser::Context & ctx, const char * data); +static bool fixHostname(InitConfigFileParser::Context & ctx, const char * data); +static bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data); +static bool fixDepricated(InitConfigFileParser::Context & ctx, const char *); +static bool saveInConfigValues(InitConfigFileParser::Context & ctx, const char *); +static bool fixFileSystemPath(InitConfigFileParser::Context & ctx, const char * data); +static bool fixBackupDataDir(InitConfigFileParser::Context & ctx, const char * data); +static bool fixShmUniqueId(InitConfigFileParser::Context & ctx, const char * data); +static bool checkLocalhostHostnameMix(InitConfigFileParser::Context & ctx, const char * data); + +const ConfigInfo::SectionRule +ConfigInfo::m_SectionRules[] = { + { "SYSTEM", transformSystem, 0 }, + { "COMPUTER", transformComputer, 0 }, + + { DB_TOKEN, transformNode, 0 }, + { API_TOKEN, transformNode, 0 }, + { MGM_TOKEN, transformNode, 0 }, + + { MGM_TOKEN, fixShmUniqueId, 0 }, + + { "TCP", checkConnectionSupport, 0 }, + { "SHM", checkConnectionSupport, 0 }, + { "SCI", checkConnectionSupport, 0 }, + + { "TCP", transformConnection, 0 }, + { "SHM", transformConnection, 0 }, + { "SCI", transformConnection, 0 }, + + { DB_TOKEN, fixNodeHostname, 0 }, + { API_TOKEN, fixNodeHostname, 0 }, + { MGM_TOKEN, fixNodeHostname, 0 }, + + { "TCP", fixNodeId, "NodeId1" }, + { "TCP", fixNodeId, "NodeId2" }, + { "SHM", fixNodeId, "NodeId1" }, + { "SHM", fixNodeId, "NodeId2" }, + { "SCI", fixNodeId, "NodeId1" }, + { "SCI", fixNodeId, "NodeId2" }, + + { "TCP", fixHostname, "HostName1" }, + { "TCP", fixHostname, "HostName2" }, + { "SHM", fixHostname, "HostName1" }, + { "SHM", fixHostname, "HostName2" }, + { "SCI", fixHostname, "HostName1" }, + { "SCI", fixHostname, "HostName2" }, + { "SHM", fixHostname, "HostName1" }, + { "SHM", fixHostname, "HostName2" }, + + { "TCP", fixPortNumber, 0 }, // has to come after fixHostName + { "SHM", fixPortNumber, 0 }, // has to come after fixHostName + { "SCI", fixPortNumber, 0 }, // has to come after fixHostName + + { "*", applyDefaultValues, "user" }, + { "*", fixDepricated, 0 }, + { "*", applyDefaultValues, "system" }, + + { "SHM", fixShmKey, 0 }, // has to come after apply default values + + { DB_TOKEN, checkLocalhostHostnameMix, 0 }, + { API_TOKEN, checkLocalhostHostnameMix, 0 }, + { MGM_TOKEN, checkLocalhostHostnameMix, 0 }, + + { DB_TOKEN, fixFileSystemPath, 0 }, + { DB_TOKEN, fixBackupDataDir, 0 }, + + { DB_TOKEN, checkDbConstraints, 0 }, + + { "TCP", checkConnectionConstraints, 0 }, + { "SHM", checkConnectionConstraints, 0 }, + { "SCI", checkConnectionConstraints, 0 }, + + { "TCP", checkTCPConstraints, "HostName1" }, + { "TCP", checkTCPConstraints, "HostName2" }, + { "SCI", checkTCPConstraints, "HostName1" }, + { "SCI", checkTCPConstraints, "HostName2" }, + { "SHM", checkTCPConstraints, "HostName1" }, + { "SHM", checkTCPConstraints, "HostName2" }, + + { "*", checkMandatory, 0 }, + + { DB_TOKEN, saveInConfigValues, 0 }, + { API_TOKEN, saveInConfigValues, 0 }, + { MGM_TOKEN, saveInConfigValues, 0 }, + + { "TCP", saveInConfigValues, 0 }, + { "SHM", saveInConfigValues, 0 }, + { "SCI", saveInConfigValues, 0 } +}; +const int ConfigInfo::m_NoOfRules = sizeof(m_SectionRules)/sizeof(SectionRule); + +/**************************************************************************** + * Config Rules declarations + ****************************************************************************/ +static bool sanity_checks(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data); +static bool add_node_connections(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data); +static bool set_connection_priorities(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data); +static bool check_node_vs_replicas(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data); + +const ConfigInfo::ConfigRule +ConfigInfo::m_ConfigRules[] = { + { sanity_checks, 0 }, + { add_node_connections, 0 }, + { set_connection_priorities, 0 }, + { check_node_vs_replicas, 0 }, + { 0, 0 } +}; + +struct DepricationTransform { + const char * m_section; + const char * m_oldName; + const char * m_newName; + double m_add; + double m_mul; +}; + +static +const DepricationTransform f_deprication[] = { + { DB_TOKEN, "Discless", "Diskless", 0, 1 }, + { DB_TOKEN, "Id", "NodeId", 0, 1 }, + { API_TOKEN, "Id", "NodeId", 0, 1 }, + { MGM_TOKEN, "Id", "NodeId", 0, 1 }, + { 0, 0, 0, 0, 0} +}; + +/** + * The default constructors create objects with suitable values for the + * configuration parameters. + * + * Some are however given the value MANDATORY which means that the value + * must be specified in the configuration file. + * + * Min and max values are also given for some parameters. + * - Attr1: Name in file (initial config file) + * - Attr2: Name in prop (properties object) + * - Attr3: Name of Section (in init config file) + * - Attr4: Updateable + * - Attr5: Type of parameter (INT or BOOL) + * - Attr6: Default Value (number only) + * - Attr7: Min value + * - Attr8: Max value + * + * Parameter constraints are coded in file Config.cpp. + * + * ******************************************************************* + * Parameters used under development should be marked "NOTIMPLEMENTED" + * ******************************************************************* + */ + +const ConfigInfo::ParamInfo ConfigInfo::m_ParamInfo[] = { + + /**************************************************************************** + * COMPUTER + ***************************************************************************/ + { + KEY_INTERNAL, + "COMPUTER", + "COMPUTER", + "Computer section", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_SECTION, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + "COMPUTER", + "Name of computer", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + KEY_INTERNAL, + "HostName", + "COMPUTER", + "Hostname of computer (e.g. mysql.com)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + KEY_INTERNAL, + "ByteOrder", + "COMPUTER", + 0, + ConfigInfo::CI_DEPRICATED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, + 0 }, + + /**************************************************************************** + * SYSTEM + ***************************************************************************/ + { + CFG_SECTION_SYSTEM, + "SYSTEM", + "SYSTEM", + "System section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)CFG_SECTION_SYSTEM, + 0, 0 }, + + { + CFG_SYS_NAME, + "Name", + "SYSTEM", + "Name of system (NDB Cluster)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_SYS_PRIMARY_MGM_NODE, + "PrimaryMGMNode", + "SYSTEM", + "Node id of Primary "MGM_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SYS_CONFIG_GENERATION, + "ConfigGenerationNumber", + "SYSTEM", + "Configuration generation number", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /*************************************************************************** + * DB + ***************************************************************************/ + { + CFG_SECTION_NODE, + DB_TOKEN, + DB_TOKEN, + "Node section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)NODE_TYPE_DB, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + DB_TOKEN, + "Name of computer for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + "localhost", + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + DB_TOKEN, + "Name of system for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + DB_TOKEN, + "", + ConfigInfo::CI_DEPRICATED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + DB_TOKEN, + "Number identifying the database node ("DB_TOKEN_PRINT")", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + KEY_INTERNAL, + "ServerPort", + DB_TOKEN, + "Port used to setup transporter", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "1", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_DB_NO_REPLICAS, + "NoOfReplicas", + DB_TOKEN, + "Number of copies of all data in the database (1-4)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + "4" }, + + { + CFG_DB_NO_ATTRIBUTES, + "MaxNoOfAttributes", + DB_TOKEN, + "Total number of attributes stored in database. I.e. sum over all tables", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1000", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_TABLES, + "MaxNoOfTables", + DB_TOKEN, + "Total number of tables stored in the database", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "128", + "8", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_ORDERED_INDEXES, + "MaxNoOfOrderedIndexes", + DB_TOKEN, + "Total number of ordered indexes that can be defined in the system", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "128", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_UNIQUE_HASH_INDEXES, + "MaxNoOfUniqueHashIndexes", + DB_TOKEN, + "Total number of unique hash indexes that can be defined in the system", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "64", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_INDEXES, + "MaxNoOfIndexes", + DB_TOKEN, + "Total number of indexes that can be defined in the system", + ConfigInfo::CI_DEPRICATED, + false, + ConfigInfo::CI_INT, + "128", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_INDEX_OPS, + "MaxNoOfConcurrentIndexOperations", + DB_TOKEN, + "Total number of index operations that can execute simultaneously on one "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "8K", + "0", + STR_VALUE(MAX_INT_RNIL) + }, + + { + CFG_DB_NO_TRIGGERS, + "MaxNoOfTriggers", + DB_TOKEN, + "Total number of triggers that can be defined in the system", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "768", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_TRIGGER_OPS, + "MaxNoOfFiredTriggers", + DB_TOKEN, + "Total number of triggers that can fire simultaneously in one "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "4000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + DB_TOKEN, + "String referencing an earlier defined COMPUTER", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_DB_NO_SAVE_MSGS, + "MaxNoOfSavedMessages", + DB_TOKEN, + "Max number of error messages in error log and max number of trace files", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "25", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_MEMLOCK, + "LockPagesInMainMemory", + DB_TOKEN, + "If set to yes, then NDB Cluster data will not be swapped out to disk", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_DB_WATCHDOG_INTERVAL, + "TimeBetweenWatchDogCheck", + DB_TOKEN, + "Time between execution checks inside a database node", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "6000", + "70", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_STOP_ON_ERROR, + "StopOnError", + DB_TOKEN, + "If set to N, "DB_TOKEN_PRINT" automatically restarts/recovers in case of node failure", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_DB_STOP_ON_ERROR_INSERT, + "RestartOnErrorInsert", + DB_TOKEN, + "See src/kernel/vm/Emulator.hpp NdbRestartType for details", + ConfigInfo::CI_INTERNAL, + true, + ConfigInfo::CI_INT, + "2", + "0", + "4" }, + + { + CFG_DB_NO_OPS, + "MaxNoOfConcurrentOperations", + DB_TOKEN, + "Max number of operation records in transaction coordinator", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "32k", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_LOCAL_OPS, + "MaxNoOfLocalOperations", + DB_TOKEN, + "Max number of operation records defined in the local storage node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_LOCAL_SCANS, + "MaxNoOfLocalScans", + DB_TOKEN, + "Max number of fragment scans in parallel in the local storage node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BATCH_SIZE, + "BatchSizePerLocalScan", + DB_TOKEN, + "Used to calculate the number of lock records for scan with hold lock", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + STR_VALUE(DEF_BATCH_SIZE), + "1", + STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) }, + + { + CFG_DB_NO_TRANSACTIONS, + "MaxNoOfConcurrentTransactions", + DB_TOKEN, + "Max number of transaction executing concurrently on the "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "4096", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_SCANS, + "MaxNoOfConcurrentScans", + DB_TOKEN, + "Max number of scans executing concurrently on the "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "256", + "2", + "500" }, + + { + CFG_DB_TRANS_BUFFER_MEM, + "TransactionBufferMemory", + DB_TOKEN, + "Dynamic buffer space (in bytes) for key and attribute data allocated for each "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1M", + "1K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_INDEX_MEM, + "IndexMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing indexes", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT64, + "18M", + "1M", + "1024G" }, + + { + CFG_DB_DATA_MEM, + "DataMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing data", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT64, + "80M", + "1M", + "1024G" }, + + { + CFG_DB_UNDO_INDEX_BUFFER, + "UndoIndexBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for index part", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "2M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_UNDO_DATA_BUFFER, + "UndoDataBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for data part", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "16M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_REDO_BUFFER, + "RedoBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing REDO logs", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "8M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_LONG_SIGNAL_BUFFER, + "LongMessageBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for internal long messages", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1M", + "512k", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_DISK_PAGE_BUFFER_MEMORY, + "DiskPageBufferMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for disk page buffer cache", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT64, + "64M", + "4M", + "1024G" }, + + { + CFG_DB_SGA, + "SharedGlobalMemory", + DB_TOKEN, + "Total number bytes on each "DB_TOKEN_PRINT" node allocated for any use", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT64, + "20M", + "0", + "65536G" }, // 32k pages * 32-bit i value + + { + CFG_DB_START_PARTIAL_TIMEOUT, + "StartPartialTimeout", + DB_TOKEN, + "Time to wait before trying to start wo/ all nodes. 0=Wait forever", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "30000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_START_PARTITION_TIMEOUT, + "StartPartitionedTimeout", + DB_TOKEN, + "Time to wait before trying to start partitioned. 0=Wait forever", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "60000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_START_FAILURE_TIMEOUT, + "StartFailureTimeout", + DB_TOKEN, + "Time to wait before terminating. 0=Wait forever", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_HEARTBEAT_INTERVAL, + "HeartbeatIntervalDbDb", + DB_TOKEN, + "Time between "DB_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "DB_TOKEN_PRINT" considered dead after 3 missed HBs", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "1500", + "10", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_API_HEARTBEAT_INTERVAL, + "HeartbeatIntervalDbApi", + DB_TOKEN, + "Time between "API_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "API_TOKEN_PRINT" connection closed after 3 missed HBs", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "1500", + "100", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_INTERVAL, + "TimeBetweenLocalCheckpoints", + DB_TOKEN, + "Time between taking snapshots of the database (expressed in 2log of bytes)", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "20", + "0", + "31" }, + + { + CFG_DB_GCP_INTERVAL, + "TimeBetweenGlobalCheckpoints", + DB_TOKEN, + "Time between doing group commit of transactions to disk", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "2000", + "10", + "32000" }, + + { + CFG_DB_NO_REDOLOG_FILES, + "NoOfFragmentLogFiles", + DB_TOKEN, + "No of 16 Mbyte Redo log files in each of 4 file sets belonging to "DB_TOKEN_PRINT" node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "16", + "3", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_MAX_OPEN_FILES, + "MaxNoOfOpenFiles", + DB_TOKEN, + "Max number of files open per "DB_TOKEN_PRINT" node.(One thread is created per file)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "40", + "20", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_INITIAL_OPEN_FILES, + "InitialNoOfOpenFiles", + DB_TOKEN, + "Initial number of files open per "DB_TOKEN_PRINT" node.(One thread is created per file)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "27", + "20", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_CHECK_INTERVAL, + "TimeBetweenInactiveTransactionAbortCheck", + DB_TOKEN, + "Time between inactive transaction checks", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "1000", + "1000", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_INACTIVE_TIMEOUT, + "TransactionInactiveTimeout", + DB_TOKEN, + "Time application can wait before executing another transaction part (ms).\n" + "This is the time the transaction coordinator waits for the application\n" + "to execute or send another part (query, statement) of the transaction.\n" + "If the application takes too long time, the transaction gets aborted.\n" + "Timeout set to 0 means that we don't timeout at all on application wait.", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + STR_VALUE(MAX_INT_RNIL), + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_DEADLOCK_TIMEOUT, + "TransactionDeadlockDetectionTimeout", + DB_TOKEN, + "Time transaction can be executing in a DB node (ms).\n" + "This is the time the transaction coordinator waits for each database node\n" + "of the transaction to execute a request. If the database node takes too\n" + "long time, the transaction gets aborted.", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_INT, + "1200", + "50", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_TUP_SR, + "NoOfDiskPagesToDiskDuringRestartTUP", + DB_TOKEN, + "DiskCheckpointSpeedSr", + ConfigInfo::CI_DEPRICATED, + true, + ConfigInfo::CI_INT, + "40", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_TUP, + "NoOfDiskPagesToDiskAfterRestartTUP", + DB_TOKEN, + "DiskCheckpointSpeed", + ConfigInfo::CI_DEPRICATED, + true, + ConfigInfo::CI_INT, + "40", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_ACC_SR, + "NoOfDiskPagesToDiskDuringRestartACC", + DB_TOKEN, + "DiskCheckpointSpeedSr", + ConfigInfo::CI_DEPRICATED, + true, + ConfigInfo::CI_INT, + "20", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_ACC, + "NoOfDiskPagesToDiskAfterRestartACC", + DB_TOKEN, + "DiskCheckpointSpeed", + ConfigInfo::CI_DEPRICATED, + true, + ConfigInfo::CI_INT, + "20", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + + { + CFG_DB_DISCLESS, + "Diskless", + DB_TOKEN, + "Run wo/ disk", + ConfigInfo::CI_USED, + true, + ConfigInfo::CI_BOOL, + "false", + "false", + "true"}, + + { + KEY_INTERNAL, + "Discless", + DB_TOKEN, + "Diskless", + ConfigInfo::CI_DEPRICATED, + true, + ConfigInfo::CI_BOOL, + "false", + "false", + "true"}, + + + + { + CFG_DB_ARBIT_TIMEOUT, + "ArbitrationTimeout", + DB_TOKEN, + "Max time (milliseconds) database partion waits for arbitration signal", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "3000", + "10", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_NODE_DATADIR, + "DataDir", + DB_TOKEN, + "Data directory for this node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MYSQLCLUSTERDIR, + 0, 0 }, + + { + CFG_DB_FILESYSTEM_PATH, + "FileSystemPath", + DB_TOKEN, + "Path to directory where the "DB_TOKEN_PRINT" node stores its data (directory must exist)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_LOGLEVEL_STARTUP, + "LogLevelStartup", + DB_TOKEN, + "Node startup info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1", + "0", + "15" }, + + { + CFG_LOGLEVEL_SHUTDOWN, + "LogLevelShutdown", + DB_TOKEN, + "Node shutdown info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_STATISTICS, + "LogLevelStatistic", + DB_TOKEN, + "Transaction, operation, transporter info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CHECKPOINT, + "LogLevelCheckpoint", + DB_TOKEN, + "Local and Global checkpoint info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_NODERESTART, + "LogLevelNodeRestart", + DB_TOKEN, + "Node restart, node failure info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CONNECTION, + "LogLevelConnection", + DB_TOKEN, + "Node connect/disconnect info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CONGESTION, + "LogLevelCongestion", + DB_TOKEN, + "Congestion info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_ERROR, + "LogLevelError", + DB_TOKEN, + "Transporter, heartbeat errors printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_INFO, + "LogLevelInfo", + DB_TOKEN, + "Heartbeat and log info printed on stdout", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "15" }, + + /** + * Backup + */ + { + CFG_DB_PARALLEL_BACKUPS, + "ParallelBackups", + DB_TOKEN, + "Maximum number of parallel backups", + ConfigInfo::CI_NOTIMPLEMENTED, + false, + ConfigInfo::CI_INT, + "1", + "1", + "1" }, + + { + CFG_DB_BACKUP_DATADIR, + "BackupDataDir", + DB_TOKEN, + "Path to where to store backups", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_DB_DISK_SYNCH_SIZE, + "DiskSyncSize", + DB_TOKEN, + "Data written to a file before a synch is forced", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "4M", + "32k", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_CHECKPOINT_SPEED, + "DiskCheckpointSpeed", + DB_TOKEN, + "Bytes per second allowed to be written by checkpoint", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "10M", + "1M", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_CHECKPOINT_SPEED_SR, + "DiskCheckpointSpeedInRestart", + DB_TOKEN, + "Bytes per second allowed to be written by checkpoint during restart", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "100M", + "1M", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_MEM, + "BackupMemory", + DB_TOKEN, + "Total memory allocated for backups per node (in bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "4M", // sum of BackupDataBufferSize and BackupLogBufferSize + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_DATA_BUFFER_MEM, + "BackupDataBufferSize", + DB_TOKEN, + "Default size of databuffer for a backup (in bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "2M", // remember to change BackupMemory + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_LOG_BUFFER_MEM, + "BackupLogBufferSize", + DB_TOKEN, + "Default size of logbuffer for a backup (in bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "2M", // remember to change BackupMemory + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_WRITE_SIZE, + "BackupWriteSize", + DB_TOKEN, + "Default size of filesystem writes made by backup (in bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "32K", + "2K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_MAX_WRITE_SIZE, + "BackupMaxWriteSize", + DB_TOKEN, + "Max size of filesystem writes made by backup (in bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "256K", + "2K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_STRING_MEMORY, + "StringMemory", + DB_TOKEN, + "Default size of string memory (0 -> 5% of max 1-100 -> %of max, >100 -> actual bytes)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /*************************************************************************** + * API + ***************************************************************************/ + { + CFG_SECTION_NODE, + API_TOKEN, + API_TOKEN, + "Node section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)NODE_TYPE_API, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + API_TOKEN, + "Name of computer for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + "", + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + API_TOKEN, + "Name of system for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + API_TOKEN, + "", + ConfigInfo::CI_DEPRICATED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + API_TOKEN, + "Number identifying application node ("API_TOKEN_PRINT")", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + API_TOKEN, + "String referencing an earlier defined COMPUTER", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_NODE_ARBIT_RANK, + "ArbitrationRank", + API_TOKEN, + "If 0, then "API_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + "2" }, + + { + CFG_NODE_ARBIT_DELAY, + "ArbitrationDelay", + API_TOKEN, + "When asked to arbitrate, arbitrator waits this long before voting (msec)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_MAX_SCAN_BATCH_SIZE, + "MaxScanBatchSize", + "API", + "The maximum collective batch size for one scan", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + STR_VALUE(MAX_SCAN_BATCH_SIZE), + "32k", + "16M" }, + + { + CFG_BATCH_BYTE_SIZE, + "BatchByteSize", + "API", + "The default batch size in bytes", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + STR_VALUE(SCAN_BATCH_SIZE), + "1k", + "1M" }, + + { + CFG_BATCH_SIZE, + "BatchSize", + "API", + "The default batch size in number of records", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + STR_VALUE(DEF_BATCH_SIZE), + "1", + STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) }, + + /**************************************************************************** + * MGM + ***************************************************************************/ + { + CFG_SECTION_NODE, + MGM_TOKEN, + MGM_TOKEN, + "Node section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)NODE_TYPE_MGM, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + MGM_TOKEN, + "Name of computer for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + "", + 0, 0 }, + + { + CFG_NODE_DATADIR, + "DataDir", + MGM_TOKEN, + "Data directory for this node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MYSQLCLUSTERDIR, + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + MGM_TOKEN, + "Name of system for this node", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + MGM_TOKEN, + "", + ConfigInfo::CI_DEPRICATED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + MGM_TOKEN, + "Number identifying the management server node ("MGM_TOKEN_PRINT")", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_LOG_DESTINATION, + "LogDestination", + MGM_TOKEN, + "String describing where logmessages are sent", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + MGM_TOKEN, + "String referencing an earlier defined COMPUTER", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "MaxNoOfSavedEvents", + MGM_TOKEN, + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "100", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_MGM_PORT, + "PortNumber", + MGM_TOKEN, + "Port number to give commands to/fetch configurations from management server", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + NDB_PORT, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + KEY_INTERNAL, + "PortNumberStats", + MGM_TOKEN, + "Port number used to get statistical information from a management server", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_NODE_ARBIT_RANK, + "ArbitrationRank", + MGM_TOKEN, + "If 0, then "MGM_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1", + "0", + "2" }, + + { + CFG_NODE_ARBIT_DELAY, + "ArbitrationDelay", + MGM_TOKEN, + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /**************************************************************************** + * TCP + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "TCP", + "TCP", + "Connection section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)CONNECTION_TYPE_TCP, + 0, 0 + }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "TCP", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "TCP", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "TCP", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "TCP", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_GROUP, + "Group", + "TCP", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "55", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "TCP", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "TCP", + "Sends id in each signal. Used in trace files.", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "true", + "false", + "true" }, + + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "TCP", + "If checksum is enabled, all signals between nodes are checked for errors", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "TCP", + "Port used for this transporter", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_TCP_SEND_BUFFER_SIZE, + "SendBufferMemory", + "TCP", + "Bytes of buffer for signals sent from this node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "256K", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_TCP_RECEIVE_BUFFER_SIZE, + "ReceiveBufferMemory", + "TCP", + "Bytes of buffer for signals received by this node", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "64K", + "16K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_TCP_PROXY, + "Proxy", + "TCP", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "TCP", + "System for node 1 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "TCP", + "System for node 2 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + + /**************************************************************************** + * SHM + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "SHM", + "SHM", + "Connection section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)CONNECTION_TYPE_SHM, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "SHM", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "SHM", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "SHM", + "Port used for this transporter", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_SHM_SIGNUM, + "Signum", + "SHM", + "Signum to be used for signalling", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "SHM", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "SHM", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_GROUP, + "Group", + "SHM", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "35", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "SHM", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "SHM", + "Sends id in each signal. Used in trace files.", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "false", + "false", + "true" }, + + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "SHM", + "If checksum is enabled, all signals between nodes are checked for errors", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_SHM_KEY, + "ShmKey", + "SHM", + "A shared memory key", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SHM_BUFFER_MEM, + "ShmSize", + "SHM", + "Size of shared memory segment", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1M", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "SHM", + "System for node 1 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "SHM", + "System for node 2 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + /**************************************************************************** + * SCI + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "SCI", + "SCI", + "Connection section", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_SECTION, + (const char *)CONNECTION_TYPE_SCI, + 0, 0 + }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "SCI", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "SCI", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_STRING, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_GROUP, + "Group", + "SCI", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "15", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "SCI", + "", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "SCI", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "SCI", + "Name/IP of computer on one side of the connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "SCI", + "Port used for this transporter", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_SCI_HOST1_ID_0, + "Host1SciId0", + "SCI", + "SCI-node id for adapter 0 on Host1 (a computer can have two adapters)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST1_ID_1, + "Host1SciId1", + "SCI", + "SCI-node id for adapter 1 on Host1 (a computer can have two adapters)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST2_ID_0, + "Host2SciId0", + "SCI", + "SCI-node id for adapter 0 on Host2 (a computer can have two adapters)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST2_ID_1, + "Host2SciId1", + "SCI", + "SCI-node id for adapter 1 on Host2 (a computer can have two adapters)", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "SCI", + "Sends id in each signal. Used in trace files.", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "SCI", + "If checksum is enabled, all signals between nodes are checked for errors", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_SCI_SEND_LIMIT, + "SendLimit", + "SCI", + "Transporter send buffer contents are sent when this no of bytes is buffered", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "8K", + "128", + "32K" }, + + { + CFG_SCI_BUFFER_MEM, + "SharedBufferSize", + "SCI", + "Size of shared memory segment", + ConfigInfo::CI_USED, + false, + ConfigInfo::CI_INT, + "1M", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "SCI", + "System for node 1 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "SCI", + "System for node 2 in connection", + ConfigInfo::CI_INTERNAL, + false, + ConfigInfo::CI_STRING, + UNDEFINED, + 0, 0 } +}; + +const int ConfigInfo::m_NoOfParams = sizeof(m_ParamInfo) / sizeof(ParamInfo); + + +/**************************************************************************** + * Ctor + ****************************************************************************/ +static void require(bool v) +{ + if(!v) + { + if (opt_core) + abort(); + else + exit(-1); + } +} + +ConfigInfo::ConfigInfo() + : m_info(true), m_systemDefaults(true) +{ + int i; + Properties *section; + const Properties *oldpinfo; + + for (i=0; i<m_NoOfParams; i++) { + const ParamInfo & param = m_ParamInfo[i]; + Uint64 default_uint64; + bool default_bool; + + // Create new section if it did not exist + if (!m_info.getCopy(param._section, §ion)) { + Properties newsection(true); + m_info.put(param._section, &newsection); + + // Get copy of section + m_info.getCopy(param._section, §ion); + } + + // Create pinfo (parameter info) entry + Properties pinfo(true); + pinfo.put("Id", param._paramId); + pinfo.put("Fname", param._fname); + pinfo.put("Description", param._description); + pinfo.put("Updateable", param._updateable); + pinfo.put("Type", param._type); + pinfo.put("Status", param._status); + + if(param._default == MANDATORY){ + pinfo.put("Mandatory", (Uint32)1); + } + + switch (param._type) { + case CI_BOOL: + { + bool tmp_bool; + require(InitConfigFileParser::convertStringToBool(param._min, tmp_bool)); + pinfo.put64("Min", tmp_bool); + require(InitConfigFileParser::convertStringToBool(param._max, tmp_bool)); + pinfo.put64("Max", tmp_bool); + break; + } + case CI_INT: + case CI_INT64: + { + Uint64 tmp_uint64; + require(InitConfigFileParser::convertStringToUint64(param._min, tmp_uint64)); + pinfo.put64("Min", tmp_uint64); + require(InitConfigFileParser::convertStringToUint64(param._max, tmp_uint64)); + pinfo.put64("Max", tmp_uint64); + break; + } + case CI_SECTION: + pinfo.put("SectionType", (Uint32)UintPtr(param._default)); + break; + case CI_STRING: + break; + } + + // Check that pinfo is really new + if (section->get(param._fname, &oldpinfo)) { + ndbout << "Error: Parameter " << param._fname + << " defined twice in section " << param._section + << "." << endl; + require(false); + } + + // Add new pinfo to section + section->put(param._fname, &pinfo); + + // Replace section with modified section + m_info.put(param._section, section, true); + delete section; + + if(param._type != ConfigInfo::CI_SECTION){ + Properties * p; + if(!m_systemDefaults.getCopy(param._section, &p)){ + p = new Properties(true); + } + if(param._default != UNDEFINED && + param._default != MANDATORY){ + switch (param._type) + { + case CI_SECTION: + break; + case CI_STRING: + require(p->put(param._fname, param._default)); + break; + case CI_BOOL: + { + bool tmp_bool; + require(InitConfigFileParser::convertStringToBool(param._default, default_bool)); + require(p->put(param._fname, default_bool)); + break; + } + case CI_INT: + case CI_INT64: + { + Uint64 tmp_uint64; + require(InitConfigFileParser::convertStringToUint64(param._default, default_uint64)); + require(p->put(param._fname, default_uint64)); + break; + } + } + } + require(m_systemDefaults.put(param._section, p, true)); + delete p; + } + } + + for (i=0; i<m_NoOfParams; i++) { + if(m_ParamInfo[i]._section == NULL){ + ndbout << "Check that each entry has a section failed." << endl; + ndbout << "Parameter \"" << m_ParamInfo[i]._fname << endl; + ndbout << "Edit file " << __FILE__ << "." << endl; + require(false); + } + + if(m_ParamInfo[i]._type == ConfigInfo::CI_SECTION) + continue; + + const Properties * p = getInfo(m_ParamInfo[i]._section); + if (!p || !p->contains(m_ParamInfo[i]._fname)) { + ndbout << "Check that each pname has an fname failed." << endl; + ndbout << "Parameter \"" << m_ParamInfo[i]._fname + << "\" does not exist in section \"" + << m_ParamInfo[i]._section << "\"." << endl; + ndbout << "Edit file " << __FILE__ << "." << endl; + require(false); + } + } +} + +/**************************************************************************** + * Getters + ****************************************************************************/ +inline void warning(const char * src, const char * arg){ + ndbout << "Illegal call to ConfigInfo::" << src << "() - " << arg << endl; + require(false); +} + +const Properties * +ConfigInfo::getInfo(const char * section) const { + const Properties * p; + if(!m_info.get(section, &p)){ + return 0; + // warning("getInfo", section); + } + return p; +} + +const Properties * +ConfigInfo::getDefaults(const char * section) const { + const Properties * p; + if(!m_systemDefaults.get(section, &p)){ + return 0; + //warning("getDefaults", section); + } + return p; +} + +static +Uint64 +getInfoInt(const Properties * section, + const char* fname, const char * type){ + Uint32 val32; + const Properties * p; + if (section->get(fname, &p) && p->get(type, &val32)) { + return val32; + } + + Uint64 val64; + if(p && p->get(type, &val64)){ + return val64; + } + + section->print(); + if(section->get(fname, &p)){ + p->print(); + } + + warning(type, fname); + return 0; +} + +static +const char * +getInfoString(const Properties * section, + const char* fname, const char * type){ + const char* val; + const Properties * p; + if (section->get(fname, &p) && p->get(type, &val)) { + return val; + } + warning(type, fname); + return val; +} + +Uint64 +ConfigInfo::getMax(const Properties * section, const char* fname) const { + return getInfoInt(section, fname, "Max"); +} + +Uint64 +ConfigInfo::getMin(const Properties * section, const char* fname) const { + return getInfoInt(section, fname, "Min"); +} + +Uint64 +ConfigInfo::getDefault(const Properties * section, const char* fname) const { + return getInfoInt(section, fname, "Default"); +} + +const char* +ConfigInfo::getDescription(const Properties * section, + const char* fname) const { + return getInfoString(section, fname, "Description"); +} + +bool +ConfigInfo::isSection(const char * section) const { + for (int i = 0; i<m_noOfSectionNames; i++) { + if(!strcasecmp(section, m_sectionNames[i])) return true; + } + return false; +} + +const char* +ConfigInfo::nameToAlias(const char * name) { + for (int i = 0; m_sectionNameAliases[i].name != 0; i++) + if(!strcasecmp(name, m_sectionNameAliases[i].name)) + return m_sectionNameAliases[i].alias; + return 0; +} + +const char* +ConfigInfo::getAlias(const char * section) { + for (int i = 0; m_sectionNameAliases[i].name != 0; i++) + if(!strcasecmp(section, m_sectionNameAliases[i].alias)) + return m_sectionNameAliases[i].name; + return 0; +} + +bool +ConfigInfo::verify(const Properties * section, const char* fname, + Uint64 value) const { + Uint64 min, max; + + min = getInfoInt(section, fname, "Min"); + max = getInfoInt(section, fname, "Max"); + if(min > max){ + warning("verify", fname); + } + if (value >= min && value <= max) + return true; + else + return false; +} + +ConfigInfo::Type +ConfigInfo::getType(const Properties * section, const char* fname) const { + return (ConfigInfo::Type) getInfoInt(section, fname, "Type"); +} + +ConfigInfo::Status +ConfigInfo::getStatus(const Properties * section, const char* fname) const { + return (ConfigInfo::Status) getInfoInt(section, fname, "Status"); +} + +/**************************************************************************** + * Printers + ****************************************************************************/ + +void ConfigInfo::print() const { + Properties::Iterator it(&m_info); + for (const char* n = it.first(); n != NULL; n = it.next()) { + print(n); + } +} + +void ConfigInfo::print(const char* section) const { + ndbout << "****** " << section << " ******" << endl << endl; + const Properties * sec = getInfo(section); + Properties::Iterator it(sec); + for (const char* n = it.first(); n != NULL; n = it.next()) { + // Skip entries with different F- and P-names + if (getStatus(sec, n) == ConfigInfo::CI_INTERNAL) continue; + if (getStatus(sec, n) == ConfigInfo::CI_DEPRICATED) continue; + if (getStatus(sec, n) == ConfigInfo::CI_NOTIMPLEMENTED) continue; + print(sec, n); + } +} + +void ConfigInfo::print(const Properties * section, + const char* parameter) const { + ndbout << parameter; + // ndbout << getDescription(section, parameter) << endl; + switch (getType(section, parameter)) { + case ConfigInfo::CI_BOOL: + ndbout << " (Boolean value)" << endl; + ndbout << getDescription(section, parameter) << endl; + if (getDefault(section, parameter) == false) { + ndbout << "Default: N (Legal values: Y, N)" << endl; + } else if (getDefault(section, parameter) == true) { + ndbout << "Default: Y (Legal values: Y, N)" << endl; + } else if (getDefault(section, parameter) == (UintPtr)MANDATORY) { + ndbout << "MANDATORY (Legal values: Y, N)" << endl; + } else { + ndbout << "UNKNOWN" << endl; + } + ndbout << endl; + break; + + case ConfigInfo::CI_INT: + case ConfigInfo::CI_INT64: + ndbout << " (Non-negative Integer)" << endl; + ndbout << getDescription(section, parameter) << endl; + if (getDefault(section, parameter) == (UintPtr)MANDATORY) { + ndbout << "MANDATORY ("; + } else if (getDefault(section, parameter) == (UintPtr)UNDEFINED) { + ndbout << "UNDEFINED ("; + } else { + ndbout << "Default: " << getDefault(section, parameter) << " ("; + } + ndbout << "Min: " << getMin(section, parameter) << ", "; + ndbout << "Max: " << getMax(section, parameter) << ")" << endl; + ndbout << endl; + break; + + case ConfigInfo::CI_STRING: + ndbout << " (String)" << endl; + ndbout << getDescription(section, parameter) << endl; + if (getDefault(section, parameter) == (UintPtr)MANDATORY) { + ndbout << "MANDATORY" << endl; + } else { + ndbout << "No default value" << endl; + } + ndbout << endl; + break; + case ConfigInfo::CI_SECTION: + break; + } +} + +/**************************************************************************** + * Section Rules + ****************************************************************************/ + +/** + * Node rule: Add "Type" and update "NoOfNodes" + */ +bool +transformNode(InitConfigFileParser::Context & ctx, const char * data){ + + Uint32 id, line; + if(!ctx.m_currentSection->get("NodeId", &id) && !ctx.m_currentSection->get("Id", &id)){ + Uint32 nextNodeId= 1; + ctx.m_userProperties.get("NextNodeId", &nextNodeId); + id= nextNodeId; + while (ctx.m_userProperties.get("AllocatedNodeId_", id, &line)) + id++; + if (id != nextNodeId) + { + fprintf(stderr,"Cluster configuration warning line %d: " + "Could not use next node id %d for section [%s], " + "using next unused node id %d.\n", + ctx.m_sectionLineno, nextNodeId, ctx.fname, id); + } + ctx.m_currentSection->put("NodeId", id); + } else if(ctx.m_userProperties.get("AllocatedNodeId_", id, &line)) { + ctx.reportError("Duplicate nodeid in section " + "[%s] starting at line: %d. Previously used on line %d.", + ctx.fname, ctx.m_sectionLineno, line); + return false; + } + + if(id >= MAX_NODES) + { + ctx.reportError("too many nodes configured, only up to %d nodes supported.", + MAX_NODES); + return false; + } + + // next node id _always_ next numbers after last used id + ctx.m_userProperties.put("NextNodeId", id+1, true); + + ctx.m_userProperties.put("AllocatedNodeId_", id, ctx.m_sectionLineno); + BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Node_%d", id); + + ctx.m_currentSection->put("Type", ctx.fname); + + Uint32 nodes = 0; + ctx.m_userProperties.get("NoOfNodes", &nodes); + ctx.m_userProperties.put("NoOfNodes", ++nodes, true); + + /** + * Update count (per type) + */ + nodes = 0; + ctx.m_userProperties.get(ctx.fname, &nodes); + ctx.m_userProperties.put(ctx.fname, ++nodes, true); + + return true; +} + +static bool checkLocalhostHostnameMix(InitConfigFileParser::Context & ctx, const char * data) +{ + DBUG_ENTER("checkLocalhostHostnameMix"); + const char * hostname= 0; + ctx.m_currentSection->get("HostName", &hostname); + if (hostname == 0 || hostname[0] == 0) + DBUG_RETURN(true); + + Uint32 localhost_used= 0; + if(!strcmp(hostname, "localhost") || !strcmp(hostname, "127.0.0.1")){ + localhost_used= 1; + ctx.m_userProperties.put("$computer-localhost-used", localhost_used); + if(!ctx.m_userProperties.get("$computer-localhost", &hostname)) + DBUG_RETURN(true); + } else { + ctx.m_userProperties.get("$computer-localhost-used", &localhost_used); + ctx.m_userProperties.put("$computer-localhost", hostname); + } + + if (localhost_used) { + ctx.reportError("Mixing of localhost (default for [NDBD]HostName) with other hostname(%s) is illegal", + hostname); + DBUG_RETURN(false); + } + + DBUG_RETURN(true); +} + +bool +fixNodeHostname(InitConfigFileParser::Context & ctx, const char * data) +{ + const char * hostname; + DBUG_ENTER("fixNodeHostname"); + + if (ctx.m_currentSection->get("HostName", &hostname)) + DBUG_RETURN(checkLocalhostHostnameMix(ctx,0)); + + const char * compId; + if(!ctx.m_currentSection->get("ExecuteOnComputer", &compId)) + DBUG_RETURN(true); + + const Properties * computer; + char tmp[255]; + BaseString::snprintf(tmp, sizeof(tmp), "Computer_%s", compId); + if(!ctx.m_config->get(tmp, &computer)){ + ctx.reportError("Computer \"%s\" not declared" + "- [%s] starting at line: %d", + compId, ctx.fname, ctx.m_sectionLineno); + DBUG_RETURN(false); + } + + if(!computer->get("HostName", &hostname)){ + ctx.reportError("HostName missing in [COMPUTER] (Id: %d) " + " - [%s] starting at line: %d", + compId, ctx.fname, ctx.m_sectionLineno); + DBUG_RETURN(false); + } + + require(ctx.m_currentSection->put("HostName", hostname)); + DBUG_RETURN(checkLocalhostHostnameMix(ctx,0)); +} + +bool +fixFileSystemPath(InitConfigFileParser::Context & ctx, const char * data){ + DBUG_ENTER("fixFileSystemPath"); + + const char * path; + if (ctx.m_currentSection->get("FileSystemPath", &path)) + DBUG_RETURN(true); + + if (ctx.m_currentSection->get("DataDir", &path)) { + require(ctx.m_currentSection->put("FileSystemPath", path)); + DBUG_RETURN(true); + } + + require(false); + DBUG_RETURN(false); +} + +bool +fixBackupDataDir(InitConfigFileParser::Context & ctx, const char * data){ + + const char * path; + if (ctx.m_currentSection->get("BackupDataDir", &path)) + return true; + + if (ctx.m_currentSection->get("FileSystemPath", &path)) { + require(ctx.m_currentSection->put("BackupDataDir", path)); + return true; + } + + require(false); + return false; +} + +/** + * Connection rule: Check support of connection + */ +bool +checkConnectionSupport(InitConfigFileParser::Context & ctx, const char * data) +{ + int error= 0; + if (strcasecmp("TCP",ctx.fname) == 0) + { + // always enabled + } + else if (strcasecmp("SHM",ctx.fname) == 0) + { +#ifndef NDB_SHM_TRANSPORTER + error= 1; +#endif + } + else if (strcasecmp("SCI",ctx.fname) == 0) + { +#ifndef NDB_SCI_TRANSPORTER + error= 1; +#endif + } + + if (error) + { + ctx.reportError("Binary not compiled with this connection support, " + "[%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + return true; +} + +/** + * Connection rule: Update "NoOfConnections" + */ +bool +transformConnection(InitConfigFileParser::Context & ctx, const char * data) +{ + Uint32 connections = 0; + ctx.m_userProperties.get("NoOfConnections", &connections); + BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Connection_%d", connections); + ctx.m_userProperties.put("NoOfConnections", ++connections, true); + + ctx.m_currentSection->put("Type", ctx.fname); + return true; +} + +/** + * System rule: Just add it + */ +bool +transformSystem(InitConfigFileParser::Context & ctx, const char * data){ + + const char * name; + if(!ctx.m_currentSection->get("Name", &name)){ + ctx.reportError("Mandatory parameter Name missing from section " + "[%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + + ndbout << "transformSystem " << name << endl; + + BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "SYSTEM_%s", name); + + return true; +} + +/** + * Computer rule: Update "NoOfComputers", add "Type" + */ +bool +transformComputer(InitConfigFileParser::Context & ctx, const char * data){ + const char * id; + if(!ctx.m_currentSection->get("Id", &id)){ + ctx.reportError("Mandatory parameter Id missing from section " + "[%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "Computer_%s", id); + + Uint32 computers = 0; + ctx.m_userProperties.get("NoOfComputers", &computers); + ctx.m_userProperties.put("NoOfComputers", ++computers, true); + + const char * hostname = 0; + ctx.m_currentSection->get("HostName", &hostname); + if(!hostname){ + return true; + } + + return checkLocalhostHostnameMix(ctx,0); +} + +/** + * Apply default values + */ +void +applyDefaultValues(InitConfigFileParser::Context & ctx, + const Properties * defaults) +{ + DBUG_ENTER("applyDefaultValues"); + if(defaults != NULL){ + Properties::Iterator it(defaults); + + for(const char * name = it.first(); name != NULL; name = it.next()){ + ConfigInfo::Status st = ctx.m_info->getStatus(ctx.m_currentInfo, name); + if(!ctx.m_currentSection->contains(name)){ + switch (ctx.m_info->getType(ctx.m_currentInfo, name)){ + case ConfigInfo::CI_INT: + case ConfigInfo::CI_BOOL:{ + Uint32 val = 0; + ::require(defaults->get(name, &val)); + ctx.m_currentSection->put(name, val); + DBUG_PRINT("info",("%s=%d #default",name,val)); + break; + } + case ConfigInfo::CI_INT64:{ + Uint64 val = 0; + ::require(defaults->get(name, &val)); + ctx.m_currentSection->put64(name, val); + DBUG_PRINT("info",("%s=%lld #default",name,val)); + break; + } + case ConfigInfo::CI_STRING:{ + const char * val; + ::require(defaults->get(name, &val)); + ctx.m_currentSection->put(name, val); + DBUG_PRINT("info",("%s=%s #default",name,val)); + break; + } + case ConfigInfo::CI_SECTION: + break; + } + } +#ifndef DBUG_OFF + else + { + switch (ctx.m_info->getType(ctx.m_currentInfo, name)){ + case ConfigInfo::CI_INT: + case ConfigInfo::CI_BOOL:{ + Uint32 val = 0; + ::require(ctx.m_currentSection->get(name, &val)); + DBUG_PRINT("info",("%s=%d",name,val)); + break; + } + case ConfigInfo::CI_INT64:{ + Uint64 val = 0; + ::require(ctx.m_currentSection->get(name, &val)); + DBUG_PRINT("info",("%s=%lld",name,val)); + break; + } + case ConfigInfo::CI_STRING:{ + const char * val; + ::require(ctx.m_currentSection->get(name, &val)); + DBUG_PRINT("info",("%s=%s",name,val)); + break; + } + case ConfigInfo::CI_SECTION: + break; + } + } +#endif + } + } + DBUG_VOID_RETURN; +} + +bool +applyDefaultValues(InitConfigFileParser::Context & ctx, const char * data){ + + if(strcmp(data, "user") == 0) + applyDefaultValues(ctx, ctx.m_userDefaults); + else if (strcmp(data, "system") == 0) + applyDefaultValues(ctx, ctx.m_systemDefaults); + else + return false; + + return true; +} + +/** + * Check that a section contains all MANDATORY parameters + */ +bool +checkMandatory(InitConfigFileParser::Context & ctx, const char * data){ + + Properties::Iterator it(ctx.m_currentInfo); + for(const char * name = it.first(); name != NULL; name = it.next()){ + const Properties * info = NULL; + ::require(ctx.m_currentInfo->get(name, &info)); + Uint32 val; + if(info->get("Mandatory", &val)){ + const char * fname; + ::require(info->get("Fname", &fname)); + if(!ctx.m_currentSection->contains(fname)){ + ctx.reportError("Mandatory parameter %s missing from section " + "[%s] starting at line: %d", + fname, ctx.fname, ctx.m_sectionLineno); + return false; + } + } + } + return true; +} + +/** + * Connection rule: Fix node id + * + * Transform a string "NodeidX" (e.g. "uppsala.32") + * into a Uint32 "NodeIdX" (e.g. 32) and a string "SystemX" (e.g. "uppsala"). + */ +static bool fixNodeId(InitConfigFileParser::Context & ctx, const char * data) +{ + char buf[] = "NodeIdX"; buf[6] = data[sizeof("NodeI")]; + char sysbuf[] = "SystemX"; sysbuf[6] = data[sizeof("NodeI")]; + const char* nodeId; + require(ctx.m_currentSection->get(buf, &nodeId)); + + char tmpLine[MAX_LINE_LENGTH]; + strncpy(tmpLine, nodeId, MAX_LINE_LENGTH); + char* token1 = strtok(tmpLine, "."); + char* token2 = strtok(NULL, "."); + Uint32 id; + + if (token2 == NULL) { // Only a number given + errno = 0; + char* p; + id = strtol(token1, &p, 10); + if (errno != 0) warning("STRTOK1", nodeId); + require(ctx.m_currentSection->put(buf, id, true)); + } else { // A pair given (e.g. "uppsala.32") + errno = 0; + char* p; + id = strtol(token2, &p, 10); + if (errno != 0) warning("STRTOK2", nodeId); + require(ctx.m_currentSection->put(buf, id, true)); + require(ctx.m_currentSection->put(sysbuf, token1)); + } + return true; +} + +/** + * Connection rule: Fix hostname + * + * Unless Hostname is not already specified, do steps: + * -# Via Connection's NodeId lookup Node + * -# Via Node's ExecuteOnComputer lookup Hostname + * -# Add HostName to Connection + */ +static bool +fixHostname(InitConfigFileParser::Context & ctx, const char * data){ + + char buf[] = "NodeIdX"; buf[6] = data[sizeof("HostNam")]; + char sysbuf[] = "SystemX"; sysbuf[6] = data[sizeof("HostNam")]; + + if(!ctx.m_currentSection->contains(data)){ + Uint32 id = 0; + require(ctx.m_currentSection->get(buf, &id)); + + const Properties * node; + if(!ctx.m_config->get("Node", id, &node)) + { + ctx.reportError("Unknown node: \"%d\" specified in connection " + "[%s] starting at line: %d", + id, ctx.fname, ctx.m_sectionLineno); + return false; + } + + const char * hostname; + require(node->get("HostName", &hostname)); + require(ctx.m_currentSection->put(data, hostname)); + } + return true; +} + +/** + * Connection rule: Fix port number (using a port number adder) + */ +static bool +fixPortNumber(InitConfigFileParser::Context & ctx, const char * data){ + + DBUG_ENTER("fixPortNumber"); + + Uint32 id1, id2; + const char *hostName1; + const char *hostName2; + require(ctx.m_currentSection->get("NodeId1", &id1)); + require(ctx.m_currentSection->get("NodeId2", &id2)); + require(ctx.m_currentSection->get("HostName1", &hostName1)); + require(ctx.m_currentSection->get("HostName2", &hostName2)); + DBUG_PRINT("info",("NodeId1=%d HostName1=\"%s\"",id1,hostName1)); + DBUG_PRINT("info",("NodeId2=%d HostName2=\"%s\"",id2,hostName2)); + + const Properties *node1, *node2; + require(ctx.m_config->get("Node", id1, &node1)); + require(ctx.m_config->get("Node", id2, &node2)); + + const char *type1, *type2; + require(node1->get("Type", &type1)); + require(node2->get("Type", &type2)); + + /* add NodeIdServer info */ + { + Uint32 nodeIdServer = id1 < id2 ? id1 : id2; + if(strcmp(type1, API_TOKEN) == 0 || strcmp(type2, MGM_TOKEN) == 0) + nodeIdServer = id2; + else if(strcmp(type2, API_TOKEN) == 0 || strcmp(type1, MGM_TOKEN) == 0) + nodeIdServer = id1; + ctx.m_currentSection->put("NodeIdServer", nodeIdServer); + + if (id2 == nodeIdServer) { + { + const char *tmp= hostName1; + hostName1= hostName2; + hostName2= tmp; + } + { + Uint32 tmp= id1; + id1= id2; + id2= tmp; + } + { + const Properties *tmp= node1; + node1= node2; + node2= tmp; + } + { + const char *tmp= type1; + type1= type2; + type2= tmp; + } + } + } + + BaseString hostname(hostName1); + + if (hostname.c_str()[0] == 0) { + ctx.reportError("Hostname required on nodeid %d since it will " + "act as server.", id1); + DBUG_RETURN(false); + } + + Uint32 port= 0; + if(strcmp(type1, MGM_TOKEN)==0) + node1->get("PortNumber",&port); + else if(strcmp(type2, MGM_TOKEN)==0) + node2->get("PortNumber",&port); + + if (!port && + !node1->get("ServerPort", &port) && + !ctx.m_userProperties.get("ServerPort_", id1, &port)) + { + Uint32 base= 0; + /* + * If the connection doesn't involve an mgm server, + * and a default port number has been set, behave the old + * way of allocating port numbers for transporters. + */ + if(ctx.m_userDefaults && ctx.m_userDefaults->get("PortNumber", &base)) + { + Uint32 adder= 0; + { + BaseString server_port_adder(hostname); + server_port_adder.append("_ServerPortAdder"); + ctx.m_userProperties.get(server_port_adder.c_str(), &adder); + ctx.m_userProperties.put(server_port_adder.c_str(), adder+1, true); + } + + if (!ctx.m_userProperties.get("ServerPortBase", &base)){ + if(!(ctx.m_userDefaults && + ctx.m_userDefaults->get("PortNumber", &base)) && + !ctx.m_systemDefaults->get("PortNumber", &base)) { + base= strtoll(NDB_TCP_BASE_PORT,0,0); + } + ctx.m_userProperties.put("ServerPortBase", base); + } + + port= base + adder; + ctx.m_userProperties.put("ServerPort_", id1, port); + } + } + + if(ctx.m_currentSection->contains("PortNumber")) { + ndbout << "PortNumber should no longer be specificied " + << "per connection, please remove from config. " + << "Will be changed to " << port << endl; + ctx.m_currentSection->put("PortNumber", port, true); + } + else + { + ctx.m_currentSection->put("PortNumber", port); + } + + DBUG_PRINT("info", ("connection %d-%d port %d host %s", + id1, id2, port, hostname.c_str())); + + DBUG_RETURN(true); +} + +static bool +fixShmUniqueId(InitConfigFileParser::Context & ctx, const char * data) +{ + DBUG_ENTER("fixShmUniqueId"); + Uint32 nodes= 0; + ctx.m_userProperties.get(ctx.fname, &nodes); + if (nodes == 1) // first management server + { + Uint32 portno= atoi(NDB_PORT); + ctx.m_currentSection->get("PortNumber", &portno); + ctx.m_userProperties.put("ShmUniqueId", portno); + } + DBUG_RETURN(true); +} + +static +bool +fixShmKey(InitConfigFileParser::Context & ctx, const char *) +{ + DBUG_ENTER("fixShmKey"); + { + static int last_signum= -1; + Uint32 signum; + if(!ctx.m_currentSection->get("Signum", &signum)) + { + signum= OPT_NDB_SHM_SIGNUM_DEFAULT; + if (signum <= 0) + { + ctx.reportError("Unable to set default parameter for [SHM]Signum" + " please specify [SHM DEFAULT]Signum"); + return false; + } + ctx.m_currentSection->put("Signum", signum); + DBUG_PRINT("info",("Added Signum=%u", signum)); + } + if ( last_signum != (int)signum && last_signum >= 0 ) + { + ctx.reportError("All shared memory transporters must have same [SHM]Signum defined." + " Use [SHM DEFAULT]Signum"); + return false; + } + last_signum= (int)signum; + } + { + Uint32 id1= 0, id2= 0, key= 0; + require(ctx.m_currentSection->get("NodeId1", &id1)); + require(ctx.m_currentSection->get("NodeId2", &id2)); + if(!ctx.m_currentSection->get("ShmKey", &key)) + { + require(ctx.m_userProperties.get("ShmUniqueId", &key)); + key= key << 16 | (id1 > id2 ? id1 << 8 | id2 : id2 << 8 | id1); + ctx.m_currentSection->put("ShmKey", key); + DBUG_PRINT("info",("Added ShmKey=0x%x", key)); + } + } + DBUG_RETURN(true); +} + +/** + * DB Node rule: Check various constraints + */ +static bool +checkDbConstraints(InitConfigFileParser::Context & ctx, const char *){ + + Uint32 t1 = 0, t2 = 0; + ctx.m_currentSection->get("MaxNoOfConcurrentOperations", &t1); + ctx.m_currentSection->get("MaxNoOfConcurrentTransactions", &t2); + + if (t1 < t2) { + ctx.reportError("MaxNoOfConcurrentOperations must be greater than " + "MaxNoOfConcurrentTransactions - [%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + + Uint32 replicas = 0, otherReplicas; + ctx.m_currentSection->get("NoOfReplicas", &replicas); + if(ctx.m_userProperties.get("NoOfReplicas", &otherReplicas)){ + if(replicas != otherReplicas){ + ctx.reportError("NoOfReplicas defined differently on different nodes" + " - [%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + } else { + ctx.m_userProperties.put("NoOfReplicas", replicas); + } + + /** + * In kernel, will calculate the MaxNoOfMeataTables use the following sum: + * Uint32 noOfMetaTables = noOfTables + noOfOrderedIndexes + + * noOfUniqueHashIndexes + 2 + * 2 is the number of the SysTables. + * So must check that the sum does't exceed the max value of Uint32. + */ + Uint32 noOfTables = 0, + noOfOrderedIndexes = 0, + noOfUniqueHashIndexes = 0; + ctx.m_currentSection->get("MaxNoOfTables", &noOfTables); + ctx.m_currentSection->get("MaxNoOfOrderedIndexes", &noOfOrderedIndexes); + ctx.m_currentSection->get("MaxNoOfUniqueHashIndexes", &noOfUniqueHashIndexes); + + Uint64 sum= (Uint64)noOfTables + noOfOrderedIndexes + noOfUniqueHashIndexes; + + if (sum > ((Uint32)~0 - 2)) { + ctx.reportError("The sum of MaxNoOfTables, MaxNoOfOrderedIndexes and" + " MaxNoOfUniqueHashIndexes must not exceed %u - [%s]" + " starting at line: %d", + ((Uint32)~0 - 2), ctx.fname, ctx.m_sectionLineno); + return false; + } + + return true; +} + +/** + * Connection rule: Check varius constraints + */ +static bool +checkConnectionConstraints(InitConfigFileParser::Context & ctx, const char *){ + + Uint32 id1 = 0, id2 = 0; + ctx.m_currentSection->get("NodeId1", &id1); + ctx.m_currentSection->get("NodeId2", &id2); + + if(id1 == id2){ + ctx.reportError("Illegal connection from node to itself" + " - [%s] starting at line: %d", + ctx.fname, ctx.m_sectionLineno); + return false; + } + + const Properties * node1; + if(!ctx.m_config->get("Node", id1, &node1)){ + ctx.reportError("Connection refering to undefined node: %d" + " - [%s] starting at line: %d", + id1, ctx.fname, ctx.m_sectionLineno); + return false; + } + + const Properties * node2; + if(!ctx.m_config->get("Node", id2, &node2)){ + ctx.reportError("Connection refering to undefined node: %d" + " - [%s] starting at line: %d", + id2, ctx.fname, ctx.m_sectionLineno); + return false; + } + + const char * type1; + const char * type2; + require(node1->get("Type", &type1)); + require(node2->get("Type", &type2)); + + /** + * Report error if the following are true + * -# None of the nodes is of type DB + * -# Not both of them are MGMs + */ + if((strcmp(type1, DB_TOKEN) != 0 && strcmp(type2, DB_TOKEN) != 0) && + !(strcmp(type1, MGM_TOKEN) == 0 && strcmp(type2, MGM_TOKEN) == 0)) + { + ctx.reportError("Invalid connection between node %d (%s) and node %d (%s)" + " - [%s] starting at line: %d", + id1, type1, id2, type2, + ctx.fname, ctx.m_sectionLineno); + return false; + } + + return true; +} + +static bool +checkTCPConstraints(InitConfigFileParser::Context & ctx, const char * data){ + + const char * host; + struct in_addr addr; + if(ctx.m_currentSection->get(data, &host) && strlen(host) && + Ndb_getInAddr(&addr, host)){ + ctx.reportError("Unable to lookup/illegal hostname %s" + " - [%s] starting at line: %d", + host, ctx.fname, ctx.m_sectionLineno); + return false; + } + return true; +} + +static +bool +transform(InitConfigFileParser::Context & ctx, + Properties & dst, + const char * oldName, + const char * newName, + double add, double mul){ + + if(ctx.m_currentSection->contains(newName)){ + ctx.reportError("Both %s and %s specified" + " - [%s] starting at line: %d", + oldName, newName, + ctx.fname, ctx.m_sectionLineno); + return false; + } + + PropertiesType oldType; + require(ctx.m_currentSection->getTypeOf(oldName, &oldType)); + ConfigInfo::Type newType = ctx.m_info->getType(ctx.m_currentInfo, newName); + + if(!((oldType == PropertiesType_Uint32 || oldType == PropertiesType_Uint64) + && (newType == ConfigInfo::CI_INT || newType == ConfigInfo::CI_INT64 || newType == ConfigInfo::CI_BOOL))){ + ndbout << "oldType: " << (int)oldType << ", newType: " << (int)newType << endl; + ctx.reportError("Unable to handle type conversion w.r.t deprication %s %s" + "- [%s] starting at line: %d", + oldName, newName, + ctx.fname, ctx.m_sectionLineno); + return false; + } + Uint64 oldVal; + require(ctx.m_currentSection->get(oldName, &oldVal)); + + Uint64 newVal = (Uint64)((Int64)oldVal * mul + add); + if(!ctx.m_info->verify(ctx.m_currentInfo, newName, newVal)){ + ctx.reportError("Unable to handle deprication, new value not within bounds" + "%s %s - [%s] starting at line: %d", + oldName, newName, + ctx.fname, ctx.m_sectionLineno); + return false; + } + + if(newType == ConfigInfo::CI_INT || newType == ConfigInfo::CI_BOOL){ + require(dst.put(newName, (Uint32)newVal)); + } else if(newType == ConfigInfo::CI_INT64) { + require(dst.put64(newName, newVal)); + } + return true; +} + +static bool +fixDepricated(InitConfigFileParser::Context & ctx, const char * data){ + const char * name; + /** + * Transform old values to new values + * Transform new values to old values (backward compatible) + */ + Properties tmp(true); + Properties::Iterator it(ctx.m_currentSection); + for (name = it.first(); name != NULL; name = it.next()) { + const DepricationTransform * p = &f_deprication[0]; + while(p->m_section != 0){ + if(strcmp(p->m_section, ctx.fname) == 0){ + double mul = p->m_mul; + double add = p->m_add; + if(strcasecmp(name, p->m_oldName) == 0){ + if(!transform(ctx, tmp, name, p->m_newName, add, mul)){ + return false; + } + } else if(strcasecmp(name, p->m_newName) == 0) { + if(!transform(ctx, tmp, name, p->m_oldName, -add/mul,1.0/mul)){ + return false; + } + } + } + p++; + } + } + + Properties::Iterator it2(&tmp); + for (name = it2.first(); name != NULL; name = it2.next()) { + PropertiesType type; + require(tmp.getTypeOf(name, &type)); + switch(type){ + case PropertiesType_Uint32:{ + Uint32 val; + require(tmp.get(name, &val)); + ::require(ctx.m_currentSection->put(name, val)); + break; + } + case PropertiesType_char:{ + const char * val; + require(tmp.get(name, &val)); + ::require(ctx.m_currentSection->put(name, val)); + break; + } + case PropertiesType_Uint64:{ + Uint64 val; + require(tmp.get(name, &val)); + ::require(ctx.m_currentSection->put64(name, val)); + break; + } + case PropertiesType_Properties: + default: + ::require(false); + } + } + return true; +} + +extern int g_print_full_config; + +static bool +saveInConfigValues(InitConfigFileParser::Context & ctx, const char * data){ + const Properties * sec; + if(!ctx.m_currentInfo->get(ctx.fname, &sec)){ + require(false); + return false; + } + + do { + const char *secName; + Uint32 id, status, typeVal; + require(sec->get("Fname", &secName)); + require(sec->get("Id", &id)); + require(sec->get("Status", &status)); + require(sec->get("SectionType", &typeVal)); + + if(id == KEY_INTERNAL || status == ConfigInfo::CI_INTERNAL){ + ndbout_c("skipping section %s", ctx.fname); + break; + } + + if (g_print_full_config) + { + const char *alias= ConfigInfo::nameToAlias(ctx.fname); + printf("[%s]\n", alias ? alias : ctx.fname); + } + + Uint32 no = 0; + ctx.m_userProperties.get("$Section", id, &no); + ctx.m_userProperties.put("$Section", id, no+1, true); + + ctx.m_configValues.openSection(id, no); + ctx.m_configValues.put(CFG_TYPE_OF_SECTION, typeVal); + + Properties::Iterator it(ctx.m_currentSection); + for (const char* n = it.first(); n != NULL; n = it.next()) { + const Properties * info; + if(!ctx.m_currentInfo->get(n, &info)) + continue; + + Uint32 id = 0; + info->get("Id", &id); + + if(id == KEY_INTERNAL) + continue; + + bool ok = true; + PropertiesType type; + require(ctx.m_currentSection->getTypeOf(n, &type)); + switch(type){ + case PropertiesType_Uint32:{ + Uint32 val; + require(ctx.m_currentSection->get(n, &val)); + ok = ctx.m_configValues.put(id, val); + if (g_print_full_config) + printf("%s=%u\n", n, val); + break; + } + case PropertiesType_Uint64:{ + Uint64 val; + require(ctx.m_currentSection->get(n, &val)); + ok = ctx.m_configValues.put64(id, val); + if (g_print_full_config) + printf("%s=%llu\n", n, val); + break; + } + case PropertiesType_char:{ + const char * val; + require(ctx.m_currentSection->get(n, &val)); + ok = ctx.m_configValues.put(id, val); + if (g_print_full_config) + printf("%s=%s\n", n, val); + break; + } + default: + require(false); + } + require(ok); + } + ctx.m_configValues.closeSection(); + } while(0); + return true; +} + +static bool +sanity_checks(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data) +{ + Uint32 db_nodes = 0; + Uint32 mgm_nodes = 0; + Uint32 api_nodes = 0; + if (!ctx.m_userProperties.get("DB", &db_nodes)) { + ctx.reportError("At least one database node (ndbd) should be defined in config file"); + return false; + } + if (!ctx.m_userProperties.get("MGM", &mgm_nodes)) { + ctx.reportError("At least one management server node (ndb_mgmd) should be defined in config file"); + return false; + } + if (!ctx.m_userProperties.get("API", &api_nodes)) { + ctx.reportError("At least one application node (for the mysqld) should be defined in config file"); + return false; + } + return true; +} + +static void +add_a_connection(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + Uint32 nodeId1, Uint32 nodeId2, bool use_shm) +{ + ConfigInfo::ConfigRuleSection s; + const char *hostname1= 0, *hostname2= 0; + const Properties *tmp; + + require(ctx.m_config->get("Node", nodeId1, &tmp)); + tmp->get("HostName", &hostname1); + + require(ctx.m_config->get("Node", nodeId2, &tmp)); + tmp->get("HostName", &hostname2); + + char buf[16]; + s.m_sectionData= new Properties(true); + BaseString::snprintf(buf, sizeof(buf), "%u", nodeId1); + s.m_sectionData->put("NodeId1", buf); + BaseString::snprintf(buf, sizeof(buf), "%u", nodeId2); + s.m_sectionData->put("NodeId2", buf); + + if (use_shm && + hostname1 && hostname1[0] && + hostname2 && hostname2[0] && + strcmp(hostname1,hostname2) == 0) + { + s.m_sectionType= BaseString("SHM"); + DBUG_PRINT("info",("adding SHM connection %d %d",nodeId1,nodeId2)); + } + else + { + s.m_sectionType= BaseString("TCP"); + DBUG_PRINT("info",("adding TCP connection %d %d",nodeId1,nodeId2)); + } + + sections.push_back(s); +} + +static bool +add_node_connections(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data) +{ + DBUG_ENTER("add_node_connections"); + Uint32 i; + Properties * props= ctx.m_config; + Properties p_connections(true); + Properties p_connections2(true); + + for (i = 0;; i++){ + const Properties * tmp; + Uint32 nodeId1, nodeId2; + + if(!props->get("Connection", i, &tmp)) break; + + if(!tmp->get("NodeId1", &nodeId1)) continue; + p_connections.put("", nodeId1, nodeId1); + if(!tmp->get("NodeId2", &nodeId2)) continue; + p_connections.put("", nodeId2, nodeId2); + + p_connections2.put("", nodeId1 + nodeId2<<16, nodeId1); + p_connections2.put("", nodeId2 + nodeId1<<16, nodeId2); + } + + Uint32 nNodes; + ctx.m_userProperties.get("NoOfNodes", &nNodes); + + Properties p_db_nodes(true); + Properties p_api_nodes(true); + Properties p_mgm_nodes(true); + + Uint32 i_db= 0, i_api= 0, i_mgm= 0, n; + for (i= 0, n= 0; n < nNodes; i++){ + const Properties * tmp; + if(!props->get("Node", i, &tmp)) continue; + n++; + + const char * type; + if(!tmp->get("Type", &type)) continue; + + if (strcmp(type,DB_TOKEN) == 0) + p_db_nodes.put("", i_db++, i); + else if (strcmp(type,API_TOKEN) == 0) + p_api_nodes.put("", i_api++, i); + else if (strcmp(type,MGM_TOKEN) == 0) + p_mgm_nodes.put("", i_mgm++, i); + } + + Uint32 nodeId1, nodeId2, dummy; + + for (i= 0; p_db_nodes.get("", i, &nodeId1); i++){ + for (Uint32 j= i+1;; j++){ + if(!p_db_nodes.get("", j, &nodeId2)) break; + if(!p_connections2.get("", nodeId1+nodeId2<<16, &dummy)) { + add_a_connection(sections,ctx,nodeId1,nodeId2,opt_ndb_shm); + } + } + } + + for (i= 0; p_api_nodes.get("", i, &nodeId1); i++){ + if(!p_connections.get("", nodeId1, &dummy)) { + for (Uint32 j= 0;; j++){ + if(!p_db_nodes.get("", j, &nodeId2)) break; + add_a_connection(sections,ctx,nodeId1,nodeId2,opt_ndb_shm); + } + } + } + + for (i= 0; p_mgm_nodes.get("", i, &nodeId1); i++){ + if(!p_connections.get("", nodeId1, &dummy)) { + for (Uint32 j= 0;; j++){ + if(!p_db_nodes.get("", j, &nodeId2)) break; + add_a_connection(sections,ctx,nodeId1,nodeId2,0); + } + } + } + + DBUG_RETURN(true); +} + +static bool set_connection_priorities(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data) +{ + DBUG_ENTER("set_connection_priorities"); + DBUG_RETURN(true); +} + +static bool +check_node_vs_replicas(Vector<ConfigInfo::ConfigRuleSection>§ions, + struct InitConfigFileParser::Context &ctx, + const char * rule_data) +{ + Uint32 db_nodes= 0; + Uint32 replicas= 0; + Uint32 db_host_count= 0; + ctx.m_userProperties.get(DB_TOKEN, &db_nodes); + ctx.m_userProperties.get("NoOfReplicas", &replicas); + if((db_nodes % replicas) != 0){ + ctx.reportError("Invalid no of db nodes wrt no of replicas.\n" + "No of nodes must be dividable with no or replicas"); + return false; + } + // check that node groups and arbitrators are ok + // just issue warning if not + if(replicas > 1){ + Properties * props= ctx.m_config; + Properties p_db_hosts(true); // store hosts which db nodes run on + Properties p_arbitrators(true); // store hosts which arbitrators run on + // arbitrator should not run together with db node on same host + Uint32 i, n, group= 0, i_group= 0; + Uint32 n_nodes; + BaseString node_group_warning, arbitration_warning; + const char *arbit_warn_fmt= + "\n arbitrator with id %d and db node with id %d on same host %s"; + const char *arbit_warn_fmt2= + "\n arbitrator with id %d has no hostname specified"; + + ctx.m_userProperties.get("NoOfNodes", &n_nodes); + for (i= 0, n= 0; n < n_nodes; i++){ + const Properties * tmp; + if(!props->get("Node", i, &tmp)) continue; + n++; + + const char * type; + if(!tmp->get("Type", &type)) continue; + + const char* host= 0; + tmp->get("HostName", &host); + + if (strcmp(type,DB_TOKEN) == 0) + { + { + Uint32 ii; + if (!p_db_hosts.get(host,&ii)) + db_host_count++; + p_db_hosts.put(host,i); + if (p_arbitrators.get(host,&ii)) + { + arbitration_warning.appfmt(arbit_warn_fmt, ii, i, host); + p_arbitrators.remove(host); // only one warning per db node + } + } + { + unsigned j; + BaseString str, str2; + str.assfmt("#group%d_",group); + p_db_hosts.put(str.c_str(),i_group,host); + str2.assfmt("##group%d_",group); + p_db_hosts.put(str2.c_str(),i_group,i); + for (j= 0; j < i_group; j++) + { + const char *other_host; + p_db_hosts.get(str.c_str(),j,&other_host); + if (strcmp(host,other_host) == 0) { + unsigned int other_i, c= 0; + p_db_hosts.get(str2.c_str(),j,&other_i); + p_db_hosts.get(str.c_str(),&c); + if (c == 0) // first warning in this node group + node_group_warning.appfmt(" Node group %d", group); + c|= 1 << j; + p_db_hosts.put(str.c_str(),c); + + node_group_warning.appfmt(",\n db node with id %d and id %d " + "on same host %s", other_i, i, host); + } + } + i_group++; + DBUG_ASSERT(i_group <= replicas); + if (i_group == replicas) + { + unsigned c= 0; + p_db_hosts.get(str.c_str(),&c); + if (c+1 == (1u << (replicas-1))) // all nodes on same machine + node_group_warning.append(".\n Host failure will " + "cause complete cluster shutdown."); + else if (c > 0) + node_group_warning.append(".\n Host failure may " + "cause complete cluster shutdown."); + group++; + i_group= 0; + } + } + } + else if (strcmp(type,API_TOKEN) == 0 || + strcmp(type,MGM_TOKEN) == 0) + { + Uint32 rank; + if(tmp->get("ArbitrationRank", &rank) && rank > 0) + { + if(host && host[0] != 0) + { + Uint32 ii; + p_arbitrators.put(host,i); + if (p_db_hosts.get(host,&ii)) + { + arbitration_warning.appfmt(arbit_warn_fmt, i, ii, host); + } + } + else + { + arbitration_warning.appfmt(arbit_warn_fmt2, i); + } + } + } + } + if (db_host_count > 1 && node_group_warning.length() > 0) + ndbout_c("Cluster configuration warning:\n%s",node_group_warning.c_str()); + if (db_host_count > 1 && arbitration_warning.length() > 0) + ndbout_c("Cluster configuration warning:%s%s",arbitration_warning.c_str(), + "\n Running arbitrator on the same host as a database node may" + "\n cause complete cluster shutdown in case of host failure."); + } + return true; +} + +template class Vector<ConfigInfo::ConfigRuleSection>; diff --git a/storage/ndb/src/mgmsrv/ConfigInfo.hpp b/storage/ndb/src/mgmsrv/ConfigInfo.hpp new file mode 100644 index 00000000000..788619db7fe --- /dev/null +++ b/storage/ndb/src/mgmsrv/ConfigInfo.hpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef ConfigInfo_H +#define ConfigInfo_H + +#include <kernel_types.h> +#include <Properties.hpp> +#include <ndb_limits.h> +#include <NdbOut.hpp> +#include "InitConfigFileParser.hpp" + +/** + * A MANDATORY parameters must be specified in the config file + * An UNDEFINED parameter may or may not be specified in the config file + */ +static const char* MANDATORY = (char*)~(UintPtr)0;// Default value for mandatory params. +static const char* UNDEFINED = 0; // Default value for undefined params. + +/** + * @class ConfigInfo + * @brief Metainformation about ALL cluster configuration parameters + * + * Use the getters to find out metainformation about parameters. + */ +class ConfigInfo { +public: + enum Type { CI_BOOL, CI_INT, CI_INT64, CI_STRING, CI_SECTION }; + enum Status { CI_USED, ///< Active + CI_DEPRICATED, ///< Can be, but shouldn't + CI_NOTIMPLEMENTED, ///< Is ignored. + CI_INTERNAL ///< Not configurable by the user + }; + + /** + * Entry for one configuration parameter + */ + struct ParamInfo { + Uint32 _paramId; + const char* _fname; + const char* _section; + const char* _description; + Status _status; + bool _updateable; + Type _type; + const char* _default; + const char* _min; + const char* _max; + }; + + struct AliasPair{ + const char * name; + const char * alias; + }; + + /** + * Entry for one section rule + */ + struct SectionRule { + const char * m_section; + bool (* m_sectionRule)(struct InitConfigFileParser::Context &, + const char * m_ruleData); + const char * m_ruleData; + }; + + /** + * Entry for config rule + */ + struct ConfigRuleSection { + BaseString m_sectionType; + Properties * m_sectionData; + }; + + struct ConfigRule { + bool (* m_configRule)(Vector<ConfigRuleSection>&, + struct InitConfigFileParser::Context &, + const char * m_ruleData); + const char * m_ruleData; + }; + + ConfigInfo(); + + /** + * Checks if the suggested value is valid for the suggested parameter + * (i.e. if it is >= than min and <= than max). + * + * @param section Init Config file section name + * @param fname Name of parameter + * @param value Value to check + * @return true if parameter value is valid. + * + * @note Result is not defined if section/name are wrong! + */ + bool verify(const Properties* secti, const char* fname, Uint64 value) const; + static const char* nameToAlias(const char*); + static const char* getAlias(const char*); + bool isSection(const char*) const; + + const char* getDescription(const Properties * sec, const char* fname) const; + Type getType(const Properties * section, const char* fname) const; + Status getStatus(const Properties* section, const char* fname) const; + Uint64 getMin(const Properties * section, const char* fname) const; + Uint64 getMax(const Properties * section, const char* fname) const; + Uint64 getDefault(const Properties * section, const char* fname) const; + + const Properties * getInfo(const char * section) const; + const Properties * getDefaults(const char * section) const; + + void print() const; + void print(const char* section) const; + void print(const Properties * section, const char* parameter) const; + +private: + Properties m_info; + Properties m_systemDefaults; + + static const AliasPair m_sectionNameAliases[]; + static const char* m_sectionNames[]; + static const int m_noOfSectionNames; + +public: + static const ParamInfo m_ParamInfo[]; + static const int m_NoOfParams; + + static const SectionRule m_SectionRules[]; + static const ConfigRule m_ConfigRules[]; + static const int m_NoOfRules; +}; + +#endif // ConfigInfo_H diff --git a/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp b/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp new file mode 100644 index 00000000000..bf5cb9d726e --- /dev/null +++ b/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp @@ -0,0 +1,970 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> + +#include "InitConfigFileParser.hpp" +#include "Config.hpp" +#include "MgmtErrorReporter.hpp" +#include <NdbOut.hpp> +#include "ConfigInfo.hpp" +#include <m_string.h> + +const int MAX_LINE_LENGTH = 1024; // Max length of line of text in config file +static void trim(char *); + +static void require(bool v) { if(!v) abort();} + +//**************************************************************************** +// Ctor / Dtor +//**************************************************************************** +InitConfigFileParser::InitConfigFileParser(FILE * out) +{ + m_info = new ConfigInfo(); + m_errstream = out ? out : stdout; +} + +InitConfigFileParser::~InitConfigFileParser() { + delete m_info; +} + +//**************************************************************************** +// Read Config File +//**************************************************************************** +InitConfigFileParser::Context::Context(const ConfigInfo * info, FILE * out) + : m_userProperties(true), m_configValues(1000, 20) { + + m_config = new Properties(true); + m_defaults = new Properties(true); + m_errstream = out; +} + +InitConfigFileParser::Context::~Context(){ + if(m_config != 0) + delete m_config; + + if(m_defaults != 0) + delete m_defaults; +} + +Config * +InitConfigFileParser::parseConfig(const char * filename) { + FILE * file = fopen(filename, "r"); + if(file == 0){ + fprintf(m_errstream, "Error opening file: %s\n", filename); + return 0; + } + + Config * ret = parseConfig(file); + fclose(file); + return ret; +} + +Config * +InitConfigFileParser::parseConfig(FILE * file) { + + char line[MAX_LINE_LENGTH]; + + Context ctx(m_info, m_errstream); + ctx.m_lineno = 0; + ctx.m_currentSection = 0; + + /************* + * Open file * + *************/ + if (file == NULL) { + return 0; + } + + /*********************** + * While lines to read * + ***********************/ + while (fgets(line, MAX_LINE_LENGTH, file)) { + ctx.m_lineno++; + + trim(line); + + if (isEmptyLine(line)) // Skip if line is empty or comment + continue; + + // End with NULL instead of newline + if (line[strlen(line)-1] == '\n') + line[strlen(line)-1] = '\0'; + + /******************************** + * 1. Parse new default section * + ********************************/ + if (char* section = parseDefaultSectionHeader(line)) { + if(!storeSection(ctx)){ + free(section); + ctx.reportError("Could not store previous default section " + "of configuration file."); + return 0; + } + BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section); free(section); + ctx.type = InitConfigFileParser::DefaultSection; + ctx.m_sectionLineno = ctx.m_lineno; + ctx.m_currentSection = new Properties(true); + ctx.m_userDefaults = NULL; + require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0); + require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0); + continue; + } + + /************************ + * 2. Parse new section * + ************************/ + if (char* section = parseSectionHeader(line)) { + if(!storeSection(ctx)){ + free(section); + ctx.reportError("Could not store previous section " + "of configuration file."); + return 0; + } + BaseString::snprintf(ctx.fname, sizeof(ctx.fname), section); + free(section); + ctx.type = InitConfigFileParser::Section; + ctx.m_sectionLineno = ctx.m_lineno; + ctx.m_currentSection = new Properties(true); + ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults); + require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0); + require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0); + continue; + } + + /**************************** + * 3. Parse name-value pair * + ****************************/ + if (!parseNameValuePair(ctx, line)) { + ctx.reportError("Could not parse name-value pair in config file."); + return 0; + } + } + + if (ferror(file)){ + ctx.reportError("Failure in reading"); + return 0; + } + + if(!storeSection(ctx)) { + ctx.reportError("Could not store section of configuration file."); + return 0; + } + + return run_config_rules(ctx); +} + +Config* +InitConfigFileParser::run_config_rules(Context& ctx) +{ + for(size_t i = 0; ConfigInfo::m_ConfigRules[i].m_configRule != 0; i++){ + ctx.type = InitConfigFileParser::Undefined; + ctx.m_currentSection = 0; + ctx.m_userDefaults = 0; + ctx.m_currentInfo = 0; + ctx.m_systemDefaults = 0; + + Vector<ConfigInfo::ConfigRuleSection> tmp; + if(!(* ConfigInfo::m_ConfigRules[i].m_configRule)(tmp, ctx, + ConfigInfo::m_ConfigRules[i].m_ruleData)) + return 0; + + for(size_t j = 0; j<tmp.size(); j++){ + BaseString::snprintf(ctx.fname, sizeof(ctx.fname), tmp[j].m_sectionType.c_str()); + ctx.type = InitConfigFileParser::Section; + ctx.m_currentSection = tmp[j].m_sectionData; + ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults); + require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0); + require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0); + if(!storeSection(ctx)) + return 0; + } + } + + Uint32 nConnections = 0; + Uint32 nComputers = 0; + Uint32 nNodes = 0; + Uint32 nExtConnections = 0; + const char * system = "?"; + ctx.m_userProperties.get("NoOfConnections", &nConnections); + ctx.m_userProperties.get("NoOfComputers", &nComputers); + ctx.m_userProperties.get("NoOfNodes", &nNodes); + ctx.m_userProperties.get("ExtNoOfConnections", &nExtConnections); + ctx.m_userProperties.get("ExtSystem", &system); + ctx.m_config->put("NoOfConnections", nConnections); + ctx.m_config->put("NoOfComputers", nComputers); + ctx.m_config->put("NoOfNodes", nNodes); + + char tmpLine[MAX_LINE_LENGTH]; + BaseString::snprintf(tmpLine, MAX_LINE_LENGTH, "EXTERNAL SYSTEM_"); + strncat(tmpLine, system, MAX_LINE_LENGTH); + strncat(tmpLine, ":NoOfConnections", MAX_LINE_LENGTH); + ctx.m_config->put(tmpLine, nExtConnections); + + Config * ret = new Config(); + ret->m_configValues = (struct ndb_mgm_configuration*)ctx.m_configValues.getConfigValues(); + ret->m_oldConfig = ctx.m_config; ctx.m_config = 0; + return ret; +} + +//**************************************************************************** +// Parse Name-Value Pair +//**************************************************************************** + +bool InitConfigFileParser::parseNameValuePair(Context& ctx, const char* line) +{ + if (ctx.m_currentSection == NULL){ + ctx.reportError("Value specified outside section"); + return false; + } + + // ************************************* + // Split string at first occurrence of + // '=' or ':' + // ************************************* + + Vector<BaseString> tmp_string_split; + if (BaseString(line).split(tmp_string_split, + "=:", 2) != 2) + { + ctx.reportError("Parse error"); + return false; + } + + // ************************************* + // Remove all after # + // ************************************* + + Vector<BaseString> tmp_string_split2; + tmp_string_split[1].split(tmp_string_split2, + "#", 2); + tmp_string_split[1]=tmp_string_split2[0]; + + // ************************************* + // Remove leading and trailing chars + // ************************************* + { + for (int i = 0; i < 2; i++) + tmp_string_split[i].trim("\r\n \t"); + } + + // ************************************* + // First in split is fname + // ************************************* + + const char *fname= tmp_string_split[0].c_str(); + + if (!ctx.m_currentInfo->contains(fname)) { + ctx.reportError("[%s] Unknown parameter: %s", ctx.fname, fname); + return false; + } + ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname); + if (status == ConfigInfo::CI_NOTIMPLEMENTED) { + ctx.reportWarning("[%s] %s not yet implemented", ctx.fname, fname); + } + if (status == ConfigInfo::CI_DEPRICATED) { + const char * desc = m_info->getDescription(ctx.m_currentInfo, fname); + if(desc && desc[0]){ + ctx.reportWarning("[%s] %s is depricated, use %s instead", + ctx.fname, fname, desc); + } else if (desc == 0){ + ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname); + } + } + + // *********************** + // Store name-value pair + // *********************** + + return storeNameValuePair(ctx, fname, tmp_string_split[1].c_str()); +} + + +//**************************************************************************** +// STORE NAME-VALUE pair in properties section +//**************************************************************************** + +bool +InitConfigFileParser::storeNameValuePair(Context& ctx, + const char* fname, + const char* value) { + + const char * pname = fname; + + if (ctx.m_currentSection->contains(pname)) { + ctx.reportError("[%s] Parameter %s specified twice", ctx.fname, fname); + return false; + } + + // *********************** + // Store name-value pair + // *********************** + + const ConfigInfo::Type type = m_info->getType(ctx.m_currentInfo, fname); + switch(type){ + case ConfigInfo::CI_BOOL: { + bool value_bool; + if (!convertStringToBool(value, value_bool)) { + ctx.reportError("Illegal boolean value for parameter %s", fname); + return false; + } + MGM_REQUIRE(ctx.m_currentSection->put(pname, value_bool)); + break; + } + case ConfigInfo::CI_INT: + case ConfigInfo::CI_INT64:{ + Uint64 value_int; + if (!convertStringToUint64(value, value_int)) { + ctx.reportError("Illegal integer value for parameter %s", fname); + return false; + } + if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) { + ctx.reportError("Illegal value %s for parameter %s.\n" + "Legal values are between %Lu and %Lu", value, fname, + m_info->getMin(ctx.m_currentInfo, fname), + m_info->getMax(ctx.m_currentInfo, fname)); + return false; + } + if(type == ConfigInfo::CI_INT){ + MGM_REQUIRE(ctx.m_currentSection->put(pname, (Uint32)value_int)); + } else { + MGM_REQUIRE(ctx.m_currentSection->put64(pname, value_int)); + } + break; + } + case ConfigInfo::CI_STRING: + MGM_REQUIRE(ctx.m_currentSection->put(pname, value)); + break; + case ConfigInfo::CI_SECTION: + abort(); + } + return true; +} + +//**************************************************************************** +// Is Empty Line +//**************************************************************************** + +bool InitConfigFileParser::isEmptyLine(const char* line) const { + int i; + + // Check if it is a comment line + if (line[0] == '#') return true; + + // Check if it is a line with only spaces + for (i = 0; i < MAX_LINE_LENGTH && line[i] != '\n' && line[i] != '\0'; i++) { + if (line[i] != ' ' && line[i] != '\t') return false; + } + return true; +} + +//**************************************************************************** +// Convert String to Int +//**************************************************************************** +bool InitConfigFileParser::convertStringToUint64(const char* s, + Uint64& val, + Uint32 log10base) { + if (s == NULL) + return false; + if (strlen(s) == 0) + return false; + + errno = 0; + char* p; + Int64 v = strtoll(s, &p, log10base); + if (errno != 0) + return false; + + long mul = 0; + if (p != &s[strlen(s)]){ + char * tmp = strdup(p); + trim(tmp); + switch(tmp[0]){ + case 'k': + case 'K': + mul = 10; + break; + case 'M': + mul = 20; + break; + case 'G': + mul = 30; + break; + default: + free(tmp); + return false; + } + free(tmp); + } + + val = (v << mul); + return true; +} + +bool InitConfigFileParser::convertStringToBool(const char* s, bool& val) { + if (s == NULL) return false; + if (strlen(s) == 0) return false; + + if (!strcmp(s, "Y") || !strcmp(s, "y") || + !strcmp(s, "Yes") || !strcmp(s, "YES") || !strcmp(s, "yes") || + !strcmp(s, "True") || !strcmp(s, "TRUE") || !strcmp(s, "true") || + !strcmp(s, "1")) { + val = true; + return true; + } + + if (!strcmp(s, "N") || !strcmp(s, "n") || + !strcmp(s, "No") || !strcmp(s, "NO") || !strcmp(s, "no") || + !strcmp(s, "False") || !strcmp(s, "FALSE") || !strcmp(s, "false") || + !strcmp(s, "0")) { + val = false; + return true; + } + + return false; // Failure to convert +} + +//**************************************************************************** +// Parse Section Header +//**************************************************************************** +static void +trim(char * str){ + int len = strlen(str); + for(len--; + (str[len] == '\r' || str[len] == '\n' || + str[len] == ' ' || str[len] == '\t') && + len > 0; + len--) + str[len] = 0; + + int pos = 0; + while(str[pos] == ' ' || str[pos] == '\t') + pos++; + + if(str[pos] == '\"' && str[len] == '\"') { + pos++; + str[len] = 0; + len--; + } + + memmove(str, &str[pos], len - pos + 2); +} + +char* +InitConfigFileParser::parseSectionHeader(const char* line) const { + char * tmp = strdup(line); + + if(tmp[0] != '['){ + free(tmp); + return NULL; + } + + if(tmp[strlen(tmp)-1] != ']'){ + free(tmp); + return NULL; + } + tmp[strlen(tmp)-1] = 0; + + tmp[0] = ' '; + trim(tmp); + + // Get the correct header name if an alias + { + const char *tmp_alias= m_info->getAlias(tmp); + if (tmp_alias) { + free(tmp); + tmp= strdup(tmp_alias); + } + } + + // Lookup token among sections + if(!m_info->isSection(tmp)) { + free(tmp); + return NULL; + } + if(m_info->getInfo(tmp)) return tmp; + + free(tmp); + return NULL; +} + +//**************************************************************************** +// Parse Default Section Header +//**************************************************************************** + +char* +InitConfigFileParser::parseDefaultSectionHeader(const char* line) const { + static char token1[MAX_LINE_LENGTH], token2[MAX_LINE_LENGTH]; + + int no = sscanf(line, "[%120[A-Z_a-z] %120[A-Z_a-z]]", token1, token2); + + // Not correct no of tokens + if (no != 2) return NULL; + + // Not correct keyword at end + if (!strcasecmp(token2, "DEFAULT") == 0) return NULL; + + const char *token1_alias= m_info->getAlias(token1); + if (token1_alias == 0) + token1_alias= token1; + + if(m_info->getInfo(token1_alias)){ + return strdup(token1_alias); + } + + // Did not find section + return NULL; +} + +const Properties * +InitConfigFileParser::getSection(const char * name, const Properties * src){ + const Properties * p; + if(src && src->get(name, &p)) + return p; + + return 0; +} + +//**************************************************************************** +// STORE section +//**************************************************************************** +bool +InitConfigFileParser::storeSection(Context& ctx){ + if(ctx.m_currentSection == NULL) + return true; + for(int i = strlen(ctx.fname) - 1; i>=0; i--){ + ctx.fname[i] = toupper(ctx.fname[i]); + } + BaseString::snprintf(ctx.pname, sizeof(ctx.pname), ctx.fname); + char buf[255]; + if(ctx.type == InitConfigFileParser::Section) + BaseString::snprintf(buf, sizeof(buf), "%s", ctx.fname); + if(ctx.type == InitConfigFileParser::DefaultSection) + BaseString::snprintf(buf, sizeof(buf), "%s DEFAULT", ctx.fname); + BaseString::snprintf(ctx.fname, sizeof(ctx.fname), buf); + if(ctx.type == InitConfigFileParser::Section){ + for(int i = 0; i<m_info->m_NoOfRules; i++){ + const ConfigInfo::SectionRule & rule = m_info->m_SectionRules[i]; + if(!strcmp(rule.m_section, "*") || !strcmp(rule.m_section, ctx.fname)){ + if(!(* rule.m_sectionRule)(ctx, rule.m_ruleData)){ + return false; + } + } + } + } + if(ctx.type == InitConfigFileParser::DefaultSection && + !ctx.m_defaults->put(ctx.pname, ctx.m_currentSection)) + { + ctx.reportError("Duplicate default section not allowed"); + return false; + } + if(ctx.type == InitConfigFileParser::Section) + require(ctx.m_config->put(ctx.pname, ctx.m_currentSection)); + delete ctx.m_currentSection; ctx.m_currentSection = NULL; + return true; +} + +void +InitConfigFileParser::Context::reportError(const char * fmt, ...){ + va_list ap; + char buf[1000]; + + va_start(ap, fmt); + if (fmt != 0) + BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap); + va_end(ap); + fprintf(m_errstream, "Error line %d: %s\n", + m_lineno, buf); + + //m_currentSection->print(); +} + +void +InitConfigFileParser::Context::reportWarning(const char * fmt, ...){ + va_list ap; + char buf[1000]; + + va_start(ap, fmt); + if (fmt != 0) + BaseString::vsnprintf(buf, sizeof(buf)-1, fmt, ap); + va_end(ap); + fprintf(m_errstream, "Warning line %d: %s\n", + m_lineno, buf); +} + +#include <my_sys.h> +#include <my_getopt.h> + +static int order = 1; +static +my_bool +parse_mycnf_opt(int, const struct my_option * opt, char * value) +{ + if(opt->comment) + ((struct my_option *)opt)->app_type++; + else + ((struct my_option *)opt)->app_type = order++; + return 0; +} + +bool +InitConfigFileParser::store_in_properties(Vector<struct my_option>& options, + InitConfigFileParser::Context& ctx, + const char * name) +{ + for(unsigned i = 0; i<options.size(); i++) + { + if(options[i].comment && + options[i].app_type && + strcmp(options[i].comment, name) == 0) + { + Uint64 value_int; + switch(options[i].var_type){ + case GET_INT: + value_int = *(Uint32*)options[i].value; + break; + case GET_LL: + value_int = *(Uint64*)options[i].value; + break; + case GET_STR: + ctx.m_currentSection->put(options[i].name, *(char**)options[i].value); + continue; + default: + abort(); + } + + const char * fname = options[i].name; + if (!m_info->verify(ctx.m_currentInfo, fname, value_int)) { + ctx.reportError("Illegal value %lld for parameter %s.\n" + "Legal values are between %Lu and %Lu", + value_int, fname, + m_info->getMin(ctx.m_currentInfo, fname), + m_info->getMax(ctx.m_currentInfo, fname)); + return false; + } + + ConfigInfo::Status status = m_info->getStatus(ctx.m_currentInfo, fname); + if (status == ConfigInfo::CI_DEPRICATED) { + const char * desc = m_info->getDescription(ctx.m_currentInfo, fname); + if(desc && desc[0]){ + ctx.reportWarning("[%s] %s is depricated, use %s instead", + ctx.fname, fname, desc); + } else if (desc == 0){ + ctx.reportWarning("[%s] %s is depricated", ctx.fname, fname); + } + } + + if (options[i].var_type == GET_INT) + ctx.m_currentSection->put(options[i].name, (Uint32)value_int); + else + ctx.m_currentSection->put(options[i].name, value_int); + } + } + return true; +} + +bool +InitConfigFileParser::handle_mycnf_defaults(Vector<struct my_option>& options, + InitConfigFileParser::Context& ctx, + const char * name) +{ + strcpy(ctx.fname, name); + ctx.type = InitConfigFileParser::DefaultSection; + ctx.m_currentSection = new Properties(true); + ctx.m_userDefaults = NULL; + require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0); + require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname)) != 0); + if(store_in_properties(options, ctx, name)) + return storeSection(ctx); + return false; +} + +static +int +load_defaults(Vector<struct my_option>& options, const char* groups[]) +{ + int argc = 1; + const char * argv[] = { "ndb_mgmd", 0, 0, 0, 0 }; + BaseString file; + BaseString extra_file; + BaseString group_suffix; + + const char *save_file = defaults_file; + char *save_extra_file = defaults_extra_file; + const char *save_group_suffix = defaults_group_suffix; + + if (defaults_file) + { + file.assfmt("--defaults-file=%s", defaults_file); + argv[argc++] = file.c_str(); + } + + if (defaults_extra_file) + { + extra_file.assfmt("--defaults-extra-file=%s", defaults_extra_file); + argv[argc++] = extra_file.c_str(); + } + + if (defaults_group_suffix) + { + group_suffix.assfmt("--defaults-group-suffix=%s", defaults_group_suffix); + argv[argc++] = group_suffix.c_str(); + } + + char ** tmp = (char**)argv; + int ret = load_defaults("my", groups, &argc, &tmp); + + defaults_file = save_file; + defaults_extra_file = save_extra_file; + defaults_group_suffix = save_group_suffix; + + if (ret == 0) + { + return handle_options(&argc, &tmp, options.getBase(), parse_mycnf_opt); + } + + return ret; +} + +bool +InitConfigFileParser::load_mycnf_groups(Vector<struct my_option> & options, + InitConfigFileParser::Context& ctx, + const char * name, + const char *groups[]) +{ + unsigned i; + Vector<struct my_option> copy; + for(i = 0; i<options.size(); i++) + { + if(options[i].comment && strcmp(options[i].comment, name) == 0) + { + options[i].app_type = 0; + copy.push_back(options[i]); + } + } + + struct my_option end; + bzero(&end, sizeof(end)); + copy.push_back(end); + + if (load_defaults(copy, groups)) + return false; + + return store_in_properties(copy, ctx, name); +} + +Config * +InitConfigFileParser::parse_mycnf() +{ + int i; + Config * res = 0; + Vector<struct my_option> options; + for(i = 0; i<ConfigInfo::m_NoOfParams; i++) + { + { + struct my_option opt; + bzero(&opt, sizeof(opt)); + const ConfigInfo::ParamInfo& param = ConfigInfo::m_ParamInfo[i]; + switch(param._type){ + case ConfigInfo::CI_BOOL: + opt.value = (gptr*)malloc(sizeof(int)); + opt.var_type = GET_INT; + break; + case ConfigInfo::CI_INT: + opt.value = (gptr*)malloc(sizeof(int)); + opt.var_type = GET_INT; + break; + case ConfigInfo::CI_INT64: + opt.value = (gptr*)malloc(sizeof(Int64)); + opt.var_type = GET_LL; + break; + case ConfigInfo::CI_STRING: + opt.value = (gptr*)malloc(sizeof(char *)); + opt.var_type = GET_STR; + break; + default: + continue; + } + opt.name = param._fname; + opt.id = 256; + opt.app_type = 0; + opt.arg_type = REQUIRED_ARG; + opt.comment = param._section; + options.push_back(opt); + } + } + + struct my_option *ndbd, *ndb_mgmd, *mysqld, *api; + + /** + * Add ndbd, ndb_mgmd, api/mysqld + */ + Uint32 idx = options.size(); + { + struct my_option opt; + bzero(&opt, sizeof(opt)); + opt.name = "ndbd"; + opt.id = 256; + opt.value = (gptr*)malloc(sizeof(char*)); + opt.var_type = GET_STR; + opt.arg_type = REQUIRED_ARG; + options.push_back(opt); + + opt.name = "ndb_mgmd"; + opt.id = 256; + opt.value = (gptr*)malloc(sizeof(char*)); + opt.var_type = GET_STR; + opt.arg_type = REQUIRED_ARG; + options.push_back(opt); + + opt.name = "mysqld"; + opt.id = 256; + opt.value = (gptr*)malloc(sizeof(char*)); + opt.var_type = GET_STR; + opt.arg_type = REQUIRED_ARG; + options.push_back(opt); + + opt.name = "api"; + opt.id = 256; + opt.value = (gptr*)malloc(sizeof(char*)); + opt.var_type = GET_STR; + opt.arg_type = REQUIRED_ARG; + options.push_back(opt); + + bzero(&opt, sizeof(opt)); + options.push_back(opt); + + ndbd = &options[idx]; + ndb_mgmd = &options[idx+1]; + mysqld = &options[idx+2]; + api = &options[idx+3]; + } + + + Context ctx(m_info, m_errstream); + const char *groups[]= { "cluster_config", 0 }; + if (load_defaults(options, groups)) + goto end; + + ctx.m_lineno = 0; + if(!handle_mycnf_defaults(options, ctx, "DB")) + goto end; + if(!handle_mycnf_defaults(options, ctx, "API")) + goto end; + if(!handle_mycnf_defaults(options, ctx, "MGM")) + goto end; + if(!handle_mycnf_defaults(options, ctx, "TCP")) + goto end; + if(!handle_mycnf_defaults(options, ctx, "SHM")) + goto end; + if(!handle_mycnf_defaults(options, ctx, "SCI")) + goto end; + + { + struct sect { struct my_option* src; const char * name; } sections[] = + { + { ndb_mgmd, "MGM" } + ,{ ndbd, "DB" } + ,{ mysqld, "API" } + ,{ api, "API" } + ,{ 0, 0 }, { 0, 0 } + }; + + for(i = 0; sections[i].src; i++) + { + for(int j = i + 1; sections[j].src; j++) + { + if (sections[j].src->app_type < sections[i].src->app_type) + { + sect swap = sections[i]; + sections[i] = sections[j]; + sections[j] = swap; + } + } + } + + ctx.type = InitConfigFileParser::Section; + ctx.m_sectionLineno = ctx.m_lineno; + for(i = 0; sections[i].src; i++) + { + if (sections[i].src->app_type) + { + strcpy(ctx.fname, sections[i].name); + BaseString str(*(char**)sections[i].src->value); + Vector<BaseString> list; + str.split(list, ","); + + const char * defaults_groups[] = { 0, 0, 0 }; + for(unsigned j = 0; j<list.size(); j++) + { + BaseString group_idx; + BaseString group_host; + group_idx.assfmt("%s.%s.%d", groups[0], + sections[i].src->name, j + 1); + group_host.assfmt("%s.%s.%s", groups[0], + sections[i].src->name, list[j].c_str()); + defaults_groups[0] = group_idx.c_str(); + if(list[j].length()) + defaults_groups[1] = group_host.c_str(); + else + defaults_groups[1] = 0; + + ctx.m_currentSection = new Properties(true); + ctx.m_userDefaults = getSection(ctx.fname, ctx.m_defaults); + require((ctx.m_currentInfo = m_info->getInfo(ctx.fname)) != 0); + require((ctx.m_systemDefaults = m_info->getDefaults(ctx.fname))!= 0); + ctx.m_currentSection->put("HostName", list[j].c_str()); + if(!load_mycnf_groups(options, ctx, sections[i].name, + defaults_groups)) + goto end; + + if(!storeSection(ctx)) + goto end; + } + } + } + } + + res = run_config_rules(ctx); + +end: + for(i = 0; options[i].name; i++) + free(options[i].value); + + return res; +} + +template class Vector<struct my_option>; + +#if 0 +struct my_option +{ + const char *name; /* Name of the option */ + int id; /* unique id or short option */ + const char *comment; /* option comment, for autom. --help */ + gptr *value; /* The variable value */ + gptr *u_max_value; /* The user def. max variable value */ + const char **str_values; /* Pointer to possible values */ + ulong var_type; + enum get_opt_arg_type arg_type; + longlong def_value; /* Default value */ + longlong min_value; /* Min allowed value */ + longlong max_value; /* Max allowed value */ + longlong sub_size; /* Subtract this from given value */ + long block_size; /* Value should be a mult. of this */ + int app_type; /* To be used by an application */ +}; +#endif diff --git a/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp b/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp new file mode 100644 index 00000000000..616fd5a62fb --- /dev/null +++ b/storage/ndb/src/mgmsrv/InitConfigFileParser.hpp @@ -0,0 +1,145 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef InitConfigFileParser_H +#define InitConfigFileParser_H + +#include <ndb_global.h> + +#include <Properties.hpp> +#include <ConfigValues.hpp> + +class Config; +class ConfigInfo; + +/** + * @class InitConfigFileParser + * @brief Reads initial config file and returns Config object + * + * This class contains one public method InitConfigFileParser::parseConfig, + * which reads an initial configuration file and returns a Config + * object if the config file has correct syntax and semantic. + */ +class InitConfigFileParser { + FILE * m_errstream; +public: + /** + * Constructor + */ + InitConfigFileParser(FILE * errstream = stdout); + ~InitConfigFileParser(); + + /** + * Reads the initial configuration file, checks syntax and semantic + * and stores internally the values of all parameters. + * + * @returns Config or NULL on failure + * @note must be freed by caller + */ + Config * parseConfig(FILE * file); + Config * parseConfig(const char * filename); + Config * parse_mycnf(); + + /** + * Parser context struct + */ + enum ContextSectionType { Undefined, Section, DefaultSection }; + + /** + * Context = Which section in init config file we are currently parsing + */ + struct Context { + Context(const ConfigInfo *, FILE * out); + ~Context(); + + ContextSectionType type; ///< Section type (e.g. default section,section) + char fname[256]; ///< Section name occuring in init config file + char pname[256]; ///< Section name stored in properties object + Uint32 m_lineno; ///< Current line no in config file + Uint32 m_sectionLineno; ///< Where did current section start + + const ConfigInfo * m_info; // The config info + Properties * m_config; // The config object + Properties * m_defaults; // The user defaults + + Properties * m_currentSection; // The current section I'm in + const Properties * m_userDefaults; // The defaults of this section + const Properties * m_systemDefaults; // The syst. defaults for this section + const Properties * m_currentInfo; // The "info" for this section + + Properties m_userProperties; // User properties (temporary values) + ConfigValuesFactory m_configValues; // + + public: + FILE * m_errstream; + void reportError(const char * msg, ...); + void reportWarning(const char * msg, ...); + }; + + static bool convertStringToUint64(const char* s, Uint64& val, Uint32 log10base = 0); + static bool convertStringToBool(const char* s, bool& val); + +private: + /** + * Check if line only contains space/comments + * @param line: The line to check + * @return true if spaces/comments only, false otherwise + */ + bool isEmptyLine(const char* line) const; + + /** + * Checks if line contains a section header + * @param line: String to search + * @return section header if matching some section header, NULL otherwise + */ + char* parseSectionHeader(const char* line) const; + + /** + * Checks if line contains a default header + * @param line: String to search + * @return section header if matching some section header, NULL otherwise + */ + char* parseDefaultSectionHeader(const char* line) const; + + bool parseNameValuePair(Context&, const char* line); + bool storeNameValuePair(Context&, const char* fname, const char* value); + + bool storeSection(Context&); + + const Properties* getSection(const char * name, const Properties* src); + + /** + * Information about parameters (min, max values etc) + */ + ConfigInfo* m_info; + + bool handle_mycnf_defaults(Vector<struct my_option>& options, + InitConfigFileParser::Context& ctx, + const char * name); + + bool load_mycnf_groups(Vector<struct my_option> & options, + InitConfigFileParser::Context& ctx, + const char * name, + const char *groups[]); + + bool store_in_properties(Vector<struct my_option>& options, + InitConfigFileParser::Context& ctx, + const char * name); + + Config* run_config_rules(Context& ctx); +}; + +#endif // InitConfigFileParser_H diff --git a/storage/ndb/src/mgmsrv/Makefile.am b/storage/ndb/src/mgmsrv/Makefile.am new file mode 100644 index 00000000000..d0c1b1219a4 --- /dev/null +++ b/storage/ndb/src/mgmsrv/Makefile.am @@ -0,0 +1,60 @@ +MYSQLDATAdir = $(localstatedir) +MYSQLSHAREdir = $(pkgdatadir) +MYSQLBASEdir= $(prefix) +#MYSQLCLUSTERdir= $(prefix)/mysql-cluster +MYSQLCLUSTERdir= . + +ndbbin_PROGRAMS = ndb_mgmd + +ndb_mgmd_SOURCES = \ + MgmtSrvr.cpp \ + MgmtSrvrGeneralSignalHandling.cpp \ + main.cpp \ + Services.cpp \ + convertStrToInt.cpp \ + SignalQueue.cpp \ + MgmtSrvrConfig.cpp \ + ConfigInfo.cpp \ + InitConfigFileParser.cpp \ + Config.cpp + +INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/ndbapi \ + -I$(top_srcdir)/storage/ndb/src/mgmapi \ + -I$(top_srcdir)/storage/ndb/src/common/mgmcommon \ + -I$(top_srcdir)/storage/ndb/src/mgmclient + +LDADD_LOC = $(top_builddir)/storage/ndb/src/mgmclient/CommandInterpreter.o \ + $(top_builddir)/storage/ndb/src/libndbclient.la \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + @readline_link@ \ + @NDB_SCI_LIBS@ \ + @TERMCAP_LIB@ + +DEFS_LOC = -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ + -DDATADIR="\"$(MYSQLDATAdir)\"" \ + -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ + -DMYSQLCLUSTERDIR="\"$(MYSQLCLUSTERdir)\"" + +include $(top_srcdir)/storage/ndb/config/common.mk.am +include $(top_srcdir)/storage/ndb/config/type_ndbapi.mk.am + +ndb_mgmd_LDFLAGS = @ndb_bin_am_ldflags@ + +# Don't update the files from bitkeeper +%::SCCS/s.% + +windoze-dsp: ndb_mgmd.dsp + +ndb_mgmd.dsp: Makefile \ + $(top_srcdir)/storage/ndb/config/win-prg.am \ + $(top_srcdir)/storage/ndb/config/win-name \ + $(top_srcdir)/storage/ndb/config/win-includes \ + $(top_srcdir)/storage/ndb/config/win-sources \ + $(top_srcdir)/storage/ndb/config/win-libraries + cat $(top_srcdir)/storage/ndb/config/win-prg.am > $@ + @$(top_srcdir)/storage/ndb/config/win-name $@ $(ndbbin_PROGRAMS) + @$(top_srcdir)/storage/ndb/config/win-includes $@ $(INCLUDES) + @$(top_srcdir)/storage/ndb/config/win-sources $@ $(ndb_mgmd_SOURCES) + @$(top_srcdir)/storage/ndb/config/win-libraries $@ LINK $(LDADD) diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp new file mode 100644 index 00000000000..29b1daf2e2a --- /dev/null +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp @@ -0,0 +1,2941 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <my_pthread.h> + +#include "MgmtSrvr.hpp" +#include "MgmtErrorReporter.hpp" +#include <ConfigRetriever.hpp> + +#include <NdbOut.hpp> +#include <NdbApiSignal.hpp> +#include <kernel_types.h> +#include <RefConvert.hpp> +#include <BlockNumbers.h> +#include <GlobalSignalNumbers.h> +#include <signaldata/TestOrd.hpp> +#include <signaldata/TamperOrd.hpp> +#include <signaldata/StartOrd.hpp> +#include <signaldata/ApiVersion.hpp> +#include <signaldata/ResumeReq.hpp> +#include <signaldata/SetLogLevelOrd.hpp> +#include <signaldata/EventSubscribeReq.hpp> +#include <signaldata/EventReport.hpp> +#include <signaldata/DumpStateOrd.hpp> +#include <signaldata/BackupSignalData.hpp> +#include <signaldata/ManagementServer.hpp> +#include <signaldata/NFCompleteRep.hpp> +#include <signaldata/NodeFailRep.hpp> +#include <signaldata/AllocNodeId.hpp> +#include <NdbSleep.h> +#include <EventLogger.hpp> +#include <DebuggerNames.hpp> +#include <ndb_version.h> + +#include <SocketServer.hpp> +#include <NdbConfig.h> + +#include <NdbAutoPtr.hpp> + +#include <ndberror.h> + +#include <mgmapi.h> +#include <mgmapi_configuration.hpp> +#include <mgmapi_config_parameters.h> +#include <m_string.h> + +#include <SignalSender.hpp> + +//#define MGM_SRV_DEBUG +#ifdef MGM_SRV_DEBUG +#define DEBUG(x) do ndbout << x << endl; while(0) +#else +#define DEBUG(x) +#endif + +#define INIT_SIGNAL_SENDER(ss,nodeId) \ + SignalSender ss(theFacade); \ + ss.lock(); /* lock will be released on exit */ \ + {\ + int result = okToSendTo(nodeId, true);\ + if (result != 0) {\ + return result;\ + }\ + } + +extern int g_no_nodeid_checks; +extern my_bool opt_core; + +static void require(bool v) +{ + if(!v) + { + if (opt_core) + abort(); + else + exit(-1); + } +} + +void * +MgmtSrvr::logLevelThread_C(void* m) +{ + MgmtSrvr *mgm = (MgmtSrvr*)m; + mgm->logLevelThreadRun(); + return 0; +} + +extern EventLogger g_eventLogger; + +static NdbOut& +operator<<(NdbOut& out, const LogLevel & ll) +{ + out << "[LogLevel: "; + for(size_t i = 0; i<LogLevel::LOGLEVEL_CATEGORIES; i++) + out << ll.getLogLevel((LogLevel::EventCategory)i) << " "; + out << "]"; + return out; +} + +void +MgmtSrvr::logLevelThreadRun() +{ + while (!_isStopThread) { + /** + * Handle started nodes + */ + m_started_nodes.lock(); + if (m_started_nodes.size() > 0) + { + // calculate max log level + EventSubscribeReq req; + { + LogLevel tmp; + m_event_listner.lock(); + for(int i = m_event_listner.m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_event_listner[i].m_logLevel); + m_event_listner.unlock(); + req = tmp; + } + req.blockRef = _ownReference; + while (m_started_nodes.size() > 0) + { + Uint32 node = m_started_nodes[0]; + m_started_nodes.erase(0, false); + m_started_nodes.unlock(); + + setEventReportingLevelImpl(node, req); + + SetLogLevelOrd ord; + ord = m_nodeLogLevel[node]; + setNodeLogLevelImpl(node, ord); + + m_started_nodes.lock(); + } + } + m_started_nodes.unlock(); + + m_log_level_requests.lock(); + while (m_log_level_requests.size() > 0) + { + EventSubscribeReq req = m_log_level_requests[0]; + m_log_level_requests.erase(0, false); + m_log_level_requests.unlock(); + + if(req.blockRef == 0){ + req.blockRef = _ownReference; + setEventReportingLevelImpl(0, req); + } else { + SetLogLevelOrd ord; + ord = req; + setNodeLogLevelImpl(req.blockRef, ord); + } + m_log_level_requests.lock(); + } + m_log_level_requests.unlock(); + NdbSleep_MilliSleep(_logLevelThreadSleep); + } +} + +void +MgmtSrvr::startEventLog() +{ + NdbMutex_Lock(m_configMutex); + + g_eventLogger.setCategory("MgmSrvr"); + + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + + if(iter.find(CFG_NODE_ID, _ownNodeId) != 0){ + NdbMutex_Unlock(m_configMutex); + return; + } + + const char * tmp; + char errStr[100]; + int err= 0; + BaseString logdest; + char *clusterLog= NdbConfig_ClusterLogFileName(_ownNodeId); + NdbAutoPtr<char> tmp_aptr(clusterLog); + + if(iter.get(CFG_LOG_DESTINATION, &tmp) == 0){ + logdest.assign(tmp); + } + NdbMutex_Unlock(m_configMutex); + + if(logdest.length() == 0 || logdest == "") { + logdest.assfmt("FILE:filename=%s,maxsize=1000000,maxfiles=6", + clusterLog); + } + errStr[0]='\0'; + if(!g_eventLogger.addHandler(logdest, &err, sizeof(errStr), errStr)) { + ndbout << "Warning: could not add log destination \"" + << logdest.c_str() << "\". Reason: "; + if(err) + ndbout << strerror(err); + if(err && errStr[0]!='\0') + ndbout << ", "; + if(errStr[0]!='\0') + ndbout << errStr; + ndbout << endl; + } +} + +void +MgmtSrvr::stopEventLog() +{ + // Nothing yet +} + +class ErrorItem +{ +public: + int _errorCode; + const char * _errorText; +}; + +bool +MgmtSrvr::setEventLogFilter(int severity, int enable) +{ + Logger::LoggerLevel level = (Logger::LoggerLevel)severity; + if (enable > 0) { + g_eventLogger.enable(level); + } else if (enable == 0) { + g_eventLogger.disable(level); + } else if (g_eventLogger.isEnable(level)) { + g_eventLogger.disable(level); + } else { + g_eventLogger.enable(level); + } + return g_eventLogger.isEnable(level); +} + +bool +MgmtSrvr::isEventLogFilterEnabled(int severity) +{ + return g_eventLogger.isEnable((Logger::LoggerLevel)severity); +} + +static ErrorItem errorTable[] = +{ + {MgmtSrvr::NO_CONTACT_WITH_PROCESS, "No contact with the process (dead ?)."}, + {MgmtSrvr::PROCESS_NOT_CONFIGURED, "The process is not configured."}, + {MgmtSrvr::WRONG_PROCESS_TYPE, + "The process has wrong type. Expected a DB process."}, + {MgmtSrvr::COULD_NOT_ALLOCATE_MEMORY, "Could not allocate memory."}, + {MgmtSrvr::SEND_OR_RECEIVE_FAILED, "Send to process or receive failed."}, + {MgmtSrvr::INVALID_LEVEL, "Invalid level. Should be between 1 and 30."}, + {MgmtSrvr::INVALID_ERROR_NUMBER, "Invalid error number. Should be >= 0."}, + {MgmtSrvr::INVALID_TRACE_NUMBER, "Invalid trace number."}, + {MgmtSrvr::NOT_IMPLEMENTED, "Not implemented."}, + {MgmtSrvr::INVALID_BLOCK_NAME, "Invalid block name"}, + + {MgmtSrvr::CONFIG_PARAM_NOT_EXIST, + "The configuration parameter does not exist for the process type."}, + {MgmtSrvr::CONFIG_PARAM_NOT_UPDATEABLE, + "The configuration parameter is not possible to update."}, + {MgmtSrvr::VALUE_WRONG_FORMAT_INT_EXPECTED, + "Incorrect value. Expected integer."}, + {MgmtSrvr::VALUE_TOO_LOW, "Value is too low."}, + {MgmtSrvr::VALUE_TOO_HIGH, "Value is too high."}, + {MgmtSrvr::VALUE_WRONG_FORMAT_BOOL_EXPECTED, + "Incorrect value. Expected TRUE or FALSE."}, + + {MgmtSrvr::CONFIG_FILE_OPEN_WRITE_ERROR, + "Could not open configuration file for writing."}, + {MgmtSrvr::CONFIG_FILE_OPEN_READ_ERROR, + "Could not open configuration file for reading."}, + {MgmtSrvr::CONFIG_FILE_WRITE_ERROR, + "Write error when writing configuration file."}, + {MgmtSrvr::CONFIG_FILE_READ_ERROR, + "Read error when reading configuration file."}, + {MgmtSrvr::CONFIG_FILE_CLOSE_ERROR, "Could not close configuration file."}, + + {MgmtSrvr::CONFIG_CHANGE_REFUSED_BY_RECEIVER, + "The change was refused by the receiving process."}, + {MgmtSrvr::COULD_NOT_SYNC_CONFIG_CHANGE_AGAINST_PHYSICAL_MEDIUM, + "The change could not be synced against physical medium."}, + {MgmtSrvr::CONFIG_FILE_CHECKSUM_ERROR, + "The config file is corrupt. Checksum error."}, + {MgmtSrvr::NOT_POSSIBLE_TO_SEND_CONFIG_UPDATE_TO_PROCESS_TYPE, + "It is not possible to send an update of a configuration variable " + "to this kind of process."}, + {MgmtSrvr::NODE_SHUTDOWN_IN_PROGESS, "Node shutdown in progress" }, + {MgmtSrvr::SYSTEM_SHUTDOWN_IN_PROGRESS, "System shutdown in progress" }, + {MgmtSrvr::NODE_SHUTDOWN_WOULD_CAUSE_SYSTEM_CRASH, + "Node shutdown would cause system crash" }, + {MgmtSrvr::UNSUPPORTED_NODE_SHUTDOWN, + "Unsupported multi node shutdown. Abort option required." }, + {MgmtSrvr::NODE_NOT_API_NODE, "The specified node is not an API node." }, + {MgmtSrvr::OPERATION_NOT_ALLOWED_START_STOP, + "Operation not allowed while nodes are starting or stopping."}, + {MgmtSrvr::NO_CONTACT_WITH_DB_NODES, "No contact with database nodes" } +}; + +int MgmtSrvr::translateStopRef(Uint32 errCode) +{ + switch(errCode){ + case StopRef::NodeShutdownInProgress: + return NODE_SHUTDOWN_IN_PROGESS; + break; + case StopRef::SystemShutdownInProgress: + return SYSTEM_SHUTDOWN_IN_PROGRESS; + break; + case StopRef::NodeShutdownWouldCauseSystemCrash: + return NODE_SHUTDOWN_WOULD_CAUSE_SYSTEM_CRASH; + break; + case StopRef::UnsupportedNodeShutdown: + return UNSUPPORTED_NODE_SHUTDOWN; + break; + } + return 4999; +} + +static int noOfErrorCodes = sizeof(errorTable) / sizeof(ErrorItem); + +int +MgmtSrvr::getNodeCount(enum ndb_mgm_node_type type) const +{ + int count = 0; + NodeId nodeId = 0; + + while (getNextNodeId(&nodeId, type)) { + count++; + } + return count; +} + +int +MgmtSrvr::getPort() const +{ + if(NdbMutex_Lock(m_configMutex)) + return 0; + + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + + if(iter.find(CFG_NODE_ID, getOwnNodeId()) != 0){ + ndbout << "Could not retrieve configuration for Node " + << getOwnNodeId() << " in config file." << endl + << "Have you set correct NodeId for this node?" << endl; + NdbMutex_Unlock(m_configMutex); + return 0; + } + + unsigned type; + if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0 || + type != NODE_TYPE_MGM){ + ndbout << "Local node id " << getOwnNodeId() + << " is not defined as management server" << endl + << "Have you set correct NodeId for this node?" << endl; + NdbMutex_Unlock(m_configMutex); + return 0; + } + + Uint32 port = 0; + if(iter.get(CFG_MGM_PORT, &port) != 0){ + ndbout << "Could not find PortNumber in the configuration file." << endl; + NdbMutex_Unlock(m_configMutex); + return 0; + } + + NdbMutex_Unlock(m_configMutex); + + return port; +} + +/* Constructor */ +int MgmtSrvr::init() +{ + if ( _ownNodeId > 0) + return 0; + return -1; +} + +MgmtSrvr::MgmtSrvr(SocketServer *socket_server, + const char *config_filename, + const char *connect_string) : + _blockNumber(1), // Hard coded block number since it makes it easy to send + // signals to other management servers. + m_socket_server(socket_server), + _ownReference(0), + theSignalIdleList(NULL), + theWaitState(WAIT_SUBSCRIBE_CONF), + m_local_mgm_handle(0), + m_event_listner(this), + m_master_node(0) +{ + + DBUG_ENTER("MgmtSrvr::MgmtSrvr"); + + _ownNodeId= 0; + + _config = NULL; + + _isStopThread = false; + _logLevelThread = NULL; + _logLevelThreadSleep = 500; + + theFacade = 0; + + m_newConfig = NULL; + if (config_filename) + m_configFilename.assign(config_filename); + + m_nextConfigGenerationNumber = 0; + + m_config_retriever= new ConfigRetriever(connect_string, + NDB_VERSION, NDB_MGM_NODE_TYPE_MGM); + // if connect_string explicitly given or + // no config filename is given then + // first try to allocate nodeid from another management server + if ((connect_string || config_filename == NULL) && + (m_config_retriever->do_connect(0,0,0) == 0)) + { + int tmp_nodeid= 0; + tmp_nodeid= m_config_retriever->allocNodeId(0 /*retry*/,0 /*delay*/); + if (tmp_nodeid == 0) + { + ndbout_c(m_config_retriever->getErrorString()); + require(false); + } + // read config from other managent server + _config= fetchConfig(); + if (_config == 0) + { + ndbout << m_config_retriever->getErrorString() << endl; + require(false); + } + _ownNodeId= tmp_nodeid; + } + + if (_ownNodeId == 0) + { + // read config locally + _config= readConfig(); + if (_config == 0) { + if (config_filename != NULL) + ndbout << "Invalid configuration file: " << config_filename << endl; + else + ndbout << "Invalid configuration file" << endl; + exit(-1); + } + } + + theMgmtWaitForResponseCondPtr = NdbCondition_Create(); + + m_configMutex = NdbMutex_Create(); + + /** + * Fill the nodeTypes array + */ + for(Uint32 i = 0; i<MAX_NODES; i++) { + nodeTypes[i] = (enum ndb_mgm_node_type)-1; + m_connect_address[i].s_addr= 0; + } + + { + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + + for(iter.first(); iter.valid(); iter.next()){ + unsigned type, id; + if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0) + continue; + + if(iter.get(CFG_NODE_ID, &id) != 0) + continue; + + MGM_REQUIRE(id < MAX_NODES); + + switch(type){ + case NODE_TYPE_DB: + nodeTypes[id] = NDB_MGM_NODE_TYPE_NDB; + break; + case NODE_TYPE_API: + nodeTypes[id] = NDB_MGM_NODE_TYPE_API; + break; + case NODE_TYPE_MGM: + nodeTypes[id] = NDB_MGM_NODE_TYPE_MGM; + break; + default: + break; + } + } + } + + _props = NULL; + BaseString error_string; + + if ((m_node_id_mutex = NdbMutex_Create()) == 0) + { + ndbout << "mutex creation failed line = " << __LINE__ << endl; + require(false); + } + + if (_ownNodeId == 0) // we did not get node id from other server + { + NodeId tmp= m_config_retriever->get_configuration_nodeid(); + int error_code; + + if (!alloc_node_id(&tmp, NDB_MGM_NODE_TYPE_MGM, + 0, 0, error_code, error_string)){ + ndbout << "Unable to obtain requested nodeid: " + << error_string.c_str() << endl; + require(false); + } + _ownNodeId = tmp; + } + + { + DBUG_PRINT("info", ("verifyConfig")); + if (!m_config_retriever->verifyConfig(_config->m_configValues, + _ownNodeId)) + { + ndbout << m_config_retriever->getErrorString() << endl; + require(false); + } + } + + // Setup clusterlog as client[0] in m_event_listner + { + Ndb_mgmd_event_service::Event_listener se; + se.m_socket = NDB_INVALID_SOCKET; + for(size_t t = 0; t<LogLevel::LOGLEVEL_CATEGORIES; t++){ + se.m_logLevel.setLogLevel((LogLevel::EventCategory)t, 7); + } + se.m_logLevel.setLogLevel(LogLevel::llError, 15); + se.m_logLevel.setLogLevel(LogLevel::llConnection, 8); + se.m_logLevel.setLogLevel(LogLevel::llBackup, 15); + m_event_listner.m_clients.push_back(se); + m_event_listner.m_logLevel = se.m_logLevel; + } + + DBUG_VOID_RETURN; +} + + +//**************************************************************************** +//**************************************************************************** +bool +MgmtSrvr::check_start() +{ + if (_config == 0) { + DEBUG("MgmtSrvr.cpp: _config is NULL."); + return false; + } + + return true; +} + +bool +MgmtSrvr::start(BaseString &error_string) +{ + int mgm_connect_result; + + DBUG_ENTER("MgmtSrvr::start"); + if (_props == NULL) { + if (!check_start()) { + error_string.append("MgmtSrvr.cpp: check_start() failed."); + DBUG_RETURN(false); + } + } + theFacade= new TransporterFacade(); + + if(theFacade == 0) { + DEBUG("MgmtSrvr.cpp: theFacade is NULL."); + error_string.append("MgmtSrvr.cpp: theFacade is NULL."); + DBUG_RETURN(false); + } + if ( theFacade->start_instance + (_ownNodeId, (ndb_mgm_configuration*)_config->m_configValues) < 0) { + DEBUG("MgmtSrvr.cpp: TransporterFacade::start_instance < 0."); + DBUG_RETURN(false); + } + + MGM_REQUIRE(_blockNumber == 1); + + // Register ourself at TransporterFacade to be able to receive signals + // and to be notified when a database process has died. + _blockNumber = theFacade->open(this, + signalReceivedNotification, + nodeStatusNotification); + + if(_blockNumber == -1){ + DEBUG("MgmtSrvr.cpp: _blockNumber is -1."); + error_string.append("MgmtSrvr.cpp: _blockNumber is -1."); + theFacade->stop_instance(); + theFacade = 0; + DBUG_RETURN(false); + } + + if((mgm_connect_result= connect_to_self()) < 0) + { + ndbout_c("Unable to connect to our own ndb_mgmd (Error %d)", + mgm_connect_result); + ndbout_c("This is probably a bug."); + } + + TransporterRegistry *reg = theFacade->get_registry(); + for(unsigned int i=0;i<reg->m_transporter_interface.size();i++) { + BaseString msg; + DBUG_PRINT("info",("Setting dynamic port %d->%d : %d", + reg->get_localNodeId(), + reg->m_transporter_interface[i].m_remote_nodeId, + reg->m_transporter_interface[i].m_s_service_port + ) + ); + int res = setConnectionDbParameter((int)reg->get_localNodeId(), + (int)reg->m_transporter_interface[i] + .m_remote_nodeId, + (int)CFG_CONNECTION_SERVER_PORT, + reg->m_transporter_interface[i] + .m_s_service_port, + msg); + DBUG_PRINT("info",("Set result: %d: %s",res,msg.c_str())); + } + + _ownReference = numberToRef(_blockNumber, _ownNodeId); + + startEventLog(); + // Set the initial confirmation count for subscribe requests confirm + // from NDB nodes in the cluster. + // + // Loglevel thread + _logLevelThread = NdbThread_Create(logLevelThread_C, + (void**)this, + 32768, + "MgmtSrvr_Loglevel", + NDB_THREAD_PRIO_LOW); + + DBUG_RETURN(true); +} + + +//**************************************************************************** +//**************************************************************************** +MgmtSrvr::~MgmtSrvr() +{ + if(theFacade != 0){ + theFacade->stop_instance(); + delete theFacade; + theFacade = 0; + } + + stopEventLog(); + + NdbMutex_Destroy(m_node_id_mutex); + NdbCondition_Destroy(theMgmtWaitForResponseCondPtr); + NdbMutex_Destroy(m_configMutex); + + if(m_newConfig != NULL) + free(m_newConfig); + + if(_config != NULL) + delete _config; + + // End set log level thread + void* res = 0; + _isStopThread = true; + + if (_logLevelThread != NULL) { + NdbThread_WaitFor(_logLevelThread, &res); + NdbThread_Destroy(&_logLevelThread); + } + + if (m_config_retriever) + delete m_config_retriever; +} + +//**************************************************************************** +//**************************************************************************** + +int MgmtSrvr::okToSendTo(NodeId nodeId, bool unCond) +{ + if(nodeId == 0 || getNodeType(nodeId) != NDB_MGM_NODE_TYPE_NDB) + return WRONG_PROCESS_TYPE; + // Check if we have contact with it + if(unCond){ + if(theFacade->theClusterMgr->getNodeInfo(nodeId).connected) + return 0; + } + else if (theFacade->get_node_alive(nodeId) == true) + return 0; + return NO_CONTACT_WITH_PROCESS; +} + +void report_unknown_signal(SimpleSignal *signal) +{ + g_eventLogger.error("Unknown signal received. SignalNumber: " + "%i from (%d, %x)", + signal->readSignalNumber(), + refToNode(signal->header.theSendersBlockRef), + refToBlock(signal->header.theSendersBlockRef)); +} + +/***************************************************************************** + * Starting and stopping database nodes + ****************************************************************************/ + +int +MgmtSrvr::start(int nodeId) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + StartOrd* const startOrd = CAST_PTR(StartOrd, ssig.getDataPtrSend()); + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_START_ORD, StartOrd::SignalLength); + startOrd->restartInfo = 0; + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + +/***************************************************************************** + * Version handling + *****************************************************************************/ + +int +MgmtSrvr::versionNode(int nodeId, Uint32 &version, const char **address) +{ + version= 0; + if (getOwnNodeId() == nodeId) + { + /** + * If we're inquiring about our own node id, + * We know what version we are (version implies connected for mgm) + * but would like to find out from elsewhere what address they're using + * to connect to us. This means that secondary mgm servers + * can list ip addresses for mgm servers. + * + * If we don't get an address (i.e. no db nodes), + * we get the address from the configuration. + */ + sendVersionReq(nodeId, version, address); + version= NDB_VERSION; + if(!*address) + { + ndb_mgm_configuration_iterator + iter(*_config->m_configValues, CFG_SECTION_NODE); + unsigned tmp= 0; + for(iter.first();iter.valid();iter.next()) + { + if(iter.get(CFG_NODE_ID, &tmp)) require(false); + if((unsigned)nodeId!=tmp) + continue; + if(iter.get(CFG_NODE_HOST, address)) require(false); + break; + } + } + } + else if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_NDB) + { + ClusterMgr::Node node= theFacade->theClusterMgr->getNodeInfo(nodeId); + if(node.connected) + version= node.m_info.m_version; + *address= get_connect_address(nodeId); + } + else if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_API || + getNodeType(nodeId) == NDB_MGM_NODE_TYPE_MGM) + { + return sendVersionReq(nodeId, version, address); + } + + return 0; +} + +int +MgmtSrvr::sendVersionReq(int v_nodeId, Uint32 &version, const char **address) +{ + SignalSender ss(theFacade); + ss.lock(); + + SimpleSignal ssig; + ApiVersionReq* req = CAST_PTR(ApiVersionReq, ssig.getDataPtrSend()); + req->senderRef = ss.getOwnRef(); + req->nodeId = v_nodeId; + ssig.set(ss, TestOrd::TraceAPI, QMGR, GSN_API_VERSION_REQ, + ApiVersionReq::SignalLength); + + int do_send = 1; + NodeId nodeId; + + while (1) + { + if (do_send) + { + bool next; + nodeId = 0; + + while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + okToSendTo(nodeId, true) != 0); + + const ClusterMgr::Node &node= + theFacade->theClusterMgr->getNodeInfo(nodeId); + if(next && node.m_state.startLevel != NodeState::SL_STARTED) + { + NodeId tmp=nodeId; + while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + okToSendTo(nodeId, true) != 0); + if(!next) + nodeId= tmp; + } + + if(!next) return NO_CONTACT_WITH_DB_NODES; + + if (ss.sendSignal(nodeId, &ssig) != SEND_OK) { + return SEND_OR_RECEIVE_FAILED; + } + do_send = 0; + } + + SimpleSignal *signal = ss.waitFor(); + + int gsn = signal->readSignalNumber(); + switch (gsn) { + case GSN_API_VERSION_CONF: { + const ApiVersionConf * const conf = + CAST_CONSTPTR(ApiVersionConf, signal->getDataPtr()); + assert((int) conf->nodeId == v_nodeId); + version = conf->version; + struct in_addr in; + in.s_addr= conf->inet_addr; + *address= inet_ntoa(in); + return 0; + } + case GSN_NF_COMPLETEREP:{ + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); + if (rep->failedNodeId == nodeId) + do_send = 1; // retry with other node + continue; + } + case GSN_NODE_FAILREP:{ + const NodeFailRep * const rep = + CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); + if (NodeBitmask::get(rep->theNodes,nodeId)) + do_send = 1; // retry with other node + continue; + } + default: + report_unknown_signal(signal); + return SEND_OR_RECEIVE_FAILED; + } + break; + } // while(1) + + return 0; +} + +int MgmtSrvr::sendStopMgmd(NodeId nodeId, + bool abort, + bool stop, + bool restart, + bool nostart, + bool initialStart) +{ + const char* hostname; + Uint32 port; + BaseString connect_string; + + { + Guard g(m_configMutex); + { + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + + if(iter.first()) return SEND_OR_RECEIVE_FAILED; + if(iter.find(CFG_NODE_ID, nodeId)) return SEND_OR_RECEIVE_FAILED; + if(iter.get(CFG_NODE_HOST, &hostname)) return SEND_OR_RECEIVE_FAILED; + } + { + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + + if(iter.first()) return SEND_OR_RECEIVE_FAILED; + if(iter.find(CFG_NODE_ID, nodeId)) return SEND_OR_RECEIVE_FAILED; + if(iter.get(CFG_MGM_PORT, &port)) return SEND_OR_RECEIVE_FAILED; + } + if( strlen(hostname) == 0 ) + return SEND_OR_RECEIVE_FAILED; + } + connect_string.assfmt("%s:%u",hostname,port); + + DBUG_PRINT("info",("connect string: %s",connect_string.c_str())); + + NdbMgmHandle h= ndb_mgm_create_handle(); + if ( h && connect_string.length() > 0 ) + { + ndb_mgm_set_connectstring(h,connect_string.c_str()); + if(ndb_mgm_connect(h,1,0,0)) + { + DBUG_PRINT("info",("failed ndb_mgm_connect")); + return SEND_OR_RECEIVE_FAILED; + } + if(!restart) + { + if(ndb_mgm_stop(h, 1, (const int*)&nodeId) < 0) + { + return SEND_OR_RECEIVE_FAILED; + } + } + else + { + int nodes[1]; + nodes[0]= (int)nodeId; + if(ndb_mgm_restart2(h, 1, nodes, initialStart, nostart, abort) < 0) + { + return SEND_OR_RECEIVE_FAILED; + } + } + } + ndb_mgm_destroy_handle(&h); + + return 0; +} + +/* + * Common method for handeling all STOP_REQ signalling that + * is used by Stopping, Restarting and Single user commands + * + * In the event that we need to stop a mgmd, we create a mgm + * client connection to that mgmd and stop it that way. + * This allows us to stop mgm servers when there isn't any real + * distributed communication up. + * + * node_ids.size()==0 means to stop all DB nodes. + * MGM nodes will *NOT* be stopped. + * + * If we work out we should be stopping or restarting ourselves, + * we return <0 in stopSelf for restart, >0 for stop + * and 0 for do nothing. + */ + +int MgmtSrvr::sendSTOP_REQ(const Vector<NodeId> &node_ids, + NodeBitmask &stoppedNodes, + Uint32 singleUserNodeId, + bool abort, + bool stop, + bool restart, + bool nostart, + bool initialStart, + int* stopSelf) +{ + int error = 0; + DBUG_ENTER("MgmtSrvr::sendSTOP_REQ"); + DBUG_PRINT("enter", ("no of nodes: %d singleUseNodeId: %d " + "abort: %d stop: %d restart: %d " + "nostart: %d initialStart: %d", + node_ids.size(), singleUserNodeId, + abort, stop, restart, nostart, initialStart)); + + stoppedNodes.clear(); + + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + + SimpleSignal ssig; + StopReq* const stopReq = CAST_PTR(StopReq, ssig.getDataPtrSend()); + ssig.set(ss, TestOrd::TraceAPI, NDBCNTR, GSN_STOP_REQ, StopReq::SignalLength); + + NdbNodeBitmask notstarted; + for (Uint32 i = 0; i<node_ids.size(); i++) + { + Uint32 nodeId = node_ids[i]; + ClusterMgr::Node node = theFacade->theClusterMgr->getNodeInfo(nodeId); + if (node.m_state.startLevel != NodeState::SL_STARTED) + notstarted.set(nodeId); + } + + stopReq->requestInfo = 0; + stopReq->apiTimeout = 5000; + stopReq->transactionTimeout = 1000; + stopReq->readOperationTimeout = 1000; + stopReq->operationTimeout = 1000; + stopReq->senderData = 12; + stopReq->senderRef = ss.getOwnRef(); + if (singleUserNodeId) + { + stopReq->singleuser = 1; + stopReq->singleUserApi = singleUserNodeId; + StopReq::setSystemStop(stopReq->requestInfo, false); + StopReq::setPerformRestart(stopReq->requestInfo, false); + StopReq::setStopAbort(stopReq->requestInfo, false); + } + else + { + stopReq->singleuser = 0; + StopReq::setSystemStop(stopReq->requestInfo, stop); + StopReq::setPerformRestart(stopReq->requestInfo, restart); + StopReq::setStopAbort(stopReq->requestInfo, abort); + StopReq::setNoStart(stopReq->requestInfo, nostart); + StopReq::setInitialStart(stopReq->requestInfo, initialStart); + } + + // send the signals + NodeBitmask nodes; + NodeId nodeId= 0; + int use_master_node= 0; + int do_send= 0; + *stopSelf= 0; + NdbNodeBitmask nodes_to_stop; + { + for (unsigned i= 0; i < node_ids.size(); i++) + { + nodeId= node_ids[i]; + ndbout << "asked to stop " << nodeId << endl; + + if ((getNodeType(nodeId) != NDB_MGM_NODE_TYPE_MGM) + &&(getNodeType(nodeId) != NDB_MGM_NODE_TYPE_NDB)) + return WRONG_PROCESS_TYPE; + + if (getNodeType(nodeId) != NDB_MGM_NODE_TYPE_MGM) + nodes_to_stop.set(nodeId); + else if (nodeId != getOwnNodeId()) + { + error= sendStopMgmd(nodeId, abort, stop, restart, + nostart, initialStart); + if (error == 0) + stoppedNodes.set(nodeId); + } + else + { + ndbout << "which is me" << endl; + *stopSelf= (restart)? -1 : 1; + stoppedNodes.set(nodeId); + } + } + } + int no_of_nodes_to_stop= nodes_to_stop.count(); + if (node_ids.size()) + { + if (no_of_nodes_to_stop) + { + do_send= 1; + if (no_of_nodes_to_stop == 1) + { + nodeId= nodes_to_stop.find(0); + } + else // multi node stop, send to master + { + use_master_node= 1; + nodes_to_stop.copyto(NdbNodeBitmask::Size, stopReq->nodes); + StopReq::setStopNodes(stopReq->requestInfo, 1); + } + } + } + else + { + nodeId= 0; + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) + { + if(okToSendTo(nodeId, true) == 0) + { + SendStatus result = ss.sendSignal(nodeId, &ssig); + if (result == SEND_OK) + nodes.set(nodeId); + } + } + } + + // now wait for the replies + while (!nodes.isclear() || do_send) + { + if (do_send) + { + int r; + assert(nodes.count() == 0); + if (use_master_node) + nodeId= m_master_node; + if ((r= okToSendTo(nodeId, true)) != 0) + { + bool next; + if (!use_master_node) + DBUG_RETURN(r); + m_master_node= nodeId= 0; + while((next= getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + (r= okToSendTo(nodeId, true)) != 0); + if (!next) + DBUG_RETURN(NO_CONTACT_WITH_DB_NODES); + } + if (ss.sendSignal(nodeId, &ssig) != SEND_OK) + DBUG_RETURN(SEND_OR_RECEIVE_FAILED); + nodes.set(nodeId); + do_send= 0; + } + SimpleSignal *signal = ss.waitFor(); + int gsn = signal->readSignalNumber(); + switch (gsn) { + case GSN_STOP_REF:{ + const StopRef * const ref = CAST_CONSTPTR(StopRef, signal->getDataPtr()); + const NodeId nodeId = refToNode(signal->header.theSendersBlockRef); +#ifdef VM_TRACE + ndbout_c("Node %d refused stop", nodeId); +#endif + assert(nodes.get(nodeId)); + nodes.clear(nodeId); + if (ref->errorCode == StopRef::MultiNodeShutdownNotMaster) + { + assert(use_master_node); + m_master_node= ref->masterNodeId; + do_send= 1; + continue; + } + error = translateStopRef(ref->errorCode); + break; + } + case GSN_STOP_CONF:{ + const StopConf * const ref = CAST_CONSTPTR(StopConf, signal->getDataPtr()); + const NodeId nodeId = refToNode(signal->header.theSendersBlockRef); +#ifdef VM_TRACE + ndbout_c("Node %d single user mode", nodeId); +#endif + assert(nodes.get(nodeId)); + if (singleUserNodeId != 0) + { + stoppedNodes.set(nodeId); + } + else + { + assert(no_of_nodes_to_stop > 1); + stoppedNodes.bitOR(nodes_to_stop); + } + nodes.clear(nodeId); + break; + } + case GSN_NF_COMPLETEREP:{ + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("sendSTOP_REQ Node %d fail completed", rep->failedNodeId); +#endif + nodes.clear(rep->failedNodeId); // clear the failed node + if (singleUserNodeId == 0) + stoppedNodes.set(rep->failedNodeId); + break; + } + case GSN_NODE_FAILREP:{ + const NodeFailRep * const rep = + CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); + NdbNodeBitmask mask; + char buf[100]; + mask.assign(NdbNodeBitmask::Size, rep->theNodes); + mask.bitAND(notstarted); + nodes.bitANDC(mask); + + if (singleUserNodeId == 0) + stoppedNodes.bitOR(mask); + break; + } + default: + report_unknown_signal(signal); +#ifdef VM_TRACE + ndbout_c("Unknown signal %d", gsn); +#endif + DBUG_RETURN(SEND_OR_RECEIVE_FAILED); + } + } + if (error && *stopSelf) + { + *stopSelf= 0; + } + DBUG_RETURN(error); +} + +/* + * Stop one nodes + */ + +int MgmtSrvr::stopNodes(const Vector<NodeId> &node_ids, + int *stopCount, bool abort, int* stopSelf) +{ + if (!abort) + { + NodeId nodeId = 0; + ClusterMgr::Node node; + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) + { + node = theFacade->theClusterMgr->getNodeInfo(nodeId); + if((node.m_state.startLevel != NodeState::SL_STARTED) && + (node.m_state.startLevel != NodeState::SL_NOTHING)) + return OPERATION_NOT_ALLOWED_START_STOP; + } + } + NodeBitmask nodes; + int ret= sendSTOP_REQ(node_ids, + nodes, + 0, + abort, + false, + false, + false, + false, + stopSelf); + if (stopCount) + *stopCount= nodes.count(); + return ret; +} + +int MgmtSrvr::shutdownMGM(int *stopCount, bool abort, int *stopSelf) +{ + NodeId nodeId = 0; + int error; + + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)) + { + if(nodeId==getOwnNodeId()) + continue; + error= sendStopMgmd(nodeId, abort, true, false, + false, false); + if (error == 0) + *stopCount++; + } + + *stopSelf= 1; + *stopCount++; + + return 0; +} + +/* + * Perform DB nodes shutdown. + * MGM servers are left in their current state + */ + +int MgmtSrvr::shutdownDB(int * stopCount, bool abort) +{ + NodeBitmask nodes; + Vector<NodeId> node_ids; + + int tmp; + + int ret = sendSTOP_REQ(node_ids, + nodes, + 0, + abort, + true, + false, + false, + false, + &tmp); + if (stopCount) + *stopCount = nodes.count(); + return ret; +} + +/* + * Enter single user mode on all live nodes + */ + +int MgmtSrvr::enterSingleUser(int * stopCount, Uint32 singleUserNodeId) +{ + if (getNodeType(singleUserNodeId) != NDB_MGM_NODE_TYPE_API) + return NODE_NOT_API_NODE; + NodeId nodeId = 0; + ClusterMgr::Node node; + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) + { + node = theFacade->theClusterMgr->getNodeInfo(nodeId); + if((node.m_state.startLevel != NodeState::SL_STARTED) && + (node.m_state.startLevel != NodeState::SL_NOTHING)) + return OPERATION_NOT_ALLOWED_START_STOP; + } + NodeBitmask nodes; + Vector<NodeId> node_ids; + int stopSelf; + int ret = sendSTOP_REQ(node_ids, + nodes, + singleUserNodeId, + false, + false, + false, + false, + false, + &stopSelf); + if (stopCount) + *stopCount = nodes.count(); + return ret; +} + +/* + * Perform node restart + */ + +int MgmtSrvr::restartNodes(const Vector<NodeId> &node_ids, + int * stopCount, bool nostart, + bool initialStart, bool abort, + int *stopSelf) +{ + NodeBitmask nodes; + int ret= sendSTOP_REQ(node_ids, + nodes, + 0, + abort, + false, + true, + true, + initialStart, + stopSelf); + + if (ret) + return ret; + + if (stopCount) + *stopCount = nodes.count(); + + // start up the nodes again + int waitTime = 12000; + NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime; + for (unsigned i = 0; i < node_ids.size(); i++) + { + NodeId nodeId= node_ids[i]; + enum ndb_mgm_node_status s; + s = NDB_MGM_NODE_STATUS_NO_CONTACT; +#ifdef VM_TRACE + ndbout_c("Waiting for %d not started", nodeId); +#endif + while (s != NDB_MGM_NODE_STATUS_NOT_STARTED && waitTime > 0) + { + Uint32 startPhase = 0, version = 0, dynamicId = 0, nodeGroup = 0; + Uint32 connectCount = 0; + bool system; + const char *address; + status(nodeId, &s, &version, &startPhase, + &system, &dynamicId, &nodeGroup, &connectCount, &address); + NdbSleep_MilliSleep(100); + waitTime = (maxTime - NdbTick_CurrentMillisecond()); + } + } + + if (nostart) + return 0; + + for (unsigned i = 0; i < node_ids.size(); i++) + { + int result = start(node_ids[i]); + } + return 0; +} + +/* + * Perform restart of all DB nodes + */ + +int MgmtSrvr::restartDB(bool nostart, bool initialStart, + bool abort, int * stopCount) +{ + NodeBitmask nodes; + Vector<NodeId> node_ids; + int tmp; + + int ret = sendSTOP_REQ(node_ids, + nodes, + 0, + abort, + true, + true, + true, + initialStart, + &tmp); + + if (ret) + return ret; + + if (stopCount) + *stopCount = nodes.count(); + +#ifdef VM_TRACE + ndbout_c("Stopped %d nodes", nodes.count()); +#endif + /** + * Here all nodes were correctly stopped, + * so we wait for all nodes to be contactable + */ + int waitTime = 12000; + NodeId nodeId = 0; + NDB_TICKS maxTime = NdbTick_CurrentMillisecond() + waitTime; + + ndbout_c(" %d", nodes.get(1)); + ndbout_c(" %d", nodes.get(2)); + + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) { + if (!nodes.get(nodeId)) + continue; + enum ndb_mgm_node_status s; + s = NDB_MGM_NODE_STATUS_NO_CONTACT; +#ifdef VM_TRACE + ndbout_c("Waiting for %d not started", nodeId); +#endif + while (s != NDB_MGM_NODE_STATUS_NOT_STARTED && waitTime > 0) { + Uint32 startPhase = 0, version = 0, dynamicId = 0, nodeGroup = 0; + Uint32 connectCount = 0; + bool system; + const char *address; + status(nodeId, &s, &version, &startPhase, + &system, &dynamicId, &nodeGroup, &connectCount, &address); + NdbSleep_MilliSleep(100); + waitTime = (maxTime - NdbTick_CurrentMillisecond()); + } + } + + if(nostart) + return 0; + + /** + * Now we start all database nodes (i.e. we make them non-idle) + * We ignore the result we get from the start command. + */ + nodeId = 0; + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) { + if (!nodes.get(nodeId)) + continue; + int result; + result = start(nodeId); + DEBUG("Starting node " << nodeId << " with result " << result); + /** + * Errors from this call are deliberately ignored. + * Maybe the user only wanted to restart a subset of the nodes. + * It is also easy for the user to check which nodes have + * started and which nodes have not. + */ + } + + return 0; +} + +int +MgmtSrvr::exitSingleUser(int * stopCount, bool abort) +{ + NodeId nodeId = 0; + int count = 0; + + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + + SimpleSignal ssig; + ResumeReq* const resumeReq = + CAST_PTR(ResumeReq, ssig.getDataPtrSend()); + ssig.set(ss,TestOrd::TraceAPI, NDBCNTR, GSN_RESUME_REQ, + ResumeReq::SignalLength); + resumeReq->senderData = 12; + resumeReq->senderRef = ss.getOwnRef(); + + while(getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){ + if(okToSendTo(nodeId, true) == 0){ + SendStatus result = ss.sendSignal(nodeId, &ssig); + if (result == SEND_OK) + count++; + } + } + + if(stopCount != 0) + * stopCount = count; + + return 0; +} + +/***************************************************************************** + * Status + ****************************************************************************/ + +#include <ClusterMgr.hpp> + +void +MgmtSrvr::updateStatus() +{ + theFacade->theClusterMgr->forceHB(); +} + +int +MgmtSrvr::status(int nodeId, + ndb_mgm_node_status * _status, + Uint32 * version, + Uint32 * _phase, + bool * _system, + Uint32 * dynamic, + Uint32 * nodegroup, + Uint32 * connectCount, + const char **address) +{ + if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_API || + getNodeType(nodeId) == NDB_MGM_NODE_TYPE_MGM) { + versionNode(nodeId, *version, address); + } else { + *address= get_connect_address(nodeId); + } + + const ClusterMgr::Node node = + theFacade->theClusterMgr->getNodeInfo(nodeId); + + if(!node.connected){ + * _status = NDB_MGM_NODE_STATUS_NO_CONTACT; + return 0; + } + + if (getNodeType(nodeId) == NDB_MGM_NODE_TYPE_NDB) { + * version = node.m_info.m_version; + } + + * dynamic = node.m_state.dynamicId; + * nodegroup = node.m_state.nodeGroup; + * connectCount = node.m_info.m_connectCount; + + switch(node.m_state.startLevel){ + case NodeState::SL_CMVMI: + * _status = NDB_MGM_NODE_STATUS_NOT_STARTED; + * _phase = 0; + return 0; + break; + case NodeState::SL_STARTING: + * _status = NDB_MGM_NODE_STATUS_STARTING; + * _phase = node.m_state.starting.startPhase; + return 0; + break; + case NodeState::SL_STARTED: + * _status = NDB_MGM_NODE_STATUS_STARTED; + * _phase = 0; + return 0; + break; + case NodeState::SL_STOPPING_1: + * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN; + * _phase = 1; + * _system = node.m_state.stopping.systemShutdown != 0; + return 0; + break; + case NodeState::SL_STOPPING_2: + * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN; + * _phase = 2; + * _system = node.m_state.stopping.systemShutdown != 0; + return 0; + break; + case NodeState::SL_STOPPING_3: + * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN; + * _phase = 3; + * _system = node.m_state.stopping.systemShutdown != 0; + return 0; + break; + case NodeState::SL_STOPPING_4: + * _status = NDB_MGM_NODE_STATUS_SHUTTING_DOWN; + * _phase = 4; + * _system = node.m_state.stopping.systemShutdown != 0; + return 0; + break; + case NodeState::SL_SINGLEUSER: + * _status = NDB_MGM_NODE_STATUS_SINGLEUSER; + * _phase = 0; + return 0; + break; + default: + * _status = NDB_MGM_NODE_STATUS_UNKNOWN; + * _phase = 0; + return 0; + } + + return -1; +} + +int +MgmtSrvr::setEventReportingLevelImpl(int nodeId, + const EventSubscribeReq& ll) +{ + SignalSender ss(theFacade); + ss.lock(); + + SimpleSignal ssig; + EventSubscribeReq * dst = + CAST_PTR(EventSubscribeReq, ssig.getDataPtrSend()); + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_EVENT_SUBSCRIBE_REQ, + EventSubscribeReq::SignalLength); + *dst = ll; + + NodeBitmask nodes; + nodes.clear(); + Uint32 max = (nodeId == 0) ? (nodeId = 1, MAX_NDB_NODES) : nodeId; + for(; (Uint32) nodeId <= max; nodeId++) + { + if (nodeTypes[nodeId] != NODE_TYPE_DB) + continue; + if (okToSendTo(nodeId, true)) + continue; + if (ss.sendSignal(nodeId, &ssig) == SEND_OK) + { + nodes.set(nodeId); + } + } + + int error = 0; + while (!nodes.isclear()) + { + SimpleSignal *signal = ss.waitFor(); + int gsn = signal->readSignalNumber(); + nodeId = refToNode(signal->header.theSendersBlockRef); + switch (gsn) { + case GSN_EVENT_SUBSCRIBE_CONF:{ + nodes.clear(nodeId); + break; + } + case GSN_EVENT_SUBSCRIBE_REF:{ + nodes.clear(nodeId); + error = 1; + break; + } + case GSN_NF_COMPLETEREP:{ + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); + nodes.clear(rep->failedNodeId); + break; + } + case GSN_NODE_FAILREP:{ + // ignore, NF_COMPLETEREP will arrive later + break; + } + default: + report_unknown_signal(signal); + return SEND_OR_RECEIVE_FAILED; + } + } + if (error) + return SEND_OR_RECEIVE_FAILED; + return 0; +} + +//**************************************************************************** +//**************************************************************************** +int +MgmtSrvr::setNodeLogLevelImpl(int nodeId, const SetLogLevelOrd & ll) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_SET_LOGLEVELORD, + SetLogLevelOrd::SignalLength); + SetLogLevelOrd* const dst = CAST_PTR(SetLogLevelOrd, ssig.getDataPtrSend()); + *dst = ll; + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + +//**************************************************************************** +//**************************************************************************** + +int +MgmtSrvr::insertError(int nodeId, int errorNo) +{ + if (errorNo < 0) { + return INVALID_ERROR_NUMBER; + } + + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_TAMPER_ORD, + TamperOrd::SignalLength); + TamperOrd* const tamperOrd = CAST_PTR(TamperOrd, ssig.getDataPtrSend()); + tamperOrd->errorNo = errorNo; + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + + + +//**************************************************************************** +//**************************************************************************** + +int +MgmtSrvr::setTraceNo(int nodeId, int traceNo) +{ + if (traceNo < 0) { + return INVALID_TRACE_NUMBER; + } + + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength); + TestOrd* const testOrd = CAST_PTR(TestOrd, ssig.getDataPtrSend()); + testOrd->clear(); + // Assume TRACE command causes toggling. Not really defined... ? TODO + testOrd->setTraceCommand(TestOrd::Toggle, + (TestOrd::TraceSpecification)traceNo); + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + +//**************************************************************************** +//**************************************************************************** + +int +MgmtSrvr::getBlockNumber(const BaseString &blockName) +{ + short bno = getBlockNo(blockName.c_str()); + if(bno != 0) + return bno; + return -1; +} + +//**************************************************************************** +//**************************************************************************** + +int +MgmtSrvr::setSignalLoggingMode(int nodeId, LogMode mode, + const Vector<BaseString>& blocks) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + // Convert from MgmtSrvr format... + + TestOrd::Command command; + if (mode == Off) { + command = TestOrd::Off; + } + else { + command = TestOrd::On; + } + + TestOrd::SignalLoggerSpecification logSpec; + switch (mode) { + case In: + logSpec = TestOrd::InputSignals; + break; + case Out: + logSpec = TestOrd::OutputSignals; + break; + case InOut: + logSpec = TestOrd::InputOutputSignals; + break; + case Off: + // In MgmtSrvr interface it's just possible to switch off all logging, both + // "in" and "out" (this should probably be changed). + logSpec = TestOrd::InputOutputSignals; + break; + default: + ndbout_c("Unexpected value %d, MgmtSrvr::setSignalLoggingMode, line %d", + (unsigned)mode, __LINE__); + assert(false); + return -1; + } + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength); + + TestOrd* const testOrd = CAST_PTR(TestOrd, ssig.getDataPtrSend()); + testOrd->clear(); + + if (blocks.size() == 0 || blocks[0] == "ALL") { + // Logg command for all blocks + testOrd->addSignalLoggerCommand(command, logSpec); + } else { + for(unsigned i = 0; i < blocks.size(); i++){ + int blockNumber = getBlockNumber(blocks[i]); + if (blockNumber == -1) { + return INVALID_BLOCK_NAME; + } + testOrd->addSignalLoggerCommand(blockNumber, command, logSpec); + } // for + } // else + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + +/***************************************************************************** + * Signal tracing + *****************************************************************************/ +int MgmtSrvr::startSignalTracing(int nodeId) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength); + + TestOrd* const testOrd = CAST_PTR(TestOrd, ssig.getDataPtrSend()); + testOrd->clear(); + testOrd->setTestCommand(TestOrd::On); + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + +int +MgmtSrvr::stopSignalTracing(int nodeId) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + SimpleSignal ssig; + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_TEST_ORD, TestOrd::SignalLength); + TestOrd* const testOrd = CAST_PTR(TestOrd, ssig.getDataPtrSend()); + testOrd->clear(); + testOrd->setTestCommand(TestOrd::Off); + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + + +/***************************************************************************** + * Dump state + *****************************************************************************/ + +int +MgmtSrvr::dumpState(int nodeId, const char* args) +{ + // Convert the space separeted args + // string to an int array + Uint32 args_array[25]; + Uint32 numArgs = 0; + + char buf[10]; + int b = 0; + memset(buf, 0, 10); + for (size_t i = 0; i <= strlen(args); i++){ + if (args[i] == ' ' || args[i] == 0){ + args_array[numArgs] = atoi(buf); + numArgs++; + memset(buf, 0, 10); + b = 0; + } else { + buf[b] = args[i]; + b++; + } + } + + return dumpState(nodeId, args_array, numArgs); +} + +int +MgmtSrvr::dumpState(int nodeId, const Uint32 args[], Uint32 no) +{ + INIT_SIGNAL_SENDER(ss,nodeId); + + const Uint32 len = no > 25 ? 25 : no; + + SimpleSignal ssig; + DumpStateOrd * const dumpOrd = + CAST_PTR(DumpStateOrd, ssig.getDataPtrSend()); + ssig.set(ss,TestOrd::TraceAPI, CMVMI, GSN_DUMP_STATE_ORD, len); + for(Uint32 i = 0; i<25; i++){ + if (i < len) + dumpOrd->args[i] = args[i]; + else + dumpOrd->args[i] = 0; + } + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + + +//**************************************************************************** +//**************************************************************************** + +const char* MgmtSrvr::getErrorText(int errorCode, char *buf, int buf_sz) +{ + + for (int i = 0; i < noOfErrorCodes; ++i) { + if (errorCode == errorTable[i]._errorCode) { + BaseString::snprintf(buf, buf_sz, errorTable[i]._errorText); + buf[buf_sz-1]= 0; + return buf; + } + } + + ndb_error_string(errorCode, buf, buf_sz); + buf[buf_sz-1]= 0; + + return buf; +} + +void +MgmtSrvr::handleReceivedSignal(NdbApiSignal* signal) +{ + // The way of handling a received signal is taken from the Ndb class. + int gsn = signal->readSignalNumber(); + + switch (gsn) { + case GSN_EVENT_SUBSCRIBE_CONF: + break; + case GSN_EVENT_SUBSCRIBE_REF: + break; + case GSN_EVENT_REP: + { + eventReport(signal->getDataPtr()); + break; + } + + case GSN_NF_COMPLETEREP: + break; + case GSN_NODE_FAILREP: + break; + + default: + g_eventLogger.error("Unknown signal received. SignalNumber: " + "%i from (%d, %x)", + gsn, + refToNode(signal->theSendersBlockRef), + refToBlock(signal->theSendersBlockRef)); + } + + if (theWaitState == NO_WAIT) { + NdbCondition_Signal(theMgmtWaitForResponseCondPtr); + } +} + +void +MgmtSrvr::handleStatus(NodeId nodeId, bool alive, bool nfComplete) +{ + DBUG_ENTER("MgmtSrvr::handleStatus"); + Uint32 theData[25]; + EventReport *rep = (EventReport *)theData; + + theData[1] = nodeId; + if (alive) { + m_started_nodes.push_back(nodeId); + rep->setEventType(NDB_LE_Connected); + } else { + rep->setEventType(NDB_LE_Connected); + if(nfComplete) + { + DBUG_VOID_RETURN; + } + } + rep->setNodeId(_ownNodeId); + eventReport(theData); + DBUG_VOID_RETURN; +} + +//**************************************************************************** +//**************************************************************************** + +void +MgmtSrvr::signalReceivedNotification(void* mgmtSrvr, + NdbApiSignal* signal, + LinearSectionPtr ptr[3]) +{ + ((MgmtSrvr*)mgmtSrvr)->handleReceivedSignal(signal); +} + + +//**************************************************************************** +//**************************************************************************** +void +MgmtSrvr::nodeStatusNotification(void* mgmSrv, Uint32 nodeId, + bool alive, bool nfComplete) +{ + DBUG_ENTER("MgmtSrvr::nodeStatusNotification"); + DBUG_PRINT("enter",("nodeid= %d, alive= %d, nfComplete= %d", nodeId, alive, nfComplete)); + ((MgmtSrvr*)mgmSrv)->handleStatus(nodeId, alive, nfComplete); + DBUG_VOID_RETURN; +} + +enum ndb_mgm_node_type +MgmtSrvr::getNodeType(NodeId nodeId) const +{ + if(nodeId >= MAX_NODES) + return (enum ndb_mgm_node_type)-1; + + return nodeTypes[nodeId]; +} + +const char *MgmtSrvr::get_connect_address(Uint32 node_id) +{ + if (m_connect_address[node_id].s_addr == 0 && + theFacade && theFacade->theTransporterRegistry && + theFacade->theClusterMgr && + getNodeType(node_id) == NDB_MGM_NODE_TYPE_NDB) + { + const ClusterMgr::Node &node= + theFacade->theClusterMgr->getNodeInfo(node_id); + if (node.connected) + { + m_connect_address[node_id]= + theFacade->theTransporterRegistry->get_connect_address(node_id); + } + } + return inet_ntoa(m_connect_address[node_id]); +} + +void +MgmtSrvr::get_connected_nodes(NodeBitmask &connected_nodes) const +{ + if (theFacade && theFacade->theClusterMgr) + { + for(Uint32 i = 0; i < MAX_NODES; i++) + { + if (getNodeType(i) == NDB_MGM_NODE_TYPE_NDB) + { + const ClusterMgr::Node &node= theFacade->theClusterMgr->getNodeInfo(i); + connected_nodes.bitOR(node.m_state.m_connected_nodes); + } + } + } +} + +int +MgmtSrvr::alloc_node_id_req(NodeId free_node_id, enum ndb_mgm_node_type type) +{ + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + + SimpleSignal ssig; + AllocNodeIdReq* req = CAST_PTR(AllocNodeIdReq, ssig.getDataPtrSend()); + ssig.set(ss, TestOrd::TraceAPI, QMGR, GSN_ALLOC_NODEID_REQ, + AllocNodeIdReq::SignalLength); + + req->senderRef = ss.getOwnRef(); + req->senderData = 19; + req->nodeId = free_node_id; + req->nodeType = type; + + int do_send = 1; + NodeId nodeId = 0; + while (1) + { + if (nodeId == 0) + { + bool next; + while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + theFacade->get_node_alive(nodeId) == false); + if (!next) + return NO_CONTACT_WITH_DB_NODES; + do_send = 1; + } + if (do_send) + { + if (ss.sendSignal(nodeId, &ssig) != SEND_OK) { + return SEND_OR_RECEIVE_FAILED; + } + do_send = 0; + } + + SimpleSignal *signal = ss.waitFor(); + + int gsn = signal->readSignalNumber(); + switch (gsn) { + case GSN_ALLOC_NODEID_CONF: + { + const AllocNodeIdConf * const conf = + CAST_CONSTPTR(AllocNodeIdConf, signal->getDataPtr()); + return 0; + } + case GSN_ALLOC_NODEID_REF: + { + const AllocNodeIdRef * const ref = + CAST_CONSTPTR(AllocNodeIdRef, signal->getDataPtr()); + if (ref->errorCode == AllocNodeIdRef::NotMaster || + ref->errorCode == AllocNodeIdRef::Busy) + { + do_send = 1; + nodeId = refToNode(ref->masterRef); + continue; + } + return ref->errorCode; + } + case GSN_NF_COMPLETEREP: + { + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Node %d fail completed", rep->failedNodeId); +#endif + if (rep->failedNodeId == nodeId) + { + do_send = 1; + nodeId = 0; + } + continue; + } + case GSN_NODE_FAILREP:{ + // ignore NF_COMPLETEREP will come + continue; + } + default: + report_unknown_signal(signal); + return SEND_OR_RECEIVE_FAILED; + } + } + return 0; +} + +bool +MgmtSrvr::alloc_node_id(NodeId * nodeId, + enum ndb_mgm_node_type type, + struct sockaddr *client_addr, + SOCKET_SIZE_TYPE *client_addr_len, + int &error_code, BaseString &error_string, + int log_event) +{ + DBUG_ENTER("MgmtSrvr::alloc_node_id"); + DBUG_PRINT("enter", ("nodeid: %d type: %d client_addr: 0x%ld", + *nodeId, type, (long) client_addr)); + if (g_no_nodeid_checks) { + if (*nodeId == 0) { + error_string.appfmt("no-nodeid-checks set in management server.\n" + "node id must be set explicitly in connectstring"); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + DBUG_RETURN(false); + } + DBUG_RETURN(true); + } + Guard g(m_node_id_mutex); + int no_mgm= 0; + NodeBitmask connected_nodes(m_reserved_nodes); + get_connected_nodes(connected_nodes); + { + for(Uint32 i = 0; i < MAX_NODES; i++) + if (getNodeType(i) == NDB_MGM_NODE_TYPE_MGM) + no_mgm++; + } + bool found_matching_id= false; + bool found_matching_type= false; + bool found_free_node= false; + unsigned id_found= 0; + const char *config_hostname= 0; + struct in_addr config_addr= {0}; + int r_config_addr= -1; + unsigned type_c= 0; + + if(NdbMutex_Lock(m_configMutex)) + { + // should not happen + error_string.appfmt("unable to lock configuration mutex"); + error_code = NDB_MGM_ALLOCID_ERROR; + DBUG_RETURN(false); + } + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + for(iter.first(); iter.valid(); iter.next()) { + unsigned tmp= 0; + if(iter.get(CFG_NODE_ID, &tmp)) require(false); + if (*nodeId && *nodeId != tmp) + continue; + found_matching_id= true; + if(iter.get(CFG_TYPE_OF_SECTION, &type_c)) require(false); + if(type_c != (unsigned)type) + continue; + found_matching_type= true; + if (connected_nodes.get(tmp)) + continue; + found_free_node= true; + if(iter.get(CFG_NODE_HOST, &config_hostname)) require(false); + if (config_hostname && config_hostname[0] == 0) + config_hostname= 0; + else if (client_addr) { + // check hostname compatability + const void *tmp_in= &(((sockaddr_in*)client_addr)->sin_addr); + if((r_config_addr= Ndb_getInAddr(&config_addr, config_hostname)) != 0 + || memcmp(&config_addr, tmp_in, sizeof(config_addr)) != 0) { + struct in_addr tmp_addr; + if(Ndb_getInAddr(&tmp_addr, "localhost") != 0 + || memcmp(&tmp_addr, tmp_in, sizeof(config_addr)) != 0) { + // not localhost +#if 0 + ndbout << "MgmtSrvr::getFreeNodeId compare failed for \"" + << config_hostname + << "\" id=" << tmp << endl; +#endif + continue; + } + // connecting through localhost + // check if config_hostname is local + if (!SocketServer::tryBind(0,config_hostname)) { + continue; + } + } + } else { // client_addr == 0 + if (!SocketServer::tryBind(0,config_hostname)) { + continue; + } + } + if (*nodeId != 0 || + type != NDB_MGM_NODE_TYPE_MGM || + no_mgm == 1) { // any match is ok + + if (config_hostname == 0 && + *nodeId == 0 && + type != NDB_MGM_NODE_TYPE_MGM) + { + if (!id_found) // only set if not set earlier + id_found= tmp; + continue; /* continue looking for a nodeid with specified + * hostname + */ + } + assert(id_found == 0); + id_found= tmp; + break; + } + if (id_found) { // mgmt server may only have one match + error_string.appfmt("Ambiguous node id's %d and %d.\n" + "Suggest specifying node id in connectstring,\n" + "or specifying unique host names in config file.", + id_found, tmp); + NdbMutex_Unlock(m_configMutex); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + DBUG_RETURN(false); + } + if (config_hostname == 0) { + error_string.appfmt("Ambiguity for node id %d.\n" + "Suggest specifying node id in connectstring,\n" + "or specifying unique host names in config file,\n" + "or specifying just one mgmt server in config file.", + tmp); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + DBUG_RETURN(false); + } + id_found= tmp; // mgmt server matched, check for more matches + } + NdbMutex_Unlock(m_configMutex); + + if (id_found && client_addr != 0) + { + int res = alloc_node_id_req(id_found, type); + unsigned save_id_found = id_found; + switch (res) + { + case 0: + // ok continue + break; + case NO_CONTACT_WITH_DB_NODES: + // ok continue + break; + default: + // something wrong + id_found = 0; + break; + + } + if (id_found == 0) + { + char buf[128]; + ndb_error_string(res, buf, sizeof(buf)); + error_string.appfmt("Cluster refused allocation of id %d. Error: %d (%s).", + save_id_found, res, buf); + g_eventLogger.warning("Cluster refused allocation of id %d. " + "Connection from ip %s. " + "Returned error string \"%s\"", save_id_found, + inet_ntoa(((struct sockaddr_in *)(client_addr))->sin_addr), + error_string.c_str()); + DBUG_RETURN(false); + } + } + + if (id_found) + { + *nodeId= id_found; + DBUG_PRINT("info", ("allocating node id %d",*nodeId)); + { + int r= 0; + if (client_addr) + m_connect_address[id_found]= + ((struct sockaddr_in *)client_addr)->sin_addr; + else if (config_hostname) + r= Ndb_getInAddr(&(m_connect_address[id_found]), config_hostname); + else { + char name[256]; + r= gethostname(name, sizeof(name)); + if (r == 0) { + name[sizeof(name)-1]= 0; + r= Ndb_getInAddr(&(m_connect_address[id_found]), name); + } + } + if (r) + m_connect_address[id_found].s_addr= 0; + } + m_reserved_nodes.set(id_found); + if (theFacade && id_found != theFacade->ownId()) + { + /** + * Make sure we're ready to accept connections from this node + */ + theFacade->lock_mutex(); + theFacade->doConnect(id_found); + theFacade->unlock_mutex(); + } + + char tmp_str[128]; + m_reserved_nodes.getText(tmp_str); + g_eventLogger.info("Mgmt server state: nodeid %d reserved for ip %s, " + "m_reserved_nodes %s.", + id_found, get_connect_address(id_found), tmp_str); + DBUG_RETURN(true); + } + + if (found_matching_type && !found_free_node) { + // we have a temporary error which might be due to that + // we have got the latest connect status from db-nodes. Force update. + updateStatus(); + } + + BaseString type_string, type_c_string; + { + const char *alias, *str; + alias= ndb_mgm_get_node_type_alias_string(type, &str); + type_string.assfmt("%s(%s)", alias, str); + alias= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type)type_c, + &str); + type_c_string.assfmt("%s(%s)", alias, str); + } + + if (*nodeId == 0) + { + if (found_matching_id) + { + if (found_matching_type) + { + if (found_free_node) + { + error_string.appfmt("Connection done from wrong host ip %s.", + (client_addr)? + inet_ntoa(((struct sockaddr_in *) + (client_addr))->sin_addr):""); + error_code = NDB_MGM_ALLOCID_ERROR; + } + else + { + error_string.appfmt("No free node id found for %s.", + type_string.c_str()); + error_code = NDB_MGM_ALLOCID_ERROR; + } + } + else + { + error_string.appfmt("No %s node defined in config file.", + type_string.c_str()); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + } + } + else + { + error_string.append("No nodes defined in config file."); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + } + } + else + { + if (found_matching_id) + { + if (found_matching_type) + { + if (found_free_node) + { + // have to split these into two since inet_ntoa overwrites itself + error_string.appfmt("Connection with id %d done from wrong host ip %s,", + *nodeId, inet_ntoa(((struct sockaddr_in *) + (client_addr))->sin_addr)); + error_string.appfmt(" expected %s(%s).", config_hostname, + r_config_addr ? + "lookup failed" : inet_ntoa(config_addr)); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + } + else + { + error_string.appfmt("Id %d already allocated by another node.", + *nodeId); + error_code = NDB_MGM_ALLOCID_ERROR; + } + } + else + { + error_string.appfmt("Id %d configured as %s, connect attempted as %s.", + *nodeId, type_c_string.c_str(), + type_string.c_str()); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + } + } + else + { + error_string.appfmt("No node defined with id=%d in config file.", + *nodeId); + error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH; + } + } + + if (log_event || error_code == NDB_MGM_ALLOCID_CONFIG_MISMATCH) + { + g_eventLogger.warning("Allocate nodeid (%d) failed. Connection from ip %s." + " Returned error string \"%s\"", + *nodeId, + client_addr != 0 + ? inet_ntoa(((struct sockaddr_in *) + (client_addr))->sin_addr) + : "<none>", + error_string.c_str()); + + NodeBitmask connected_nodes2; + get_connected_nodes(connected_nodes2); + BaseString tmp_connected, tmp_not_connected; + for(Uint32 i = 0; i < MAX_NODES; i++) + { + if (connected_nodes2.get(i)) + { + if (!m_reserved_nodes.get(i)) + tmp_connected.appfmt(" %d", i); + } + else if (m_reserved_nodes.get(i)) + { + tmp_not_connected.appfmt(" %d", i); + } + } + if (tmp_connected.length() > 0) + g_eventLogger.info("Mgmt server state: node id's %s connected but not reserved", + tmp_connected.c_str()); + if (tmp_not_connected.length() > 0) + g_eventLogger.info("Mgmt server state: node id's %s not connected but reserved", + tmp_not_connected.c_str()); + } + DBUG_RETURN(false); +} + +bool +MgmtSrvr::getNextNodeId(NodeId * nodeId, enum ndb_mgm_node_type type) const +{ + NodeId tmp = * nodeId; + + tmp++; + while(nodeTypes[tmp] != type && tmp < MAX_NODES) + tmp++; + + if(tmp == MAX_NODES){ + return false; + } + + * nodeId = tmp; + return true; +} + +#include "Services.hpp" + +void +MgmtSrvr::eventReport(const Uint32 * theData) +{ + const EventReport * const eventReport = (EventReport *)&theData[0]; + + NodeId nodeId = eventReport->getNodeId(); + Ndb_logevent_type type = eventReport->getEventType(); + // Log event + g_eventLogger.log(type, theData, nodeId, + &m_event_listner[0].m_logLevel); + m_event_listner.log(type, theData, nodeId); +} + +/*************************************************************************** + * Backup + ***************************************************************************/ + +int +MgmtSrvr::startBackup(Uint32& backupId, int waitCompleted) +{ + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + + NodeId nodeId = m_master_node; + if (okToSendTo(nodeId, false) != 0) + { + bool next; + nodeId = m_master_node = 0; + while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + okToSendTo(nodeId, false) != 0); + if(!next) + return NO_CONTACT_WITH_DB_NODES; + } + + SimpleSignal ssig; + BackupReq* req = CAST_PTR(BackupReq, ssig.getDataPtrSend()); + ssig.set(ss, TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ, + BackupReq::SignalLength); + + req->senderData = 19; + req->backupDataLen = 0; + assert(waitCompleted < 3); + req->flags = waitCompleted & 0x3; + + BackupEvent event; + int do_send = 1; + while (1) { + if (do_send) + { + if (ss.sendSignal(nodeId, &ssig) != SEND_OK) { + return SEND_OR_RECEIVE_FAILED; + } + if (waitCompleted == 0) + return 0; + do_send = 0; + } + SimpleSignal *signal = ss.waitFor(); + + int gsn = signal->readSignalNumber(); + switch (gsn) { + case GSN_BACKUP_CONF:{ + const BackupConf * const conf = + CAST_CONSTPTR(BackupConf, signal->getDataPtr()); + event.Event = BackupEvent::BackupStarted; + event.Started.BackupId = conf->backupId; + event.Nodes = conf->nodes; +#ifdef VM_TRACE + ndbout_c("Backup(%d) master is %d", conf->backupId, + refToNode(signal->header.theSendersBlockRef)); +#endif + backupId = conf->backupId; + if (waitCompleted == 1) + return 0; + // wait for next signal + break; + } + case GSN_BACKUP_COMPLETE_REP:{ + const BackupCompleteRep * const rep = + CAST_CONSTPTR(BackupCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Backup(%d) completed", rep->backupId); +#endif + event.Event = BackupEvent::BackupCompleted; + event.Completed.BackupId = rep->backupId; + + event.Completed.NoOfBytes = rep->noOfBytesLow; + event.Completed.NoOfLogBytes = rep->noOfLogBytes; + event.Completed.NoOfRecords = rep->noOfRecordsLow; + event.Completed.NoOfLogRecords = rep->noOfLogRecords; + event.Completed.stopGCP = rep->stopGCP; + event.Completed.startGCP = rep->startGCP; + event.Nodes = rep->nodes; + + if (signal->header.theLength >= BackupCompleteRep::SignalLength) + { + event.Completed.NoOfBytes += ((Uint64)rep->noOfBytesHigh) << 32; + event.Completed.NoOfRecords += ((Uint64)rep->noOfRecordsHigh) << 32; + } + + backupId = rep->backupId; + return 0; + } + case GSN_BACKUP_REF:{ + const BackupRef * const ref = + CAST_CONSTPTR(BackupRef, signal->getDataPtr()); + if(ref->errorCode == BackupRef::IAmNotMaster){ + m_master_node = nodeId = refToNode(ref->masterRef); +#ifdef VM_TRACE + ndbout_c("I'm not master resending to %d", nodeId); +#endif + do_send = 1; // try again + continue; + } + event.Event = BackupEvent::BackupFailedToStart; + event.FailedToStart.ErrorCode = ref->errorCode; + return ref->errorCode; + } + case GSN_BACKUP_ABORT_REP:{ + const BackupAbortRep * const rep = + CAST_CONSTPTR(BackupAbortRep, signal->getDataPtr()); + event.Event = BackupEvent::BackupAborted; + event.Aborted.Reason = rep->reason; + event.Aborted.BackupId = rep->backupId; + event.Aborted.ErrorCode = rep->reason; +#ifdef VM_TRACE + ndbout_c("Backup %d aborted", rep->backupId); +#endif + return rep->reason; + } + case GSN_NF_COMPLETEREP:{ + const NFCompleteRep * const rep = + CAST_CONSTPTR(NFCompleteRep, signal->getDataPtr()); +#ifdef VM_TRACE + ndbout_c("Node %d fail completed", rep->failedNodeId); +#endif + if (rep->failedNodeId == nodeId || + waitCompleted == 1) + return 1326; + // wait for next signal + // master node will report aborted backup + break; + } + case GSN_NODE_FAILREP:{ + const NodeFailRep * const rep = + CAST_CONSTPTR(NodeFailRep, signal->getDataPtr()); + if (NodeBitmask::get(rep->theNodes,nodeId) || + waitCompleted == 1) + return 1326; + // wait for next signal + // master node will report aborted backup + break; + } + default: + report_unknown_signal(signal); + return SEND_OR_RECEIVE_FAILED; + } + } +} + +int +MgmtSrvr::abortBackup(Uint32 backupId) +{ + SignalSender ss(theFacade); + ss.lock(); // lock will be released on exit + + bool next; + NodeId nodeId = 0; + while((next = getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)) == true && + theFacade->get_node_alive(nodeId) == false); + + if(!next){ + return NO_CONTACT_WITH_DB_NODES; + } + + SimpleSignal ssig; + + AbortBackupOrd* ord = CAST_PTR(AbortBackupOrd, ssig.getDataPtrSend()); + ssig.set(ss, TestOrd::TraceAPI, BACKUP, GSN_ABORT_BACKUP_ORD, + AbortBackupOrd::SignalLength); + + ord->requestType = AbortBackupOrd::ClientAbort; + ord->senderData = 19; + ord->backupId = backupId; + + return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED; +} + + +MgmtSrvr::Allocated_resources::Allocated_resources(MgmtSrvr &m) + : m_mgmsrv(m) +{ + m_reserved_nodes.clear(); + m_alloc_timeout= 0; +} + +MgmtSrvr::Allocated_resources::~Allocated_resources() +{ + Guard g(m_mgmsrv.m_node_id_mutex); + if (!m_reserved_nodes.isclear()) { + m_mgmsrv.m_reserved_nodes.bitANDC(m_reserved_nodes); + // node has been reserved, force update signal to ndb nodes + m_mgmsrv.updateStatus(); + + char tmp_str[128]; + m_mgmsrv.m_reserved_nodes.getText(tmp_str); + g_eventLogger.info("Mgmt server state: nodeid %d freed, m_reserved_nodes %s.", + get_nodeid(), tmp_str); + } +} + +void +MgmtSrvr::Allocated_resources::reserve_node(NodeId id, NDB_TICKS timeout) +{ + m_reserved_nodes.set(id); + m_alloc_timeout= NdbTick_CurrentMillisecond() + timeout; +} + +bool +MgmtSrvr::Allocated_resources::is_timed_out(NDB_TICKS tick) +{ + if (m_alloc_timeout && tick > m_alloc_timeout) + { + g_eventLogger.info("Mgmt server state: nodeid %d timed out.", + get_nodeid()); + return true; + } + return false; +} + +NodeId +MgmtSrvr::Allocated_resources::get_nodeid() const +{ + for(Uint32 i = 0; i < MAX_NODES; i++) + { + if (m_reserved_nodes.get(i)) + return i; + } + return 0; +} + +int +MgmtSrvr::setDbParameter(int node, int param, const char * value, + BaseString& msg){ + + if(NdbMutex_Lock(m_configMutex)) + return -1; + + /** + * Check parameter + */ + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_NODE); + if(iter.first() != 0){ + msg.assign("Unable to find node section (iter.first())"); + NdbMutex_Unlock(m_configMutex); + return -1; + } + + Uint32 type = NODE_TYPE_DB + 1; + if(node != 0){ + if(iter.find(CFG_NODE_ID, node) != 0){ + msg.assign("Unable to find node (iter.find())"); + NdbMutex_Unlock(m_configMutex); + return -1; + } + if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0){ + msg.assign("Unable to get node type(iter.get(CFG_TYPE_OF_SECTION))"); + NdbMutex_Unlock(m_configMutex); + return -1; + } + } else { + do { + if(iter.get(CFG_TYPE_OF_SECTION, &type) != 0){ + msg.assign("Unable to get node type(iter.get(CFG_TYPE_OF_SECTION))"); + NdbMutex_Unlock(m_configMutex); + return -1; + } + if(type == NODE_TYPE_DB) + break; + } while(iter.next() == 0); + } + + if(type != NODE_TYPE_DB){ + msg.assfmt("Invalid node type or no such node (%d %d)", + type, NODE_TYPE_DB); + NdbMutex_Unlock(m_configMutex); + return -1; + } + + int p_type; + unsigned val_32; + Uint64 val_64; + const char * val_char; + do { + p_type = 0; + if(iter.get(param, &val_32) == 0){ + val_32 = atoi(value); + break; + } + + p_type++; + if(iter.get(param, &val_64) == 0){ + val_64 = strtoll(value, 0, 10); + break; + } + p_type++; + if(iter.get(param, &val_char) == 0){ + val_char = value; + break; + } + msg.assign("Could not get parameter"); + NdbMutex_Unlock(m_configMutex); + return -1; + } while(0); + + bool res = false; + do { + int ret = iter.get(CFG_TYPE_OF_SECTION, &type); + assert(ret == 0); + + if(type != NODE_TYPE_DB) + continue; + + Uint32 node; + ret = iter.get(CFG_NODE_ID, &node); + assert(ret == 0); + + ConfigValues::Iterator i2(_config->m_configValues->m_config, + iter.m_config); + switch(p_type){ + case 0: + res = i2.set(param, val_32); + ndbout_c("Updating node %d param: %d to %d", node, param, val_32); + break; + case 1: + res = i2.set(param, val_64); + ndbout_c("Updating node %d param: %d to %u", node, param, val_32); + break; + case 2: + res = i2.set(param, val_char); + ndbout_c("Updating node %d param: %d to %s", node, param, val_char); + break; + default: + require(false); + } + assert(res); + } while(node == 0 && iter.next() == 0); + + msg.assign("Success"); + NdbMutex_Unlock(m_configMutex); + return 0; +} +int +MgmtSrvr::setConnectionDbParameter(int node1, + int node2, + int param, + int value, + BaseString& msg){ + Uint32 current_value,new_value; + + DBUG_ENTER("MgmtSrvr::setConnectionDbParameter"); + + if(NdbMutex_Lock(m_configMutex)) + { + DBUG_RETURN(-1); + } + + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_CONNECTION); + + if(iter.first() != 0){ + msg.assign("Unable to find connection section (iter.first())"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-1); + } + + for(;iter.valid();iter.next()) { + Uint32 n1,n2; + iter.get(CFG_CONNECTION_NODE_1, &n1); + iter.get(CFG_CONNECTION_NODE_2, &n2); + if((n1 == (unsigned)node1 && n2 == (unsigned)node2) + || (n1 == (unsigned)node2 && n2 == (unsigned)node1)) + break; + } + if(!iter.valid()) { + msg.assign("Unable to find connection between nodes"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-2); + } + + if(iter.get(param, ¤t_value) != 0) { + msg.assign("Unable to get current value of parameter"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-3); + } + + ConfigValues::Iterator i2(_config->m_configValues->m_config, + iter.m_config); + + if(i2.set(param, (unsigned)value) == false) { + msg.assign("Unable to set new value of parameter"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-4); + } + + if(iter.get(param, &new_value) != 0) { + msg.assign("Unable to get parameter after setting it."); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-5); + } + + msg.assfmt("%u -> %u",current_value,new_value); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(1); +} + + +int +MgmtSrvr::getConnectionDbParameter(int node1, + int node2, + int param, + int *value, + BaseString& msg){ + DBUG_ENTER("MgmtSrvr::getConnectionDbParameter"); + + if(NdbMutex_Lock(m_configMutex)) + { + DBUG_RETURN(-1); + } + + ndb_mgm_configuration_iterator + iter(* _config->m_configValues, CFG_SECTION_CONNECTION); + + if(iter.first() != 0){ + msg.assign("Unable to find connection section (iter.first())"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-1); + } + + for(;iter.valid();iter.next()) { + Uint32 n1=0,n2=0; + iter.get(CFG_CONNECTION_NODE_1, &n1); + iter.get(CFG_CONNECTION_NODE_2, &n2); + if((n1 == (unsigned)node1 && n2 == (unsigned)node2) + || (n1 == (unsigned)node2 && n2 == (unsigned)node1)) + break; + } + if(!iter.valid()) { + msg.assign("Unable to find connection between nodes"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-1); + } + + if(iter.get(param, (Uint32*)value) != 0) { + msg.assign("Unable to get current value of parameter"); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(-1); + } + + msg.assfmt("%d",*value); + NdbMutex_Unlock(m_configMutex); + DBUG_RETURN(1); +} + +void MgmtSrvr::transporter_connect(NDB_SOCKET_TYPE sockfd) +{ + if (theFacade->get_registry()->connect_server(sockfd)) + { + /** + * Force an update_connections() so that the + * ClusterMgr and TransporterFacade is up to date + * with the new connection. + * Important for correct node id reservation handling + */ + NdbMutex_Lock(theFacade->theMutexPtr); + theFacade->get_registry()->update_connections(); + NdbMutex_Unlock(theFacade->theMutexPtr); + } +} + +int MgmtSrvr::connect_to_self(void) +{ + int r= 0; + m_local_mgm_handle= ndb_mgm_create_handle(); + snprintf(m_local_mgm_connect_string,sizeof(m_local_mgm_connect_string), + "localhost:%u",getPort()); + ndb_mgm_set_connectstring(m_local_mgm_handle, m_local_mgm_connect_string); + + if((r= ndb_mgm_connect(m_local_mgm_handle, 0, 0, 0)) < 0) + { + ndb_mgm_destroy_handle(&m_local_mgm_handle); + return r; + } + // TransporterRegistry now owns this NdbMgmHandle and will destroy it. + theFacade->get_registry()->set_mgm_handle(m_local_mgm_handle); + + return 0; +} + + + +template class MutexVector<unsigned short>; +template class MutexVector<Ndb_mgmd_event_service::Event_listener>; +template class MutexVector<EventSubscribeReq>; diff --git a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp new file mode 100644 index 00000000000..5eee7447c98 --- /dev/null +++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp @@ -0,0 +1,683 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef MgmtSrvr_H +#define MgmtSrvr_H + +#include <kernel_types.h> +#include "Config.hpp" +#include <NdbCondition.h> +#include <mgmapi.h> + +#include <NdbTCP.h> +#include <ConfigRetriever.hpp> +#include <Vector.hpp> +#include <NodeBitmask.hpp> +#include <signaldata/ManagementServer.hpp> +#include <ndb_version.h> +#include <EventLogger.hpp> +#include <signaldata/EventSubscribeReq.hpp> + +#include <SignalSender.hpp> + +/** + * @desc Block number for Management server. + * @todo This should probably be somewhere else. I don't know where atm. + */ +#define MGMSRV 1 + +class ConfigInfoServer; +class NdbApiSignal; +class Config; +class SetLogLevelOrd; +class SocketServer; + +class Ndb_mgmd_event_service : public EventLoggerBase +{ + friend class MgmtSrvr; +public: + struct Event_listener : public EventLoggerBase { + NDB_SOCKET_TYPE m_socket; + Uint32 m_parsable; + }; + +private: + class MgmtSrvr * m_mgmsrv; + MutexVector<Event_listener> m_clients; +public: + Ndb_mgmd_event_service(class MgmtSrvr * m) : m_clients(5) { + m_mgmsrv = m; + } + + void add_listener(const Event_listener&); + void check_listeners(); + void update_max_log_level(const LogLevel&); + void update_log_level(const LogLevel&); + + void log(int eventType, const Uint32* theData, NodeId nodeId); + + void stop_sessions(); + + Event_listener& operator[](unsigned i) { return m_clients[i]; } + const Event_listener& operator[](unsigned i) const { return m_clients[i]; } + void lock() { m_clients.lock(); } + void unlock(){ m_clients.unlock(); } +}; + +/** + * @class MgmtSrvr + * @brief Main class for the management server. + * + * It has one interface to be used by a local client. + * With the methods it's possible to send different kind of commands to + * DB processes, as log level, set trace number etc. + * + * A MgmtSrvr creates a ConfigInfoServer which serves request on TCP sockets. + * The requests come typical from DB and API processes which want + * to fetch its configuration parameters. The MgmtSrvr knows about the + * configuration by reading a configuration file. + * + * The MgmtSrvr class corresponds in some ways to the Ndb class in API. + * It creates a TransporterFacade, receives signals and defines signals + * to send and receive. + */ +class MgmtSrvr { + +public: + // some compilers need all of this + class Allocated_resources; + friend class Allocated_resources; + class Allocated_resources { + public: + Allocated_resources(class MgmtSrvr &m); + ~Allocated_resources(); + // methods to reserve/allocate resources which + // will be freed when running destructor + void reserve_node(NodeId id, NDB_TICKS timeout); + bool is_timed_out(NDB_TICKS tick); + bool is_reserved(NodeId nodeId) { return m_reserved_nodes.get(nodeId); } + bool is_reserved(NodeBitmask mask) { return !mask.bitAND(m_reserved_nodes).isclear(); } + bool isclear() { return m_reserved_nodes.isclear(); } + NodeId get_nodeid() const; + private: + MgmtSrvr &m_mgmsrv; + NodeBitmask m_reserved_nodes; + NDB_TICKS m_alloc_timeout; + }; + NdbMutex *m_node_id_mutex; + + /** + * Start/initate the event log. + */ + void startEventLog(); + + /** + * Stop the event log. + */ + void stopEventLog(); + + /** + * Enable/disable eventlog log levels/severities. + * + * @param serverity the log level/serverity. + * @return true if the severity was enabled. + */ + bool setEventLogFilter(int severity, int enable); + + /** + * Returns true if the log level/severity is enabled. + * + * @param severity the severity level. + */ + bool isEventLogFilterEnabled(int severity); + + STATIC_CONST( NO_CONTACT_WITH_PROCESS = 5000 ); + STATIC_CONST( PROCESS_NOT_CONFIGURED = 5001 ); + STATIC_CONST( WRONG_PROCESS_TYPE = 5002 ); + STATIC_CONST( COULD_NOT_ALLOCATE_MEMORY = 5003 ); + STATIC_CONST( SEND_OR_RECEIVE_FAILED = 5005 ); + STATIC_CONST( INVALID_LEVEL = 5006 ); + STATIC_CONST( INVALID_ERROR_NUMBER = 5007 ); + STATIC_CONST( INVALID_TRACE_NUMBER = 5008 ); + STATIC_CONST( NOT_IMPLEMENTED = 5009 ); + STATIC_CONST( INVALID_BLOCK_NAME = 5010 ); + + STATIC_CONST( CONFIG_PARAM_NOT_EXIST = 5011 ); + STATIC_CONST( CONFIG_PARAM_NOT_UPDATEABLE = 5012 ); + STATIC_CONST( VALUE_WRONG_FORMAT_INT_EXPECTED = 5013 ); + STATIC_CONST( VALUE_TOO_LOW = 5014 ); + STATIC_CONST( VALUE_TOO_HIGH = 5015 ); + STATIC_CONST( VALUE_WRONG_FORMAT_BOOL_EXPECTED = 5016 ); + + STATIC_CONST( CONFIG_FILE_OPEN_WRITE_ERROR = 5017 ); + STATIC_CONST( CONFIG_FILE_OPEN_READ_ERROR = 5018 ); + STATIC_CONST( CONFIG_FILE_WRITE_ERROR = 5019 ); + STATIC_CONST( CONFIG_FILE_READ_ERROR = 5020 ); + STATIC_CONST( CONFIG_FILE_CLOSE_ERROR = 5021 ); + + STATIC_CONST( CONFIG_CHANGE_REFUSED_BY_RECEIVER = 5022 ); + STATIC_CONST( COULD_NOT_SYNC_CONFIG_CHANGE_AGAINST_PHYSICAL_MEDIUM = 5023 ); + STATIC_CONST( CONFIG_FILE_CHECKSUM_ERROR = 5024 ); + STATIC_CONST( NOT_POSSIBLE_TO_SEND_CONFIG_UPDATE_TO_PROCESS_TYPE = 5025 ); + + STATIC_CONST( NODE_SHUTDOWN_IN_PROGESS = 5026 ); + STATIC_CONST( SYSTEM_SHUTDOWN_IN_PROGRESS = 5027 ); + STATIC_CONST( NODE_SHUTDOWN_WOULD_CAUSE_SYSTEM_CRASH = 5028 ); + + STATIC_CONST( NO_CONTACT_WITH_DB_NODES = 5030 ); + STATIC_CONST( UNSUPPORTED_NODE_SHUTDOWN = 5031 ); + + STATIC_CONST( NODE_NOT_API_NODE = 5062 ); + STATIC_CONST( OPERATION_NOT_ALLOWED_START_STOP = 5063 ); + + /** + * This enum specifies the different signal loggig modes possible to set + * with the setSignalLoggingMode method. + */ + enum LogMode {In, Out, InOut, Off}; + + /* Constructor */ + + MgmtSrvr(SocketServer *socket_server, + const char *config_filename, /* Where to save config */ + const char *connect_string); + int init(); + NodeId getOwnNodeId() const {return _ownNodeId;}; + + /** + * Read (initial) config file, create TransporterFacade, + * define signals, create ConfigInfoServer. + * @return true if succeeded, otherwise false + */ + bool check_start(); // may be run before start to check that some things are ok + bool start(BaseString &error_string); + + ~MgmtSrvr(); + + /** + * Get status on a node. + * address may point to a common area (e.g. from inet_addr) + * There is no gaurentee that it is preserved across calls. + * Copy the string if you are not going to use it immediately. + */ + int status(int nodeId, + ndb_mgm_node_status * status, + Uint32 * version, + Uint32 * phase, + bool * systemShutdown, + Uint32 * dynamicId, + Uint32 * nodeGroup, + Uint32 * connectCount, + const char **address); + + // All the functions below may return any of this error codes: + // NO_CONTACT_WITH_PROCESS, PROCESS_NOT_CONFIGURED, WRONG_PROCESS_TYPE, + // COULD_NOT_ALLOCATE_MEMORY, SEND_OR_RECEIVE_FAILED + + /** + * Save a configuration to permanent storage + */ + int saveConfig(const Config *); + + /** + * Save the running configuration + */ + int saveConfig() { + return saveConfig(_config); + }; + + /** + * Read configuration from file, or from another MGM server + */ + Config *readConfig(); + + /** + * Fetch configuration from another MGM server + */ + Config *fetchConfig(); + + /** + * Stop a node + * + * @param processId: Id of the DB process to stop + * @return 0 if succeeded, otherwise: as stated above, plus: + */ + int stopNodes(const Vector<NodeId> &node_ids, int *stopCount, bool abort, + int *stopSelf); + + int shutdownMGM(int *stopCount, bool abort, int *stopSelf); + + /** + * shutdown the DB nodes + */ + int shutdownDB(int * cnt = 0, bool abort = false); + + /** + * print version info about a node + * + * @param processId: Id of the DB process to stop + * @return 0 if succeeded, otherwise: as stated above, plus: + */ + int versionNode(int nodeId, Uint32 &version, const char **address); + + /** + * Maintenance on the system + */ + int enterSingleUser(int * cnt = 0, Uint32 singleuserNodeId = 0); + + + /** + * Resume from maintenance on the system + */ + int exitSingleUser(int * cnt = 0, bool abort = false); + + /** + * Start DB process. + * @param processId: Id of the DB process to start + * @return 0 if succeeded, otherwise: as stated above, plus: + */ + int start(int processId); + + /** + * Restart nodes + * @param processId: Id of the DB process to start + */ + int restartNodes(const Vector<NodeId> &node_ids, + int *stopCount, bool nostart, + bool initialStart, bool abort, int *stopSelf); + + /** + * Restart all DB nodes + */ + int restartDB(bool nostart, bool initialStart, + bool abort = false, + int * stopCount = 0); + + struct BackupEvent { + enum Event { + BackupStarted = 1, + BackupFailedToStart = 2, + BackupCompleted = 3, + BackupAborted = 4 + } Event; + + NdbNodeBitmask Nodes; + union { + struct { + Uint32 BackupId; + } Started ; + struct { + Uint32 ErrorCode; + } FailedToStart ; + struct { + Uint64 NoOfBytes; + Uint64 NoOfRecords; + Uint32 BackupId; + Uint32 NoOfLogBytes; + Uint32 NoOfLogRecords; + Uint32 startGCP; + Uint32 stopGCP; + } Completed ; + struct { + Uint32 BackupId; + Uint32 Reason; + Uint32 ErrorCode; + } Aborted ; + }; + }; + + /** + * Backup functionallity + */ + int startBackup(Uint32& backupId, int waitCompleted= 2); + int abortBackup(Uint32 backupId); + int performBackup(Uint32* backupId); + + //************************************************************************** + // Description: Set event report level for a DB process + // Parameters: + // processId: Id of the DB process + // level: Event report level + // isResend: Flag to indicate for resending log levels during node restart + // Returns: 0 if succeeded, otherwise: as stated above, plus: + // INVALID_LEVEL + //************************************************************************** + + int setEventReportingLevelImpl(int processId, const EventSubscribeReq& ll); + int setNodeLogLevelImpl(int processId, const SetLogLevelOrd & ll); + + /** + * Insert an error in a DB process. + * @param processId: Id of the DB process + * @param errorNo: The error number. > 0. + * @return 0 if succeeded, otherwise: as stated above, plus: + * INVALID_ERROR_NUMBER + */ + int insertError(int processId, int errorNo); + + + + int setTraceNo(int processId, int traceNo); + //************************************************************************** + // Description: Set trace number in a DB process. + // Parameters: + // processId: Id of the DB process + // trace: Trace number + // Returns: 0 if succeeded, otherwise: as stated above, plus: + // INVALID_TRACE_NUMBER + //************************************************************************** + + + int setSignalLoggingMode(int processId, LogMode mode, + const Vector<BaseString> &blocks); + + int setSignalLoggingMode(int processId, LogMode mode, + BaseString &block) { + Vector<BaseString> v; + v.push_back(block); + return setSignalLoggingMode(processId, mode, v); + } + //************************************************************************** + // Description: Set signal logging mode for blocks in a DB process. + // Parameters: + // processId: Id of the DB process + // mode: The log mode + // blocks: Which blocks to be affected (container of strings) + // Returns: 0 if succeeded, otherwise: as stated above, plus: + // INVALID_BLOCK_NAME + //************************************************************************** + + + int startSignalTracing(int processId); + //************************************************************************** + // Description: Start signal tracing for a DB process. + // Parameters: + // processId: Id of the DB process + // Returns: 0 if succeeded, otherwise: as stated above. + //************************************************************************** + + + int stopSignalTracing(int processId); + //************************************************************************** + // Description: Stop signal tracing for a DB process. + // Parameters: + // processId: Id of the DB process + // Returns: 0 if succeeded, otherwise: as stated above. + //************************************************************************** + + /** + * Dump State + */ + int dumpState(int processId, const Uint32 args[], Uint32 argNo); + int dumpState(int processId, const char* args); + + /** + * Get next node id (node id gt that _nodeId) + * of specified type and save it in _nodeId + * + * @return false if none found + */ + bool getNextNodeId(NodeId * _nodeId, enum ndb_mgm_node_type type) const ; + bool alloc_node_id(NodeId * _nodeId, enum ndb_mgm_node_type type, + struct sockaddr *client_addr, + SOCKET_SIZE_TYPE *client_addr_len, + int &error_code, BaseString &error_string, + int log_event = 1); + + /** + * + */ + enum ndb_mgm_node_type getNodeType(NodeId) const; + + /** + * Get error text + * + * @param errorCode: Error code to get a match error text for. + * @return The error text. + */ + const char* getErrorText(int errorCode, char *buf, int buf_sz); + + /** + * Get configuration + */ + const Config * getConfig() const; + + /** + * Returns the node count for the specified node type. + * + * @param type The node type. + * @return The number of nodes of the specified type. + */ + int getNodeCount(enum ndb_mgm_node_type type) const; + + /** + * Returns the port number. + * @return port number. + */ + int getPort() const; + + int setDbParameter(int node, int parameter, const char * value, BaseString&); + int setConnectionDbParameter(int node1, int node2, int param, int value, + BaseString& msg); + int getConnectionDbParameter(int node1, int node2, int param, + int *value, BaseString& msg); + + int connect_to_self(void); + + void transporter_connect(NDB_SOCKET_TYPE sockfd); + + ConfigRetriever *get_config_retriever() { return m_config_retriever; }; + + const char *get_connect_address(Uint32 node_id); + void get_connected_nodes(NodeBitmask &connected_nodes) const; + SocketServer *get_socket_server() { return m_socket_server; } + + void updateStatus(); + + //************************************************************************** +private: + //************************************************************************** + + int sendStopMgmd(NodeId nodeId, + bool abort, + bool stop, + bool restart, + bool nostart, + bool initialStart); + + int sendSTOP_REQ(const Vector<NodeId> &node_ids, + NodeBitmask &stoppedNodes, + Uint32 singleUserNodeId, + bool abort, + bool stop, + bool restart, + bool nostart, + bool initialStart, + int *stopSelf); + + /** + * Check if it is possible to send a signal to a (DB) process + * + * @param processId: Id of the process to send to + * @return 0 OK, 1 process dead, 2 API or MGMT process, 3 not configured + */ + int okToSendTo(NodeId nodeId, bool unCond = false); + + /** + * Get block number for a block + * + * @param blockName: Block to get number for + * @return -1 if block not found, otherwise block number + */ + int getBlockNumber(const BaseString &blockName); + + int alloc_node_id_req(NodeId free_node_id, enum ndb_mgm_node_type type); + //************************************************************************** + + int _blockNumber; + NodeId _ownNodeId; + SocketServer *m_socket_server; + + BlockReference _ownReference; + NdbMutex *m_configMutex; + const Config * _config; + Config * m_newConfig; + BaseString m_configFilename; + Uint32 m_nextConfigGenerationNumber; + + NodeBitmask m_reserved_nodes; + struct in_addr m_connect_address[MAX_NODES]; + + //************************************************************************** + // Specific signal handling methods + //************************************************************************** + + static void defineSignals(int blockNumber); + //************************************************************************** + // Description: Define all signals to be sent or received for a block + // Parameters: + // blockNumber: The block number send/receive + // Returns: - + //************************************************************************** + + void handleReceivedSignal(NdbApiSignal* signal); + //************************************************************************** + // Description: This method is called from "another" thread when a signal + // is received. If expect the received signal and succeed to handle it + // we signal with a condition variable to the waiting + // thread (receiveOptimisedResponse) that the signal has arrived. + // Parameters: + // signal: The recieved signal + // Returns: - + //************************************************************************** + + void handleStatus(NodeId nodeId, bool alive, bool nfComplete); + //************************************************************************** + // Description: Handle the death of a process + // Parameters: + // processId: Id of the dead process. + // Returns: - + //************************************************************************** + + //************************************************************************** + // Specific signal handling data + //************************************************************************** + + + //************************************************************************** + //************************************************************************** + // General signal handling methods + // This functions are more or less copied from the Ndb class. + + + /** + * WaitSignalType defines states where each state define a set of signals + * we accept to receive. + * The state is set after we have sent a signal. + * When a signal arrives we first check current state (handleReceivedSignal) + * to verify that we expect the arrived signal. + * It's only then we are in state accepting the arrived signal + * we handle the signal. + */ + enum WaitSignalType { + NO_WAIT, // We don't expect to receive any signal + WAIT_SET_VAR, // Accept SET_VAR_CONF and SET_VAR_REF + WAIT_SUBSCRIBE_CONF // Accept event subscription confirmation + }; + + /** + * This function is called from "outside" of MgmtSrvr + * when a signal is sent to MgmtSrvr. + * @param mgmtSrvr: The MgmtSrvr object which shall recieve the signal. + * @param signal: The received signal. + */ + static void signalReceivedNotification(void* mgmtSrvr, + NdbApiSignal* signal, + struct LinearSectionPtr ptr[3]); + + /** + * Called from "outside" of MgmtSrvr when a DB process has died. + * @param mgmtSrvr: The MgmtSrvr object wreceiveOptimisedResponsehich + * shall receive the notification. + * @param processId: Id of the dead process. + */ + static void nodeStatusNotification(void* mgmSrv, Uint32 nodeId, + bool alive, bool nfCompleted); + + /** + * An event from <i>nodeId</i> has arrived + */ + void eventReport(const Uint32 * theData); + + + //************************************************************************** + //************************************************************************** + // General signal handling data + + STATIC_CONST( WAIT_FOR_RESPONSE_TIMEOUT = 300000 ); // Milliseconds + // Max time to wait for a signal to arrive + + NdbApiSignal* theSignalIdleList; + // List of unused signals + + Uint32 theWaitNode; + WaitSignalType theWaitState; + // State denoting a set of signals we accept to recieve. + + NdbCondition* theMgmtWaitForResponseCondPtr; + // Condition variable used when we wait for a signal to arrive/a + // signal arrives. + // We wait in receiveOptimisedResponse and signal in handleReceivedSignal. + + NdbMgmHandle m_local_mgm_handle; + char m_local_mgm_connect_string[20]; + class TransporterFacade * theFacade; + + int sendVersionReq( int processId, Uint32 &version, const char **address); + int translateStopRef(Uint32 errCode); + + bool _isStopThread; + int _logLevelThreadSleep; + MutexVector<NodeId> m_started_nodes; + MutexVector<EventSubscribeReq> m_log_level_requests; + LogLevel m_nodeLogLevel[MAX_NODES]; + enum ndb_mgm_node_type nodeTypes[MAX_NODES]; + friend class MgmApiSession; + friend class Ndb_mgmd_event_service; + Ndb_mgmd_event_service m_event_listner; + + NodeId m_master_node; + + /** + * Handles the thread wich upon a 'Node is started' event will + * set the node's previous loglevel settings. + */ + struct NdbThread* _logLevelThread; + static void *logLevelThread_C(void *); + void logLevelThreadRun(); + + Config *_props; + + ConfigRetriever *m_config_retriever; +}; + +inline +const Config * +MgmtSrvr::getConfig() const { + return _config; +} + +#endif // MgmtSrvr_H diff --git a/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp b/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp new file mode 100644 index 00000000000..e56643a3d7e --- /dev/null +++ b/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp @@ -0,0 +1,76 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <signaldata/TestOrd.hpp> +#include <OutputStream.hpp> + +#include "MgmtSrvr.hpp" +#include "SignalQueue.hpp" +#include <InitConfigFileParser.hpp> +#include <ConfigRetriever.hpp> +#include <ndb_version.h> + +/** + * Save a configuration to the running configuration file + */ +int +MgmtSrvr::saveConfig(const Config *conf) { + BaseString newfile; + newfile.appfmt("%s.new", m_configFilename.c_str()); + + /* Open and write to the new config file */ + FILE *f = fopen(newfile.c_str(), "w"); + if(f == NULL) { + /** @todo Send something apropriate to the log */ + return -1; + } + FileOutputStream stream(f); + conf->printConfigFile(stream); + + fclose(f); + + /* Rename file to real name */ + rename(newfile.c_str(), m_configFilename.c_str()); + + return 0; +} + +Config * +MgmtSrvr::readConfig() { + Config *conf; + InitConfigFileParser parser; + if (m_configFilename.length()) + { + conf = parser.parseConfig(m_configFilename.c_str()); + } + else + { + ndbout_c("Reading cluster configuration using my.cnf"); + conf = parser.parse_mycnf(); + } + return conf; +} + +Config * +MgmtSrvr::fetchConfig() { + struct ndb_mgm_configuration * tmp = m_config_retriever->getConfig(); + if(tmp != 0){ + Config * conf = new Config(); + conf->m_configValues = tmp; + return conf; + } + return 0; +} diff --git a/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp b/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp new file mode 100644 index 00000000000..c99936e1861 --- /dev/null +++ b/storage/ndb/src/mgmsrv/MgmtSrvrGeneralSignalHandling.cpp @@ -0,0 +1,22 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +//****************************************************************************** +// General signal handling methods +// All implementations stolen from the Ndb class. +// Some kind of reuse should be preferred. +//****************************************************************************** + diff --git a/storage/ndb/src/mgmsrv/ParamInfo.cpp b/storage/ndb/src/mgmsrv/ParamInfo.cpp new file mode 100644 index 00000000000..01662fab588 --- /dev/null +++ b/storage/ndb/src/mgmsrv/ParamInfo.cpp @@ -0,0 +1,2077 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <../../include/kernel/ndb_limits.h> +#include "ParamInfo.hpp" +#include <mgmapi_config_parameters.h> + +#ifndef MYSQLCLUSTERDIR +#define MYSQLCLUSTERDIR "." +#endif + +#define KEY_INTERNAL 0 +#define MAX_INT_RNIL 0xfffffeff +#define MAX_PORT_NO 65535 + +#define _STR_VALUE(x) #x +#define STR_VALUE(x) _STR_VALUE(x) + +/**************************************************************************** + * Section names + ****************************************************************************/ +#define DB_TOKEN_PRINT "ndbd(DB)" +#define MGM_TOKEN_PRINT "ndb_mgmd(MGM)" +#define API_TOKEN_PRINT "mysqld(API)" + +/** + * A MANDATORY parameters must be specified in the config file + * An UNDEFINED parameter may or may not be specified in the config file + */ +static const char* MANDATORY = (char*)~(UintPtr)0;// Default value for mandatory params. +static const char* UNDEFINED = 0; // Default value for undefined params. + +extern const ParamInfo ParamInfoArray[]; +extern const int ParamInfoNum; + +/** + * The default constructors create objects with suitable values for the + * configuration parameters. + * + * Some are however given the value MANDATORY which means that the value + * must be specified in the configuration file. + * + * Min and max values are also given for some parameters. + * - Attr1: Name in file (initial config file) + * - Attr2: Name in prop (properties object) + * - Attr3: Name of Section (in init config file) + * - Attr4: Updateable + * - Attr5: Type of parameter (INT or BOOL) + * - Attr6: Default Value (number only) + * - Attr7: Min value + * - Attr8: Max value + * + * Parameter constraints are coded in file Config.cpp. + * + * ******************************************************************* + * Parameters used under development should be marked "NOTIMPLEMENTED" + * ******************************************************************* + */ +const ParamInfo ParamInfoArray[] = { + + /**************************************************************************** + * COMPUTER + ***************************************************************************/ + { + KEY_INTERNAL, + "COMPUTER", + "COMPUTER", + "Computer section", + CI_INTERNAL, + false, + CI_SECTION, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + "COMPUTER", + "Name of computer", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + KEY_INTERNAL, + "HostName", + "COMPUTER", + "Hostname of computer (e.g. mysql.com)", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + KEY_INTERNAL, + "ByteOrder", + "COMPUTER", + 0, + CI_DEPRICATED, + false, + CI_STRING, + UNDEFINED, + 0, + 0 }, + + /**************************************************************************** + * SYSTEM + ***************************************************************************/ + { + CFG_SECTION_SYSTEM, + "SYSTEM", + "SYSTEM", + "System section", + CI_USED, + false, + CI_SECTION, + (const char *)CFG_SECTION_SYSTEM, + 0, 0 }, + + { + CFG_SYS_NAME, + "Name", + "SYSTEM", + "Name of system (NDB Cluster)", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_SYS_PRIMARY_MGM_NODE, + "PrimaryMGMNode", + "SYSTEM", + "Node id of Primary "MGM_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SYS_CONFIG_GENERATION, + "ConfigGenerationNumber", + "SYSTEM", + "Configuration generation number", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /*************************************************************************** + * DB + ***************************************************************************/ + { + CFG_SECTION_NODE, + DB_TOKEN, + DB_TOKEN, + "Node section", + CI_USED, + false, + CI_SECTION, + (const char *)NODE_TYPE_DB, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + DB_TOKEN, + "Name of computer for this node", + CI_INTERNAL, + false, + CI_STRING, + "localhost", + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + DB_TOKEN, + "Name of system for this node", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + DB_TOKEN, + "", + CI_DEPRICATED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + DB_TOKEN, + "Number identifying the database node ("DB_TOKEN_PRINT")", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + KEY_INTERNAL, + "ServerPort", + DB_TOKEN, + "Port used to setup transporter", + CI_USED, + false, + CI_INT, + UNDEFINED, + "1", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_DB_NO_REPLICAS, + "NoOfReplicas", + DB_TOKEN, + "Number of copies of all data in the database (1-4)", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", + "4" }, + + { + CFG_DB_NO_ATTRIBUTES, + "MaxNoOfAttributes", + DB_TOKEN, + "Total number of attributes stored in database. I.e. sum over all tables", + CI_USED, + false, + CI_INT, + "1000", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_TABLES, + "MaxNoOfTables", + DB_TOKEN, + "Total number of tables stored in the database", + CI_USED, + false, + CI_INT, + "128", + "8", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_ORDERED_INDEXES, + "MaxNoOfOrderedIndexes", + DB_TOKEN, + "Total number of ordered indexes that can be defined in the system", + CI_USED, + false, + CI_INT, + "128", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_UNIQUE_HASH_INDEXES, + "MaxNoOfUniqueHashIndexes", + DB_TOKEN, + "Total number of unique hash indexes that can be defined in the system", + CI_USED, + false, + CI_INT, + "64", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_INDEXES, + "MaxNoOfIndexes", + DB_TOKEN, + "Total number of indexes that can be defined in the system", + CI_DEPRICATED, + false, + CI_INT, + "128", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_INDEX_OPS, + "MaxNoOfConcurrentIndexOperations", + DB_TOKEN, + "Total number of index operations that can execute simultaneously on one "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "8K", + "0", + STR_VALUE(MAX_INT_RNIL) + }, + + { + CFG_DB_NO_TRIGGERS, + "MaxNoOfTriggers", + DB_TOKEN, + "Total number of triggers that can be defined in the system", + CI_USED, + false, + CI_INT, + "768", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_TRIGGER_OPS, + "MaxNoOfFiredTriggers", + DB_TOKEN, + "Total number of triggers that can fire simultaneously in one "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "4000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + DB_TOKEN, + "String referencing an earlier defined COMPUTER", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_DB_NO_SAVE_MSGS, + "MaxNoOfSavedMessages", + DB_TOKEN, + "Max number of error messages in error log and max number of trace files", + CI_USED, + true, + CI_INT, + "25", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_MEMLOCK, + "LockPagesInMainMemory", + DB_TOKEN, + "If set to yes, then NDB Cluster data will not be swapped out to disk", + CI_USED, + true, + CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_DB_WATCHDOG_INTERVAL, + "TimeBetweenWatchDogCheck", + DB_TOKEN, + "Time between execution checks inside a database node", + CI_USED, + true, + CI_INT, + "6000", + "70", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_STOP_ON_ERROR, + "StopOnError", + DB_TOKEN, + "If set to N, "DB_TOKEN_PRINT" automatically restarts/recovers in case of node failure", + CI_USED, + true, + CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_DB_STOP_ON_ERROR_INSERT, + "RestartOnErrorInsert", + DB_TOKEN, + "See src/kernel/vm/Emulator.hpp NdbRestartType for details", + CI_INTERNAL, + true, + CI_INT, + "2", + "0", + "4" }, + + { + CFG_DB_NO_OPS, + "MaxNoOfConcurrentOperations", + DB_TOKEN, + "Max number of operation records in transaction coordinator", + CI_USED, + false, + CI_INT, + "32k", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_LOCAL_OPS, + "MaxNoOfLocalOperations", + DB_TOKEN, + "Max number of operation records defined in the local storage node", + CI_USED, + false, + CI_INT, + UNDEFINED, + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_LOCAL_SCANS, + "MaxNoOfLocalScans", + DB_TOKEN, + "Max number of fragment scans in parallel in the local storage node", + CI_USED, + false, + CI_INT, + UNDEFINED, + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BATCH_SIZE, + "BatchSizePerLocalScan", + DB_TOKEN, + "Used to calculate the number of lock records for scan with hold lock", + CI_USED, + false, + CI_INT, + STR_VALUE(DEF_BATCH_SIZE), + "1", + STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) }, + + { + CFG_DB_NO_TRANSACTIONS, + "MaxNoOfConcurrentTransactions", + DB_TOKEN, + "Max number of transaction executing concurrently on the "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "4096", + "32", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_NO_SCANS, + "MaxNoOfConcurrentScans", + DB_TOKEN, + "Max number of scans executing concurrently on the "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "256", + "2", + "500" }, + + { + CFG_DB_TRANS_BUFFER_MEM, + "TransactionBufferMemory", + DB_TOKEN, + "Dynamic buffer space (in bytes) for key and attribute data allocated for each "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "1M", + "1K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_INDEX_MEM, + "IndexMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing indexes", + CI_USED, + false, + CI_INT64, + "18M", + "1M", + "1024G" }, + + { + CFG_DB_DATA_MEM, + "DataMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for storing data", + CI_USED, + false, + CI_INT64, + "80M", + "1M", + "1024G" }, + + { + CFG_DB_UNDO_INDEX_BUFFER, + "UndoIndexBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for index part", + CI_USED, + false, + CI_INT, + "2M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_UNDO_DATA_BUFFER, + "UndoDataBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing UNDO logs for data part", + CI_USED, + false, + CI_INT, + "16M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_REDO_BUFFER, + "RedoBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for writing REDO logs", + CI_USED, + false, + CI_INT, + "8M", + "1M", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_LONG_SIGNAL_BUFFER, + "LongMessageBuffer", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for internal long messages", + CI_USED, + false, + CI_INT, + "1M", + "512k", + STR_VALUE(MAX_INT_RNIL)}, + + { + CFG_DB_DISK_PAGE_BUFFER_MEMORY, + "DiskPageBufferMemory", + DB_TOKEN, + "Number bytes on each "DB_TOKEN_PRINT" node allocated for disk page buffer cache", + CI_USED, + false, + CI_INT64, + "64M", + "4M", + "1024G" }, + + { + CFG_DB_SGA, + "SharedGlobalMemory", + DB_TOKEN, + "Total number bytes on each "DB_TOKEN_PRINT" node allocated for any use", + CI_USED, + false, + CI_INT64, + "20M", + "0", + "65536G" }, // 32k pages * 32-bit i value + + { + CFG_DB_START_PARTIAL_TIMEOUT, + "StartPartialTimeout", + DB_TOKEN, + "Time to wait before trying to start wo/ all nodes. 0=Wait forever", + CI_USED, + true, + CI_INT, + "30000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_START_PARTITION_TIMEOUT, + "StartPartitionedTimeout", + DB_TOKEN, + "Time to wait before trying to start partitioned. 0=Wait forever", + CI_USED, + true, + CI_INT, + "60000", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_START_FAILURE_TIMEOUT, + "StartFailureTimeout", + DB_TOKEN, + "Time to wait before terminating. 0=Wait forever", + CI_USED, + true, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_HEARTBEAT_INTERVAL, + "HeartbeatIntervalDbDb", + DB_TOKEN, + "Time between "DB_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "DB_TOKEN_PRINT" considered dead after 3 missed HBs", + CI_USED, + true, + CI_INT, + "1500", + "10", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_API_HEARTBEAT_INTERVAL, + "HeartbeatIntervalDbApi", + DB_TOKEN, + "Time between "API_TOKEN_PRINT"-"DB_TOKEN_PRINT" heartbeats. "API_TOKEN_PRINT" connection closed after 3 missed HBs", + CI_USED, + true, + CI_INT, + "1500", + "100", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_INTERVAL, + "TimeBetweenLocalCheckpoints", + DB_TOKEN, + "Time between taking snapshots of the database (expressed in 2log of bytes)", + CI_USED, + true, + CI_INT, + "20", + "0", + "31" }, + + { + CFG_DB_GCP_INTERVAL, + "TimeBetweenGlobalCheckpoints", + DB_TOKEN, + "Time between doing group commit of transactions to disk", + CI_USED, + true, + CI_INT, + "2000", + "10", + "32000" }, + + { + CFG_DB_NO_REDOLOG_FILES, + "NoOfFragmentLogFiles", + DB_TOKEN, + "No of 16 Mbyte Redo log files in each of 4 file sets belonging to "DB_TOKEN_PRINT" node", + CI_USED, + false, + CI_INT, + "16", + "3", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_MAX_OPEN_FILES, + "MaxNoOfOpenFiles", + DB_TOKEN, + "Max number of files open per "DB_TOKEN_PRINT" node.(One thread is created per file)", + CI_USED, + false, + CI_INT, + "40", + "20", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_INITIAL_OPEN_FILES, + "InitialNoOfOpenFiles", + DB_TOKEN, + "Initial number of files open per "DB_TOKEN_PRINT" node.(One thread is created per file)", + CI_USED, + false, + CI_INT, + "27", + "20", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_CHECK_INTERVAL, + "TimeBetweenInactiveTransactionAbortCheck", + DB_TOKEN, + "Time between inactive transaction checks", + CI_USED, + true, + CI_INT, + "1000", + "1000", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_INACTIVE_TIMEOUT, + "TransactionInactiveTimeout", + DB_TOKEN, + "Time application can wait before executing another transaction part (ms).\n" + "This is the time the transaction coordinator waits for the application\n" + "to execute or send another part (query, statement) of the transaction.\n" + "If the application takes too long time, the transaction gets aborted.\n" + "Timeout set to 0 means that we don't timeout at all on application wait.", + CI_USED, + true, + CI_INT, + STR_VALUE(MAX_INT_RNIL), + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_TRANSACTION_DEADLOCK_TIMEOUT, + "TransactionDeadlockDetectionTimeout", + DB_TOKEN, + "Time transaction can be executing in a DB node (ms).\n" + "This is the time the transaction coordinator waits for each database node\n" + "of the transaction to execute a request. If the database node takes too\n" + "long time, the transaction gets aborted.", + CI_USED, + true, + CI_INT, + "1200", + "50", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_TUP_SR, + "NoOfDiskPagesToDiskDuringRestartTUP", + DB_TOKEN, + "DiskCheckpointSpeedSr", + CI_DEPRICATED, + true, + CI_INT, + "40", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_TUP, + "NoOfDiskPagesToDiskAfterRestartTUP", + DB_TOKEN, + "DiskCheckpointSpeed", + CI_DEPRICATED, + true, + CI_INT, + "40", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_ACC_SR, + "NoOfDiskPagesToDiskDuringRestartACC", + DB_TOKEN, + "DiskCheckpointSpeedSr", + CI_DEPRICATED, + true, + CI_INT, + "20", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_LCP_DISC_PAGES_ACC, + "NoOfDiskPagesToDiskAfterRestartACC", + DB_TOKEN, + "DiskCheckpointSpeed", + CI_DEPRICATED, + true, + CI_INT, + "20", + "1", + STR_VALUE(MAX_INT_RNIL) }, + + + { + CFG_DB_DISCLESS, + "Diskless", + DB_TOKEN, + "Run wo/ disk", + CI_USED, + true, + CI_BOOL, + "false", + "false", + "true"}, + + { + KEY_INTERNAL, + "Discless", + DB_TOKEN, + "Diskless", + CI_DEPRICATED, + true, + CI_BOOL, + "false", + "false", + "true"}, + + + + { + CFG_DB_ARBIT_TIMEOUT, + "ArbitrationTimeout", + DB_TOKEN, + "Max time (milliseconds) database partion waits for arbitration signal", + CI_USED, + false, + CI_INT, + "3000", + "10", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_NODE_DATADIR, + "DataDir", + DB_TOKEN, + "Data directory for this node", + CI_USED, + false, + CI_STRING, + MYSQLCLUSTERDIR, + 0, 0 }, + + { + CFG_DB_FILESYSTEM_PATH, + "FileSystemPath", + DB_TOKEN, + "Path to directory where the "DB_TOKEN_PRINT" node stores its data (directory must exist)", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_LOGLEVEL_STARTUP, + "LogLevelStartup", + DB_TOKEN, + "Node startup info printed on stdout", + CI_USED, + false, + CI_INT, + "1", + "0", + "15" }, + + { + CFG_LOGLEVEL_SHUTDOWN, + "LogLevelShutdown", + DB_TOKEN, + "Node shutdown info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_STATISTICS, + "LogLevelStatistic", + DB_TOKEN, + "Transaction, operation, transporter info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CHECKPOINT, + "LogLevelCheckpoint", + DB_TOKEN, + "Local and Global checkpoint info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_NODERESTART, + "LogLevelNodeRestart", + DB_TOKEN, + "Node restart, node failure info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CONNECTION, + "LogLevelConnection", + DB_TOKEN, + "Node connect/disconnect info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_CONGESTION, + "LogLevelCongestion", + DB_TOKEN, + "Congestion info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_ERROR, + "LogLevelError", + DB_TOKEN, + "Transporter, heartbeat errors printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + { + CFG_LOGLEVEL_INFO, + "LogLevelInfo", + DB_TOKEN, + "Heartbeat and log info printed on stdout", + CI_USED, + false, + CI_INT, + "0", + "0", + "15" }, + + /** + * Backup + */ + { + CFG_DB_PARALLEL_BACKUPS, + "ParallelBackups", + DB_TOKEN, + "Maximum number of parallel backups", + CI_NOTIMPLEMENTED, + false, + CI_INT, + "1", + "1", + "1" }, + + { + CFG_DB_BACKUP_DATADIR, + "BackupDataDir", + DB_TOKEN, + "Path to where to store backups", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_DB_DISK_SYNCH_SIZE, + "DiskSyncSize", + DB_TOKEN, + "Data written to a file before a synch is forced", + CI_USED, + false, + CI_INT, + "4M", + "32k", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_CHECKPOINT_SPEED, + "DiskCheckpointSpeed", + DB_TOKEN, + "Bytes per second allowed to be written by checkpoint", + CI_USED, + false, + CI_INT, + "10M", + "1M", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_CHECKPOINT_SPEED_SR, + "DiskCheckpointSpeedInRestart", + DB_TOKEN, + "Bytes per second allowed to be written by checkpoint during restart", + CI_USED, + false, + CI_INT, + "100M", + "1M", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_MEM, + "BackupMemory", + DB_TOKEN, + "Total memory allocated for backups per node (in bytes)", + CI_USED, + false, + CI_INT, + "4M", // sum of BackupDataBufferSize and BackupLogBufferSize + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_DATA_BUFFER_MEM, + "BackupDataBufferSize", + DB_TOKEN, + "Default size of databuffer for a backup (in bytes)", + CI_USED, + false, + CI_INT, + "2M", // remember to change BackupMemory + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_LOG_BUFFER_MEM, + "BackupLogBufferSize", + DB_TOKEN, + "Default size of logbuffer for a backup (in bytes)", + CI_USED, + false, + CI_INT, + "2M", // remember to change BackupMemory + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_WRITE_SIZE, + "BackupWriteSize", + DB_TOKEN, + "Default size of filesystem writes made by backup (in bytes)", + CI_USED, + false, + CI_INT, + "32K", + "2K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_BACKUP_MAX_WRITE_SIZE, + "BackupMaxWriteSize", + DB_TOKEN, + "Max size of filesystem writes made by backup (in bytes)", + CI_USED, + false, + CI_INT, + "256K", + "2K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_DB_STRING_MEMORY, + "StringMemory", + DB_TOKEN, + "Default size of string memory (0 -> 5% of max 1-100 -> %of max, >100 -> actual bytes)", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /*************************************************************************** + * API + ***************************************************************************/ + { + CFG_SECTION_NODE, + API_TOKEN, + API_TOKEN, + "Node section", + CI_USED, + false, + CI_SECTION, + (const char *)NODE_TYPE_API, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + API_TOKEN, + "Name of computer for this node", + CI_INTERNAL, + false, + CI_STRING, + "", + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + API_TOKEN, + "Name of system for this node", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + API_TOKEN, + "", + CI_DEPRICATED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + API_TOKEN, + "Number identifying application node ("API_TOKEN_PRINT")", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + API_TOKEN, + "String referencing an earlier defined COMPUTER", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_NODE_ARBIT_RANK, + "ArbitrationRank", + API_TOKEN, + "If 0, then "API_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2", + CI_USED, + false, + CI_INT, + "0", + "0", + "2" }, + + { + CFG_NODE_ARBIT_DELAY, + "ArbitrationDelay", + API_TOKEN, + "When asked to arbitrate, arbitrator waits this long before voting (msec)", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_MAX_SCAN_BATCH_SIZE, + "MaxScanBatchSize", + "API", + "The maximum collective batch size for one scan", + CI_USED, + false, + CI_INT, + STR_VALUE(MAX_SCAN_BATCH_SIZE), + "32k", + "16M" }, + + { + CFG_BATCH_BYTE_SIZE, + "BatchByteSize", + "API", + "The default batch size in bytes", + CI_USED, + false, + CI_INT, + STR_VALUE(SCAN_BATCH_SIZE), + "1k", + "1M" }, + + { + CFG_BATCH_SIZE, + "BatchSize", + "API", + "The default batch size in number of records", + CI_USED, + false, + CI_INT, + STR_VALUE(DEF_BATCH_SIZE), + "1", + STR_VALUE(MAX_PARALLEL_OP_PER_SCAN) }, + + /**************************************************************************** + * MGM + ***************************************************************************/ + { + CFG_SECTION_NODE, + MGM_TOKEN, + MGM_TOKEN, + "Node section", + CI_USED, + false, + CI_SECTION, + (const char *)NODE_TYPE_MGM, + 0, 0 + }, + + { + CFG_NODE_HOST, + "HostName", + MGM_TOKEN, + "Name of computer for this node", + CI_INTERNAL, + false, + CI_STRING, + "", + 0, 0 }, + + { + CFG_NODE_DATADIR, + "DataDir", + MGM_TOKEN, + "Data directory for this node", + CI_USED, + false, + CI_STRING, + MYSQLCLUSTERDIR, + 0, 0 }, + + { + CFG_NODE_SYSTEM, + "System", + MGM_TOKEN, + "Name of system for this node", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + KEY_INTERNAL, + "Id", + MGM_TOKEN, + "", + CI_DEPRICATED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_NODE_ID, + "NodeId", + MGM_TOKEN, + "Number identifying the management server node ("MGM_TOKEN_PRINT")", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", + STR_VALUE(MAX_NODES) }, + + { + CFG_LOG_DESTINATION, + "LogDestination", + MGM_TOKEN, + "String describing where logmessages are sent", + CI_USED, + false, + CI_STRING, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "ExecuteOnComputer", + MGM_TOKEN, + "String referencing an earlier defined COMPUTER", + CI_USED, + false, + CI_STRING, + 0, + 0, 0 }, + + { + KEY_INTERNAL, + "MaxNoOfSavedEvents", + MGM_TOKEN, + "", + CI_USED, + false, + CI_INT, + "100", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_MGM_PORT, + "PortNumber", + MGM_TOKEN, + "Port number to give commands to/fetch configurations from management server", + CI_USED, + false, + CI_INT, + NDB_PORT, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + KEY_INTERNAL, + "PortNumberStats", + MGM_TOKEN, + "Port number used to get statistical information from a management server", + CI_USED, + false, + CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_NODE_ARBIT_RANK, + "ArbitrationRank", + MGM_TOKEN, + "If 0, then "MGM_TOKEN_PRINT" is not arbitrator. Kernel selects arbitrators in order 1, 2", + CI_USED, + false, + CI_INT, + "1", + "0", + "2" }, + + { + CFG_NODE_ARBIT_DELAY, + "ArbitrationDelay", + MGM_TOKEN, + "", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + /**************************************************************************** + * TCP + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "TCP", + "TCP", + "Connection section", + CI_USED, + false, + CI_SECTION, + (const char *)CONNECTION_TYPE_TCP, + 0, 0 + }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "TCP", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "TCP", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "TCP", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "TCP", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_GROUP, + "Group", + "TCP", + "", + CI_USED, + false, + CI_INT, + "55", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "TCP", + "", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "TCP", + "Sends id in each signal. Used in trace files.", + CI_USED, + false, + CI_BOOL, + "true", + "false", + "true" }, + + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "TCP", + "If checksum is enabled, all signals between nodes are checked for errors", + CI_USED, + false, + CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "TCP", + "Port used for this transporter", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_TCP_SEND_BUFFER_SIZE, + "SendBufferMemory", + "TCP", + "Bytes of buffer for signals sent from this node", + CI_USED, + false, + CI_INT, + "256K", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_TCP_RECEIVE_BUFFER_SIZE, + "ReceiveBufferMemory", + "TCP", + "Bytes of buffer for signals received by this node", + CI_USED, + false, + CI_INT, + "64K", + "16K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_TCP_PROXY, + "Proxy", + "TCP", + "", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "TCP", + "System for node 1 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "TCP", + "System for node 2 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + + /**************************************************************************** + * SHM + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "SHM", + "SHM", + "Connection section", + CI_USED, + false, + CI_SECTION, + (const char *)CONNECTION_TYPE_SHM, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "SHM", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "SHM", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "SHM", + "Port used for this transporter", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_SHM_SIGNUM, + "Signum", + "SHM", + "Signum to be used for signalling", + CI_USED, + false, + CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "SHM", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "SHM", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + 0, 0 }, + + { + CFG_CONNECTION_GROUP, + "Group", + "SHM", + "", + CI_USED, + false, + CI_INT, + "35", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "SHM", + "", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "SHM", + "Sends id in each signal. Used in trace files.", + CI_USED, + false, + CI_BOOL, + "false", + "false", + "true" }, + + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "SHM", + "If checksum is enabled, all signals between nodes are checked for errors", + CI_USED, + false, + CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_SHM_KEY, + "ShmKey", + "SHM", + "A shared memory key", + CI_USED, + false, + CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SHM_BUFFER_MEM, + "ShmSize", + "SHM", + "Size of shared memory segment", + CI_USED, + false, + CI_INT, + "1M", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "SHM", + "System for node 1 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "SHM", + "System for node 2 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + /**************************************************************************** + * SCI + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "SCI", + "SCI", + "Connection section", + CI_USED, + false, + CI_SECTION, + (const char *)CONNECTION_TYPE_SCI, + 0, 0 + }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "SCI", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "SCI", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_STRING, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_GROUP, + "Group", + "SCI", + "", + CI_USED, + false, + CI_INT, + "15", + "0", "200" }, + + { + CFG_CONNECTION_NODE_ID_SERVER, + "NodeIdServer", + "SCI", + "", + CI_USED, + false, + CI_INT, + MANDATORY, + "1", "63" }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "SCI", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "SCI", + "Name/IP of computer on one side of the connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_SERVER_PORT, + "PortNumber", + "SCI", + "Port used for this transporter", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_PORT_NO) }, + + { + CFG_SCI_HOST1_ID_0, + "Host1SciId0", + "SCI", + "SCI-node id for adapter 0 on Host1 (a computer can have two adapters)", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST1_ID_1, + "Host1SciId1", + "SCI", + "SCI-node id for adapter 1 on Host1 (a computer can have two adapters)", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST2_ID_0, + "Host2SciId0", + "SCI", + "SCI-node id for adapter 0 on Host2 (a computer can have two adapters)", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_SCI_HOST2_ID_1, + "Host2SciId1", + "SCI", + "SCI-node id for adapter 1 on Host2 (a computer can have two adapters)", + CI_USED, + false, + CI_INT, + "0", + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "SCI", + "Sends id in each signal. Used in trace files.", + CI_USED, + false, + CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "SCI", + "If checksum is enabled, all signals between nodes are checked for errors", + CI_USED, + false, + CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_SCI_SEND_LIMIT, + "SendLimit", + "SCI", + "Transporter send buffer contents are sent when this no of bytes is buffered", + CI_USED, + false, + CI_INT, + "8K", + "128", + "32K" }, + + { + CFG_SCI_BUFFER_MEM, + "SharedBufferSize", + "SCI", + "Size of shared memory segment", + CI_USED, + false, + CI_INT, + "1M", + "64K", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "SCI", + "System for node 1 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "SCI", + "System for node 2 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + /**************************************************************************** + * OSE + ***************************************************************************/ + { + CFG_SECTION_CONNECTION, + "OSE", + "OSE", + "Connection section", + CI_USED, + false, + CI_SECTION, + (const char *)CONNECTION_TYPE_OSE, + 0, 0 + }, + + { + CFG_CONNECTION_HOSTNAME_1, + "HostName1", + "OSE", + "Name of computer on one side of the connection", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_HOSTNAME_2, + "HostName2", + "OSE", + "Name of computer on one side of the connection", + CI_USED, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_1, + "NodeId1", + "OSE", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_INT, + MANDATORY, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_NODE_2, + "NodeId2", + "OSE", + "Id of node ("DB_TOKEN_PRINT", "API_TOKEN_PRINT" or "MGM_TOKEN_PRINT") on one side of the connection", + CI_USED, + false, + CI_INT, + UNDEFINED, + "0", + STR_VALUE(MAX_INT_RNIL) }, + + { + CFG_CONNECTION_SEND_SIGNAL_ID, + "SendSignalId", + "OSE", + "Sends id in each signal. Used in trace files.", + CI_USED, + false, + CI_BOOL, + "true", + "false", + "true" }, + + { + CFG_CONNECTION_CHECKSUM, + "Checksum", + "OSE", + "If checksum is enabled, all signals between nodes are checked for errors", + CI_USED, + false, + CI_BOOL, + "false", + "false", + "true" }, + + { + CFG_CONNECTION_NODE_1_SYSTEM, + "NodeId1_System", + "OSE", + "System for node 1 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, + + { + CFG_CONNECTION_NODE_2_SYSTEM, + "NodeId2_System", + "OSE", + "System for node 2 in connection", + CI_INTERNAL, + false, + CI_STRING, + UNDEFINED, + 0, 0 }, +}; + +const int ParamInfoNum = sizeof(ParamInfoArray) / sizeof(ParamInfo); diff --git a/storage/ndb/src/mgmsrv/ParamInfo.hpp b/storage/ndb/src/mgmsrv/ParamInfo.hpp new file mode 100644 index 00000000000..7d12cd6252f --- /dev/null +++ b/storage/ndb/src/mgmsrv/ParamInfo.hpp @@ -0,0 +1,44 @@ +#ifndef PARAMINFO_H +#define PARAMINFO_H + +#define DB_TOKEN "DB" +#define MGM_TOKEN "MGM" +#define API_TOKEN "API" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * The Configuration parameter type and status + */ + +enum ParameterType { CI_BOOL, CI_INT, CI_INT64, CI_STRING, CI_SECTION }; +enum ParameterStatus { CI_USED, ///< Active + CI_DEPRICATED, ///< Can be, but shouldn't + CI_NOTIMPLEMENTED, ///< Is ignored. + CI_INTERNAL ///< Not configurable by the user +}; + +/** + * Entry for one configuration parameter + */ +typedef struct m_ParamInfo { + Uint32 _paramId; + const char* _fname; + const char* _section; + const char* _description; + ParameterStatus _status; + bool _updateable; + ParameterType _type; + const char* _default; + const char* _min; + const char* _max; +}ParamInfo; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/storage/ndb/src/mgmsrv/Services.cpp b/storage/ndb/src/mgmsrv/Services.cpp new file mode 100644 index 00000000000..672e50729df --- /dev/null +++ b/storage/ndb/src/mgmsrv/Services.cpp @@ -0,0 +1,1857 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <ctype.h> + +#include <uucode.h> +#include <socket_io.h> +#include <ndb_version.h> +#include <mgmapi.h> +#include <EventLogger.hpp> +#include <signaldata/SetLogLevelOrd.hpp> +#include <LogLevel.hpp> +#include <BaseString.hpp> + +#include <ConfigValues.hpp> +#include <mgmapi_configuration.hpp> +#include <Vector.hpp> +#include "Services.hpp" +#include "../mgmapi/ndb_logevent.hpp" + +#include <base64.h> +#include <ndberror.h> + +extern bool g_StopServer; +extern bool g_RestartServer; +extern EventLogger g_eventLogger; + +static const unsigned int MAX_READ_TIMEOUT = 1000 ; +static const unsigned int MAX_WRITE_TIMEOUT = 100 ; + +/** + const char * name; + const char * realName; + const Type type; + const ArgType argType; + const ArgRequired argRequired; + const ArgMinMax argMinMax; + const int minVal; + const int maxVal; + void (T::* function)(const class Properties & args); + const char * description; +*/ + +#define MGM_CMD(name, fun, desc) \ + { name, \ + 0, \ + ParserRow<MgmApiSession>::Cmd, \ + ParserRow<MgmApiSession>::String, \ + ParserRow<MgmApiSession>::Optional, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + 0, 0, \ + fun, \ + desc, 0 } + +#define MGM_ARG(name, type, opt, desc) \ + { name, \ + 0, \ + ParserRow<MgmApiSession>::Arg, \ + ParserRow<MgmApiSession>::type, \ + ParserRow<MgmApiSession>::opt, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + 0, 0, \ + 0, \ + desc, 0 } + +#define MGM_ARG2(name, type, opt, min, max, desc) \ + { name, \ + 0, \ + ParserRow<MgmApiSession>::Arg, \ + ParserRow<MgmApiSession>::type, \ + ParserRow<MgmApiSession>::opt, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + min, max, \ + 0, \ + desc, 0 } + +#define MGM_END() \ + { 0, \ + 0, \ + ParserRow<MgmApiSession>::Arg, \ + ParserRow<MgmApiSession>::Int, \ + ParserRow<MgmApiSession>::Optional, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + 0, 0, \ + 0, \ + 0, 0 } + +#define MGM_CMD_ALIAS(name, realName, fun) \ + { name, \ + realName, \ + ParserRow<MgmApiSession>::CmdAlias, \ + ParserRow<MgmApiSession>::Int, \ + ParserRow<MgmApiSession>::Optional, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + 0, 0, \ + 0, \ + 0, 0 } + +#define MGM_ARG_ALIAS(name, realName, fun) \ + { name, \ + realName, \ + ParserRow<MgmApiSession>::ArgAlias, \ + ParserRow<MgmApiSession>::Int, \ + ParserRow<MgmApiSession>::Optional, \ + ParserRow<MgmApiSession>::IgnoreMinMax, \ + 0, 0, \ + 0, \ + 0, 0 } + +const +ParserRow<MgmApiSession> commands[] = { + MGM_CMD("get config", &MgmApiSession::getConfig, ""), + MGM_ARG("version", Int, Mandatory, "Configuration version number"), + MGM_ARG("node", Int, Optional, "Node ID"), + + MGM_CMD("get nodeid", &MgmApiSession::get_nodeid, ""), + MGM_ARG("version", Int, Mandatory, "Configuration version number"), + MGM_ARG("nodetype", Int, Mandatory, "Node type"), + MGM_ARG("transporter", String, Optional, "Transporter type"), + MGM_ARG("nodeid", Int, Optional, "Node ID"), + MGM_ARG("user", String, Mandatory, "Password"), + MGM_ARG("password", String, Mandatory, "Password"), + MGM_ARG("public key", String, Mandatory, "Public key"), + MGM_ARG("endian", String, Optional, "Endianness"), + MGM_ARG("name", String, Optional, "Name of connection"), + MGM_ARG("timeout", Int, Optional, "Timeout in seconds"), + MGM_ARG("log_event", Int, Optional, "Log failure in cluster log"), + + MGM_CMD("get version", &MgmApiSession::getVersion, ""), + + MGM_CMD("get status", &MgmApiSession::getStatus, ""), + + MGM_CMD("get info clusterlog", &MgmApiSession::getInfoClusterLog, ""), + MGM_CMD("get cluster loglevel", &MgmApiSession::getClusterLogLevel, ""), + + MGM_CMD("restart node", &MgmApiSession::restart_v1, ""), + MGM_ARG("node", String, Mandatory, "Nodes to restart"), + MGM_ARG("initialstart", Int, Optional, "Initial start"), + MGM_ARG("nostart", Int, Optional, "No start"), + MGM_ARG("abort", Int, Optional, "Abort"), + + MGM_CMD("restart node v2", &MgmApiSession::restart_v2, ""), + MGM_ARG("node", String, Mandatory, "Nodes to restart"), + MGM_ARG("initialstart", Int, Optional, "Initial start"), + MGM_ARG("nostart", Int, Optional, "No start"), + MGM_ARG("abort", Int, Optional, "Abort"), + + MGM_CMD("restart all", &MgmApiSession::restartAll, ""), + MGM_ARG("initialstart", Int, Optional, "Initial start"), + MGM_ARG("nostart", Int, Optional, "No start"), + MGM_ARG("abort", Int, Optional, "Abort"), + + MGM_CMD("insert error", &MgmApiSession::insertError, ""), + MGM_ARG("node", Int, Mandatory, "Node to receive error"), + MGM_ARG("error", Int, Mandatory, "Errorcode to insert"), + + MGM_CMD("set trace", &MgmApiSession::setTrace, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + MGM_ARG("trace", Int, Mandatory, "Trace number"), + + MGM_CMD("log signals", &MgmApiSession::logSignals, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + MGM_ARG("blocks", String, Mandatory, "Blocks (space separated)"), + MGM_ARG("in", Int, Mandatory, "Log input signals"), + MGM_ARG("out", Int, Mandatory, "Log output signals"), + + MGM_CMD("start signallog", &MgmApiSession::startSignalLog, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + + MGM_CMD("stop signallog", &MgmApiSession::stopSignalLog, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + + MGM_CMD("dump state", &MgmApiSession::dumpState, ""), + MGM_ARG("node", Int, Mandatory ,"Node"), + MGM_ARG("args", String, Mandatory, "Args(space separated int's)"), + + MGM_CMD("start backup", &MgmApiSession::startBackup, ""), + MGM_ARG("completed", Int, Optional ,"Wait until completed"), + + MGM_CMD("abort backup", &MgmApiSession::abortBackup, ""), + MGM_ARG("id", Int, Mandatory, "Backup id"), + + MGM_CMD("stop", &MgmApiSession::stop_v1, ""), + MGM_ARG("node", String, Mandatory, "Node"), + MGM_ARG("abort", Int, Mandatory, "Node"), + + MGM_CMD("stop v2", &MgmApiSession::stop_v2, ""), + MGM_ARG("node", String, Mandatory, "Node"), + MGM_ARG("abort", Int, Mandatory, "Node"), + + MGM_CMD("stop all", &MgmApiSession::stopAll, ""), + MGM_ARG("abort", Int, Mandatory, "Node"), + MGM_ARG("stop", String, Optional, "MGM/DB or both"), + + MGM_CMD("enter single user", &MgmApiSession::enterSingleUser, ""), + MGM_ARG("nodeId", Int, Mandatory, "Node"), + + MGM_CMD("exit single user", &MgmApiSession::exitSingleUser, ""), + + + MGM_CMD("start", &MgmApiSession::start, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + + MGM_CMD("start all", &MgmApiSession::startAll, ""), + + MGM_CMD("bye", &MgmApiSession::bye, ""), + + MGM_CMD("end session", &MgmApiSession::endSession, ""), + + MGM_CMD("set loglevel", &MgmApiSession::setLogLevel, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + MGM_ARG("category", Int, Mandatory, "Event category"), + MGM_ARG("level", Int, Mandatory, "Log level (0-15)"), + + MGM_CMD("set cluster loglevel", &MgmApiSession::setClusterLogLevel, ""), + MGM_ARG("node", Int, Mandatory, "Node"), + MGM_ARG("category", Int, Mandatory, "Event category"), + MGM_ARG("level", Int, Mandatory, "Log level (0-15)"), + + MGM_CMD("set logfilter", &MgmApiSession::setLogFilter, ""), + MGM_ARG("level", Int, Mandatory, "Severety level"), + MGM_ARG("enable", Int, Mandatory, "1=disable, 0=enable, -1=toggle"), + + MGM_CMD("set parameter", &MgmApiSession::setParameter, ""), + MGM_ARG("node", String, Mandatory, "Node"), + MGM_ARG("parameter", String, Mandatory, "Parameter"), + MGM_ARG("value", String, Mandatory, "Value"), + + MGM_CMD("set connection parameter", + &MgmApiSession::setConnectionParameter, ""), + MGM_ARG("node1", String, Mandatory, "Node1 ID"), + MGM_ARG("node2", String, Mandatory, "Node2 ID"), + MGM_ARG("param", String, Mandatory, "Parameter"), + MGM_ARG("value", String, Mandatory, "Value"), + + MGM_CMD("get connection parameter", + &MgmApiSession::getConnectionParameter, ""), + MGM_ARG("node1", String, Mandatory, "Node1 ID"), + MGM_ARG("node2", String, Mandatory, "Node2 ID"), + MGM_ARG("param", String, Mandatory, "Parameter"), + + MGM_CMD("listen event", &MgmApiSession::listen_event, ""), + MGM_ARG("node", Int, Optional, "Node"), + MGM_ARG("parsable", Int, Optional, "Parsable"), + MGM_ARG("filter", String, Mandatory, "Event category"), + + MGM_CMD("purge stale sessions", &MgmApiSession::purge_stale_sessions, ""), + + MGM_CMD("check connection", &MgmApiSession::check_connection, ""), + + MGM_CMD("transporter connect", &MgmApiSession::transporter_connect, ""), + + MGM_CMD("get mgmd nodeid", &MgmApiSession::get_mgmd_nodeid, ""), + + MGM_CMD("report event", &MgmApiSession::report_event, ""), + MGM_ARG("length", Int, Mandatory, "Length"), + MGM_ARG("data", String, Mandatory, "Data"), + + MGM_CMD("list sessions", &MgmApiSession::listSessions, ""), + + MGM_CMD("get session id", &MgmApiSession::getSessionId, ""), + + MGM_CMD("get session", &MgmApiSession::getSession, ""), + MGM_ARG("id", Int, Mandatory, "SessionID"), + + MGM_END() +}; + +struct PurgeStruct +{ + NodeBitmask free_nodes;/* free nodes as reported + * by ndbd in apiRegReqConf + */ + BaseString *str; + NDB_TICKS tick; +}; + +MgmApiSession::MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock, Uint64 session_id) + : SocketServer::Session(sock), m_mgmsrv(mgm) +{ + DBUG_ENTER("MgmApiSession::MgmApiSession"); + m_input = new SocketInputStream(sock); + m_output = new SocketOutputStream(sock); + m_parser = new Parser_t(commands, *m_input, true, true, true); + m_allocated_resources= new MgmtSrvr::Allocated_resources(m_mgmsrv); + m_stopSelf= 0; + m_ctx= NULL; + m_session_id= session_id; + m_mutex= NdbMutex_Create(); + DBUG_VOID_RETURN; +} + +MgmApiSession::~MgmApiSession() +{ + DBUG_ENTER("MgmApiSession::~MgmApiSession"); + if (m_input) + delete m_input; + if (m_output) + delete m_output; + if (m_parser) + delete m_parser; + if (m_allocated_resources) + delete m_allocated_resources; + if(m_socket != NDB_INVALID_SOCKET) + { + NDB_CLOSE_SOCKET(m_socket); + m_socket= NDB_INVALID_SOCKET; + } + if(m_stopSelf < 0) + g_RestartServer= true; + if(m_stopSelf) + g_StopServer= true; + NdbMutex_Destroy(m_mutex); + DBUG_VOID_RETURN; +} + +void +MgmApiSession::runSession() +{ + DBUG_ENTER("MgmApiSession::runSession"); + + Parser_t::Context ctx; + ctx.m_mutex= m_mutex; + m_ctx= &ctx; + bool stop= false; + while(!stop) { + NdbMutex_Lock(m_mutex); + + m_parser->run(ctx, *this); + + if(ctx.m_currentToken == 0) + { + NdbMutex_Unlock(m_mutex); + break; + } + + switch(ctx.m_status) { + case Parser_t::UnknownCommand: +#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT + /* Backwards compatibility for old NDBs that still use + * the old "GET CONFIG" command. + */ + size_t i; + for(i=0; i<strlen(ctx.m_currentToken); i++) + ctx.m_currentToken[i] = toupper(ctx.m_currentToken[i]); + + if(strncmp("GET CONFIG ", + ctx.m_currentToken, + strlen("GET CONFIG ")) == 0) + getConfig_old(ctx); +#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */ + break; + default: + break; + } + + stop= m_stop; + NdbMutex_Unlock(m_mutex); + }; + + NdbMutex_Lock(m_mutex); + m_ctx= NULL; + if(m_socket != NDB_INVALID_SOCKET) + { + NDB_CLOSE_SOCKET(m_socket); + m_socket= NDB_INVALID_SOCKET; + } + NdbMutex_Unlock(m_mutex); + DBUG_VOID_RETURN; +} + +#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT +void +MgmApiSession::getConfig_old(Parser_t::Context &ctx) { + Properties args; + + Uint32 version, node; + + if(sscanf(ctx.m_currentToken, "GET CONFIG %d %d", + (int *)&version, (int *)&node) != 2) { + m_output->println("Expected 2 arguments for GET CONFIG"); + return; + } + + /* Put arguments in properties object so we can call the real function */ + args.put("version", version); + args.put("node", node); + getConfig_common(ctx, args, true); +} +#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */ + +void +MgmApiSession::getConfig(Parser_t::Context &ctx, + const class Properties &args) { + getConfig_common(ctx, args); +} + +static Properties * +backward(const char * base, const Properties* reply){ + Properties * ret = new Properties(); + Properties::Iterator it(reply); + for(const char * name = it.first(); name != 0; name=it.next()){ + PropertiesType type; + reply->getTypeOf(name, &type); + switch(type){ + case PropertiesType_Uint32:{ + Uint32 val; + reply->get(name, &val); + ret->put(name, val); + } + break; + case PropertiesType_char: + { + const char * val; + reply->get(name, &val); + ret->put(name, val); + if(!strcmp(name, "Type") && !strcmp(val, "DB")){ + ret->put("NoOfDiskBufferPages", (unsigned)0); + ret->put("NoOfDiskFiles", (unsigned)0); + ret->put("NoOfDiskClusters", (unsigned)0); + ret->put("NoOfFreeDiskClusters", (unsigned)0); + ret->put("NoOfDiskClustersPerDiskFile", (unsigned)0); + ret->put("NoOfConcurrentCheckpointsDuringRestart", (unsigned)1); + ret->put("NoOfConcurrentCheckpointsAfterRestart", (unsigned)1); + ret->put("NoOfConcurrentProcessesHandleTakeover", (unsigned)1); + } + } + break; + case PropertiesType_Properties: + { + const Properties * recurse; + reply->get(name, &recurse); + Properties * val = backward(name, recurse); + ret->put(name, val); + } + break; + case PropertiesType_Uint64: + break; + } + } + return ret; +} + +void +MgmApiSession::get_nodeid(Parser_t::Context &, + const class Properties &args) +{ + const char *cmd= "get nodeid reply"; + Uint32 version, nodeid= 0, nodetype= 0xff; + Uint32 timeout= 20; // default seconds timeout + const char * transporter; + const char * user; + const char * password; + const char * public_key; + const char * endian= NULL; + const char * name= NULL; + Uint32 log_event= 1; + bool log_event_version; + union { long l; char c[sizeof(long)]; } endian_check; + + args.get("version", &version); + args.get("nodetype", &nodetype); + args.get("transporter", &transporter); + args.get("nodeid", &nodeid); + args.get("user", &user); + args.get("password", &password); + args.get("public key", &public_key); + args.get("endian", &endian); + args.get("name", &name); + args.get("timeout", &timeout); + /* for backwards compatability keep track if client uses new protocol */ + log_event_version= args.get("log_event", &log_event); + + endian_check.l = 1; + if(endian + && strcmp(endian,(endian_check.c[sizeof(long)-1])?"big":"little")!=0) { + m_output->println(cmd); + m_output->println("result: Node does not have the same endianness as the management server."); + m_output->println(""); + return; + } + + bool compatible; + switch (nodetype) { + case NODE_TYPE_MGM: + case NODE_TYPE_API: + compatible = ndbCompatible_mgmt_api(NDB_VERSION, version); + break; + case NODE_TYPE_DB: + compatible = ndbCompatible_mgmt_ndb(NDB_VERSION, version); + break; + default: + m_output->println(cmd); + m_output->println("result: unknown nodetype %d", nodetype); + m_output->println(""); + return; + } + + struct sockaddr_in addr; + SOCKET_SIZE_TYPE addrlen= sizeof(addr); + int r = getpeername(m_socket, (struct sockaddr*)&addr, &addrlen); + if (r != 0 ) { + m_output->println(cmd); + m_output->println("result: getpeername(%d) failed, err= %d", m_socket, r); + m_output->println(""); + return; + } + + NodeId tmp= nodeid; + if(tmp == 0 || !m_allocated_resources->is_reserved(tmp)){ + BaseString error_string; + int error_code; + NDB_TICKS tick= 0; + /* only report error on second attempt as not to clog the cluster log */ + while (!m_mgmsrv.alloc_node_id(&tmp, (enum ndb_mgm_node_type)nodetype, + (struct sockaddr*)&addr, &addrlen, + error_code, error_string, + tick == 0 ? 0 : log_event)) + { + /* NDB_MGM_ALLOCID_CONFIG_MISMATCH is a non retriable error */ + if (tick == 0 && error_code != NDB_MGM_ALLOCID_CONFIG_MISMATCH) + { + // attempt to free any timed out reservations + tick= NdbTick_CurrentMillisecond(); + struct PurgeStruct ps; + m_mgmsrv.get_connected_nodes(ps.free_nodes); + // invert connected_nodes to get free nodes + ps.free_nodes.bitXORC(NodeBitmask()); + ps.str= 0; + ps.tick= tick; + m_mgmsrv.get_socket_server()-> + foreachSession(stop_session_if_timed_out,&ps); + m_mgmsrv.get_socket_server()->checkSessions(); + error_string = ""; + continue; + } + const char *alias; + const char *str; + alias= ndb_mgm_get_node_type_alias_string((enum ndb_mgm_node_type) + nodetype, &str); + m_output->println(cmd); + m_output->println("result: %s", error_string.c_str()); + /* only use error_code protocol if client knows about it */ + if (log_event_version) + m_output->println("error_code: %d", error_code); + m_output->println(""); + return; + } + } + +#if 0 + if (!compatible){ + m_output->println(cmd); + m_output->println("result: incompatible version mgmt 0x%x and node 0x%x", + NDB_VERSION, version); + m_output->println(""); + return; + } +#endif + + m_output->println(cmd); + m_output->println("nodeid: %u", tmp); + m_output->println("result: Ok"); + m_output->println(""); + m_allocated_resources->reserve_node(tmp, timeout*1000); + + if (name) + g_eventLogger.info("Node %d: %s", tmp, name); + + return; +} + +void +MgmApiSession::getConfig_common(Parser_t::Context &, + const class Properties &args, + bool compat) { + Uint32 version, node = 0; + + args.get("version", &version); + args.get("node", &node); + + const Config *conf = m_mgmsrv.getConfig(); + if(conf == NULL) { + m_output->println("get config reply"); + m_output->println("result: Could not fetch configuration"); + m_output->println(""); + return; + } + + if(version > 0 && version < makeVersion(3, 5, 0) && compat){ + Properties *reply = backward("", conf->m_oldConfig); + reply->put("Version", version); + reply->put("LocalNodeId", node); + + backward("", reply); + //reply->print(); + + const Uint32 size = reply->getPackedSize(); + Uint32 *buffer = new Uint32[size/4+1]; + + reply->pack(buffer); + delete reply; + + const int uurows = (size + 44)/45; + char * uubuf = new char[uurows * 62+5]; + + const int uusz = uuencode_mem(uubuf, (char *)buffer, size); + delete[] buffer; + + m_output->println("GET CONFIG %d %d %d %d %d", + 0, version, node, size, uusz); + + m_output->println("begin 664 Ndb_cfg.bin"); + + /* XXX Need to write directly to the socket, because the uubuf is not + * NUL-terminated. This could/should probably be done in a nicer way. + */ + write_socket(m_socket, MAX_WRITE_TIMEOUT, uubuf, uusz); + delete[] uubuf; + + m_output->println("end"); + m_output->println(""); + return; + } + + if(compat){ + m_output->println("GET CONFIG %d %d %d %d %d",1, version, 0, 0, 0); + return; + } + + if(node != 0){ + bool compatible; + switch (m_mgmsrv.getNodeType(node)) { + case NDB_MGM_NODE_TYPE_NDB: + compatible = ndbCompatible_mgmt_ndb(NDB_VERSION, version); + break; + case NDB_MGM_NODE_TYPE_API: + case NDB_MGM_NODE_TYPE_MGM: + compatible = ndbCompatible_mgmt_api(NDB_VERSION, version); + break; + default: + m_output->println("get config"); + m_output->println("result: unrecognignized node type"); + m_output->println(""); + return; + } + + if (!compatible){ + m_output->println("get config"); + m_output->println("result: incompatible version mgmt 0x%x and node 0x%x", + NDB_VERSION, version); + m_output->println(""); + return; + } + } + + NdbMutex_Lock(m_mgmsrv.m_configMutex); + const ConfigValues * cfg = &conf->m_configValues->m_config; + const Uint32 size = cfg->getPackedSize(); + + UtilBuffer src; + cfg->pack(src); + NdbMutex_Unlock(m_mgmsrv.m_configMutex); + + char *tmp_str = (char *) malloc(base64_needed_encoded_length(src.length())); + int res = base64_encode(src.get_data(), src.length(), tmp_str); + + m_output->println("get config reply"); + m_output->println("result: Ok"); + m_output->println("Content-Length: %d", strlen(tmp_str)); + m_output->println("Content-Type: ndbconfig/octet-stream"); + m_output->println("Content-Transfer-Encoding: base64"); + m_output->println(""); + m_output->println(tmp_str); + + free(tmp_str); + return; +} + +void +MgmApiSession::insertError(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node = 0, error = 0; + + args.get("node", &node); + args.get("error", &error); + + int result = m_mgmsrv.insertError(node, error); + + m_output->println("insert error reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::setTrace(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node = 0, trace = 0; + + args.get("node", &node); + args.get("trace", &trace); + + int result = m_mgmsrv.setTraceNo(node, trace); + + m_output->println("set trace reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::getVersion(Parser<MgmApiSession>::Context &, + Properties const &) { + m_output->println("version"); + m_output->println("id: %d", NDB_VERSION); + m_output->println("major: %d", getMajor(NDB_VERSION)); + m_output->println("minor: %d", getMinor(NDB_VERSION)); + m_output->println("string: %s", NDB_VERSION_STRING); + m_output->println(""); +} + +void +MgmApiSession::startBackup(Parser<MgmApiSession>::Context &, + Properties const &args) { + DBUG_ENTER("MgmApiSession::startBackup"); + unsigned backupId; + Uint32 completed= 2; + int result; + + args.get("completed", &completed); + + result = m_mgmsrv.startBackup(backupId, completed); + + m_output->println("start backup reply"); + if(result != 0) + { + m_output->println("result: %s", get_error_text(result)); + } + else{ + m_output->println("result: Ok"); + if (completed) + m_output->println("id: %d", backupId); + } + m_output->println(""); + DBUG_VOID_RETURN; +} + +void +MgmApiSession::abortBackup(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 id = 0; + + args.get("id", &id); + + int result = m_mgmsrv.abortBackup(id); + + m_output->println("abort backup reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +/*****************************************************************************/ + +void +MgmApiSession::dumpState(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node; + BaseString args_str; + + args.get("node", &node); + args.get("args", args_str); + + int result = m_mgmsrv.dumpState(node, args_str.c_str()); + m_output->println("dump state reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + + +void +MgmApiSession::bye(Parser<MgmApiSession>::Context &, + Properties const &) { + m_stop = true; +} + +void +MgmApiSession::endSession(Parser<MgmApiSession>::Context &, + Properties const &) { + if(m_allocated_resources) + delete m_allocated_resources; + + m_allocated_resources= new MgmtSrvr::Allocated_resources(m_mgmsrv); + + m_output->println("end session reply"); +} + +void +MgmApiSession::getClusterLogLevel(Parser<MgmApiSession>::Context & , Properties const &) { + const char* names[] = { "startup", + "shutdown", + "statistics", + "checkpoint", + "noderestart", + "connection", + "info", + "warning", + "error", + "congestion", + "debug", + "backup" }; + + int loglevel_count = (CFG_MAX_LOGLEVEL - CFG_MIN_LOGLEVEL + 1) ; + LogLevel::EventCategory category; + + m_output->println("get cluster loglevel"); + for(int i = 0; i < loglevel_count; i++) { + category = (LogLevel::EventCategory) i; + m_output->println("%s: %d", names[i], m_mgmsrv.m_event_listner[0].m_logLevel.getLogLevel(category)); + } + m_output->println(""); +} + +void +MgmApiSession::setClusterLogLevel(Parser<MgmApiSession>::Context &, + Properties const &args) { + const char *reply= "set cluster loglevel reply"; + Uint32 node, level, cat; + BaseString errorString; + SetLogLevelOrd logLevel; + int result; + DBUG_ENTER("MgmApiSession::setClusterLogLevel"); + args.get("node", &node); + args.get("category", &cat); + args.get("level", &level); + + DBUG_PRINT("enter",("node=%d, category=%d, level=%d", node, cat, level)); + + /* XXX should use constants for this value */ + if(level > 15) { + m_output->println(reply); + m_output->println("result: Invalid loglevel %d", level); + m_output->println(""); + DBUG_VOID_RETURN; + } + + LogLevel::EventCategory category= + (LogLevel::EventCategory)(cat-(int)CFG_MIN_LOGLEVEL); + + m_mgmsrv.m_event_listner.lock(); + if (m_mgmsrv.m_event_listner[0].m_logLevel.setLogLevel(category,level)) + { + m_output->println(reply); + m_output->println("result: Invalid category %d", category); + m_output->println(""); + m_mgmsrv.m_event_listner.unlock(); + DBUG_VOID_RETURN; + } + m_mgmsrv.m_event_listner.unlock(); + + { + LogLevel tmp; + m_mgmsrv.m_event_listner.update_max_log_level(tmp); + } + + m_output->println(reply); + m_output->println("result: Ok"); + m_output->println(""); + DBUG_VOID_RETURN; +} + +void +MgmApiSession::setLogLevel(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node = 0, level = 0, cat; + BaseString errorString; + SetLogLevelOrd logLevel; + int result; + logLevel.clear(); + args.get("node", &node); + args.get("category", &cat); + args.get("level", &level); + + /* XXX should use constants for this value */ + if(level > 15) { + m_output->println("set loglevel reply"); + m_output->println("result: Invalid loglevel", errorString.c_str()); + m_output->println(""); + return; + } + + LogLevel::EventCategory category= + (LogLevel::EventCategory)(cat-(int)CFG_MIN_LOGLEVEL); + + { + LogLevel ll; + ll.setLogLevel(category,level); + m_mgmsrv.m_event_listner.update_max_log_level(ll); + } + + m_output->println("set loglevel reply"); + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::stopSignalLog(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node; + + args.get("node", &node); + + int result = m_mgmsrv.stopSignalTracing(node); + + m_output->println("stop signallog"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::restart_v1(Parser<MgmApiSession>::Context &, + Properties const &args) { + restart(args,1); +} + +void +MgmApiSession::restart_v2(Parser<MgmApiSession>::Context &, + Properties const &args) { + restart(args,2); +} + +void +MgmApiSession::restart(Properties const &args, int version) { + Uint32 + nostart = 0, + initialstart = 0, + abort = 0; + char *nodes_str; + Vector<NodeId> nodes; + + args.get("initialstart", &initialstart); + args.get("nostart", &nostart); + args.get("abort", &abort); + args.get("node", (const char **)&nodes_str); + + char *p, *last; + for((p = strtok_r(nodes_str, " ", &last)); + p; + (p = strtok_r(NULL, " ", &last))) { + nodes.push_back(atoi(p)); + } + + int restarted = 0; + int result= m_mgmsrv.restartNodes(nodes, + &restarted, + nostart != 0, + initialstart != 0, + abort != 0, + &m_stopSelf); + + m_output->println("restart reply"); + if(result != 0){ + m_output->println("result: %d-%s", result, get_error_text(result)); + } else + m_output->println("result: Ok"); + m_output->println("restarted: %d", restarted); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); + m_output->println(""); +} + +void +MgmApiSession::restartAll(Parser<MgmApiSession>::Context &, + Properties const &args) +{ + Uint32 nostart = 0; + Uint32 initialstart = 0; + Uint32 abort = 0; + + args.get("initialstart", &initialstart); + args.get("abort", &abort); + args.get("nostart", &nostart); + + int count = 0; + int result = m_mgmsrv.restartDB(nostart, initialstart, abort, &count); + + m_output->println("restart reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println("restarted: %d", count); + m_output->println(""); +} + +static void +printNodeStatus(OutputStream *output, + MgmtSrvr &mgmsrv, + enum ndb_mgm_node_type type) { + NodeId nodeId = 0; + mgmsrv.updateStatus(); + while(mgmsrv.getNextNodeId(&nodeId, type)) { + enum ndb_mgm_node_status status; + Uint32 startPhase = 0, + version = 0, + dynamicId = 0, + nodeGroup = 0, + connectCount = 0; + bool system; + const char *address= NULL; + mgmsrv.status(nodeId, &status, &version, &startPhase, + &system, &dynamicId, &nodeGroup, &connectCount, + &address); + output->println("node.%d.type: %s", + nodeId, + ndb_mgm_get_node_type_string(type)); + output->println("node.%d.status: %s", + nodeId, + ndb_mgm_get_node_status_string(status)); + output->println("node.%d.version: %d", nodeId, version); + output->println("node.%d.startphase: %d", nodeId, startPhase); + output->println("node.%d.dynamic_id: %d", nodeId, dynamicId); + output->println("node.%d.node_group: %d", nodeId, nodeGroup); + output->println("node.%d.connect_count: %d", nodeId, connectCount); + output->println("node.%d.address: %s", nodeId, address ? address : ""); + } + +} + +void +MgmApiSession::getStatus(Parser<MgmApiSession>::Context &, + Properties const &) { + int noOfNodes = 0; + + NodeId nodeId = 0; + while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_NDB)){ + noOfNodes++; + } + nodeId = 0; + while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_API)){ + noOfNodes++; + } + nodeId = 0; + while(m_mgmsrv.getNextNodeId(&nodeId, NDB_MGM_NODE_TYPE_MGM)){ + noOfNodes++; + } + + m_output->println("node status"); + m_output->println("nodes: %d", noOfNodes); + printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_NDB); + printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_MGM); + printNodeStatus(m_output, m_mgmsrv, NDB_MGM_NODE_TYPE_API); + + nodeId = 0; + + m_output->println(""); +} + +void +MgmApiSession::getInfoClusterLog(Parser<MgmApiSession>::Context &, + Properties const &) { + const char* names[] = { "enabled", + "debug", + "info", + "warning", + "error", + "critical", + "alert" }; + + m_output->println("clusterlog"); + for(int i = 0; i < 7; i++) { + m_output->println("%s: %d", + names[i], m_mgmsrv.isEventLogFilterEnabled(i)); + } + m_output->println(""); +} + +void +MgmApiSession::stop_v1(Parser<MgmApiSession>::Context &, + Properties const &args) { + stop(args,1); +} + +void +MgmApiSession::stop_v2(Parser<MgmApiSession>::Context &, + Properties const &args) { + stop(args,2); +} + +void +MgmApiSession::stop(Properties const &args, int version) { + Uint32 abort; + char *nodes_str; + Vector<NodeId> nodes; + + args.get("node", (const char **)&nodes_str); + if(nodes_str == NULL) + { + m_output->println("stop reply"); + m_output->println("result: empty node list"); + m_output->println(""); + return; + } + args.get("abort", &abort); + + char *p, *last; + for((p = strtok_r(nodes_str, " ", &last)); + p; + (p = strtok_r(NULL, " ", &last))) { + nodes.push_back(atoi(p)); + } + + int stopped= 0; + int result= 0; + if (nodes.size()) + result= m_mgmsrv.stopNodes(nodes, &stopped, abort != 0, &m_stopSelf); + + m_output->println("stop reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println("stopped: %d", stopped); + if(version>1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); + m_output->println(""); +} + +void +MgmApiSession::stopAll(Parser<MgmApiSession>::Context &, + Properties const &args) { + int stopped[2] = {0,0}; + Uint32 abort; + args.get("abort", &abort); + + BaseString stop; + const char* tostop= "db"; + int ver=1; + if (args.get("stop", stop)) + { + tostop= stop.c_str(); + ver= 2; + } + + int result= 0; + if(strstr(tostop,"db")) + result= m_mgmsrv.shutdownDB(&stopped[0], abort != 0); + if(!result && strstr(tostop,"mgm")) + result= m_mgmsrv.shutdownMGM(&stopped[1], abort!=0, &m_stopSelf); + + m_output->println("stop reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println("stopped: %d", stopped[0]+stopped[1]); + if(ver >1) + m_output->println("disconnect: %d", (m_stopSelf)?1:0); + m_output->println(""); +} + +void +MgmApiSession::enterSingleUser(Parser<MgmApiSession>::Context &, + Properties const &args) { + int stopped = 0; + Uint32 nodeId = 0; + args.get("nodeId", &nodeId); + int result = m_mgmsrv.enterSingleUser(&stopped, nodeId); + m_output->println("enter single user reply"); + if(result != 0) { + m_output->println("result: %s", get_error_text(result)); + } + else { + m_output->println("result: Ok"); + } + m_output->println(""); +} + +void +MgmApiSession::exitSingleUser(Parser<MgmApiSession>::Context &, + Properties const &args) { + int stopped = 0; + int result = m_mgmsrv.exitSingleUser(&stopped, false); + m_output->println("exit single user reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + + +void +MgmApiSession::startSignalLog(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node; + + args.get("node", &node); + + int result = m_mgmsrv.startSignalTracing(node); + + m_output->println("start signallog reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::logSignals(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node = 0, in = 0, out = 0; + // BaseString blocks; + BaseString blockList; + char * blockName; + args.get("node", &node); + args.get("in", &in); + args.get("out", &out); + args.get("blocks", blockList); + // fast fix - pekka + char buf[200]; + BaseString::snprintf(buf, 200, "%s", blockList.c_str()); + Vector<BaseString> blocks; + + blockName=strtok(buf,"|"); + while( blockName != NULL) + { + blocks.push_back(blockName); + blockName=strtok(NULL,"|"); + } + + + if(in > 1 || out > 1) + return; /* Invalid arguments */ + + const MgmtSrvr::LogMode modes[] = { + MgmtSrvr::Off, + MgmtSrvr::Out, + MgmtSrvr::In, + MgmtSrvr::InOut, + }; + MgmtSrvr::LogMode mode = modes[in<<1 | out]; + + int result = m_mgmsrv.setSignalLoggingMode(node, mode, blocks); + + m_output->println("log signals reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::start(Parser<MgmApiSession>::Context &, + Properties const &args) { + Uint32 node; + + args.get("node", &node); + + int result = m_mgmsrv.start(node); + + m_output->println("start reply"); + if(result != 0) + m_output->println("result: %s", get_error_text(result)); + else + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::startAll(Parser<MgmApiSession>::Context &, + Properties const &) { + NodeId node = 0; + int started = 0; + + while(m_mgmsrv.getNextNodeId(&node, NDB_MGM_NODE_TYPE_NDB)) + if(m_mgmsrv.start(node) == 0) + started++; + + m_output->println("start reply"); + m_output->println("result: Ok"); + m_output->println("started: %d", started); + m_output->println(""); +} + +void +MgmApiSession::setLogFilter(Parser_t::Context &ctx, + const class Properties &args) { + Uint32 severity; + Uint32 enable; + + args.get("level", &severity); + args.get("enable", &enable); + + int result = m_mgmsrv.setEventLogFilter(severity, enable); + + m_output->println("set logfilter reply"); + m_output->println("result: %d", result); + m_output->println(""); +} + +static NdbOut& +operator<<(NdbOut& out, const LogLevel & ll) +{ + out << "[LogLevel: "; + for(size_t i = 0; i<LogLevel::LOGLEVEL_CATEGORIES; i++) + out << ll.getLogLevel((LogLevel::EventCategory)i) << " "; + out << "]"; + return out; +} + +void +Ndb_mgmd_event_service::log(int eventType, const Uint32* theData, NodeId nodeId){ + + Uint32 threshold; + LogLevel::EventCategory cat; + Logger::LoggerLevel severity; + EventLoggerBase::EventTextFunction textF; + int i, n; + DBUG_ENTER("Ndb_mgmd_event_service::log"); + DBUG_PRINT("enter",("eventType=%d, nodeid=%d", eventType, nodeId)); + + if (EventLoggerBase::event_lookup(eventType,cat,threshold,severity,textF)) + DBUG_VOID_RETURN; + + char m_text[512]; + EventLogger::getText(m_text, sizeof(m_text), + textF, theData, nodeId); + + BaseString str("log event reply\n"); + str.appfmt("type=%d\n", eventType); + str.appfmt("time=%d\n", 0); + str.appfmt("source_nodeid=%d\n", nodeId); + for (i= 0; ndb_logevent_body[i].token; i++) + { + if ( ndb_logevent_body[i].type != eventType) + continue; + int val= theData[ndb_logevent_body[i].index]; + if (ndb_logevent_body[i].index_fn) + val= (*(ndb_logevent_body[i].index_fn))(val); + str.appfmt("%s=%d\n",ndb_logevent_body[i].token, val); + if(strcmp(ndb_logevent_body[i].token,"error") == 0) + { + int m_text_len= strlen(m_text); + if(sizeof(m_text)-m_text_len-3 > 0) + { + BaseString::snprintf(m_text+m_text_len, 4 , " - "); + ndb_error_string(val, m_text+(m_text_len+3), sizeof(m_text)-m_text_len-3); + } + } + } + + Vector<NDB_SOCKET_TYPE> copy; + m_clients.lock(); + for(i = m_clients.size() - 1; i >= 0; i--) + { + if(threshold <= m_clients[i].m_logLevel.getLogLevel(cat)) + { + NDB_SOCKET_TYPE fd= m_clients[i].m_socket; + if(fd != NDB_INVALID_SOCKET) + { + int r; + if (m_clients[i].m_parsable) + r= println_socket(fd, + MAX_WRITE_TIMEOUT, str.c_str()); + else + r= println_socket(fd, + MAX_WRITE_TIMEOUT, m_text); + if (r == -1) { + copy.push_back(fd); + m_clients.erase(i, false); + } + } + } + } + m_clients.unlock(); + + if ((n= (int)copy.size())) + { + for(i= 0; i < n; i++) + NDB_CLOSE_SOCKET(copy[i]); + + LogLevel tmp; tmp.clear(); + m_clients.lock(); + for(i= m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_clients[i].m_logLevel); + m_clients.unlock(); + update_log_level(tmp); + } + DBUG_VOID_RETURN; +} + +void +Ndb_mgmd_event_service::update_max_log_level(const LogLevel &log_level) +{ + LogLevel tmp = log_level; + m_clients.lock(); + for(int i = m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_clients[i].m_logLevel); + m_clients.unlock(); + update_log_level(tmp); +} + +void +Ndb_mgmd_event_service::update_log_level(const LogLevel &tmp) +{ + m_logLevel = tmp; + EventSubscribeReq req; + req = tmp; + // send update to all nodes + req.blockRef = 0; + m_mgmsrv->m_log_level_requests.push_back(req); +} + +void +Ndb_mgmd_event_service::check_listeners() +{ + int i, n= 0; + DBUG_ENTER("Ndb_mgmd_event_service::check_listeners"); + m_clients.lock(); + for(i= m_clients.size() - 1; i >= 0; i--) + { + int fd= m_clients[i].m_socket; + DBUG_PRINT("info",("%d %d",i,fd)); + char buf[1]; + buf[0]=0; + if (fd != NDB_INVALID_SOCKET && + println_socket(fd,MAX_WRITE_TIMEOUT,"<PING>") == -1) + { + NDB_CLOSE_SOCKET(fd); + m_clients.erase(i, false); + n=1; + } + } + if (n) + { + LogLevel tmp; tmp.clear(); + for(i= m_clients.size() - 1; i >= 0; i--) + tmp.set_max(m_clients[i].m_logLevel); + update_log_level(tmp); + } + m_clients.unlock(); + DBUG_VOID_RETURN; +} + +void +Ndb_mgmd_event_service::add_listener(const Event_listener& client) +{ + DBUG_ENTER("Ndb_mgmd_event_service::add_listener"); + DBUG_PRINT("enter",("client.m_socket: %d", client.m_socket)); + + check_listeners(); + + m_clients.push_back(client); + update_max_log_level(client.m_logLevel); + + DBUG_VOID_RETURN; +} + +void +Ndb_mgmd_event_service::stop_sessions(){ + m_clients.lock(); + for(int i = m_clients.size() - 1; i >= 0; i--){ + if(m_clients[i].m_socket != NDB_INVALID_SOCKET){ + NDB_CLOSE_SOCKET(m_clients[i].m_socket); + m_clients.erase(i); + } + } + m_clients.unlock(); +} + +void +MgmApiSession::setParameter(Parser_t::Context &, + Properties const &args) { + BaseString node, param, value; + args.get("node", node); + args.get("parameter", param); + args.get("value", value); + + BaseString result; + int ret = m_mgmsrv.setDbParameter(atoi(node.c_str()), + atoi(param.c_str()), + value.c_str(), + result); + + m_output->println("set parameter reply"); + m_output->println("message: %s", result.c_str()); + m_output->println("result: %d", ret); + m_output->println(""); +} + +void +MgmApiSession::setConnectionParameter(Parser_t::Context &ctx, + Properties const &args) { + BaseString node1, node2, param, value; + args.get("node1", node1); + args.get("node2", node2); + args.get("param", param); + args.get("value", value); + + BaseString result; + int ret = m_mgmsrv.setConnectionDbParameter(atoi(node1.c_str()), + atoi(node2.c_str()), + atoi(param.c_str()), + atoi(value.c_str()), + result); + + m_output->println("set connection parameter reply"); + m_output->println("message: %s", result.c_str()); + m_output->println("result: %s", (ret>0)?"Ok":"Failed"); + m_output->println(""); +} + +void +MgmApiSession::getConnectionParameter(Parser_t::Context &ctx, + Properties const &args) { + BaseString node1, node2, param; + int value = 0; + + args.get("node1", node1); + args.get("node2", node2); + args.get("param", param); + + BaseString result; + int ret = m_mgmsrv.getConnectionDbParameter(atoi(node1.c_str()), + atoi(node2.c_str()), + atoi(param.c_str()), + &value, + result); + + m_output->println("get connection parameter reply"); + m_output->println("value: %d", value); + m_output->println("result: %s", (ret>0)?"Ok":result.c_str()); + m_output->println(""); +} + +void +MgmApiSession::listen_event(Parser<MgmApiSession>::Context & ctx, + Properties const & args) { + Uint32 parsable= 0; + BaseString node, param, value; + args.get("node", node); + args.get("filter", param); + args.get("parsable", &parsable); + + int result = 0; + BaseString msg; + + Ndb_mgmd_event_service::Event_listener le; + le.m_parsable = parsable; + le.m_socket = m_socket; + + Vector<BaseString> list; + param.trim(); + param.split(list, " ,"); + for(size_t i = 0; i<list.size(); i++){ + Vector<BaseString> spec; + list[i].trim(); + list[i].split(spec, "=:"); + if(spec.size() != 2){ + msg.appfmt("Invalid filter specification: >%s< >%s< %d", + param.c_str(), list[i].c_str(), spec.size()); + result = -1; + goto done; + } + + spec[0].trim().ndb_toupper(); + int category = ndb_mgm_match_event_category(spec[0].c_str()); + if(category == NDB_MGM_ILLEGAL_EVENT_CATEGORY){ + category = atoi(spec[0].c_str()); + if(category < NDB_MGM_MIN_EVENT_CATEGORY || + category > NDB_MGM_MAX_EVENT_CATEGORY){ + msg.appfmt("Unknown category: >%s<", spec[0].c_str()); + result = -1; + goto done; + } + } + + int level = atoi(spec[1].c_str()); + if(level < 0 || level > 15){ + msg.appfmt("Invalid level: >%s<", spec[1].c_str()); + result = -1; + goto done; + } + category -= CFG_MIN_LOGLEVEL; + le.m_logLevel.setLogLevel((LogLevel::EventCategory)category, level); + } + + if(list.size() == 0){ + msg.appfmt("Empty filter specification"); + result = -1; + goto done; + } + +done: + m_output->println("listen event"); + m_output->println("result: %d", result); + if(result != 0) + m_output->println("msg: %s", msg.c_str()); + m_output->println(""); + + if(result==0) + { + m_mgmsrv.m_event_listner.add_listener(le); + m_stop = true; + m_socket = NDB_INVALID_SOCKET; + } +} + +void +MgmApiSession::stop_session_if_not_connected(SocketServer::Session *_s, void *data) +{ + MgmApiSession *s= (MgmApiSession *)_s; + struct PurgeStruct &ps= *(struct PurgeStruct *)data; + if (s->m_allocated_resources->is_reserved(ps.free_nodes)) + { + if (ps.str) + ps.str->appfmt(" %d", s->m_allocated_resources->get_nodeid()); + s->stopSession(); + } +} + +void +MgmApiSession::stop_session_if_timed_out(SocketServer::Session *_s, void *data) +{ + MgmApiSession *s= (MgmApiSession *)_s; + struct PurgeStruct &ps= *(struct PurgeStruct *)data; + if (s->m_allocated_resources->is_reserved(ps.free_nodes) && + s->m_allocated_resources->is_timed_out(ps.tick)) + { + s->stopSession(); + } +} + +void +MgmApiSession::purge_stale_sessions(Parser_t::Context &ctx, + const class Properties &args) +{ + struct PurgeStruct ps; + BaseString str; + ps.str = &str; + + m_mgmsrv.get_connected_nodes(ps.free_nodes); + ps.free_nodes.bitXORC(NodeBitmask()); // invert connected_nodes to get free nodes + + m_mgmsrv.get_socket_server()->foreachSession(stop_session_if_not_connected,&ps); + m_mgmsrv.get_socket_server()->checkSessions(); + + m_output->println("purge stale sessions reply"); + if (str.length() > 0) + m_output->println("purged:%s",str.c_str()); + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::check_connection(Parser_t::Context &ctx, + const class Properties &args) +{ + m_output->println("check connection reply"); + m_output->println("result: Ok"); + m_output->println(""); +} + +void +MgmApiSession::transporter_connect(Parser_t::Context &ctx, + Properties const &args) +{ + m_mgmsrv.transporter_connect(m_socket); + + m_stop= true; + m_stopped= true; // force a stop (no closing socket) + m_socket= NDB_INVALID_SOCKET; // so nobody closes it +} + +void +MgmApiSession::get_mgmd_nodeid(Parser_t::Context &ctx, + Properties const &args) +{ + m_output->println("get mgmd nodeid reply"); + m_output->println("nodeid:%u",m_mgmsrv.getOwnNodeId()); + m_output->println(""); +} + +void +MgmApiSession::report_event(Parser_t::Context &ctx, + Properties const &args) +{ + Uint32 length; + const char *data_string; + Uint32 data[25]; + + args.get("length", &length); + args.get("data", &data_string); + + BaseString tmp(data_string); + Vector<BaseString> item; + tmp.split(item, " "); + for (int i = 0; (Uint32) i < length ; i++) + { + sscanf(item[i].c_str(), "%u", data+i); + } + + m_mgmsrv.eventReport(data); + m_output->println("report event reply"); + m_output->println("result: ok"); + m_output->println(""); +} + +void +MgmApiSession::list_session(SocketServer::Session *_s, void *data) +{ + MgmApiSession *s= (MgmApiSession *)_s; + MgmApiSession *lister= (MgmApiSession*) data; + + if(s!=lister) + NdbMutex_Lock(s->m_mutex); + + Uint64 id= s->m_session_id; + lister->m_output->println("session: %llu",id); + lister->m_output->println("session.%llu.m_stopSelf: %d",id,s->m_stopSelf); + lister->m_output->println("session.%llu.m_stop: %d",id,s->m_stop); + lister->m_output->println("session.%llu.allocated.nodeid: %d",id,s->m_allocated_resources->get_nodeid()); + if(s->m_ctx) + { + int l= strlen(s->m_ctx->m_tokenBuffer); + char *buf= (char*) malloc(2*l+1); + char *b= buf; + for(int i=0; i<l;i++) + if(s->m_ctx->m_tokenBuffer[i]=='\n') + { + *b++='\\'; + *b++='n'; + } + else + { + *b++= s->m_ctx->m_tokenBuffer[i]; + } + *b= '\0'; + + lister->m_output->println("session.%llu.parser.buffer.len: %u",id,l); + lister->m_output->println("session.%llu.parser.buffer: %s",id,buf); + lister->m_output->println("session.%llu.parser.status: %d",id,s->m_ctx->m_status); + + free(buf); + } + + if(s!=lister) + NdbMutex_Unlock(s->m_mutex); +} + +void +MgmApiSession::listSessions(Parser_t::Context &ctx, + Properties const &args) { + m_mgmsrv.get_socket_server()->foreachSession(list_session,(void*)this); + + m_output->println(""); +} + +void +MgmApiSession::getSessionId(Parser_t::Context &ctx, + Properties const &args) { + m_output->println("get session id reply"); + m_output->println("id: %llu",m_session_id); + m_output->println(""); +} + +struct get_session_param { + MgmApiSession *l; + Uint64 id; + int found; +}; + +void +MgmApiSession::get_session(SocketServer::Session *_s, void *data) +{ + struct get_session_param *p= (struct get_session_param*)data; + MgmApiSession *s= (MgmApiSession *)_s; + + if(s!=p->l) + NdbMutex_Lock(s->m_mutex); + + if(p->id != s->m_session_id) + { + if(s!=p->l) + NdbMutex_Unlock(s->m_mutex); + return; + } + + p->found= true; + p->l->m_output->println("id: %llu",s->m_session_id); + p->l->m_output->println("m_stopSelf: %d",s->m_stopSelf); + p->l->m_output->println("m_stop: %d",s->m_stop); + p->l->m_output->println("nodeid: %d",s->m_allocated_resources->get_nodeid()); + if(s->m_ctx) + { + int l= strlen(s->m_ctx->m_tokenBuffer); + p->l->m_output->println("parser_buffer_len: %u",l); + p->l->m_output->println("parser_status: %d",s->m_ctx->m_status); + } + + if(s!=p->l) + NdbMutex_Unlock(s->m_mutex); +} + +void +MgmApiSession::getSession(Parser_t::Context &ctx, + Properties const &args) { + Uint64 id; + struct get_session_param p; + + args.get("id", &id); + + p.l= this; + p.id= id; + p.found= false; + + m_output->println("get session reply"); + m_mgmsrv.get_socket_server()->foreachSession(get_session,(void*)&p); + + if(p.found==false) + m_output->println("id: 0"); + + m_output->println(""); +} + +template class MutexVector<int>; +template class Vector<ParserRow<MgmApiSession> const*>; diff --git a/storage/ndb/src/mgmsrv/Services.hpp b/storage/ndb/src/mgmsrv/Services.hpp new file mode 100644 index 00000000000..8b9ca7156b9 --- /dev/null +++ b/storage/ndb/src/mgmsrv/Services.hpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef MGMAPI_SERVICE_HPP +#define MGMAPI_SERVICE_HPP + +#include <SocketServer.hpp> +#include <NdbSleep.h> +#include <Parser.hpp> +#include <OutputStream.hpp> +#include <InputStream.hpp> + +#include "MgmtSrvr.hpp" + +/** Undefine this to remove backwards compatibility for "GET CONFIG". */ +#define MGM_GET_CONFIG_BACKWARDS_COMPAT + +class MgmApiSession : public SocketServer::Session +{ + static void stop_session_if_timed_out(SocketServer::Session *_s, void *data); + static void stop_session_if_not_connected(SocketServer::Session *_s, void *data); + static void list_session(SocketServer::Session *_s, void *data); + static void get_session(SocketServer::Session *_s, void *data); +private: + typedef Parser<MgmApiSession> Parser_t; + + class MgmtSrvr & m_mgmsrv; + InputStream *m_input; + OutputStream *m_output; + Parser_t *m_parser; + MgmtSrvr::Allocated_resources *m_allocated_resources; + char m_err_str[1024]; + int m_stopSelf; // -1 is restart, 0 do nothing, 1 stop + NdbMutex *m_mutex; + + // for listing sessions and other fun: + Parser_t::Context *m_ctx; + Uint64 m_session_id; + + void getConfig_common(Parser_t::Context &ctx, + const class Properties &args, + bool compat = false); + const char *get_error_text(int err_no) + { return m_mgmsrv.getErrorText(err_no, m_err_str, sizeof(m_err_str)); } + +public: + MgmApiSession(class MgmtSrvr & mgm, NDB_SOCKET_TYPE sock, Uint64 session_id); + virtual ~MgmApiSession(); + void runSession(); + + void getConfig(Parser_t::Context &ctx, const class Properties &args); +#ifdef MGM_GET_CONFIG_BACKWARDS_COMPAT + void getConfig_old(Parser_t::Context &ctx); +#endif /* MGM_GET_CONFIG_BACKWARDS_COMPAT */ + + void get_nodeid(Parser_t::Context &ctx, const class Properties &args); + void getVersion(Parser_t::Context &ctx, const class Properties &args); + void getStatus(Parser_t::Context &ctx, const class Properties &args); + void getInfoClusterLog(Parser_t::Context &ctx, const class Properties &args); + void restart(const class Properties &args, int version); + void restart_v1(Parser_t::Context &ctx, const class Properties &args); + void restart_v2(Parser_t::Context &ctx, const class Properties &args); + void restartAll(Parser_t::Context &ctx, const class Properties &args); + void insertError(Parser_t::Context &ctx, const class Properties &args); + void setTrace(Parser_t::Context &ctx, const class Properties &args); + void logSignals(Parser_t::Context &ctx, const class Properties &args); + void startSignalLog(Parser_t::Context &ctx, const class Properties &args); + void stopSignalLog(Parser_t::Context &ctx, const class Properties &args); + void dumpState(Parser_t::Context &ctx, const class Properties &args); + void startBackup(Parser_t::Context &ctx, const class Properties &args); + void abortBackup(Parser_t::Context &ctx, const class Properties &args); + void enterSingleUser(Parser_t::Context &ctx, const class Properties &args); + void exitSingleUser(Parser_t::Context &ctx, const class Properties &args); + void stop_v1(Parser_t::Context &ctx, const class Properties &args); + void stop_v2(Parser_t::Context &ctx, const class Properties &args); + void stop(const class Properties &args, int version); + void stopAll(Parser_t::Context &ctx, const class Properties &args); + void start(Parser_t::Context &ctx, const class Properties &args); + void startAll(Parser_t::Context &ctx, const class Properties &args); + void bye(Parser_t::Context &ctx, const class Properties &args); + void endSession(Parser_t::Context &ctx, const class Properties &args); + void setLogLevel(Parser_t::Context &ctx, const class Properties &args); + void getClusterLogLevel(Parser_t::Context &ctx, + const class Properties &args); + void setClusterLogLevel(Parser_t::Context &ctx, + const class Properties &args); + void setLogFilter(Parser_t::Context &ctx, const class Properties &args); + + void setParameter(Parser_t::Context &ctx, const class Properties &args); + void setConnectionParameter(Parser_t::Context &ctx, + const class Properties &args); + void getConnectionParameter(Parser_t::Context &ctx, + Properties const &args); + + void listen_event(Parser_t::Context &ctx, const class Properties &args); + + void purge_stale_sessions(Parser_t::Context &ctx, const class Properties &args); + void check_connection(Parser_t::Context &ctx, const class Properties &args); + + void transporter_connect(Parser_t::Context &ctx, Properties const &args); + + void get_mgmd_nodeid(Parser_t::Context &ctx, Properties const &args); + + void report_event(Parser_t::Context &ctx, Properties const &args); + + void listSessions(Parser_t::Context &ctx, Properties const &args); + + void getSessionId(Parser_t::Context &ctx, Properties const &args); + void getSession(Parser_t::Context &ctx, Properties const &args); +}; + +class MgmApiService : public SocketServer::Service { + class MgmtSrvr * m_mgmsrv; + Uint64 m_next_session_id; // Protected by m_sessions mutex it SocketServer +public: + MgmApiService(){ + m_mgmsrv = 0; + m_next_session_id= 1; + } + + void setMgm(class MgmtSrvr * mgmsrv){ + m_mgmsrv = mgmsrv; + } + + SocketServer::Session * newSession(NDB_SOCKET_TYPE socket){ + return new MgmApiSession(* m_mgmsrv, socket, m_next_session_id++); + } +}; + +#endif diff --git a/storage/ndb/src/mgmsrv/SignalQueue.cpp b/storage/ndb/src/mgmsrv/SignalQueue.cpp new file mode 100644 index 00000000000..08ad5f363a6 --- /dev/null +++ b/storage/ndb/src/mgmsrv/SignalQueue.cpp @@ -0,0 +1,104 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include "SignalQueue.hpp" + +SignalQueue::SignalQueue() { + m_mutex = NdbMutex_Create(); + m_cond = NdbCondition_Create(); + m_signalQueueHead = NULL; +} + +SignalQueue::~SignalQueue() { + { + Guard g(m_mutex); + while(m_signalQueueHead != NULL) + delete pop(); + } + NdbMutex_Destroy(m_mutex); + m_mutex = NULL; + NdbCondition_Destroy(m_cond); + m_cond = NULL; +} + +NdbApiSignal * +SignalQueue::pop() { + NdbApiSignal *ret; + + if(m_signalQueueHead == NULL) + return NULL; + + ret = m_signalQueueHead->signal; + + QueueEntry *old = m_signalQueueHead; + m_signalQueueHead = m_signalQueueHead->next; + + delete old; + + return ret; +} + +void +SignalQueue::receive(void *me, NdbApiSignal *signal) { + SignalQueue *q = (SignalQueue *)me; + q->receive(signal); +} + +void +SignalQueue::receive(NdbApiSignal *signal) { + QueueEntry *n = new QueueEntry(); + n->signal = signal; + n->next = NULL; + + Guard guard(m_mutex); + + if(m_signalQueueHead == NULL) { + m_signalQueueHead = n; + NdbCondition_Broadcast(m_cond); + return; + } + + QueueEntry *cur = m_signalQueueHead; + + while(cur->next != NULL) + cur = cur->next; + + cur->next = n; + + NdbCondition_Broadcast(m_cond); +} + +NdbApiSignal * +SignalQueue::waitFor(int gsn, NodeId nodeid, Uint32 timeout) { + Guard g(m_mutex); + + if(m_signalQueueHead == NULL) + NdbCondition_WaitTimeout(m_cond, m_mutex, timeout); + + if(m_signalQueueHead == NULL) + return NULL; + + if(gsn != 0 && + m_signalQueueHead->signal->readSignalNumber() != gsn) + return NULL; + + if(nodeid != 0 && + refToNode(m_signalQueueHead->signal->theSendersBlockRef) != nodeid) + return NULL; + + return pop(); +} diff --git a/storage/ndb/src/mgmsrv/SignalQueue.hpp b/storage/ndb/src/mgmsrv/SignalQueue.hpp new file mode 100644 index 00000000000..bacbad53415 --- /dev/null +++ b/storage/ndb/src/mgmsrv/SignalQueue.hpp @@ -0,0 +1,100 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef __SIGNALQUEUE_HPP_INCLUDED__ +#define __SIGNALQUEUE_HPP_INCLUDED__ + +#include <NdbApiSignal.hpp> +#include <NdbMutex.h> +#include <NdbCondition.h> +#include <Vector.hpp> + +/* XXX Look for an already existing definition */ +#define DEFAULT_TIMEOUT 5000 + +class SignalQueue { +public: + typedef void (* SignalHandler)(void *obj, int gsn, NdbApiSignal *signal); + + SignalQueue(); + ~SignalQueue(); + + /** + * Static wrapper making it possible to call receive without knowing the + * type of the receiver + */ + static void receive(void *me, NdbApiSignal *signal); + + /** + * Enqueues a signal, and notifies any thread waiting for signals. + */ + void receive(NdbApiSignal *signal); + + NdbApiSignal *waitFor(int gsn, + NodeId nodeid = 0, + Uint32 timeout = DEFAULT_TIMEOUT); + template<class T> bool waitFor(Vector<T> &t, + T **handler, + NdbApiSignal **signal, + Uint32 timeout = DEFAULT_TIMEOUT); +private: + NdbMutex *m_mutex; /* Locks all data in SignalQueue */ + NdbCondition *m_cond; /* Notifies about new signal in the queue */ + + /** + * Returns the last recently received signal. Must be called with + * m_mutex locked. + * The caller takes responsibility for deleting the returned object. + * + * @returns NULL if failed, or a received signal + */ + NdbApiSignal *pop(); + + class QueueEntry { + public: + NdbApiSignal *signal; + QueueEntry *next; + }; + QueueEntry *m_signalQueueHead; /** Head of the queue. + * New entries added on the tail + */ +}; + +template<class T> bool +SignalQueue::waitFor(Vector<T> &t, + T **handler, + NdbApiSignal **signal, + Uint32 timeout) { + Guard g(m_mutex); + + if(m_signalQueueHead == NULL) + NdbCondition_WaitTimeout(m_cond, m_mutex, timeout); + + if(m_signalQueueHead == NULL) + return false; + + for(size_t i = 0; i < t.size(); i++) { + if(t[i].check(m_signalQueueHead->signal)) { + * handler = &t[i]; + * signal = pop(); + return true; + } + } + + return false; +} + +#endif /* !__SIGNALQUEUE_HPP_INCLUDED__ */ diff --git a/storage/ndb/src/mgmsrv/convertStrToInt.cpp b/storage/ndb/src/mgmsrv/convertStrToInt.cpp new file mode 100644 index 00000000000..e5216047d10 --- /dev/null +++ b/storage/ndb/src/mgmsrv/convertStrToInt.cpp @@ -0,0 +1,43 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> + +bool convert(const char* s, int& val) { + + if (s == NULL) { + return false; + } + + if (strlen(s) == 0) { + return false; + } + + errno = 0; + char* p; + long v = strtol(s, &p, 10); + if (errno != 0) { + return false; + } + if (p != &s[strlen(s)]) { + return false; + } + + val = v; + return true; +} + + diff --git a/storage/ndb/src/mgmsrv/convertStrToInt.hpp b/storage/ndb/src/mgmsrv/convertStrToInt.hpp new file mode 100644 index 00000000000..0b2a96ed0bf --- /dev/null +++ b/storage/ndb/src/mgmsrv/convertStrToInt.hpp @@ -0,0 +1,25 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +//****************************************************************************** +// Description: +// +// Author: Peter Lind +//****************************************************************************** + +extern bool convert(const char* s, int& val); + + diff --git a/storage/ndb/src/mgmsrv/main.cpp b/storage/ndb/src/mgmsrv/main.cpp new file mode 100644 index 00000000000..dd7f4680dff --- /dev/null +++ b/storage/ndb/src/mgmsrv/main.cpp @@ -0,0 +1,388 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <ndb_opts.h> + +#include "MgmtSrvr.hpp" +#include "EventLogger.hpp" +#include <Config.hpp> +#include "InitConfigFileParser.hpp" +#include <SocketServer.hpp> +#include "Services.hpp" +#include <version.h> +#include <kernel_types.h> +#include <Properties.hpp> +#include <NdbOut.hpp> +#include <NdbMain.h> +#include <NdbDaemon.h> +#include <NdbConfig.h> +#include <NdbHost.h> +#include <ndb_version.h> +#include <ConfigRetriever.hpp> +#include <mgmapi_config_parameters.h> + +#include <NdbAutoPtr.hpp> + +#include <ndb_mgmclient.hpp> + +#undef DEBUG +#define DEBUG(x) ndbout << x << endl; + +const char progname[] = "mgmtsrvr"; +const char *load_default_groups[]= { "mysql_cluster","ndb_mgmd",0 }; + +// copied from mysql.cc to get readline +extern "C" { +#if defined( __WIN__) +#include <conio.h> +#elif !defined(__NETWARE__) +#include <readline/readline.h> +extern "C" int add_history(const char *command); /* From readline directory */ +#define HAVE_READLINE +#endif +} + +static int +read_and_execute(Ndb_mgmclient* com, const char * prompt, int _try_reconnect) +{ + static char *line_read = (char *)NULL; + + /* If the buffer has already been allocated, return the memory + to the free pool. */ + if (line_read) + { + free (line_read); + line_read = (char *)NULL; + } +#ifdef HAVE_READLINE + /* Get a line from the user. */ + line_read = readline (prompt); + /* If the line has any text in it, save it on the history. */ + if (line_read && *line_read) + add_history (line_read); +#else + static char linebuffer[254]; + fputs(prompt, stdout); + linebuffer[sizeof(linebuffer)-1]=0; + line_read = fgets(linebuffer, sizeof(linebuffer)-1, stdin); + if (line_read == linebuffer) { + char *q=linebuffer; + while (*q > 31) q++; + *q=0; + line_read= strdup(linebuffer); + } +#endif + return com->execute(line_read,_try_reconnect); +} + +/** + * @struct MgmGlobals + * @brief Global Variables used in the management server + *****************************************************************************/ + +/** Command line arguments */ +static int opt_daemon; // NOT bool, bool need not be int +static int opt_non_interactive; +static int opt_interactive; +static const char * opt_config_filename= 0; +static int opt_mycnf = 0; + +struct MgmGlobals { + MgmGlobals(); + ~MgmGlobals(); + + /** Stuff found in environment or in local config */ + NodeId localNodeId; + bool use_specific_ip; + char * interface_name; + short unsigned int port; + + /** The Mgmt Server */ + MgmtSrvr * mgmObject; + + /** The Socket Server */ + SocketServer * socketServer; +}; + +int g_no_nodeid_checks= 0; +int g_print_full_config; +static MgmGlobals *glob= 0; + +/****************************************************************************** + * Function prototypes + ******************************************************************************/ +/** + * Global variables + */ +bool g_StopServer; +bool g_RestartServer; +extern EventLogger g_eventLogger; + +extern int global_mgmt_server_check; + +enum ndb_mgmd_options { + OPT_INTERACTIVE = NDB_STD_OPTIONS_LAST, + OPT_NO_NODEID_CHECKS, + OPT_NO_DAEMON +}; +NDB_STD_OPTS_VARS; + +static struct my_option my_long_options[] = +{ + NDB_STD_OPTS("ndb_mgmd"), + { "config-file", 'f', "Specify cluster configuration file", + (gptr*) &opt_config_filename, (gptr*) &opt_config_filename, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "print-full-config", 'P', "Print full config and exit", + (gptr*) &g_print_full_config, (gptr*) &g_print_full_config, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "daemon", 'd', "Run ndb_mgmd in daemon mode (default)", + (gptr*) &opt_daemon, (gptr*) &opt_daemon, 0, + GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 }, + { "interactive", OPT_INTERACTIVE, + "Run interactive. Not supported but provided for testing purposes", + (gptr*) &opt_interactive, (gptr*) &opt_interactive, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "no-nodeid-checks", OPT_NO_NODEID_CHECKS, + "Do not provide any node id checks", + (gptr*) &g_no_nodeid_checks, (gptr*) &g_no_nodeid_checks, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "nodaemon", OPT_NO_DAEMON, + "Don't run as daemon, but don't read from stdin", + (gptr*) &opt_non_interactive, (gptr*) &opt_non_interactive, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "mycnf", 256, + "Read cluster config from my.cnf", + (gptr*) &opt_mycnf, (gptr*) &opt_mycnf, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; + +static void short_usage_sub(void) +{ + printf("Usage: %s [OPTIONS]\n", my_progname); +} +static void usage() +{ + short_usage_sub(); + ndb_std_print_version(); + print_defaults(MYSQL_CONFIG_NAME,load_default_groups); + puts(""); + my_print_help(my_long_options); + my_print_variables(my_long_options); +} + +/* + * MAIN + */ +int main(int argc, char** argv) +{ + int mgm_connect_result; + + NDB_INIT(argv[0]); + + load_defaults("my",load_default_groups,&argc,&argv); + + int ho_error; +#ifndef DBUG_OFF + opt_debug= "d:t:O,/tmp/ndb_mgmd.trace"; +#endif + if ((ho_error=handle_options(&argc, &argv, my_long_options, + ndb_std_get_one_option))) + exit(ho_error); + +start: + glob= new MgmGlobals; + + global_mgmt_server_check = 1; + + if (opt_interactive || + opt_non_interactive || + g_print_full_config) { + opt_daemon= 0; + } + + if (opt_mycnf && opt_config_filename) + { + ndbout_c("Both --mycnf and -f is not supported"); + return 0; + } + + if (opt_mycnf == 0 && opt_config_filename == 0) + { + struct stat buf; + if (stat("config.ini", &buf) != -1) + opt_config_filename = "config.ini"; + } + + glob->socketServer = new SocketServer(); + + MgmApiService * mapi = new MgmApiService(); + + glob->mgmObject = new MgmtSrvr(glob->socketServer, + opt_config_filename, + opt_connect_str); + + if (g_print_full_config) + goto the_end; + + if (glob->mgmObject->init()) + goto error_end; + + my_setwd(NdbConfig_get_path(0), MYF(0)); + + glob->localNodeId= glob->mgmObject->getOwnNodeId(); + if (glob->localNodeId == 0) { + goto error_end; + } + + glob->port= glob->mgmObject->getPort(); + + if (glob->port == 0) + goto error_end; + + glob->interface_name = 0; + glob->use_specific_ip = false; + + if(!glob->use_specific_ip){ + int count= 5; // no of retries for tryBind + while(!glob->socketServer->tryBind(glob->port, glob->interface_name)){ + if (--count > 0) { + NdbSleep_MilliSleep(1000); + continue; + } + ndbout_c("Unable to setup port: %s:%d!\n" + "Please check if the port is already used,\n" + "(perhaps a ndb_mgmd is already running),\n" + "and if you are executing on the correct computer", + (glob->interface_name ? glob->interface_name : "*"), glob->port); + goto error_end; + } + free(glob->interface_name); + glob->interface_name = 0; + } + + if(!glob->socketServer->setup(mapi, &glob->port, glob->interface_name)) + { + ndbout_c("Unable to setup management port: %d!\n" + "Please check if the port is already used,\n" + "(perhaps a ndb_mgmd is already running),\n" + "and if you are executing on the correct computer", + glob->port); + delete mapi; + goto error_end; + } + + if(!glob->mgmObject->check_start()){ + ndbout_c("Unable to check start management server."); + ndbout_c("Probably caused by illegal initial configuration file."); + goto error_end; + } + + if (opt_daemon) { + // Become a daemon + char *lockfile= NdbConfig_PidFileName(glob->localNodeId); + char *logfile= NdbConfig_StdoutFileName(glob->localNodeId); + NdbAutoPtr<char> tmp_aptr1(lockfile), tmp_aptr2(logfile); + + if (NdbDaemon_Make(lockfile, logfile, 0) == -1) { + ndbout << "Cannot become daemon: " << NdbDaemon_ErrorText << endl; + return 1; + } + } + +#ifndef NDB_WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + { + BaseString error_string; + if(!glob->mgmObject->start(error_string)){ + ndbout_c("Unable to start management server."); + ndbout_c("Probably caused by illegal initial configuration file."); + ndbout_c(error_string.c_str()); + goto error_end; + } + } + + //glob->mgmObject->saveConfig(); + mapi->setMgm(glob->mgmObject); + + char msg[256]; + BaseString::snprintf(msg, sizeof(msg), + "NDB Cluster Management Server. %s", NDB_VERSION_STRING); + ndbout_c(msg); + g_eventLogger.info(msg); + + BaseString::snprintf(msg, 256, "Id: %d, Command port: %d", + glob->localNodeId, glob->port); + ndbout_c(msg); + g_eventLogger.info(msg); + + g_StopServer = false; + g_RestartServer= false; + glob->socketServer->startServer(); + + if(opt_interactive) { + BaseString con_str; + if(glob->interface_name) + con_str.appfmt("host=%s:%d", glob->interface_name, glob->port); + else + con_str.appfmt("localhost:%d", glob->port); + Ndb_mgmclient com(con_str.c_str(), 1); + while(g_StopServer != true && read_and_execute(&com, "ndb_mgm> ", 1)); + } else + { + while(g_StopServer != true) + NdbSleep_MilliSleep(500); + } + + if(g_RestartServer) + g_eventLogger.info("Restarting server..."); + else + g_eventLogger.info("Shutting down server..."); + glob->socketServer->stopServer(); + // We disconnect from the ConfigRetreiver mgmd when we delete glob below + glob->socketServer->stopSessions(true); + g_eventLogger.info("Shutdown complete"); + the_end: + delete glob; + if(g_RestartServer) + goto start; + ndb_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); + return 0; + error_end: + delete glob; + ndb_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); + return 1; +} + +MgmGlobals::MgmGlobals(){ + // Default values + port = 0; + interface_name = 0; + socketServer = 0; + mgmObject = 0; +} + +MgmGlobals::~MgmGlobals(){ + if (socketServer) + delete socketServer; + if (mgmObject) + delete mgmObject; + if (interface_name) + free(interface_name); +} diff --git a/storage/ndb/src/mgmsrv/mkconfig/Makefile b/storage/ndb/src/mgmsrv/mkconfig/Makefile new file mode 100644 index 00000000000..43574eefbd1 --- /dev/null +++ b/storage/ndb/src/mgmsrv/mkconfig/Makefile @@ -0,0 +1,13 @@ +include .defs.mk + +TYPE := ndbapi + +BIN_TARGET := mkconfig +BIN_TARGET_ARCHIVES := logger trace mgmsrvcommon portlib general + +SOURCES := mkconfig.cpp + +CCFLAGS_LOC += -I.. -I$(call fixpath,$(NDB_TOP)/src/common/mgmcommon) +CFLAGS_mkconfig.cpp := -I$(call fixpath,$(NDB_TOP)/src/mgmapi) + +include $(NDB_TOP)/Epilogue.mk diff --git a/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp b/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp new file mode 100644 index 00000000000..28823aaa35e --- /dev/null +++ b/storage/ndb/src/mgmsrv/mkconfig/mkconfig.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <ndb_global.h> +#include <ndb_version.h> +#include <mgmapi_configuration.hpp> + +#include <NdbMain.h> +#include <Properties.hpp> + +#include "InitConfigFileParser.hpp" +#include <Config.hpp> + +void usage(const char * prg){ + ndbout << "Usage " << prg << ": <Init config> <Binary file>" << endl; + +} + +NDB_COMMAND(mkconfig, + "mkconfig", "mkconfig", + "Make a binary configuration from a config file", 16384){ + ndb_init(); + if(argc < 3){ + usage(argv[0]); + return 0; + } + + InitConfigFileParser parser; + Config* _cp; + + if ((_cp = parser.parseConfig(argv[1])) == 0) + return false; + + ConfigValues* cp = &_cp->m_configValues->m_config; + Uint32 sz = cp->getPackedSize(); + UtilBuffer buf; + if(!cp->pack(buf)) + return -1; + + FILE * f = fopen(argv[2], "w"); + if(fwrite(buf.get_data(), 1, buf.length(), f) != sz){ + fclose(f); + unlink(argv[2]); + return -1; + } + fclose(f); + return 0; +} |