List:Commits« Previous MessageNext Message »
From:Magnus Svensson Date:October 21 2008 2:42pm
Subject:bzr commit into mysql-5.1 branch (msvensson:2879) WL#4350
View as plain text  
#At file:///home/msvensson/mysql/6.4-wl4350/

 2879 Magnus Svensson	2008-10-21
      WL#4350 ndb_mgmd - consistent configuration
       Add ConfigManager to ndb_mgmd
removed:
  storage/ndb/include/kernel/signaldata/ManagementServer.hpp
  storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
added:
  storage/ndb/include/kernel/signaldata/ConfigChange.hpp
  storage/ndb/src/mgmsrv/ConfigManager.cpp
  storage/ndb/src/mgmsrv/ConfigManager.hpp
  storage/ndb/src/mgmsrv/ConfigSubscriber.hpp
  storage/ndb/src/mgmsrv/DirIterator.hpp
  storage/ndb/src/mgmsrv/MgmtThread.hpp
modified:
  mysql-test/mysql-test-run.pl
  mysql-test/suite/ndb/r/ndb_config.result
  mysql-test/suite/ndb/r/ndb_config2.result
  storage/ndb/include/kernel/BlockNumbers.h
  storage/ndb/include/kernel/GlobalSignalNumbers.h
  storage/ndb/include/mgmcommon/ConfigRetriever.hpp
  storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
  storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp
  storage/ndb/src/kernel/vm/Configuration.cpp
  storage/ndb/src/mgmsrv/Config.cpp
  storage/ndb/src/mgmsrv/Config.hpp
  storage/ndb/src/mgmsrv/ConfigInfo.cpp
  storage/ndb/src/mgmsrv/ConfigInfo.hpp
  storage/ndb/src/mgmsrv/InitConfigFileParser.cpp
  storage/ndb/src/mgmsrv/Makefile.am
  storage/ndb/src/mgmsrv/MgmtSrvr.cpp
  storage/ndb/src/mgmsrv/MgmtSrvr.hpp
  storage/ndb/src/mgmsrv/Services.cpp
  storage/ndb/src/mgmsrv/main.cpp
  storage/ndb/src/ndbapi/ClusterMgr.cpp
  storage/ndb/src/ndbapi/SignalSender.cpp
  storage/ndb/src/ndbapi/SignalSender.hpp
  storage/ndb/src/ndbapi/TransporterFacade.cpp
  storage/ndb/src/ndbapi/TransporterFacade.hpp
  storage/ndb/test/run-test/Makefile.am

per-file messages:
  mysql-test/mysql-test-run.pl
    Start ndb_mgmd with specified --datadir and --ndb-nodeid
  mysql-test/suite/ndb/r/ndb_config.result
    One more connection between ndb_mgmd's automatically generated from config.ini
  mysql-test/suite/ndb/r/ndb_config2.result
    One more connection between ndb_mgmd's automatically generated from config.ini
  storage/ndb/include/kernel/BlockNumbers.h
    Add support for dynamic blocks(using SignalSender) with a fixed block number
  storage/ndb/include/kernel/GlobalSignalNumbers.h
    Allocate signal numbers for ConfigManager
  storage/ndb/include/kernel/signaldata/ConfigChange.hpp
    ConfigManager signals
  storage/ndb/include/kernel/signaldata/ManagementServer.hpp
    Remove old file
  storage/ndb/include/mgmcommon/ConfigRetriever.hpp
    Add 'is_connected' function to ConfigRetriever
  storage/ndb/src/common/debugger/signaldata/SignalNames.cpp
    Add signal names for ConfigManager signals
  storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp
    Use getConfigValues so that the values are "taken" and not freed ConfigValuesFactory
goes out of scope
    Add 'is_cconnected' implementation
    Add debug prints
    Move the setting of NbdConfig_SetPath(<DataDir>) our of verifyConfig to ndbd and
ndb_mgmd code
  storage/ndb/src/kernel/vm/Configuration.cpp
    Move the setting of NbdConfig_SetPath(<DataDir>) our of verifyConfig to ndbd
code
  storage/ndb/src/mgmsrv/Config.cpp
    Add copy constructor
    Generic getter and setter plus setter for 'Name' of system
    Fix problem where the diff for 'Name' was not properly shown
  storage/ndb/src/mgmsrv/Config.hpp
    Add copy constructor
    Generic getter and setter plus setter for 'Name' of system
  storage/ndb/src/mgmsrv/ConfigInfo.cpp
    Auto generate SYSTEM section in config
    Auto generate Name of SYSTEM
    Add transporter connection between all ndb_mgmd's in the system
    Change default value for ndb_mgmd's DataDir from MYSQLCLUSTERDIR(which _was_ set to
".") into ""(emtpy string)
  storage/ndb/src/mgmsrv/ConfigInfo.hpp
    Fix copyright
  storage/ndb/src/mgmsrv/ConfigManager.cpp
    Add ConfigManager running as a separate thread in ndb_mgmd  controlling the
distribution and
    updating of the binary configuration
  storage/ndb/src/mgmsrv/ConfigManager.hpp
    Add ConfigManager running as a separate thread in ndb_mgmd  controlling the
distribution and
    updating of the binary configuration
  storage/ndb/src/mgmsrv/ConfigSubscriber.hpp
    Callback interface used to subscribe to ConfigChanges from other classes in the
ndb_mgmd
  storage/ndb/src/mgmsrv/DirIterator.hpp
    Iterator for listing all files in a directory
  storage/ndb/src/mgmsrv/InitConfigFileParser.cpp
    Use Config's copy constructor for easier code
  storage/ndb/src/mgmsrv/Makefile.am
    Change the default compiled in value for MYSQLCLUSTERDIR from "." to
"prefix/mysql-cluster", this will normally expand to "/usr/local/mysql/mysql-cluster"
    Remove build of MgmtSrvrConfig.cpp
    Add build of ConfigManager.cpp
  storage/ndb/src/mgmsrv/MgmtSrvr.cpp
    Add --datadir
    Reorganize code so that ConfigManager get full reponsibilty of all configuration
change related things.
    Subscribe MgmtSrvr to ConfigManager's config change callback
  storage/ndb/src/mgmsrv/MgmtSrvr.hpp
    Add --datadir
    Reorganize code so that ConfigManager get full reponsibilty of all configuration
change related things.
    Subscribe MgmtSrvr to ConfigManager's config change callback
  storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp
    Remove old code
  storage/ndb/src/mgmsrv/MgmtThread.hpp
    Add MgmtThread class for easily run a class in a thread inside ndb_mgmd
  storage/ndb/src/mgmsrv/Services.cpp
    Use "getPackedConfig" function from MgmtSrvr when getting a config to send to
requestor, this
    reduces the need to expose internal parts(like mutexes) between classes in ndb_mgmd
  storage/ndb/src/mgmsrv/main.cpp
    Add --datadir
    Move the installation of SIG_PIPE ignore earlier
  storage/ndb/src/ndbapi/ClusterMgr.cpp
    Send the APU_REGREQ to different blocks depending if the reciver is ndb_mgmd or ndbd
    Set m_api_reg_conf to true as soon as send to other node suceeds - the connection 
    is now working both ways.
  storage/ndb/src/ndbapi/SignalSender.hpp
    Add 'getNodes' that will return a bitmask for all nodes of a specific type
    Add sendSignal with more args to encapsulate 'set' + 'sendSignal'
    Add 'broadcastSignal' which will send same signal too all available nodes in a
bitmask. Return another bitmask with all nodes where the signal was successfully sent.
  storage/ndb/src/ndbapi/TransporterFacade.cpp
    Rewrite support for "dynamic blocks with fixed block numbers" in NdbApi
    Add support to SignalSender to send a signal to own node.
  storage/ndb/src/ndbapi/TransporterFacade.hpp
    Rewrite support for "dynamic blocks with fixed block numbers" in NdbApi
  storage/ndb/test/run-test/Makefile.am
    Remove define of MYSQLCLUSTERDIR, not used here in test/run-test/
=== modified file 'mysql-test/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl	2008-10-03 12:04:01 +0000
+++ b/mysql-test/mysql-test-run.pl	2008-10-21 12:41:59 +0000
@@ -2794,6 +2794,8 @@ sub ndb_mgmd_start ($) {
   mtr_add_arg($args, "--core");
   mtr_add_arg($args, "--nodaemon");
   mtr_add_arg($args, "--config-file=%s", "$cluster->{'data_dir'}/config.ini");
+  mtr_add_arg($args, "--datadir=%s", "$cluster->{'data_dir'}");
+  mtr_add_arg($args, "--ndb-nodeid=%d", $cluster->{'nodes'} + 1);
 
 
   my $path_ndb_mgmd_log=
"$cluster->{'data_dir'}/\l$cluster->{'name'}_ndb_mgmd.log";

=== modified file 'mysql-test/suite/ndb/r/ndb_config.result'
--- a/mysql-test/suite/ndb/r/ndb_config.result	2008-09-13 16:15:54 +0000
+++ b/mysql-test/suite/ndb/r/ndb_config.result	2008-10-21 12:41:59 +0000
@@ -8,7 +8,7 @@ ndb_mgmd,5,localhost,, mysqld,6,localhos
 ndb_mgmd,5,localhost mysqld,6, mysqld,7, mysqld,8, mysqld,9, mysqld,10, ndbd,1,localhost
ndbd,2,localhost ndbd,3,localhost ndbd,4,localhost
 ndb_mgmd,6,localhost mysqld,1, mysqld,7, mysqld,8, mysqld,9, mysqld,10, ndbd,2,localhost
ndbd,3,localhost ndbd,4,localhost ndbd,5,localhost
 ndb_mgmd,1,localhost ndb_mgmd,2,localhost mysqld,11, mysqld,12, mysqld,13, mysqld,14,
mysqld,15, ndbd,3,localhost ndbd,4,localhost ndbd,5,localhost ndbd,6,localhost
-shm,3,4,35,3 shm,3,5,35,3 shm,3,6,35,3 shm,4,5,35,4 shm,4,6,35,4 shm,5,6,35,5
tcp,11,3,55,3 tcp,11,4,55,4 tcp,11,5,55,5 tcp,11,6,55,6 tcp,12,3,55,3 tcp,12,4,55,4
tcp,12,5,55,5 tcp,12,6,55,6 tcp,13,3,55,3 tcp,13,4,55,4 tcp,13,5,55,5 tcp,13,6,55,6
tcp,14,3,55,3 tcp,14,4,55,4 tcp,14,5,55,5 tcp,14,6,55,6 tcp,15,3,55,3 tcp,15,4,55,4
tcp,15,5,55,5 tcp,15,6,55,6 tcp,1,3,55,1 tcp,1,4,55,1 tcp,1,5,55,1 tcp,1,6,55,1
tcp,2,3,55,2 tcp,2,4,55,2 tcp,2,5,55,2 tcp,2,6,55,2
+shm,3,4,35,3 shm,3,5,35,3 shm,3,6,35,3 shm,4,5,35,4 shm,4,6,35,4 shm,5,6,35,5
tcp,11,3,55,3 tcp,11,4,55,4 tcp,11,5,55,5 tcp,11,6,55,6 tcp,12,3,55,3 tcp,12,4,55,4
tcp,12,5,55,5 tcp,12,6,55,6 tcp,13,3,55,3 tcp,13,4,55,4 tcp,13,5,55,5 tcp,13,6,55,6
tcp,14,3,55,3 tcp,14,4,55,4 tcp,14,5,55,5 tcp,14,6,55,6 tcp,15,3,55,3 tcp,15,4,55,4
tcp,15,5,55,5 tcp,15,6,55,6 tcp,1,3,55,1 tcp,1,4,55,1 tcp,1,5,55,1 tcp,1,6,55,1
tcp,2,3,55,2 tcp,2,4,55,2 tcp,2,5,55,2 tcp,2,6,55,2 tcp,1,2,55,2
 3 1 2
 
 3 1 2

=== modified file 'mysql-test/suite/ndb/r/ndb_config2.result'
--- a/mysql-test/suite/ndb/r/ndb_config2.result	2007-06-27 12:28:02 +0000
+++ b/mysql-test/suite/ndb/r/ndb_config2.result	2008-10-21 12:41:59 +0000
@@ -1 +1 @@
-shm,3,4,35,3 shm,3,5,35,3 shm,3,6,35,3 shm,4,5,35,4 shm,4,6,35,4 shm,5,6,35,5
tcp,11,3,55,3 tcp,11,4,55,4 tcp,11,5,55,5 tcp,11,6,55,6 tcp,12,3,55,3 tcp,12,4,55,4
tcp,12,5,55,5 tcp,12,6,55,6 tcp,13,3,55,3 tcp,13,4,55,4 tcp,13,5,55,5 tcp,13,6,55,6
tcp,14,3,55,3 tcp,14,4,55,4 tcp,14,5,55,5 tcp,14,6,55,6 tcp,15,3,55,3 tcp,15,4,55,4
tcp,15,5,55,5 tcp,15,6,55,6 tcp,1,3,55,1 tcp,1,4,55,1 tcp,1,5,55,1 tcp,1,6,55,1
tcp,2,3,55,2 tcp,2,4,55,2 tcp,2,5,55,2 tcp,2,6,55,2
+shm,3,4,35,3 shm,3,5,35,3 shm,3,6,35,3 shm,4,5,35,4 shm,4,6,35,4 shm,5,6,35,5
tcp,11,3,55,3 tcp,11,4,55,4 tcp,11,5,55,5 tcp,11,6,55,6 tcp,12,3,55,3 tcp,12,4,55,4
tcp,12,5,55,5 tcp,12,6,55,6 tcp,13,3,55,3 tcp,13,4,55,4 tcp,13,5,55,5 tcp,13,6,55,6
tcp,14,3,55,3 tcp,14,4,55,4 tcp,14,5,55,5 tcp,14,6,55,6 tcp,15,3,55,3 tcp,15,4,55,4
tcp,15,5,55,5 tcp,15,6,55,6 tcp,1,3,55,1 tcp,1,4,55,1 tcp,1,5,55,1 tcp,1,6,55,1
tcp,2,3,55,2 tcp,2,4,55,2 tcp,2,5,55,2 tcp,2,6,55,2 tcp,1,2,55,2

=== modified file 'storage/ndb/include/kernel/BlockNumbers.h'
--- a/storage/ndb/include/kernel/BlockNumbers.h	2006-12-23 19:20:40 +0000
+++ b/storage/ndb/include/kernel/BlockNumbers.h	2008-10-21 12:41:59 +0000
@@ -19,7 +19,7 @@
 #include <kernel_types.h>
 #include <RefConvert.hpp>
 
-/* 240 */
+/* 32768 */
 #define MIN_API_BLOCK_NO  0x8000
 
 /* 2047 */
@@ -28,6 +28,14 @@
 /* 4002 */
 #define API_CLUSTERMGR 0x0FA2
 
+/* Fixed block numbers in API */
+#define NO_API_FIXED_BLOCKS    1
+#define MIN_API_FIXED_BLOCK_NO (API_CLUSTERMGR+1)
+#define MGM_CONFIG_MAN MIN_API_FIXED_BLOCK_NO
+
+#define MAX_API_FIXED_BLOCK_NO (MIN_API_FIXED_BLOCK_NO + NO_API_FIXED_BLOCKS)
+
+
 #define BACKUP      0xF4
 #define DBTC        0xF5
 #define DBDIH       0xF6

=== modified file 'storage/ndb/include/kernel/GlobalSignalNumbers.h'
--- a/storage/ndb/include/kernel/GlobalSignalNumbers.h	2008-09-12 12:55:29 +0000
+++ b/storage/ndb/include/kernel/GlobalSignalNumbers.h	2008-10-21 12:41:59 +0000
@@ -92,15 +92,18 @@ extern const GlobalSignalNumber NO_OF_SI
 
 #define GSN_TCKEY_FAILREFCONF_R         40
 
-/* 41 unused */
-/* 42 unused */
-/* 43 unused */
-/* 44 unused */
-/* 45 unused */
-/* 46 unused */
-/* 47 unused */
-/* 48 unused */
-/* 49 unused */
+#define GSN_CONFIG_CHANGE_REQ           41
+#define GSN_CONFIG_CHANGE_REF           42
+#define GSN_CONFIG_CHANGE_CONF          43
+
+#define GSN_CONFIG_CHANGE_IMPL_REQ      44
+#define GSN_CONFIG_CHANGE_IMPL_REF      45
+#define GSN_CONFIG_CHANGE_IMPL_CONF     46
+
+#define GSN_CONFIG_CHECK_REQ            47
+#define GSN_CONFIG_CHECK_REF            48
+#define GSN_CONFIG_CHECK_CONF           49
+
 /* 50 unused */
 /* 51 unused */
 /* 52 unused */

=== added file 'storage/ndb/include/kernel/signaldata/ConfigChange.hpp'
--- a/storage/ndb/include/kernel/signaldata/ConfigChange.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/include/kernel/signaldata/ConfigChange.hpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,253 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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_CHANGE_H
+#define CONFIG_CHANGE_H
+
+#include "SignalData.hpp"
+
+class ConfigChangeReq {
+  /**
+   * Sender
+   */
+  friend class MgmtSrvr;
+
+  /**
+   * Receiver
+   */
+  friend class ConfigManager;
+
+public:
+  STATIC_CONST( SignalLength = 1 );
+
+private:
+  Uint32 length; // Length of the config data in long signal
+};
+
+
+class ConfigChangeConf {
+  /**
+   * Sender
+   */
+  friend class ConfigManager;
+
+  /**
+   * Receiver
+   */
+  friend class MgmtSrvr;
+
+public:
+  STATIC_CONST( SignalLength = 1 );
+
+private:
+
+  Uint32 unused;
+};
+
+
+class ConfigChangeRef {
+  /**
+   * Sender
+   */
+  friend class ConfigManager;
+
+  /**
+   * Receiver
+   */
+  friend class MgmtSrvr;
+
+
+  enum ErrorCode {
+    OK                      = 0,
+    ConfigChangeOnGoing     = 1,
+    NotMaster               = 2,
+    NoConfigData            = 3,
+    ConfigChangeAborted     = 4,
+    FailedToStart           = 5,
+    SetGenerationFailed     = 10,
+    PrepareFailed           = 11,
+    IllegalConfigChange     = 13,
+    FailedToUnpack          = 14,
+    InvalidGeneration       = 15,
+    InvalidConfigName       = 16,
+    IllegalState            = 17,
+    IllegalInitialGeneration = 18,
+    DifferentInitial        = 19,
+    NotAllStarted           = 20
+  } ;
+
+public:
+  STATIC_CONST( SignalLength = 1 );
+
+  static const char* errorMessage(Uint32 error) {
+    switch (error){
+    case NoConfigData:
+      return "No config data in signal";
+    case ConfigChangeAborted:
+      return "Config change was aborted";
+    case FailedToStart:
+      return "Failed to start config change";
+    case SetGenerationFailed:
+      return "setGeneration failed";
+    case FailedToUnpack:
+      return "Failed to unpack the configuration";
+    case IllegalConfigChange:
+      return "Illegal config change";
+
+    default:
+      return "ConfigChangeRef, unknown error";
+    }
+  }
+
+private:
+
+  Uint32 errorCode;
+};
+
+
+class ConfigChangeImplReq {
+  /**
+   * Receiver and sender
+   */
+  friend class ConfigManager;
+
+  enum RequestType {
+    Prepare,
+    Commit,
+    Abort
+  };
+
+public:
+  STATIC_CONST( SignalLength = 3 );
+
+private:
+
+  Uint32 requestType;
+  Uint32 initial; // Valid when requestType = Prepare
+  Uint32 length; // Length of the config data in long signal
+};
+
+
+class ConfigChangeImplConf  {
+  /**
+   * Receiver and sender
+   */
+  friend class ConfigManager;
+
+public:
+  STATIC_CONST( SignalLength = 1 );
+
+private:
+
+  Uint32 requestType;
+};
+
+
+class ConfigChangeImplRef  {
+  /**
+   * Receiver and sender
+   */
+  friend class ConfigManager;
+
+public:
+  STATIC_CONST( SignalLength = 1 );
+
+private:
+
+  Uint32 errorCode;
+};
+
+
+class ConfigCheckReq  {
+  /**
+   * Sender
+   */
+  friend class MgmtSrvr;
+
+  /**
+   * Receiver
+   */
+  friend class ConfigManager;
+
+public:
+  STATIC_CONST( SignalLength = 2 );
+
+private:
+  Uint32 state;
+  Uint32 generation;
+};
+
+
+class ConfigCheckConf {
+  /**
+   * Sender
+   */
+  friend class ConfigManager;
+
+  /**
+   * Receiver
+   */
+  friend class MgmtSrvr;
+
+public:
+  STATIC_CONST( SignalLength = 2 );
+
+private:
+
+  Uint32 state;
+  Uint32 generation;
+};
+
+
+class ConfigCheckRef  {
+  /**
+   * Sender
+   */
+  friend class ConfigManager;
+
+  /**
+   * Receiver
+   */
+  friend class MgmtSrvr;
+
+  enum ErrorCode {
+    WrongState                 = 1,
+    WrongGeneration            = 2
+  };
+
+  static const char* errorMessage(Uint32 error) {
+    switch (error){
+    case WrongState:
+      return "Wrong state";
+    case WrongGeneration:
+      return "Wrong generation";
+
+    default:
+      return "ConfigCheckRef, unknown error";
+    }
+  }
+
+public:
+  STATIC_CONST( SignalLength = 5 );
+private:
+  Uint32 error;
+  Uint32 generation;
+  Uint32 expected_generation;
+  Uint32 state;
+  Uint32 expected_state;
+};
+
+
+#endif

=== removed file 'storage/ndb/include/kernel/signaldata/ManagementServer.hpp'
--- a/storage/ndb/include/kernel/signaldata/ManagementServer.hpp	2006-12-23 19:20:40 +0000
+++ b/storage/ndb/include/kernel/signaldata/ManagementServer.hpp	1970-01-01 00:00:00 +0000
@@ -1,86 +0,0 @@
-/* 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; version 2 of the License.
-
-   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 MANAGEMENTSERVER_HPP
-#define MANAGEMENTSERVER_HPP
-
-#include "SignalData.hpp"
-
-/**
- * Request to lock configuration
- */
-class MgmLockConfigReq {
-  friend class MgmtSrvr;
-
-public:
-  STATIC_CONST( SignalLength = 1 );
-
-private:
-  Uint32 newConfigGeneration;
-};
-
-/**
- * Confirm configuration lock
- */
-class MgmLockConfigRep {
-  friend class MgmtSrvr;
-public:
-  STATIC_CONST( SignalLength = 1 );
-
-  /* Error codes */
-  enum ErrorCode {
-    OK,
-    UNKNOWN_ERROR,
-    GENERATION_MISMATCH,
-    ALREADY_LOCKED
-  };
-
-private:
-  Uint32 errorCode;
-};
-
-/**
- * Unlock configuration
- */
-class MgmUnlockConfigReq {
-  friend class MgmtSrvr;
-
-public:
-  STATIC_CONST( SignalLength = 1 );
-
-private:
-  Uint32 commitConfig;
-};
-
-/**
- * Confirm config unlock
- */
-class MgmUnlockConfigRep {
-  friend class MgmtSrvr;
-public:
-  STATIC_CONST( SignalLength = 1 );
-
-  /* Error codes */
-  enum ErrorCode {
-    OK,
-    UNKNOWN_ERROR,
-    NOT_LOCKED
-  };
-
-private:
-  Uint32 errorCode;
-};
-
-#endif /* !MANAGEMENTSERVER_HPP */

=== modified file 'storage/ndb/include/mgmcommon/ConfigRetriever.hpp'
--- a/storage/ndb/include/mgmcommon/ConfigRetriever.hpp	2007-03-22 11:35:31 +0000
+++ b/storage/ndb/include/mgmcommon/ConfigRetriever.hpp	2008-10-21 12:41:59 +0000
@@ -34,7 +34,8 @@ public:
 
   int do_connect(int no_retries, int retry_delay_in_seconds, int verbose);
   int disconnect();
-  
+  bool is_connected();
+
   /**
    * Get configuration for current node.
    * 

=== modified file 'storage/ndb/src/common/debugger/signaldata/SignalNames.cpp'
--- a/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp	2008-09-12 12:55:29 +0000
+++ b/storage/ndb/src/common/debugger/signaldata/SignalNames.cpp	2008-10-21 12:41:59 +0000
@@ -709,5 +709,19 @@ const GsnName SignalNames [] = {
   ,{ GSN_DROP_NODEGROUP_IMPL_REQ, "DROP_NODEGROUP_IMPL_REQ" }
   ,{ GSN_DROP_NODEGROUP_IMPL_CONF, "DROP_NODEGROUP_IMPL_CONF" }
   ,{ GSN_DROP_NODEGROUP_IMPL_REF, "DROP_NODEGROUP_IMPL_REF" }
+
+  ,{ GSN_CONFIG_CHECK_REQ, "CONFIG_CHECK_REQ" }
+  ,{ GSN_CONFIG_CHECK_REF, "CONFIG_CHECK_REF" }
+  ,{ GSN_CONFIG_CHECK_CONF, "CONFIG_CHECK_CONF" }
+
+  ,{ GSN_CONFIG_CHANGE_REQ, "CONFIG_CHANGE_REQ" }
+  ,{ GSN_CONFIG_CHANGE_REF, "CONFIG_CHANGE_REF" }
+  ,{ GSN_CONFIG_CHANGE_CONF, "CONFIG_CHANGE_CONF" }
+
+  ,{ GSN_CONFIG_CHANGE_IMPL_REQ, "CONFIG_CHANGE_IMPL_REQ" }
+  ,{ GSN_CONFIG_CHANGE_IMPL_REF, "CONFIG_CHANGE_IMPL_REF" }
+  ,{ GSN_CONFIG_CHANGE_IMPL_CONF, "CONFIG_CHANGE_IMPL_CONF" }
+
+
 };
 const unsigned short NO_OF_SIGNAL_NAMES = sizeof(SignalNames)/sizeof(GsnName);

=== modified file 'storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp'
--- a/storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp	2008-06-10 20:06:47 +0000
+++ b/storage/ndb/src/common/mgmcommon/ConfigRetriever.cpp	2008-10-21 12:41:59 +0000
@@ -29,7 +29,6 @@
 #include <Properties.hpp>
 
 #include <socket_io.h>
-#include <NdbConfig.h>
 
 #include <NdbAutoPtr.hpp>
  
@@ -48,6 +47,9 @@ ConfigRetriever::ConfigRetriever(const c
                                  int timeout_ms)
 {
   DBUG_ENTER("ConfigRetriever::ConfigRetriever");
+  DBUG_PRINT("enter", ("%s, version: %d, node_type: %d, bind: %s, timeout: %d",
+                       _connect_string, version, node_type,
+                       _bindaddress, timeout_ms));
 
   m_version = version;
   m_node_type = node_type;
@@ -137,6 +139,12 @@ ConfigRetriever::disconnect()
   return ndb_mgm_disconnect(m_handle);
 }
 
+bool
+ConfigRetriever::is_connected(void)
+{
+  return (ndb_mgm_is_connected(m_handle) != 0);
+}
+
 //****************************************************************************
 //****************************************************************************
 //****************************************************************************
@@ -208,14 +216,12 @@ ConfigRetriever::getConfig(const char * 
   
   ConfigValuesFactory cvf;
   if(!cvf.unpack(buf2, bytes)){
-    char buf[255];
-    BaseString::snprintf(buf, sizeof(buf), "Error while unpacking"); 
-    setError(CR_ERROR, buf);
+    setError(CR_ERROR,  "Error while unpacking");
     delete []buf2;
     return 0;
   }
   delete [] buf2;
-  return (ndb_mgm_configuration*)cvf.m_cfg;
+  return (ndb_mgm_configuration*)cvf.getConfigValues();
 #else
   return 0;
 #endif
@@ -243,6 +249,7 @@ ConfigRetriever::getErrorString(){
   return errorString.c_str();
 }
 
+
 bool
 ConfigRetriever::verifyConfig(const struct ndb_mgm_configuration * conf, Uint32 nodeid){
 
@@ -252,8 +259,7 @@ ConfigRetriever::verifyConfig(const stru
 					     CFG_SECTION_NODE);
 
   if(it == 0){
-    BaseString::snprintf(buf, 255, "Unable to create config iterator");
-    setError(CR_ERROR, buf);
+    setError(CR_ERROR, "Unable to create config iterator");
     return false;
     
   }
@@ -272,11 +278,6 @@ ConfigRetriever::verifyConfig(const stru
     return false;
   }
 
-  const char * datadir;
-  if(!ndb_mgm_get_string_parameter(it, CFG_NODE_DATADIR, &datadir)){
-    NdbConfig_SetPath(datadir);
-  }
-
   if (hostname && hostname[0] != 0 &&
       !SocketServer::tryBind(0,hostname)) {
     BaseString::snprintf(buf, 255, "Config hostname(%s) don't match a local interface,"
@@ -347,6 +348,7 @@ ConfigRetriever::verifyConfig(const stru
       }
     }
   }
+
   return true;
 }
 

=== modified file 'storage/ndb/src/kernel/vm/Configuration.cpp'
--- a/storage/ndb/src/kernel/vm/Configuration.cpp	2008-08-21 06:31:25 +0000
+++ b/storage/ndb/src/kernel/vm/Configuration.cpp	2008-10-21 12:41:59 +0000
@@ -26,6 +26,7 @@
 #include <NdbMem.h>
 #include <NdbOut.hpp>
 #include <WatchDog.hpp>
+#include <NdbConfig.h>
 
 #include <mgmapi_configuration.hpp>
 #include <mgmapi_config_parameters_debug.h>
@@ -338,6 +339,13 @@ Configuration::fetch_configuration(){
 	      "StopOnError missing");
   }
 
+  const char * datadir;
+  if(iter.get(CFG_NODE_DATADIR, &datadir)){
+    ERROR_SET(fatal, NDBD_EXIT_INVALID_CONFIG, "Invalid configuration fetched",
+	      "DataDir missing");
+  }
+  NdbConfig_SetPath(datadir);
+
   m_mgmds.clear();
   for(ndb_mgm_first(&iter); ndb_mgm_valid(&iter); ndb_mgm_next(&iter))
   {

=== modified file 'storage/ndb/src/mgmsrv/Config.cpp'
--- a/storage/ndb/src/mgmsrv/Config.cpp	2008-09-16 09:37:22 +0000
+++ b/storage/ndb/src/mgmsrv/Config.cpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 
    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
@@ -19,14 +19,12 @@
 #include <NdbOut.hpp>
 #include "ConfigInfo.hpp"
 
-
 static void require(bool b)
 {
   if (!b)
     abort();
 }
 
-
 Config::Config(struct ndb_mgm_configuration *config_values) :
   m_configValues(config_values)
 {
@@ -38,6 +36,18 @@ Config::Config(ConfigValues *config_valu
 {
 }
 
+Config::Config(const Config* conf)
+{
+  // TODO Magnus, improve copy constructor
+  // to not use pack/unpack
+  assert(conf);
+  UtilBuffer buf;
+  conf->pack(buf);
+  ConfigValuesFactory cvf;
+  cvf.unpack(buf);
+  m_configValues= (struct ndb_mgm_configuration*)cvf.getConfigValues();
+}
+
 
 Config::~Config() {
   if(m_configValues != 0){
@@ -110,29 +120,44 @@ Config::getGeneration() const
 }
 
 
+const char*
+Config::getName() const
+{
+  const char* name;
+  ConfigIter iter(this, CFG_SECTION_SYSTEM);
+
+  if (iter.get(CFG_SYS_NAME, &name))
+    return 0;
+
+  return name;
+}
+
 
 bool
 Config::setValue(Uint32 section, Uint32 section_no,
                  Uint32 id, Uint32 new_val)
 {
   ConfigValues::Iterator iter(m_configValues->m_config);
-  if (iter.openSection(section, section_no)){
-    if (!iter.set(id, new_val))
-      return false;
-  }
-  else
-  {
-    ConfigValuesFactory cf(&m_configValues->m_config);
-    if (!cf.openSection(section, section_no))
-      return false;
-    if (!cf.put(CFG_TYPE_OF_SECTION, section))
-      return false;
-    if (!cf.put(id, new_val))
-      return false;
-    cf.closeSection();
+  if (!iter.openSection(section, section_no))
+    return false;
 
-    m_configValues= (struct ndb_mgm_configuration*)cf.getConfigValues();
-  }
+  if (!iter.set(id, new_val))
+    return false;
+
+  return true;
+}
+
+
+bool
+Config::setValue(Uint32 section, Uint32 section_no,
+                 Uint32 id, const char* new_val)
+{
+  ConfigValues::Iterator iter(m_configValues->m_config);
+  if (!iter.openSection(section, section_no))
+    return false;
+
+  if (!iter.set(id, new_val))
+    return false;
 
   return true;
 }
@@ -147,6 +172,15 @@ Config::setGeneration(Uint32 new_gen)
 }
 
 
+bool
+Config::setName(const char* new_name)
+{
+  return setValue(CFG_SECTION_SYSTEM, 0,
+                  CFG_SYS_NAME,
+                  new_name);
+}
+
+
 Uint32
 Config::pack(UtilBuffer& buf) const
 {
@@ -168,7 +202,6 @@ add_diff(const char* name, const char* k
          Properties& diff,
          const char* value_name, Properties* value)
 {
-
   Properties *section;
   // Create a new section if it did not exist
   if (!diff.getCopy(key, &section)){
@@ -187,7 +220,8 @@ add_diff(const char* name, const char* k
   require(value->get("Type", &type));
 
   // Add the value to the section if not already added
-  if (!section->put(value_name, value))
+  require(value->put("Name", value_name));
+  if (!section->put("Value", value))
     require(section->getPropertiesErrno() ==
             E_PROPERTIES_ELEMENT_ALREADY_EXISTS);
 
@@ -537,6 +571,7 @@ Config::diff2str(const Properties& diff_
 
       Uint32 type;
       require(what->get("Type", &type));
+      require(what->get("Name", &name));
       switch (type) {
       case DT_DIFF:
       {

=== modified file 'storage/ndb/src/mgmsrv/Config.hpp'
--- a/storage/ndb/src/mgmsrv/Config.hpp	2008-09-16 09:37:22 +0000
+++ b/storage/ndb/src/mgmsrv/Config.hpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 
    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
@@ -33,6 +33,7 @@ class Config {
 public:
   Config(struct ndb_mgm_configuration *config_values = NULL);
   Config(ConfigValues* config_values);
+  Config(const Config*);
   virtual ~Config();
 
   void print() const;
@@ -45,6 +46,12 @@ public:
   bool setGeneration(Uint32);
 
   /*
+    Returns name of the config
+  */
+  const char* getName() const;
+  bool setName(const char* new_name);
+
+  /*
    Pack the config into a UtilBuffer and return it's size in bytes
   */
   Uint32 pack(UtilBuffer&) const;
@@ -80,7 +87,9 @@ public:
 
 private:
   bool setValue(Uint32 section, Uint32 section_no,
-                Uint32 id, Uint32 new_gen);
+                Uint32 id, Uint32 new_val);
+  bool setValue(Uint32 section, Uint32 section_no,
+                Uint32 id, const char* new_val);
 
   bool illegal_change(const Properties&) const;
   bool equal(const Properties&) const;

=== modified file 'storage/ndb/src/mgmsrv/ConfigInfo.cpp'
--- a/storage/ndb/src/mgmsrv/ConfigInfo.cpp	2008-09-17 13:23:21 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigInfo.cpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 
    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
@@ -191,6 +191,9 @@ const int ConfigInfo::m_NoOfRules = size
 /****************************************************************************
  * Config Rules declarations
  ****************************************************************************/
+static bool add_system_section(Vector<ConfigInfo::ConfigRuleSection>&sections,
+                               struct InitConfigFileParser::Context &ctx,
+                               const char * rule_data);
 static bool sanity_checks(Vector<ConfigInfo::ConfigRuleSection>&sections, 
 			  struct InitConfigFileParser::Context &ctx, 
 			  const char * rule_data);
@@ -211,11 +214,12 @@ static bool saveSectionsInConfigValues(V
 
 const ConfigInfo::ConfigRule 
 ConfigInfo::m_ConfigRules[] = {
+  { add_system_section, 0 },
   { sanity_checks, 0 },
   { add_node_connections, 0 },
   { set_connection_priorities, 0 },
   { check_node_vs_replicas, 0 },
-  { saveSectionsInConfigValues, "Node,Connection" },
+  { saveSectionsInConfigValues, "SYSTEM,Node,Connection" },
   { 0, 0 }
 };
 	  
@@ -348,6 +352,10 @@ const ConfigInfo::ParamInfo ConfigInfo::
     "0",
     STR_VALUE(MAX_INT_RNIL) },
 
+  /***************************************************************************
+   * DB
+   ***************************************************************************/
+
   {
     CFG_SYS_CONFIG_GENERATION,
     "ConfigGenerationNumber",
@@ -407,10 +415,7 @@ const ConfigInfo::ParamInfo ConfigInfo::
     "false",
     "false",
     "true"},
-  
-  /***************************************************************************
-   * DB
-   ***************************************************************************/
+
   {
     CFG_SECTION_NODE,
     DB_TOKEN,
@@ -1817,7 +1822,7 @@ const ConfigInfo::ParamInfo ConfigInfo::
     ConfigInfo::CI_USED,
     false,
     ConfigInfo::CI_STRING,
-    MYSQLCLUSTERDIR,
+    "",
     0, 0 },
 
   {
@@ -3237,8 +3242,6 @@ transformSystem(InitConfigFileParser::Co
     return false;
   }
 
-  ndbout << "transformSystem " << name << endl;
-
   BaseString::snprintf(ctx.pname, sizeof(ctx.pname), "SYSTEM_%s", name);
   
   return true;
@@ -3976,6 +3979,42 @@ saveInConfigValues(InitConfigFileParser:
   return true;
 }
 
+
+static bool
+add_system_section(Vector<ConfigInfo::ConfigRuleSection>&sections,
+                   struct InitConfigFileParser::Context &ctx,
+                   const char * rule_data)
+{
+  if (!ctx.m_userProperties.contains("SYSTEM")) {
+    ConfigInfo::ConfigRuleSection s;
+
+    // Generate a unique name for this new cluster
+    time_t now = ::time((time_t*)NULL);
+    struct tm* tm_now = ::localtime(&now);
+
+    char name_buf[18];
+    BaseString::snprintf(name_buf, sizeof(name_buf),
+                         "MC_%d%.2d%.2d%.2d%.2d%.2d",
+                         tm_now->tm_year + 1900,
+                         tm_now->tm_mon + 1,
+                         tm_now->tm_mday,
+                         tm_now->tm_hour,
+                         tm_now->tm_min,
+                         tm_now->tm_sec);
+
+    s.m_sectionType = BaseString("SYSTEM");
+    s.m_sectionData = new Properties(true);
+    s.m_sectionData->put("Name", name_buf);
+    s.m_sectionData->put("Type", "SYSTEM");
+
+    // ndbout_c("Generated new SYSTEM section with name '%s'", name_buf);
+
+    sections.push_back(s);
+  }
+  return true;
+}
+
+
 static bool
 sanity_checks(Vector<ConfigInfo::ConfigRuleSection>&sections, 
 	      struct InitConfigFileParser::Context &ctx, 
@@ -4161,6 +4200,7 @@ add_node_connections(Vector<ConfigInfo::
 
   Uint32 nodeId1, nodeId2, dummy;
 
+  // DB -> DB
   for (i= 0; p_db_nodes.get("", i, &nodeId1); i++){
     for (Uint32 j= i+1;; j++){
       if(!p_db_nodes.get("", j, &nodeId2)) break;
@@ -4172,6 +4212,7 @@ add_node_connections(Vector<ConfigInfo::
     }
   }
 
+  // API -> DB
   for (i= 0; p_api_nodes.get("", i, &nodeId1); i++){
     if(!p_connections.get("", nodeId1, &dummy)) {
       for (Uint32 j= 0;; j++){
@@ -4182,6 +4223,7 @@ add_node_connections(Vector<ConfigInfo::
     }
   }
 
+  // MGM -> DB
   for (i= 0; p_mgm_nodes.get("", i, &nodeId1); i++){
     if(!p_connections.get("", nodeId1, &dummy)) {
       for (Uint32 j= 0;; j++){
@@ -4191,7 +4233,19 @@ add_node_connections(Vector<ConfigInfo::
       }
     }
   }
-  
+
+  // MGM -> MGM
+  for (i= 0; p_mgm_nodes.get("", i, &nodeId1); i++){
+    for (Uint32 j= i+1;; j++){
+      if(!p_mgm_nodes.get("", j, &nodeId2)) break;
+      if(!p_connections2.get("", nodeId1+nodeId2<<16, &dummy))
+      {
+	if (!add_a_connection(sections,ctx,nodeId1,nodeId2,0))
+	  goto err;
+     }
+    }
+  }
+
   DBUG_RETURN(true);
 err:
   DBUG_RETURN(false);

=== modified file 'storage/ndb/src/mgmsrv/ConfigInfo.hpp'
--- a/storage/ndb/src/mgmsrv/ConfigInfo.hpp	2008-07-25 10:02:59 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigInfo.hpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 
    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

=== added file 'storage/ndb/src/mgmsrv/ConfigManager.cpp'
--- a/storage/ndb/src/mgmsrv/ConfigManager.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigManager.cpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,1548 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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 "ConfigManager.hpp"
+#include "MgmtSrvr.hpp"
+
+#include <NdbConfig.h>
+
+#include <SignalSender.hpp>
+#include <NdbApiSignal.hpp>
+#include <signaldata/NFCompleteRep.hpp>
+#include <signaldata/NodeFailRep.hpp>
+#include <signaldata/ApiRegSignalData.hpp>
+#include <ndb_version.h>
+
+
+#ifdef VM_TRACE
+#define require(v)  assert(v)
+#else
+static void
+require(bool v)
+{
+  if (!v)
+    abort();
+}
+#endif
+
+extern const char* opt_connect_str;
+
+ConfigManager::ConfigManager(const MgmtSrvr::MgmtOpts& opts) :
+  MgmtThread("ConfigManager"),
+  m_opts(opts),
+  m_facade(NULL),
+  m_config_mutex(NULL),
+  m_config(NULL),
+  m_new_config(NULL),
+  m_config_retriever(opt_connect_str,
+                     NDB_VERSION,
+                     NDB_MGM_NODE_TYPE_MGM,
+                     opts.bind_address),
+  m_config_change_state(CCS_IDLE),
+  m_config_state(CS_UNINITIALIZED),
+  m_previous_state(CS_UNINITIALIZED),
+  m_client_ref(RNIL),
+  m_prepared_config(NULL),
+  m_node_id(0),
+  m_datadir(NULL)
+{
+}
+
+
+ConfigManager::~ConfigManager()
+{
+  delete m_config;
+  delete m_new_config;
+  delete m_prepared_config;
+  NdbMutex_Destroy(m_config_mutex);
+}
+
+
+
+bool
+ConfigManager::init_nodeid(void)
+{
+  DBUG_ENTER("ConfigManager::init_nodeid");
+
+  NodeId nodeid= m_config_retriever.get_configuration_nodeid();
+  DBUG_PRINT("info", ("nodeid: %d", nodeid));
+
+  if (nodeid)
+  {
+    // Nodeid was specifed on command line or in NDB_CONNECTSTRING
+    m_node_id= nodeid;
+    DBUG_RETURN(true);
+  }
+
+#if 0
+  // We _could_ try connecting to other running mgmd(s)
+  // and fetch our nodeid. But, that introduces a dependency
+  // that is not beneficial for a shared nothing cluster, since
+  // it might only work when other mgmd(s) are started. If all
+  // mgmd(s) is down it would require manual intervention.
+  // Better to require the node id to always be specified
+  // on the command line(or some other "local" magic by
+  // examining --datadir)
+
+  // Try to alloc nodeid from other mgmd
+  char buf[128];
+  g_eventLogger->info("Trying to get nodeid from other mgmd(s) "\
+                      "using '%s'...",
+                      m_config_retriever.get_connectstring(buf, sizeof(buf)));
+
+  if (m_config_retriever.do_connect(3, /* retry */
+                                    1, /* delay */
+                                    0  /* verbose */) == 0)
+  {
+    g_eventLogger->info("Connected...");
+
+    nodeid = m_config_retriever.allocNodeId(3, /* retry */
+                                            1  /* delay */);
+    if (nodeid == 0)
+    {
+      // Failed to alloc nodeid from other mgmd
+      g_eventLogger->error(m_config_retriever.getErrorString());
+      DBUG_RETURN(false);
+    }
+  }
+#endif
+
+  g_eventLogger->error("Could not determine which nodeid to use for "\
+                       "this node. Specify it with --ndb-nodeid=<nodeid> "\
+                       "on command line");
+  DBUG_RETURN(false);
+}
+
+
+bool
+ConfigManager::init(void)
+{
+  DBUG_ENTER("ConfigManager::init");
+
+  m_config_mutex = NdbMutex_Create();
+  if (!m_config_mutex)
+  {
+    g_eventLogger->error("Failed to create mutex in ConfigManager!");
+    DBUG_RETURN(false);
+  }
+
+  require(m_config_state == CS_UNINITIALIZED);
+
+  if (m_config_retriever.hasError())
+  {
+    g_eventLogger->error(m_config_retriever.getErrorString());
+    DBUG_RETURN(false);
+  }
+
+  // Check datadir
+  if (m_opts.datadir)
+  {
+    // Specified on commmand line
+    if (access(m_opts.datadir, F_OK))
+    {
+      g_eventLogger->error("Directory '%s' specified with --datadir "   \
+                           "does not exist", m_opts.datadir);
+      DBUG_RETURN(false);
+    }
+    m_datadir= m_opts.datadir;
+  }
+  else
+  {
+    // Compiled in path MYSQLCLUSTERDIR
+    if (access(MYSQLCLUSTERDIR, F_OK))
+    {
+      g_eventLogger->error("The default data directory '%s' "            \
+                           "does not exist. Either create it or "       \
+                           "specify a different directory with "        \
+                           "--datadir=<path>",
+                           MYSQLCLUSTERDIR);
+      DBUG_RETURN(false);
+    }
+    m_datadir= MYSQLCLUSTERDIR;
+  }
+  DBUG_PRINT("info", ("datadir: %s", m_datadir));
+
+  if (!init_nodeid())
+    DBUG_RETURN(false);
+
+  BaseString config_bin_name;
+  if (saved_config_exists(config_bin_name))
+  {
+    Config* conf = NULL;
+    if (!(conf = load_saved_config(config_bin_name)))
+      DBUG_RETURN(false);
+
+    if (!config_ok(conf))
+      DBUG_RETURN(false);
+
+    set_config(conf);
+    m_config_state = CS_CONFIRMED;
+
+    if (m_opts.mycnf || m_opts.config_filename)
+    {
+      Config* new_conf = NULL;
+      if (m_opts.mycnf && (new_conf = load_init_mycnf()) == NULL)
+      {
+        g_eventLogger->error("Could not load configuration from 'my.cnf'");
+        DBUG_RETURN(false);
+      }
+      else if (m_opts.config_filename &&
+               (new_conf = load_init_config(m_opts.config_filename)) == NULL)
+      {
+        g_eventLogger->error("Could not load configuration from '%s'",
+                             m_opts.config_filename);
+        DBUG_RETURN(false);
+      }
+
+      /* Copy the necessary values from old to new config */
+      if (!new_conf->setGeneration(m_config->getGeneration()))
+      {
+        g_eventLogger->error("Failed to copy generation from old config");
+        DBUG_RETURN(false);
+      }
+
+      if (!new_conf->setName(m_config->getName()))
+      {
+        g_eventLogger->error("Failed to copy name from old config");
+        DBUG_RETURN(false);
+      }
+
+      /* Check if config has changed */
+      if (!m_config->equal(new_conf))
+      {
+        /* Loaded config is different */
+        BaseString buf;
+        g_eventLogger->info("Detected change of %s on disk, will try to " \
+                            "set it when all ndb_mgmd(s) started. "     \
+                            "This is the actual diff:\n%s",
+                            m_opts.mycnf ? "my.cnf" : m_opts.config_filename,
+                            m_config->diff2str(new_conf, buf));
+        m_new_config= new_conf;
+      }
+      else
+      {
+        /* Loaded config was equal to current */
+        g_eventLogger->info("Config equal!");
+        delete new_conf;
+      }
+    }
+  }
+  else
+  {
+    if (m_opts.mycnf || m_opts.config_filename)
+    {
+      Config* conf = NULL;
+      if (m_opts.mycnf && (conf = load_init_mycnf()) == NULL)
+      {
+        g_eventLogger->error("Could not load configuration from 'my.cnf'");
+        DBUG_RETURN(false);
+      }
+      else if (m_opts.config_filename &&
+               (conf = load_init_config(m_opts.config_filename)) == NULL)
+      {
+        g_eventLogger->error("Could not load initial configuration from '%s'",
+                             m_opts.config_filename);
+        DBUG_RETURN(false);
+      }
+
+      if (!config_ok(conf))
+        DBUG_RETURN(false);
+
+      /* Use the initial config for now */
+      set_config(conf);
+
+      g_eventLogger->info("Got initial configuration from '%s', will try " \
+                          "to set it when all ndb_mgmd(s) started",
+                          m_opts.mycnf ? "my.cnf" : m_opts.config_filename);
+      m_new_config = new Config(conf); // Copy config
+      m_config_state = CS_INITIAL;
+    }
+    else
+    {
+      Config* conf = NULL;
+      if (!(conf = fetch_config()))
+      {
+        g_eventLogger->error("Could not fetch config!");
+        DBUG_RETURN(false);
+      }
+
+      if (!config_ok(conf))
+        DBUG_RETURN(false);
+
+      /* Use the fetched config for now */
+      set_config(conf);
+      m_new_config = new Config(conf); // Copy config
+
+      if (m_config->getGeneration() == 0)
+      {
+        g_eventLogger->info("Fetched initial configuration, " \
+                            "generation: %d, name: '%s'. "\
+                            "Will try to set it when all ndb_mgmd(s) started",
+                            m_config->getGeneration(), m_config->getName());
+        m_config_state= CS_INITIAL;
+      }
+      else
+      {
+        g_eventLogger->info("Fetched confirmed configuration, " \
+                            "generation: %d, name: '%s'. " \
+                            "Trying to write it to disk...",
+                            m_config->getGeneration(), m_config->getName());
+        if (!prepareConfigChange(m_config))
+        {
+          abortConfigChange();
+          g_eventLogger->error("Failed to write the fetched config to disk");
+          DBUG_RETURN(false);
+        }
+        commitConfigChange();
+        m_config_state = CS_CONFIRMED;
+        g_eventLogger->info("The fetched configuration has been saved!");
+      }
+    }
+  }
+
+  require(m_config_state != CS_UNINITIALIZED);
+  DBUG_RETURN(true);
+}
+
+
+bool
+ConfigManager::prepareConfigChange(const Config* config)
+{
+  if (m_prepared_config)
+  {
+    g_eventLogger->error("Can't prepare configuration change " \
+                         "when already prepared");
+    return false;
+  }
+  Uint32 generation= config->getGeneration();
+  if (generation == 0)
+  {
+    g_eventLogger->error("Can't prepare configuration change for "\
+                         "configuration with generation 0");
+    return false;
+  }
+
+  assert(m_node_id);
+  m_config_name.assfmt("%s/ndb_%u_config.bin.%u",
+                       m_datadir, m_node_id, generation);
+  g_eventLogger->debug("Preparing configuration, generation: %d name: %s",
+                      m_config_name.c_str());
+
+  /* Check file name is free */
+  if (access(m_config_name.c_str(), F_OK) == 0)
+  {
+    g_eventLogger->error("The file '%s' already exist while preparing",
+                         m_config_name.c_str());
+    return false;
+  }
+
+  /* Pack the config */
+  UtilBuffer buf;
+  if(!config->pack(buf))
+  {
+    /* Failed to pack config */
+    g_eventLogger->error("Failed to pack configuration while preparing");
+    return false;
+  }
+
+  /* Write config to temporary file */
+  BaseString prep_config_name(m_config_name);
+  prep_config_name.append(".tmp");
+  FILE * f = fopen(prep_config_name.c_str(), "w");
+  if(f == NULL)
+  {
+    g_eventLogger->error("Failed to open file '%s' while preparing, errno: %d",
+                         prep_config_name.c_str(), errno);
+    return false;
+  }
+
+  if(fwrite(buf.get_data(), 1, buf.length(), f) != (size_t)buf.length())
+  {
+    g_eventLogger->error("Failed to write file '%s' while preparing, errno: %d",
+                         prep_config_name.c_str(), errno);
+    fclose(f);
+    unlink(prep_config_name.c_str());
+    return false;
+  }
+
+  if (fflush(f))
+  {
+    g_eventLogger->error("Failed to flush file '%s' while preparing, errno: %d",
+                         prep_config_name.c_str(), errno);
+    fclose(f);
+    unlink(prep_config_name.c_str());
+    return false;
+  }
+
+  if (fsync(fileno(f)))
+  {
+    g_eventLogger->error("Failed to sync file '%s' while preparing, errno: %d",
+                         prep_config_name.c_str(), errno);
+    fclose(f);
+    unlink(prep_config_name.c_str());
+    return false;
+  }
+  fclose(f);
+
+  m_prepared_config = new Config(config->m_configValues);
+  g_eventLogger->debug("Configuration prepared");
+
+  return true;
+}
+
+
+void
+ConfigManager::commitConfigChange(void)
+{
+  require(m_prepared_config);
+
+  /* Set new config locally and in all subscribers */
+  set_config(m_prepared_config);
+  m_prepared_config= NULL;
+
+  /* Rename file to real name */
+  require(m_config_name.length());
+  BaseString prep_config_name(m_config_name);
+  prep_config_name.append(".tmp");
+  if(rename(prep_config_name.c_str(), m_config_name.c_str()))
+  {
+    g_eventLogger->error("rename from '%s' to '%s' failed while committing, " \
+                         "errno: %d",
+                         prep_config_name.c_str(), m_config_name.c_str(),
+                         errno);
+    // Crash and leave the prepared config file in place
+    abort();
+  }
+  m_config_name.clear();
+
+  g_eventLogger->info("Configuration %d commited", m_config->getGeneration());
+}
+
+
+void
+ConfigManager::set_config(Config* new_config)
+{
+  delete m_config;
+  m_config = new_config;
+
+  for (unsigned i = 0; i < m_subscribers.size(); i++)
+    m_subscribers[i]->config_changed(m_node_id, new_config);
+
+}
+
+
+int
+ConfigManager::add_config_change_subscriber(ConfigSubscriber* subscriber)
+{
+  return m_subscribers.push_back(subscriber);
+}
+
+
+bool
+ConfigManager::config_ok(const Config* conf)
+{
+  assert(m_node_id);
+  if (!m_config_retriever.verifyConfig(conf->m_configValues, m_node_id))
+  {
+    g_eventLogger->error(m_config_retriever.getErrorString());
+    return false;
+  }
+
+  // Check if --datadir is same as DataDir from config
+  assert(m_datadir);
+  ConfigIter iter(conf, CFG_SECTION_NODE);
+  require(iter.find(CFG_NODE_ID, m_node_id) == 0);
+
+  const char *datadir;
+  require(iter.get(CFG_NODE_DATADIR, &datadir) == 0);
+
+  if (strcmp(datadir, "") != 0 &&        // Not set -> empty string
+      strcmp(m_datadir, datadir) != 0)   // Different
+  {
+    // Not same --datadir and DataDir
+    if (strcmp(m_datadir, MYSQLCLUSTERDIR) == 0 ||
+        strcmp(datadir, MYSQLCLUSTERDIR) == 0)
+    {
+      // Using the builtin default --datadir
+      g_eventLogger->error("The builtin data directory '%s' does "      \
+                           "not match DataDir=%s specified in "         \
+                           "configuation. Either specify --datadir=%s " \
+                           "on command line or remove the DataDir=%s "  \
+                           "from configuration in order to use the "    \
+                           "builtin default.",
+                           m_datadir, datadir, m_datadir, datadir);
+      return false;
+    }
+    else
+    {
+      // Using --datadir specified on command line
+      g_eventLogger->error("The data directory specified on command line " \
+                           "with --datadir=%s does not match DataDir=%s " \
+                           "specified in configuration. You need to "    \
+                           "change one of them.",
+                           m_datadir, datadir);
+      return false;
+    }
+  }
+  NdbConfig_SetPath(m_datadir);
+
+  return true;
+}
+
+
+void
+ConfigManager::abortConfigChange(void)
+{
+  /* Should always succeed */
+
+  /* Remove the prepared file */
+  BaseString prep_config_name(m_config_name);
+  prep_config_name.append(".tmp");
+  unlink(prep_config_name.c_str());
+  m_config_name.clear();
+
+  delete m_prepared_config;
+  m_prepared_config= NULL;
+}
+
+
+
+void
+ConfigManager::sendConfigChangeImplRef(SignalSender& ss, NodeId nodeId,
+                                       ConfigChangeRef::ErrorCode error) const
+{
+  SimpleSignal ssig;
+  ConfigChangeImplRef* const ref =
+    CAST_PTR(ConfigChangeImplRef, ssig.getDataPtrSend());
+  ref->errorCode = error;
+
+  g_eventLogger->debug("Send CONFIG_CHANGE_IMPL_REF to node: %d, error: %d",
+                       nodeId, error);
+
+  ss.sendSignal(nodeId, ssig,
+                MGM_CONFIG_MAN, GSN_CONFIG_CHANGE_IMPL_REF,
+                ConfigChangeImplRef::SignalLength);
+}
+
+
+
+void
+ConfigManager::execCONFIG_CHANGE_IMPL_REQ(SignalSender& ss, SimpleSignal* sig)
+{
+  NodeId nodeId = refToNode(sig->header.theSendersBlockRef);
+  const ConfigChangeImplReq * const req =
+    CAST_CONSTPTR(ConfigChangeImplReq, sig->getDataPtr());
+
+  g_eventLogger->debug("Got CONFIG_CHANGE_IMPL_REQ from node: %d, "\
+                       "requestType: %d",
+                       nodeId, req->requestType);
+
+  Guard g(m_config_mutex);
+
+  switch(req->requestType){
+  case ConfigChangeImplReq::Prepare:{
+    if (sig->header.m_noOfSections != 1)
+    {
+      sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::NoConfigData);
+      return;
+    }
+
+    ConfigValuesFactory cf;
+    if (!cf.unpack(sig->ptr[0].p, req->length))
+    {
+      sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::FailedToUnpack);
+      return;
+    }
+
+    Config* new_config = new Config(cf.getConfigValues());
+    Uint32 new_generation = new_config->getGeneration();
+    Uint32 curr_generation = m_config->getGeneration();
+    const char* new_name = new_config->getName();
+    const char* curr_name = m_config->getName();
+
+    if (m_config->illegal_change(new_config))
+    {
+      sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::IllegalConfigChange);
+      return;
+    }
+
+    if (req->initial)
+    {
+      // Check own state
+      if (m_config_state != CS_INITIAL)
+      {
+        g_eventLogger->warning("Refusing to start initial "             \
+                               "configuration change since this node "  \
+                               "is not in INITIAL state");
+        sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::IllegalState);
+        return;
+      }
+
+      // Check generation
+      if (new_generation != 0)
+      {
+        g_eventLogger->warning("Refusing to start initial "             \
+                               "configuration change since new "        \
+                               "generation is not 0 (new_generation: %d)",
+                               new_generation);
+        sendConfigChangeImplRef(ss, nodeId,
+                                ConfigChangeRef::IllegalInitialGeneration);
+        return;
+      }
+      new_generation = 1;
+
+      // Check config is equal to our initial config
+      {
+        Config new_config_copy(new_config);
+        require(new_config_copy.setName(new_name));
+        unsigned exclude[]= {CFG_SECTION_SYSTEM, 0};
+        if (!new_config_copy.equal(m_new_config, exclude))
+        {
+          BaseString buf;
+          g_eventLogger->warning("Refusing to start initial config "    \
+                                 "change when nodes have different "    \
+                                 "config\n"                             \
+                                 "This is the actual diff:\n%s",
+                                 new_config_copy.diff2str(m_new_config, buf));
+          sendConfigChangeImplRef(ss, nodeId,
+                                  ConfigChangeRef::DifferentInitial);
+          return;
+        }
+      }
+
+      /*
+         Scrap the m_new_config, it's been used to check that other node
+         started from equal initial config, now it's not needed anymore
+      */
+      delete m_new_config;
+      m_new_config = NULL;
+
+    }
+    else
+    {
+      if (new_generation == 0 ||
+          new_generation != curr_generation)
+      {
+        BaseString buf;
+        g_eventLogger->warning("Refusing to start config change "     \
+                               "requested by node with different "    \
+                               "generation: %d. Our generation: %d\n" \
+                               "This is the actual diff:\n%s",
+                               new_generation, curr_generation,
+                               new_config->diff2str(m_config, buf));
+        sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::InvalidGeneration);
+        return;
+      }
+      new_generation++;
+
+      // Check same cluster name
+      if (strcmp(new_name, curr_name))
+      {
+        BaseString buf;
+        g_eventLogger->warning("Refusing to start config change "       \
+                               "requested by node with different "      \
+                               "name: '%s'. Our name: '%s'\n"           \
+                               "This is the actual diff:\n%s",
+                               new_name, curr_name,
+                               new_config->diff2str(m_config, buf));
+        sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::InvalidConfigName);
+        return;
+      }
+    }
+
+    // Set new generation
+    if(!new_config->setGeneration(new_generation))
+    {
+      g_eventLogger->error("Failed to set new generation to %d",
+                           new_generation);
+      sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::SetGenerationFailed);
+      return;
+    }
+
+    if (!prepareConfigChange(new_config))
+    {
+      sendConfigChangeImplRef(ss, nodeId, ConfigChangeRef::PrepareFailed);
+      return;
+    }
+    break;
+  }
+
+  case ConfigChangeImplReq::Commit:
+    commitConfigChange();
+
+    // All nodes has agreed on config -> CONFIRMED
+    m_config_state = CS_CONFIRMED;
+
+    break;
+
+  case ConfigChangeImplReq::Abort:
+    abortConfigChange();
+    break;
+
+  default:
+    g_eventLogger->error("execCONFIG_CHANGE_IMPL_REQ: unhandled state");
+    abort();
+    break;
+  }
+
+  /* Send CONF */
+  SimpleSignal ssig;
+  ConfigChangeImplConf* const conf =
+    CAST_PTR(ConfigChangeImplConf, ssig.getDataPtrSend());
+  conf->requestType = req->requestType;
+
+  g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_CONF to node: %d",
+                       nodeId);
+
+  ss.sendSignal(nodeId, ssig,
+                MGM_CONFIG_MAN,
+                GSN_CONFIG_CHANGE_IMPL_CONF,
+                ConfigChangeImplConf::SignalLength);
+}
+
+
+void
+ConfigManager::execCONFIG_CHANGE_IMPL_REF(SignalSender& ss, SimpleSignal* sig)
+{
+  NodeId nodeId = refToNode(sig->header.theSendersBlockRef);
+  g_eventLogger->debug("Got CONFIG_CHANGE_IMPL_REF from node: %d", nodeId);
+
+  const ConfigChangeImplRef * const ref =
+    CAST_CONSTPTR(ConfigChangeImplRef, sig->getDataPtr());
+  g_eventLogger->warning("Node %d refused configuration change, error: %d",
+                         nodeId, ref->errorCode);
+
+  switch(m_config_change_state){
+
+  case CCS_PREPARING:{
+    /* Got ref while preparing */
+    m_config_change_state = CCS_ABORT;
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    /* Abort all other nodes */
+    SimpleSignal ssig;
+    ConfigChangeImplReq* const req =
+      CAST_PTR(ConfigChangeImplReq, ssig.getDataPtrSend());
+    req->requestType = ConfigChangeImplReq::Abort;
+
+    g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_REQ(abort) to node %d",
+                         nodeId);
+    require(m_waiting_for.isclear());
+    m_waiting_for = ss.broadcastSignal(m_all_mgm, ssig,
+                                  MGM_CONFIG_MAN,
+                                  GSN_CONFIG_CHANGE_IMPL_REQ,
+                                  ConfigChangeImplReq::SignalLength);
+    if (m_waiting_for.isclear())
+      m_config_change_state = CCS_IDLE;
+    else
+      m_config_change_state = CCS_ABORTING;
+    break;
+  }
+
+  case CCS_COMITTING:
+    /* Got ref while comitting, impossible */
+    abort();
+    break;
+
+  case CCS_ABORT:{
+    /* Got ref(another) while already decided to abort */
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    /* Abort all other nodes */
+    SimpleSignal ssig;
+    ConfigChangeImplReq* const req =
+      CAST_PTR(ConfigChangeImplReq, ssig.getDataPtrSend());
+    req->requestType = ConfigChangeImplReq::Abort;
+
+    g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_REQ(abort) to node %d",
+                         nodeId);
+    require(m_waiting_for.isclear());
+    m_waiting_for = ss.broadcastSignal(m_all_mgm, ssig,
+                                  MGM_CONFIG_MAN,
+                                  GSN_CONFIG_CHANGE_IMPL_REQ,
+                                  ConfigChangeImplReq::SignalLength);
+    if (m_waiting_for.isclear())
+      m_config_change_state = CCS_IDLE;
+    else
+      m_config_change_state = CCS_ABORTING;
+    break;
+  }
+
+  case CCS_ABORTING:
+    /* Got ref while aborting, impossible */
+    abort();
+    break;
+
+  default:
+    g_eventLogger->error("execCONFIG_CHANGE_IMPL_REF: unhandled state");
+    abort();
+    break;
+  }
+}
+
+
+void
+ConfigManager::execCONFIG_CHANGE_IMPL_CONF(SignalSender& ss, SimpleSignal* sig)
+{
+  NodeId nodeId = refToNode(sig->header.theSendersBlockRef);
+  const ConfigChangeImplConf * const conf =
+    CAST_CONSTPTR(ConfigChangeImplConf, sig->getDataPtr());
+  g_eventLogger->debug("Got CONFIG_CHANGE_IMPL_CONF from node %d", nodeId);
+
+  switch(m_config_change_state){
+  case CCS_PREPARING:{
+    require(conf->requestType == ConfigChangeImplReq::Prepare);
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    /* Send commit to all nodes */
+    SimpleSignal ssig;
+    ConfigChangeImplReq* const req =
+      CAST_PTR(ConfigChangeImplReq, ssig.getDataPtrSend());
+
+    req->requestType = ConfigChangeImplReq::Commit;
+
+    g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_REQ(commit)");
+    require(m_waiting_for.isclear());
+    m_waiting_for = ss.broadcastSignal(m_all_mgm, ssig,
+                                  MGM_CONFIG_MAN,
+                                  GSN_CONFIG_CHANGE_IMPL_REQ,
+                                  ConfigChangeImplReq::SignalLength);
+    if (m_waiting_for.isclear())
+      m_config_change_state = CCS_IDLE;
+    else
+      m_config_change_state = CCS_COMITTING;
+    break;
+  }
+
+  case CCS_COMITTING:{
+    require(conf->requestType == ConfigChangeImplReq::Commit);
+
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    require(m_client_ref != RNIL);
+    if (m_client_ref == ss.getOwnRef())
+    {
+      g_eventLogger->info("Config change completed! New generation: %d",
+                          m_config->getGeneration());
+    }
+    else
+    {
+      /* Send CONF to requestor */
+      sendConfigChangeConf(ss, m_client_ref);
+    }
+    m_client_ref = RNIL;
+    m_config_change_state = CCS_IDLE;
+    break;
+  }
+
+  case CCS_ABORT:{
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    /* Abort all other nodes */
+    SimpleSignal ssig;
+    ConfigChangeImplReq* const req =
+      CAST_PTR(ConfigChangeImplReq, ssig.getDataPtrSend());
+    req->requestType = ConfigChangeImplReq::Abort;
+
+    g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_REQ(abort)");
+    require(m_waiting_for.isclear());
+    m_waiting_for = ss.broadcastSignal(m_all_mgm, ssig,
+                                  MGM_CONFIG_MAN,
+                                  GSN_CONFIG_CHANGE_IMPL_REQ,
+                                  ConfigChangeImplReq::SignalLength);
+    if (m_waiting_for.isclear())
+        m_config_change_state = CCS_IDLE;
+    else
+      m_config_change_state = CCS_ABORTING;
+    break;
+  }
+
+  case CCS_ABORTING:{
+    m_waiting_for.clear(nodeId);
+    if (!m_waiting_for.isclear())
+      return;
+
+    require(m_client_ref != RNIL);
+    if (m_client_ref == ss.getOwnRef())
+    {
+      g_eventLogger->error("Config change failed!");
+      exit(1);
+    }
+    else
+    {
+      /* Send ref to the requestor */
+      sendConfigChangeRef(ss, m_client_ref,
+                          ConfigChangeRef::ConfigChangeAborted);
+    }
+    m_client_ref = RNIL;
+    m_config_change_state = CCS_IDLE;
+    break;
+  }
+
+  default:
+    g_eventLogger->error("execCONFIG_CHANGE_IMPL_CONF: unhandled state");
+    abort();
+    break;
+  }
+}
+
+
+void
+ConfigManager::sendConfigChangeRef(SignalSender& ss, BlockReference to,
+                                   ConfigChangeRef::ErrorCode error) const
+{
+  NodeId nodeId = refToNode(to);
+  SimpleSignal ssig;
+  ConfigChangeRef* const ref =
+    CAST_PTR(ConfigChangeRef, ssig.getDataPtrSend());
+  ref->errorCode = error;
+
+  g_eventLogger->debug("Send CONFIG_CHANGE_REF to node: %d, error: %d",
+                       nodeId, error);
+
+  ss.sendSignal(nodeId, ssig, refToBlock(to),
+                GSN_CONFIG_CHANGE_REF, ConfigChangeRef::SignalLength);
+}
+
+
+void
+ConfigManager::sendConfigChangeConf(SignalSender& ss, BlockReference to) const
+{
+  NodeId nodeId = refToNode(to);
+  SimpleSignal ssig;
+
+  g_eventLogger->debug("Send CONFIG_CHANGE_CONF to node: %d", nodeId);
+
+  ss.sendSignal(nodeId, ssig, refToBlock(to),
+                GSN_CONFIG_CHANGE_CONF, ConfigChangeConf::SignalLength);
+}
+
+
+void
+ConfigManager::startInitConfigChange(SignalSender& ss)
+{
+  require(m_config_state == CS_INITIAL);
+  g_eventLogger->info("Starting initial configuration change");
+  m_client_ref = ss.getOwnRef();
+  sendConfigChangeImplReq(ss, m_new_config);
+}
+
+
+void
+ConfigManager::startNewConfigChange(SignalSender& ss)
+{
+  require(m_config_state == CS_CONFIRMED);
+  g_eventLogger->info("Starting configuration change, generation: %d",
+                      m_new_config->getGeneration());
+  m_client_ref = ss.getOwnRef();
+  sendConfigChangeImplReq(ss, m_new_config);
+
+  /* The new config has been sent and can now be discarded */
+  delete m_new_config;
+  m_new_config = NULL;
+}
+
+
+void
+ConfigManager::sendConfigChangeImplReq(SignalSender& ss, const Config* conf)
+{
+  require(m_client_ref != RNIL);
+
+  /* Send prepare to all MGM nodes */
+  SimpleSignal ssig;
+
+  UtilBuffer buf;
+  conf->pack(buf);
+  ssig.ptr[0].p = (Uint32*)buf.get_data();
+  ssig.ptr[0].sz = (buf.length() + 3) / 4;
+  ssig.header.m_noOfSections = 1;
+
+  ConfigChangeImplReq* const req =
+    CAST_PTR(ConfigChangeImplReq, ssig.getDataPtrSend());
+  req->requestType = ConfigChangeImplReq::Prepare;
+  req->initial = (m_config_state == CS_INITIAL);
+  req->length = buf.length();
+
+  g_eventLogger->debug("Sending CONFIG_CHANGE_IMPL_REQ(prepare)");
+
+  require(m_waiting_for.isclear());
+  m_waiting_for = ss.broadcastSignal(m_all_mgm, ssig,
+                                MGM_CONFIG_MAN,
+                                GSN_CONFIG_CHANGE_IMPL_REQ,
+                                ConfigChangeImplReq::SignalLength);
+  if (!m_waiting_for.isclear())
+    m_config_change_state = CCS_ABORT;
+  else
+    require(m_config_change_state == CCS_IDLE);
+  m_config_change_state = CCS_PREPARING;
+}
+
+
+void
+ConfigManager::execCONFIG_CHANGE_REQ(SignalSender& ss, SimpleSignal* sig)
+{
+  BlockReference from = sig->header.theSendersBlockRef;
+  const ConfigChangeReq * const req =
+    CAST_CONSTPTR(ConfigChangeReq, sig->getDataPtr());
+
+  if (!m_started.equal(m_all_mgm)) // Not all started
+  {
+    sendConfigChangeRef(ss, from, ConfigChangeRef::NotAllStarted);
+    return;
+  }
+
+  if (m_all_mgm.find(0) != m_facade->ownId()) // Not the master
+  {
+    sendConfigChangeRef(ss, from, ConfigChangeRef::NotMaster);
+    return;
+  }
+
+  if (m_config_change_state != CCS_IDLE)
+  {
+    sendConfigChangeRef(ss, from, ConfigChangeRef::ConfigChangeOnGoing);
+    return;
+  }
+
+  if (sig->header.m_noOfSections != 1)
+  {
+    sendConfigChangeRef(ss, from, ConfigChangeRef::NoConfigData);
+    return;
+  }
+
+  ConfigValuesFactory cf;
+  if (!cf.unpack(sig->ptr[0].p, req->length))
+  {
+    sendConfigChangeRef(ss, from, ConfigChangeRef::FailedToUnpack);
+    return;
+  }
+
+  Config new_config(cf.getConfigValues());
+  if (!config_ok(&new_config))
+  {
+    g_eventLogger->warning("Refusing to start config change, the config "\
+                           "is not ok");
+    sendConfigChangeRef(ss, from, ConfigChangeRef::FailedToUnpack);
+    return;
+  }
+
+  m_client_ref = from;
+  sendConfigChangeImplReq(ss, &new_config);
+  return;
+}
+
+
+void
+ConfigManager::execCONFIG_CHECK_REQ(SignalSender& ss, SimpleSignal* sig)
+{
+  Guard g(m_config_mutex);
+  BlockReference from = sig->header.theSendersBlockRef;
+  NodeId nodeId = refToNode(from);
+  const ConfigCheckReq * const req =
+    CAST_CONSTPTR(ConfigCheckReq, sig->getDataPtr());
+
+  Uint32 other_generation = req->generation;
+  ConfigState other_state = (ConfigState)req->state;
+
+  Uint32 generation = m_config->getGeneration();
+
+  g_eventLogger->debug("Got CONFIG_CHECK_REQ from node: %d. "   \
+                       "generation: %d, other_generation: %d, " \
+                       "our state: %d, other state: %d",
+                       generation, other_generation,
+                       m_config_state, other_state, nodeId);
+
+  switch (m_config_state)
+  {
+  default:
+  case CS_UNINITIALIZED:
+    g_eventLogger->error("execCONFIG_CHECK_REQ: unhandled state");
+    abort();
+    break;
+
+  case CS_INITIAL:
+    if (other_state != CS_INITIAL)
+    {
+      g_eventLogger->error("Other node are not in INITIAL");
+      exit(1);
+    }
+
+    require(generation == 0);
+    if (other_generation != generation)
+    {
+      g_eventLogger->warning("Refusing other node, it has different "   \
+                             "generation: %d, expected: %d",
+                             other_generation, generation);
+      sendConfigCheckRef(ss, from, ConfigCheckRef::WrongGeneration,
+                         generation, other_generation,
+                         m_config_state, other_state);
+      return;
+    }
+    break;
+
+  case CS_CONFIRMED:
+    if (other_state != CS_CONFIRMED)
+    {
+      g_eventLogger->warning("Refusing other node, it's in different "  \
+                             "state: %d, expected: %d",
+                             other_state, m_config_state);
+      sendConfigCheckRef(ss, from, ConfigCheckRef::WrongState,
+                         generation, other_generation,
+                         m_config_state, other_state);
+      return;
+    }
+
+    if (other_generation == generation)
+    {
+      ;// OK
+    }
+    else if (other_generation < generation)
+    {
+      g_eventLogger->warning("Refusing other node, it has lower "       \
+                             " generation: %d, expected: %d",
+                             other_generation, generation);
+      sendConfigCheckRef(ss, from, ConfigCheckRef::WrongGeneration,
+                         generation, other_generation,
+                         m_config_state, other_state);
+      return;
+    }
+    else
+    {
+      g_eventLogger->error("Other node has higher generation: %d, this " \
+                           "node is out of sync with generation: %d",
+                           other_generation, generation);
+      exit(1);
+    }
+
+    break;
+  }
+
+  sendConfigCheckConf(ss, from);
+  return;
+}
+
+
+void
+ConfigManager::sendConfigCheckReq(SignalSender& ss, NodeBitmask to)
+{
+  SimpleSignal ssig;
+  ConfigCheckReq* const req =
+    CAST_PTR(ConfigCheckReq, ssig.getDataPtrSend());
+  req->state =        m_config_state;
+  req->generation =   m_config->getGeneration();
+
+  BaseString buf("Sending CONFIG_CHECK_REQ to node(s) ");
+  unsigned i = 0;
+  while((i = to.find(i+1)) != NodeBitmask::NotFound)
+    buf.appfmt("%d ", i);
+  g_eventLogger->debug(buf);
+
+  require(m_waiting_for.isclear());
+  m_waiting_for = ss.broadcastSignal(to, ssig, MGM_CONFIG_MAN,
+                                GSN_CONFIG_CHECK_REQ,
+                                ConfigCheckReq::SignalLength);
+}
+
+
+void
+ConfigManager::sendConfigCheckRef(SignalSender& ss, BlockReference to,
+                                  ConfigCheckRef::ErrorCode error,
+                                  Uint32 generation,
+                                  Uint32 other_generation,
+                                  ConfigState state,
+                                  ConfigState other_state) const
+{
+  NodeId nodeId = refToNode(to);
+  SimpleSignal ssig;
+  ConfigCheckRef* const ref =
+    CAST_PTR(ConfigCheckRef, ssig.getDataPtrSend());
+  ref->error = error;
+  ref->generation = other_generation;
+  ref->expected_generation = generation;
+  ref->state = other_state;
+  ref->expected_state = state;
+
+  g_eventLogger->debug("Send CONFIG_CHECK_REF with error: %d to node: %d",
+                       error, nodeId);
+
+  ss.sendSignal(nodeId, ssig, MGM_CONFIG_MAN,
+                GSN_CONFIG_CHECK_REF, ConfigCheckReq::SignalLength);
+}
+
+
+void
+ConfigManager::sendConfigCheckConf(SignalSender& ss, BlockReference to) const
+{
+  NodeId nodeId = refToNode(to);
+  SimpleSignal ssig;
+  ConfigCheckConf* const conf =
+    CAST_PTR(ConfigCheckConf, ssig.getDataPtrSend());
+  conf->state = m_config_state;
+  conf->generation = m_config->getGeneration();
+
+  g_eventLogger->debug("Send CONFIG_CHECK_CONF to node: %d", nodeId);
+
+  ss.sendSignal(nodeId, ssig, MGM_CONFIG_MAN,
+                GSN_CONFIG_CHECK_CONF, ConfigCheckReq::SignalLength);
+}
+
+
+void
+ConfigManager::execCONFIG_CHECK_CONF(SignalSender& ss, SimpleSignal* sig)
+{
+  BlockReference from = sig->header.theSendersBlockRef;
+  NodeId nodeId = refToNode(from);
+  m_waiting_for.clear(nodeId);
+  m_checked.set(nodeId);
+
+  g_eventLogger->debug("Got CONFIG_CHECK_CONF from node: %d",
+                       nodeId);
+
+  return;
+}
+
+
+void
+ConfigManager::execCONFIG_CHECK_REF(SignalSender& ss, SimpleSignal* sig)
+{
+  BlockReference from = sig->header.theSendersBlockRef;
+  NodeId nodeId = refToNode(from);
+
+  const ConfigCheckRef* const ref =
+    CAST_CONSTPTR(ConfigCheckRef, sig->getDataPtr());
+
+  g_eventLogger->error("Got CONFIG_CHECK_REF from node %d, "   \
+                       "error: %d, message: '%s'\n"            \
+                       "generation: %d, expected generation: %d\n"\
+                       "state: %d, expected state: %d",
+                       nodeId, ref->error,
+                       ConfigCheckRef::errorMessage(ref->error),
+                       ref->generation, ref->expected_generation,
+                       ref->state, ref->expected_state);
+  exit(1);
+}
+
+
+void
+ConfigManager::run()
+{
+  assert(m_facade);
+  SignalSender ss(m_facade, MGM_CONFIG_MAN);
+  ss.lock();
+
+  ss.getNodes(m_all_mgm, NodeInfo::MGM);
+
+  m_started.set(m_facade->ownId());
+
+  while (!is_stopped())
+  {
+
+    if (m_config_change_state == CCS_IDLE)
+    {
+      bool print_state = false;
+      if (m_previous_state != m_config_state)
+      {
+        print_state = true;
+        m_previous_state = m_config_state;
+      }
+
+      /*
+        Check if it's necessary to start something to get
+        out of the current state
+      */
+      switch (m_config_state){
+
+      case CS_UNINITIALIZED:
+        abort();
+        break;
+
+      case CS_INITIAL:
+        /*
+          INITIAL => CONFIRMED
+          When all mgm nodes has been started and checked that they
+          are also in INITIAL, the node with the lowest node id
+          will start an initial config change. When completed
+          all nodes will be in CONFIRMED
+        */
+
+        if (print_state)
+          ndbout_c("==INITIAL==");
+
+        if (m_new_config &&                     // Updated config.ini was found
+            m_started.equal(m_all_mgm) &&       // All mgmd started
+            m_checked.equal(m_started) &&       // All nodes checked
+            m_all_mgm.find(0) == m_facade->ownId()) // Lowest nodeid
+        {
+            startInitConfigChange(ss);
+        }
+        break;
+
+      case CS_CONFIRMED:
+        if (print_state)
+          ndbout_c("==CONFIRMED==");
+
+        if (m_new_config &&                 // Updated config.ini was found
+            m_started.equal(m_all_mgm) &&   // All mgmd started
+            m_checked.equal(m_started))     // All nodes checked
+        {
+          startNewConfigChange(ss);
+        }
+
+        break;
+
+      default:
+        break;
+      }
+
+      // Send CHECK_CONFIG to all nodes not yet checked
+      if (m_waiting_for.isclear() &&    // Nothing else ongoing
+          !m_checked.equal(m_started))  // Some nodes have not been checked
+      {
+        NodeBitmask not_checked;
+        not_checked.assign(m_started);
+        not_checked.bitANDC(m_checked);
+        sendConfigCheckReq(ss, not_checked);
+      }
+    }
+
+
+    SimpleSignal *sig = ss.waitFor((Uint32)1000);
+    if (!sig)
+      continue;
+
+    switch (sig->readSignalNumber()) {
+
+    case GSN_CONFIG_CHANGE_REQ:
+      execCONFIG_CHANGE_REQ(ss, sig);
+      break;
+
+    case GSN_CONFIG_CHANGE_IMPL_REQ:
+      execCONFIG_CHANGE_IMPL_REQ(ss, sig);
+      break;
+
+    case GSN_CONFIG_CHANGE_IMPL_REF:
+      execCONFIG_CHANGE_IMPL_REF(ss, sig);
+      break;
+
+    case GSN_CONFIG_CHANGE_IMPL_CONF:
+      execCONFIG_CHANGE_IMPL_CONF(ss, sig);
+      break;
+
+    case GSN_NF_COMPLETEREP:{
+      const NFCompleteRep * const rep =
+        CAST_CONSTPTR(NFCompleteRep, sig->getDataPtr());
+      NodeId nodeId= rep->failedNodeId;
+
+      if (m_all_mgm.get(nodeId)) // Not mgm node
+        break;
+
+      ndbout_c("Node %d failed", nodeId);
+      m_started.clear(nodeId);
+      m_checked.clear(nodeId);
+
+      if (m_config_change_state != CCS_IDLE)
+      {
+        g_eventLogger->info("Node %d failed during config change!!",
+                            nodeId);
+        // TODO start take over of config change protocol
+      }
+      break;
+    }
+
+    case GSN_NODE_FAILREP:
+      // ignore, NF_COMPLETEREP will come
+      break;
+
+    case GSN_API_REGCONF:{
+      NodeId nodeId = refToNode(sig->header.theSendersBlockRef);
+      if (m_all_mgm.get(nodeId) &&      // Is a mgm node
+          !m_started.get(nodeId))       // Not already marked as started
+      {
+        g_eventLogger->info("Node %d connected", nodeId);
+        m_started.set(nodeId);
+      }
+      break;
+    }
+
+    case GSN_CONFIG_CHECK_REQ:
+      execCONFIG_CHECK_REQ(ss, sig);
+      break;
+
+    case GSN_CONFIG_CHECK_REF:
+      execCONFIG_CHECK_REF(ss, sig);
+      break;
+
+    case GSN_CONFIG_CHECK_CONF:
+      execCONFIG_CHECK_CONF(ss, sig);
+      break;
+
+    default:
+      sig->print();
+      g_eventLogger->error("Unknown signal received. SignalNumber: "
+                           "%i from (%d, 0x%x)",
+                           sig->readSignalNumber(),
+                           refToNode(sig->header.theSendersBlockRef),
+                           refToBlock(sig->header.theSendersBlockRef));
+      abort();
+      break;
+    }
+  }
+}
+
+
+#include "InitConfigFileParser.hpp"
+
+Config*
+ConfigManager::load_init_config(const char* config_filename)
+{
+   InitConfigFileParser parser;
+   g_eventLogger->info("Reading cluster configuration from '%s'",
+                       config_filename);
+  return parser.parseConfig(config_filename);
+}
+
+
+Config*
+ConfigManager::load_init_mycnf(void)
+{
+  InitConfigFileParser parser;
+  g_eventLogger->info("Reading cluster configuration using my.cnf");
+  return parser.parse_mycnf();
+}
+
+
+Config*
+ConfigManager::fetch_config(void)
+{
+  DBUG_ENTER("ConfigManager::fetch_config");
+
+  /* Loop until config loaded from other mgmd(s) */
+  char buf[128];
+  g_eventLogger->info("Trying to get configuration from other mgmd(s) "\
+                      "using '%s'...",
+                      m_config_retriever.get_connectstring(buf, sizeof(buf)));
+
+  if (!m_config_retriever.is_connected())
+  {
+
+    if (m_config_retriever.do_connect(-1 /* retry */,
+                                      1 /* delay */,
+                                      0 /* verbose */) != 0)
+    {
+      g_eventLogger->error("INTERNAL ERROR: Inifinite wait for connect " \
+                           "returned!");
+      abort();
+    }
+    g_eventLogger->info("Connected...");
+
+  }
+
+  // read config from other management server
+  ndb_mgm_configuration * tmp =
+    m_config_retriever.getConfig(m_config_retriever.get_mgmHandle());
+  if (tmp == NULL) {
+    g_eventLogger->error(m_config_retriever.getErrorString());
+    DBUG_RETURN(false);
+  }
+
+  DBUG_RETURN(new Config(tmp));
+}
+
+
+#include "DirIterator.hpp"
+
+bool
+ConfigManager::saved_config_exists(BaseString& config_name) const
+{
+  DirIterator iter;
+
+  if (iter.open(m_datadir) != 0)
+    return false;
+
+  const char* name;
+  unsigned nodeid;
+  char extra; // Avoid matching ndb_2_config.bin.2.tmp
+  unsigned version, max_version= 0;
+  while ((name= iter.next_file()) != NULL)
+  {
+    if (sscanf(name,
+               "ndb_%u_config.bin.%u%c",
+               &nodeid, &version, &extra) == 2)
+    {
+      // ndbout_c("match: %s", name);
+
+      if (nodeid != m_node_id)
+        continue;
+
+      if (version>max_version)
+        max_version= version;
+    }
+  }
+
+  if (max_version == 0)
+    return false;
+
+  config_name.assfmt("%s/ndb_%u_config.bin.%u",
+                     m_datadir, m_node_id, max_version);
+  return true;
+}
+
+
+Config*
+ConfigManager::load_saved_config(const BaseString& config_name)
+{
+  struct ndb_mgm_configuration * tmp =
+    m_config_retriever.getConfig(config_name.c_str());
+  if(tmp == NULL)
+  {
+    g_eventLogger->error("Failed to load config from '%s', error: '%s'",
+                         config_name.c_str(),
+                         m_config_retriever.getErrorString());
+    return NULL;
+  }
+
+  g_eventLogger->info("Loaded config from '%s'", config_name.c_str());
+  Config* conf = new Config(tmp);
+  if (conf == NULL)
+    g_eventLogger->error("Failed to load config, out of memory");
+  return conf;
+}
+
+
+bool
+ConfigManager::get_packed_config(UtilBuffer& pack_buf)
+{
+  Guard g(m_config_mutex);
+
+  /* Only allow the config to be exported if it's been confirmed */
+  if (m_config_state != CS_CONFIRMED)
+    return false;
+
+  require(m_config);
+  return m_config->pack(pack_buf);
+}
+
+
+template class Vector<ConfigSubscriber*>;
+

=== added file 'storage/ndb/src/mgmsrv/ConfigManager.hpp'
--- a/storage/ndb/src/mgmsrv/ConfigManager.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigManager.hpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,153 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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 ConfigManager_H
+#define ConfigManager_H
+
+#include "MgmtThread.hpp"
+#include "Config.hpp"
+#include "ConfigSubscriber.hpp"
+#include "MgmtSrvr.hpp"
+
+#include <ConfigRetriever.hpp>
+
+#include <SignalSender.hpp>
+#include <NodeBitmask.hpp>
+
+#include <signaldata/ConfigChange.hpp>
+
+class ConfigManager : public MgmtThread {
+  const MgmtSrvr::MgmtOpts& m_opts;
+  TransporterFacade* m_facade;
+
+  NdbMutex *m_config_mutex;
+  const Config * m_config;
+  const Config * m_new_config;
+
+  ConfigRetriever m_config_retriever;
+
+  enum ConfigChangeState {
+    CCS_IDLE,
+    CCS_PREPARING,
+    CCS_COMITTING,
+    CCS_ABORT,
+    CCS_ABORTING
+  } m_config_change_state;
+
+  enum ConfigState {
+    CS_UNINITIALIZED,
+
+    CS_INITIAL,      // Initial config.ini, ie. no config.bin.X found
+
+    CS_CONFIRMED,    // Started and all agreed
+    CS_FORCED        // Forced start
+  };
+
+  ConfigState m_config_state;
+  ConfigState m_previous_state;
+
+  BlockReference m_client_ref;
+  BaseString m_config_name;
+  Config* m_prepared_config;
+
+  NodeBitmask m_all_mgm;
+  NodeBitmask m_started;
+  NodeBitmask m_waiting_for;
+  NodeBitmask m_checked;
+
+  NodeId m_node_id;
+
+  const char* m_datadir;
+
+  /* Functions used from 'init' */
+  Config* load_init_config(const char*);
+  Config* load_init_mycnf(void);
+  Config* fetch_config(void);
+  bool save_config(const Config* conf);
+  bool save_config(void);
+  bool saved_config_exists(BaseString& config_name) const;
+  Config* load_saved_config(const BaseString& config_name);
+  bool init_nodeid(void);
+
+  /* Set the new config and inform subscribers */
+  void set_config(Config* config);
+  Vector<ConfigSubscriber*> m_subscribers;
+
+  /* Check config is ok */
+  bool config_ok(const Config* conf);
+
+  /* Functions for writing config.bin to disk */
+  bool prepareConfigChange(const Config* config);
+  void commitConfigChange();
+  void abortConfigChange();
+
+  /* Functions for starting config change from ConfigManager */
+  void startInitConfigChange(SignalSender& ss);
+  void startNewConfigChange(SignalSender& ss);
+
+  /* CONFIG_CHANGE - controlling config change from other node */
+  void execCONFIG_CHANGE_REQ(SignalSender& ss, SimpleSignal* sig);
+  void execCONFIG_CHANGE_REF(SignalSender& ss, SimpleSignal* sig);
+  void sendConfigChangeRef(SignalSender& ss, BlockReference,
+                           ConfigChangeRef::ErrorCode) const;
+  void sendConfigChangeConf(SignalSender& ss, BlockReference) const;
+
+  /*
+    CONFIG_CHANGE_IMPL - protocol for starting config change
+    between nodes
+  */
+  void execCONFIG_CHANGE_IMPL_REQ(SignalSender& ss, SimpleSignal* sig);
+  void execCONFIG_CHANGE_IMPL_REF(SignalSender& ss, SimpleSignal* sig);
+  void execCONFIG_CHANGE_IMPL_CONF(SignalSender& ss, SimpleSignal* sig);
+  void sendConfigChangeImplRef(SignalSender& ss, NodeId nodeId,
+                               ConfigChangeRef::ErrorCode) const;
+  void sendConfigChangeImplReq(SignalSender& ss, const Config* conf);
+
+  /*
+    CONFIG_CHECK - protocol for exchanging and checking config state
+    between nodes
+  */
+  void execCONFIG_CHECK_REQ(SignalSender& ss, SimpleSignal* sig);
+  void execCONFIG_CHECK_CONF(SignalSender& ss, SimpleSignal* sig);
+  void execCONFIG_CHECK_REF(SignalSender& ss, SimpleSignal* sig);
+  void sendConfigCheckReq(SignalSender& ss, NodeBitmask to);
+  void sendConfigCheckRef(SignalSender& ss, BlockReference to,
+                          ConfigCheckRef::ErrorCode,
+                          Uint32, Uint32, ConfigState, ConfigState) const;
+  void sendConfigCheckConf(SignalSender& ss, BlockReference to) const;
+
+public:
+  ConfigManager(const MgmtSrvr::MgmtOpts&);
+  virtual ~ConfigManager();
+  bool init();
+  void set_facade(TransporterFacade* facade) { m_facade= facade; };
+  virtual void run();
+
+
+  /*
+    Installs subscriber that will be notified when
+    config has changed
+   */
+  int add_config_change_subscriber(ConfigSubscriber*);
+
+  /*
+    Retrieve the current configuration in packed format
+   */
+  bool get_packed_config(UtilBuffer& pack_buf);
+};
+
+
+
+#endif

=== added file 'storage/ndb/src/mgmsrv/ConfigSubscriber.hpp'
--- a/storage/ndb/src/mgmsrv/ConfigSubscriber.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/ConfigSubscriber.hpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,26 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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 ConfigSubscriber_H
+#define ConfigSubscriber_H
+
+
+class ConfigSubscriber {
+public:
+  virtual void config_changed(NodeId, const class Config*)= 0;
+  virtual ~ConfigSubscriber() {};
+};
+
+#endif

=== added file 'storage/ndb/src/mgmsrv/DirIterator.hpp'
--- a/storage/ndb/src/mgmsrv/DirIterator.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/DirIterator.hpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,46 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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 DirIterator_HPP
+#define DirIterator_HPP
+
+#include <dirent.h>
+
+class DirIterator {
+  DIR* m_dirp;
+public:
+  DirIterator():
+    m_dirp(NULL) {};
+  ~DirIterator() {
+    closedir(m_dirp);
+  }
+
+  int open(const char* path){
+    if ((m_dirp= opendir(path)) == NULL){
+      return -1;
+    }
+    return 0;
+  }
+
+  const char* next_file(void){
+    struct dirent* dp;
+    while ((dp= readdir(m_dirp)) != NULL &&
+           dp->d_type != DT_REG)
+      ;
+    return dp ? dp->d_name : NULL;
+  }
+};
+
+#endif

=== modified file 'storage/ndb/src/mgmsrv/InitConfigFileParser.cpp'
--- a/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp	2008-09-12 12:55:29 +0000
+++ b/storage/ndb/src/mgmsrv/InitConfigFileParser.cpp	2008-10-21 12:41:59 +0000
@@ -212,10 +212,8 @@ InitConfigFileParser::run_config_rules(C
   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();
-  return ret;
+
+  return new Config(ctx.m_configValues.getConfigValues());
 }
 
 //****************************************************************************

=== modified file 'storage/ndb/src/mgmsrv/Makefile.am'
--- a/storage/ndb/src/mgmsrv/Makefile.am	2008-09-17 13:23:21 +0000
+++ b/storage/ndb/src/mgmsrv/Makefile.am	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-# Copyright (C) 2004-2006 MySQL AB
+# Copyright (C) 2004-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 # 
 # 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
@@ -18,8 +18,7 @@ EXTRA_DIST = CMakeLists.txt
 MYSQLDATAdir =		$(localstatedir)
 MYSQLSHAREdir =		$(pkgdatadir)
 MYSQLBASEdir=		$(prefix)
-#MYSQLCLUSTERdir=        $(prefix)/mysql-cluster
-MYSQLCLUSTERdir=        .
+MYSQLCLUSTERdir=        $(prefix)/mysql-cluster
 
 ndbbin_PROGRAMS = ndb_mgmd
 
@@ -27,10 +26,10 @@ ndb_mgmd_SOURCES = \
 	MgmtSrvr.cpp \
 	main.cpp \
 	Services.cpp \
-	MgmtSrvrConfig.cpp \
 	ConfigInfo.cpp \
 	InitConfigFileParser.cpp \
-	Config.cpp
+	Config.cpp \
+	ConfigManager.cpp
 
 
 noinst_PROGRAMS = testConfig

=== modified file 'storage/ndb/src/mgmsrv/MgmtSrvr.cpp'
--- a/storage/ndb/src/mgmsrv/MgmtSrvr.cpp	2008-09-17 13:23:21 +0000
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.cpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 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
@@ -14,11 +14,11 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
 #include <ndb_global.h>
-#include <my_pthread.h>
 
 #include "MgmtSrvr.hpp"
 #include "ndb_mgmd_error.h"
 #include "Services.hpp"
+#include "ConfigManager.hpp"
 
 #include <NdbOut.hpp>
 #include <NdbApiSignal.hpp>
@@ -55,7 +55,6 @@
 #include <mgmapi.h>
 #include <mgmapi_configuration.hpp>
 #include <mgmapi_config_parameters.h>
-#include <m_string.h>
 
 #include <SignalSender.hpp>
 
@@ -258,11 +257,9 @@ MgmtSrvr::MgmtSrvr(const MgmtOpts& opts,
   _blockNumber(-1),
   _ownNodeId(0),
   m_port(0),
-  m_config_retriever(connect_str,
-                     NDB_VERSION,
-                     NDB_MGM_NODE_TYPE_MGM),
+  m_local_config(NULL),
   _ownReference(0),
-  _config(NULL),
+  m_config_manager(NULL),
   theFacade(NULL),
   _isStopThread(false),
   _logLevelThreadSleep(500),
@@ -272,9 +269,9 @@ MgmtSrvr::MgmtSrvr(const MgmtOpts& opts,
 {
   DBUG_ENTER("MgmtSrvr::MgmtSrvr");
 
-  m_configMutex = NdbMutex_Create();
+  m_local_config_mutex= NdbMutex_Create();
   m_node_id_mutex = NdbMutex_Create();
-  if (!m_configMutex || !m_node_id_mutex)
+  if (!m_local_config_mutex || !m_node_id_mutex)
   {
     g_eventLogger->error("Failed to create MgmtSrvr mutexes");
     require(false);
@@ -310,67 +307,66 @@ MgmtSrvr::init()
 {
   DBUG_ENTER("MgmtSrvr::init");
 
-  if (m_opts.mycnf || m_opts.config_filename)
+
+  if (!(m_config_manager= new ConfigManager(m_opts)))
   {
-    Config* conf= NULL;
-    if (m_opts.mycnf && (conf= load_init_mycnf()) == NULL)
-    {
-      g_eventLogger->error("Could not load config from 'my.cnf'");
-      DBUG_RETURN(false);
-    }
-    else if (m_opts.config_filename && (conf= load_init_config()) == NULL)
-    {
-      g_eventLogger->error("Could not load initial config from '%s'",
-                          m_opts.config_filename);
-      DBUG_RETURN(false);
-     }
-    setConfig(conf);
+    g_eventLogger->error("Failed to create ConfigManager");
+    DBUG_RETURN(false);
   }
-  else
+
+  if (m_config_manager->add_config_change_subscriber(this) < 0)
   {
-    if (fetch_config())
-    {
-      g_eventLogger->error("Could not fetch config");
-      DBUG_RETURN(false);
-    }
+    g_eventLogger->error("Failed to add MgmtSrvr as config change subscriber");
+    DBUG_RETURN(false);
   }
 
-  if (m_opts.print_full_config)
+  if (!m_config_manager->init())
   {
-    print_config();
     DBUG_RETURN(false);
   }
 
-  if (_ownNodeId == 0)
+  /* 'config_changed' should have been called from 'init' */
+  require(m_local_config);
+
+  if (m_opts.print_full_config)
   {
-    _ownNodeId= m_config_retriever.get_configuration_nodeid();
+    Guard g(m_local_config_mutex);
+    m_local_config->print();
+    DBUG_RETURN(false);
+  }
 
-    int error_code;
-    BaseString error_string;
-    if (!alloc_node_id(&_ownNodeId, NDB_MGM_NODE_TYPE_MGM,
-                       0, 0, error_code, error_string, 0))
-    {
-      g_eventLogger->error("Unable to obtain requested nodeid, error: '%s'",
-                           error_string.c_str());
-      DBUG_RETURN(false);
-    }
+  assert(_ownNodeId);
 
-    if (!m_config_retriever.verifyConfig(_config->m_configValues,
-                                         _ownNodeId))
-    {
-      g_eventLogger->error(m_config_retriever.getErrorString());
-      DBUG_RETURN(false);
-    }
+  /* Reserve the node id with ourself */
+  NodeId nodeId= _ownNodeId;
+  int error_code;
+  BaseString error_string;
+  if (!alloc_node_id(&nodeId, NDB_MGM_NODE_TYPE_MGM,
+                     0, /* client_addr */
+                     0, /* client_addr_len */
+                     error_code, error_string,
+                     0 /* log_event */ ))
+  {
+    g_eventLogger->error("INTERNAL ERROR: Could not allocate nodeid: %d, " \
+                         "error: %d, '%s'",
+                         _ownNodeId, error_code, error_string.c_str());
+    DBUG_RETURN(false);
   }
 
-  setClusterLog();
+  if (nodeId != _ownNodeId)
+  {
+    g_eventLogger->error("INTERNAL ERROR: Nodeid %d allocated " \
+                         "when %d was requested",
+                         nodeId, _ownNodeId);
+    DBUG_RETURN(false);
+  }
 
   DBUG_RETURN(true);
 }
 
 
 bool
-MgmtSrvr::start_transporter()
+MgmtSrvr::start_transporter(const Config* config)
 {
   DBUG_ENTER("MgmtSrvr::start_transporter");
 
@@ -382,7 +378,7 @@ MgmtSrvr::start_transporter()
   }
 
   if (theFacade->start_instance(_ownNodeId,
-                                _config->m_configValues) < 0)
+                                config->m_configValues) < 0)
   {
     g_eventLogger->error("Failed to start transporter");
     delete theFacade;
@@ -424,15 +420,14 @@ MgmtSrvr::start_transporter()
 
 
 bool
-MgmtSrvr::start_mgm_service()
+MgmtSrvr::start_mgm_service(const Config* config)
 {
   DBUG_ENTER("MgmtSrvr::start_mgm_service");
 
   assert(m_port == 0);
   {
     // Find the portnumber to use for mgm service
-    Guard g(m_configMutex);
-    ConfigIter iter(_config, CFG_SECTION_NODE);
+    ConfigIter iter(config, CFG_SECTION_NODE);
 
     if(iter.find(CFG_NODE_ID, _ownNodeId) != 0){
       g_eventLogger->error("Could not find node %d in config", _ownNodeId);
@@ -525,15 +520,17 @@ MgmtSrvr::start()
 {
   DBUG_ENTER("MgmtSrvr::start");
 
+  Guard g(m_local_config_mutex);
+
   /* Start transporter */
-  if(!start_transporter())
+  if(!start_transporter(m_local_config))
   {
     g_eventLogger->error("Failed to start transporter!");
     DBUG_RETURN(false);
   }
 
   /* Start mgm service */
-  if (!start_mgm_service())
+  if (!start_mgm_service(m_local_config))
   {
     g_eventLogger->error("Failed to start mangement service!");
     DBUG_RETURN(false);
@@ -546,6 +543,14 @@ MgmtSrvr::start()
     DBUG_RETURN(false);
   }
 
+  /* Start config manager */
+  m_config_manager->set_facade(theFacade);
+  if (!m_config_manager->start())
+  {
+    g_eventLogger->error("Failed to start ConfigManager");
+    DBUG_RETURN(false);
+  }
+
   /* Loglevel thread */
   assert(_isStopThread == false);
   _logLevelThread = NdbThread_Create(logLevelThread_C,
@@ -559,7 +564,7 @@ MgmtSrvr::start()
 
 
 void
-MgmtSrvr::setClusterLog(void)
+MgmtSrvr::setClusterLog(const Config* config)
 {
   BaseString logdest;
 
@@ -567,7 +572,8 @@ MgmtSrvr::setClusterLog(void)
 
   // Get log destination from config
   DBUG_ASSERT(_ownNodeId);
-  ConfigIter iter(_config, CFG_SECTION_NODE);
+
+  ConfigIter iter(config, CFG_SECTION_NODE);
   require(iter.find(CFG_NODE_ID, _ownNodeId) == 0);
 
   const char *value;
@@ -603,15 +609,26 @@ MgmtSrvr::setClusterLog(void)
 
 
 void
-MgmtSrvr::setConfig(Config* conf)
+MgmtSrvr::config_changed(NodeId node_id, const Config* new_config)
 {
-  DBUG_ENTER("MgmtSrvr::setConfig");
-  Guard g(m_configMutex);
+  DBUG_ENTER("MgmtSrvr::config_changed");
+
+  Guard g(m_local_config_mutex);
 
-  _config= conf;
+  // Don't allow nodeid to change, once it's been set
+  require(_ownNodeId == 0 || _ownNodeId == node_id);
+
+  _ownNodeId= node_id;
+
+  // TODO Magnus, Copy information about dynamic ports from
+  // new to old or save that info elsewhere
+
+  delete m_local_config;
+  m_local_config= new Config(new_config); // Copy
+  require(m_local_config);
 
   /* Rebuild node arrays */
-  ConfigIter iter(_config, CFG_SECTION_NODE);
+  ConfigIter iter(m_local_config, CFG_SECTION_NODE);
   for(Uint32 i = 0; i<MAX_NODES; i++) {
 
     m_connect_address[i].s_addr= 0;
@@ -644,13 +661,32 @@ MgmtSrvr::setConfig(Config* conf)
 
   }
 
+  setClusterLog(m_local_config);
+
+  // TODO Magnus, Reload ClusterMgr::theNodes
+
   DBUG_VOID_RETURN;
 }
 
 
+bool
+MgmtSrvr::getPackedConfig(UtilBuffer& pack_buf)
+{
+  return m_config_manager->get_packed_config(pack_buf);
+}
+
+
 MgmtSrvr::~MgmtSrvr()
 {
 
+  /* Stop config manager */
+  if (m_config_manager != 0)
+  {
+    m_config_manager->stop();
+    delete m_config_manager;
+    m_config_manager= 0;
+  }
+
   /* Stop log level thread */
   void* res = 0;
   _isStopThread = true;
@@ -673,10 +709,10 @@ MgmtSrvr::~MgmtSrvr()
     theFacade = 0;
   }
 
-  delete _config;
+  delete m_local_config;
 
+  NdbMutex_Destroy(m_local_config_mutex);
   NdbMutex_Destroy(m_node_id_mutex);
-  NdbMutex_Destroy(m_configMutex);
 }
 
 
@@ -697,13 +733,16 @@ int MgmtSrvr::okToSendTo(NodeId nodeId, 
   return NO_CONTACT_WITH_PROCESS;
 }
 
-void report_unknown_signal(SimpleSignal *signal)
+void
+MgmtSrvr::report_unknown_signal(SimpleSignal *signal)
 {
+  signal->print();
   g_eventLogger->error("Unknown signal received. SignalNumber: "
-                       "%i from (%d, %x)",
+                       "%i from (%d, 0x%x)",
                        signal->readSignalNumber(),
                        refToNode(signal->header.theSendersBlockRef),
                        refToBlock(signal->header.theSendersBlockRef));
+  assert(false);
 }
 
 /*****************************************************************************
@@ -750,8 +789,8 @@ MgmtSrvr::versionNode(int nodeId, Uint32
     mysql_version = NDB_MYSQL_VERSION_D;
     if(!*address)
     {
-      ndb_mgm_configuration_iterator
-	iter(*_config->m_configValues, CFG_SECTION_NODE);
+      Guard g(m_local_config_mutex);
+      ConfigIter iter(m_local_config, CFG_SECTION_NODE);
       unsigned tmp= 0;
       for(iter.first();iter.valid();iter.next())
       {
@@ -861,6 +900,8 @@ MgmtSrvr::sendVersionReq(int v_nodeId, 
 	do_send = 1; // retry with other node
       continue;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -883,18 +924,16 @@ int MgmtSrvr::sendStopMgmd(NodeId nodeId
   BaseString connect_string;
 
   {
-    Guard g(m_configMutex);
+    Guard g(m_local_config_mutex);
     {
-      ndb_mgm_configuration_iterator
-        iter(* _config->m_configValues, CFG_SECTION_NODE);
+      ConfigIter iter(m_local_config, 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);
+      ConfigIter iter(m_local_config, CFG_SECTION_NODE);
 
       if(iter.first())                   return SEND_OR_RECEIVE_FAILED;
       if(iter.find(CFG_NODE_ID, nodeId)) return SEND_OR_RECEIVE_FAILED;
@@ -902,6 +941,7 @@ int MgmtSrvr::sendStopMgmd(NodeId nodeId
     }
     if( strlen(hostname) == 0 )
       return SEND_OR_RECEIVE_FAILED;
+
   }
   connect_string.assfmt("%s:%u",hostname,port);
 
@@ -1175,6 +1215,8 @@ int MgmtSrvr::sendSTOP_REQ(const Vector<
 	stoppedNodes.bitOR(mask);
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
 #ifdef VM_TRACE
@@ -1499,6 +1541,7 @@ MgmtSrvr::exitSingleUser(int * stopCount
   SimpleSignal ssig;
   ResumeReq* const resumeReq = 
     CAST_PTR(ResumeReq, ssig.getDataPtrSend());
+
   ssig.set(ss,TestOrd::TraceAPI, NDBCNTR, GSN_RESUME_REQ, 
 	   ResumeReq::SignalLength);
   resumeReq->senderData = 12;
@@ -1735,6 +1778,8 @@ MgmtSrvr::setEventReportingLevelImpl(int
       nodes.clear(rep->failedNodeId);
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -1799,6 +1844,7 @@ MgmtSrvr::insertError(int nodeId, int er
   return ss.sendSignal(nodeId, &ssig) == SEND_OK ? 0 : SEND_OR_RECEIVE_FAILED;
 }
 
+
 int
 MgmtSrvr::startSchemaTrans(SignalSender& ss, NodeId & out_nodeId,
                            Uint32 transId, Uint32 & out_transKey)
@@ -1868,6 +1914,8 @@ retry:
       }
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -1924,6 +1972,8 @@ MgmtSrvr::endSchemaTrans(SignalSender& s
       }
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -2015,6 +2065,8 @@ MgmtSrvr::createNodegroup(int *nodes, in
       }
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -2087,6 +2139,8 @@ MgmtSrvr::dropNodegroup(int ng)
       }
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -2297,10 +2351,6 @@ MgmtSrvr::handleReceivedSignal(NdbApiSig
   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(), signal->getLength());
@@ -2315,13 +2365,16 @@ MgmtSrvr::handleReceivedSignal(NdbApiSig
   case GSN_TAMPER_ORD:
     ndbout << "TAMPER ORD" << endl;
     break;
+  case GSN_API_REGCONF:
+    break;
 
   default:
     g_eventLogger->error("Unknown signal received. SignalNumber: "
-                         "%i from (%d, %x)",
+                         "%i from (%d, 0x%x)",
                          gsn,
                          refToNode(signal->theSendersBlockRef),
                          refToBlock(signal->theSendersBlockRef));
+    assert(false);
   }
 }
 
@@ -2403,7 +2456,7 @@ const char *MgmtSrvr::get_connect_addres
 void
 MgmtSrvr::get_connected_nodes(NodeBitmask &connected_nodes) const
 {
-  if (theFacade && theFacade->theClusterMgr) 
+  if (theFacade && theFacade->theClusterMgr)
   {
     for(Uint32 i = 0; i < MAX_NDB_NODES; i++)
     {
@@ -2504,6 +2557,8 @@ MgmtSrvr::alloc_node_id_req(NodeId free_
       // ignore NF_COMPLETEREP will come
       continue;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -2550,15 +2605,8 @@ MgmtSrvr::alloc_node_id(NodeId * nodeId,
   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);
+  NdbMutex_Lock(m_local_config_mutex);
+  ConfigIter iter(m_local_config, CFG_SECTION_NODE);
   for(iter.first(); iter.valid(); iter.next()) {
     unsigned tmp= 0;
     if(iter.get(CFG_NODE_ID, &tmp)) require(false);
@@ -2625,7 +2673,7 @@ MgmtSrvr::alloc_node_id(NodeId * nodeId,
 			  "Suggest specifying node id in connectstring,\n"
 			  "or specifying unique host names in config file.",
 			  id_found, tmp);
-      NdbMutex_Unlock(m_configMutex);
+      NdbMutex_Unlock(m_local_config_mutex);
       error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH;
       DBUG_RETURN(false);
     }
@@ -2635,13 +2683,13 @@ MgmtSrvr::alloc_node_id(NodeId * nodeId,
 			  "or specifying unique host names in config file,\n"
 			  "or specifying just one mgmt server in config file.",
 			  tmp);
-      NdbMutex_Unlock(m_configMutex);
+      NdbMutex_Unlock(m_local_config_mutex);
       error_code = NDB_MGM_ALLOCID_CONFIG_MISMATCH;
       DBUG_RETURN(false);
     }
     id_found= tmp; // mgmt server matched, check for more matches
   }
-  NdbMutex_Unlock(m_configMutex);
+  NdbMutex_Unlock(m_local_config_mutex);
 
   if (id_found && client_addr != 0)
   {
@@ -2907,12 +2955,12 @@ MgmtSrvr::startBackup(Uint32& backupId, 
   BlockNumber backupBlockNo = numberToBlock(BACKUP, 1);
   if(input_backupId > 0)
   {
-    ssig.set(ss, TestOrd::TraceAPI, backupBlockNo, GSN_BACKUP_REQ, 
+    ssig.set(ss, TestOrd::TraceAPI, backupBlockNo, GSN_BACKUP_REQ,
 	     BackupReq::SignalLength);
     req->inputBackupId = input_backupId;
   }
   else
-    ssig.set(ss, TestOrd::TraceAPI, backupBlockNo, GSN_BACKUP_REQ, 
+    ssig.set(ss, TestOrd::TraceAPI, BACKUP, GSN_BACKUP_REQ, 
 	     BackupReq::SignalLength - 1);
   
   req->senderData = 19;
@@ -3030,6 +3078,8 @@ MgmtSrvr::startBackup(Uint32& backupId, 
       // master node will report aborted backup
       break;
     }
+    case GSN_API_REGCONF:
+      break;
     default:
       report_unknown_signal(signal);
       return SEND_OR_RECEIVE_FAILED;
@@ -3120,50 +3170,44 @@ MgmtSrvr::Allocated_resources::get_nodei
 
 int
 MgmtSrvr::setDbParameter(int node, int param, const char * value,
-			 BaseString& msg){
+			 BaseString& msg)
+{
 
-  if(NdbMutex_Lock(m_configMutex))
-    return -1;
+  Guard g(m_local_config_mutex);
 
   /**
    * Check parameter
    */
-  ndb_mgm_configuration_iterator
-    iter(* _config->m_configValues, CFG_SECTION_NODE);
+  ConfigIter iter(m_local_config, 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;
   }
 
@@ -3177,7 +3221,7 @@ MgmtSrvr::setDbParameter(int node, int p
       val_32 = atoi(value);
       break;
     }
-    
+
     p_type++;
     if(iter.get(param, &val_64) == 0){
       val_64 = strtoll(value, 0, 10);
@@ -3189,23 +3233,22 @@ MgmtSrvr::setDbParameter(int node, int p
       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, 
+
+    ConfigValues::Iterator i2(m_local_config->m_configValues->m_config, 
 			      iter.m_config);
     switch(p_type){
     case 0:
@@ -3227,30 +3270,25 @@ MgmtSrvr::setDbParameter(int node, int p
   } 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;
-
+				   BaseString& msg)
+{
   DBUG_ENTER("MgmtSrvr::setConnectionDbParameter");
 
-  if(NdbMutex_Lock(m_configMutex))
-  {
-    DBUG_RETURN(-1);
-  }
-
-  ndb_mgm_configuration_iterator 
-    iter(* _config->m_configValues, CFG_SECTION_CONNECTION);
+  Uint32 current_value,new_value;
+  Guard g(m_local_config_mutex);
+  ConfigIter iter(m_local_config, CFG_SECTION_CONNECTION);
 
   if(iter.first() != 0){
     msg.assign("Unable to find connection section (iter.first())");
-    NdbMutex_Unlock(m_configMutex);
     DBUG_RETURN(-1);
   }
 
@@ -3264,33 +3302,32 @@ MgmtSrvr::setConnectionDbParameter(int n
   }
   if(!iter.valid()) {
     msg.assign("Unable to find connection between nodes");
-    NdbMutex_Unlock(m_configMutex);
     DBUG_RETURN(-2);
   }
-  
+
   if(iter.get(param, &current_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, 
+  ConfigValues::Iterator i2(m_local_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);
   }
-  
+
+  // TODO Magnus, in theory this new config should be saved on
+  // nodes, but it's probably a better idea to save this
+  // dynamic information elsewhere instead.
+
   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);
 }
 
@@ -3300,20 +3337,15 @@ MgmtSrvr::getConnectionDbParameter(int n
 				   int node2,
 				   int param,
 				   int *value,
-				   BaseString& msg){
-  DBUG_ENTER("MgmtSrvr::getConnectionDbParameter");
-
-  if(NdbMutex_Lock(m_configMutex))
-  {
-    DBUG_RETURN(-1);
-  }
+				   BaseString& msg)
+{
 
-  ndb_mgm_configuration_iterator
-    iter(* _config->m_configValues, CFG_SECTION_CONNECTION);
+  DBUG_ENTER("MgmtSrvr::getConnectionDbParameter");
+  Guard g(m_local_config_mutex);
+  ConfigIter iter(m_local_config, CFG_SECTION_CONNECTION);
 
   if(iter.first() != 0){
     msg.assign("Unable to find connection section (iter.first())");
-    NdbMutex_Unlock(m_configMutex);
     DBUG_RETURN(-1);
   }
 
@@ -3327,21 +3359,20 @@ MgmtSrvr::getConnectionDbParameter(int n
   }
   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))

=== modified file 'storage/ndb/src/mgmsrv/MgmtSrvr.hpp'
--- a/storage/ndb/src/mgmsrv/MgmtSrvr.hpp	2008-09-12 12:55:29 +0000
+++ b/storage/ndb/src/mgmsrv/MgmtSrvr.hpp	2008-10-21 12:41:59 +0000
@@ -17,9 +17,9 @@
 #define MgmtSrvr_H
 
 #include "Config.hpp"
-#include <mgmapi.h>
+#include "ConfigSubscriber.hpp"
 
-#include <ConfigRetriever.hpp>
+#include <mgmapi.h>
 #include <Vector.hpp>
 #include <NodeBitmask.hpp>
 #include <ndb_version.h>
@@ -70,8 +70,8 @@ public:
   @class MgmtSrvr
   @brief Main class for the management server.
  */
-class MgmtSrvr {
-  
+class MgmtSrvr : private ConfigSubscriber {
+
 public:
   // some compilers need all of this
   class Allocated_resources;
@@ -130,6 +130,7 @@ public:
     const char* bind_address;
     int no_nodeid_checks;
     int print_full_config;
+    const char* datadir;
   };
 
   MgmtSrvr(); // Not implemented
@@ -139,21 +140,10 @@ public:
   ~MgmtSrvr();
 
   /*
-    To be called after constructor. Loads configuration
-    from disk or fetches it from other server
-   */
+    To be called after constructor.
+  */
   bool init();
 
-private:
-  /* Functions used from 'init' */
-  Config* load_init_config(void);
-  Config* load_init_mycnf(void);
-  bool fetch_config(void);
-  bool save_config(const Config* conf);
-  bool save_config(void);
-
-public:
-
   /*
     To be called after 'init', starts up the services
     this server will expose
@@ -161,16 +151,14 @@ public:
   bool start(void);
 private:
   /* Functions used from 'start' */
-  bool start_transporter(void);
-  bool start_mgm_service(void);
+  bool start_transporter(const Config*);
+  bool start_mgm_service(const Config*);
   bool connect_to_self(void);
 
 public:
 
   NodeId getOwnNodeId() const {return _ownNodeId;};
 
-  void print_config() { _config->print(); };
-
   /**
    * Get status on a node.
    * address may point to a common area (e.g. from inet_addr)
@@ -390,13 +378,9 @@ public:
    */
   const char* getErrorText(int errorCode, char *buf, int buf_sz);
 
-  /**
-   *   Get configuration
-   */
-  const Config * getConfig() const;
 private:
-  void setConfig(Config* conf);
-  void setClusterLog(void);
+  void config_changed(NodeId, const Config*);
+  void setClusterLog(const Config* conf);
 public:
 
   /**
@@ -474,11 +458,13 @@ private:
   NodeId _ownNodeId;
   Uint32 m_port;
   SocketServer m_socket_server;
-  ConfigRetriever m_config_retriever;
 
-  BlockReference _ownReference; 
-  NdbMutex *m_configMutex;
-  const Config * _config;
+  NdbMutex* m_local_config_mutex;
+  const Config* m_local_config;
+
+  BlockReference _ownReference;
+
+  class ConfigManager* m_config_manager;
 
   NodeBitmask m_reserved_nodes;
   struct in_addr m_connect_address[MAX_NODES];
@@ -538,12 +524,17 @@ private:
   struct NdbThread* _logLevelThread;
   static void *logLevelThread_C(void *);
   void logLevelThreadRun();
+
+  void report_unknown_signal(SimpleSignal *signal);
+
+
+public:
+  /*
+    Get packed copy of configuration in the supplied buffer
+  */
+  bool getPackedConfig(UtilBuffer& pack_buf);
+
 };
 
-inline
-const Config *
-MgmtSrvr::getConfig() const {
-  return _config;
-}
 
 #endif // MgmtSrvr_H

=== removed file 'storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp'
--- a/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp	2008-08-04 21:01:04 +0000
+++ b/storage/ndb/src/mgmsrv/MgmtSrvrConfig.cpp	1970-01-01 00:00:00 +0000
@@ -1,81 +0,0 @@
-/* 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; version 2 of the License.
-
-   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 "MgmtSrvr.hpp"
-#include <InitConfigFileParser.hpp>
-#include <ConfigRetriever.hpp>
-#include <NdbSleep.h>
-
-
-Config*
-MgmtSrvr::load_init_config(void)
-{
-   InitConfigFileParser parser;
-   g_eventLogger->info("Reading cluster configuration from '%s'",
-                      m_opts.config_filename);
-  return parser.parseConfig(m_opts.config_filename);
-}
-
-
-Config*
-MgmtSrvr::load_init_mycnf(void)
-{
-  InitConfigFileParser parser;
-  g_eventLogger->info("Reading cluster configuration using my.cnf");
-  return parser.parse_mycnf();
-}
-
-
-bool
-MgmtSrvr::fetch_config(void)
-{
-  char buf[128];
-  DBUG_ENTER("MgmtSrvr::fetch_config");
-  assert(_config == NULL);
-
-  /* Loop until config loaded from other mgmd(s) */
-  g_eventLogger->info("Trying to get configuration from other mgmd(s)"\
-                     "using '%.128s'...",
-                     m_config_retriever.get_connectstring(buf, sizeof(buf)));
-
-
-  int retry= 0, delay= 0, verbose= 1;
-  while (m_config_retriever.do_connect(retry, delay, verbose) != 0) {
-    g_eventLogger->info("Waiting for connection to other mgmd(s)...");
-    NdbSleep_SecSleep(1);
-  }
-  g_eventLogger->info("Connected...");
-
-  // "login" and alloc node id from the other mgmd
-  _ownNodeId= m_config_retriever.allocNodeId(retry, delay);
-  if (_ownNodeId == 0) {
-    g_eventLogger->error(m_config_retriever.getErrorString());
-    DBUG_RETURN(false);
-  }
-
-  // read config from other managent server
-  struct ndb_mgm_configuration * tmp = m_config_retriever.getConfig();
-  if (tmp == NULL) {
-    g_eventLogger->error(m_config_retriever.getErrorString());
-    DBUG_RETURN(false);
-  }
-
-  setConfig(new Config(tmp));
-
-  DBUG_RETURN(true);
-}
-
-
-

=== added file 'storage/ndb/src/mgmsrv/MgmtThread.hpp'
--- a/storage/ndb/src/mgmsrv/MgmtThread.hpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/mgmsrv/MgmtThread.hpp	2008-10-21 12:41:59 +0000
@@ -0,0 +1,73 @@
+/* Copyright (C) 2008 Sun Microsystems, Inc.
+
+   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; version 2 of the License.
+
+   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 MgmtThread_H
+#define MgmtThread_H
+
+#include <ndb_global.h>
+#include <NdbThread.h>
+
+class MgmtThread {
+  bool m_running;
+  const char* m_name;
+  size_t m_stack_size;
+  NDB_THREAD_PRIO m_thread_prio;
+  struct NdbThread* m_thread;
+
+  static void* run_C(void* t) {
+    MgmtThread *thread = (MgmtThread*)t;
+    thread->run();
+    return 0;
+  }
+public:
+  MgmtThread(); // Not implemented
+  MgmtThread(const MgmtThread&); // Not implemented
+  MgmtThread(const char* name,
+             size_t stack_size= 32768,
+             NDB_THREAD_PRIO thread_prio= NDB_THREAD_PRIO_LOW) :
+    m_running(true),
+    m_name(name),
+    m_stack_size(stack_size),
+    m_thread_prio(thread_prio),
+    m_thread(NULL){
+  };
+  virtual ~MgmtThread() {
+    if (m_thread)
+      stop();
+  };
+
+  virtual void run()= 0;
+  bool start(){
+    assert(m_running);
+    m_thread = NdbThread_Create(run_C, (void**)this, m_stack_size,
+                                m_name, m_thread_prio);
+    return (m_thread != NULL);
+  }
+  bool stop(){
+    void* res = 0;
+    if (!m_thread)
+      return false;
+
+    m_running= false;
+
+    NdbThread_WaitFor(m_thread, &res);
+    NdbThread_Destroy(&m_thread);
+    return true;
+
+  }
+  bool is_stopped() { return !m_running; };
+};
+
+#endif

=== modified file 'storage/ndb/src/mgmsrv/Services.cpp'
--- a/storage/ndb/src/mgmsrv/Services.cpp	2008-10-03 08:22:13 +0000
+++ b/storage/ndb/src/mgmsrv/Services.cpp	2008-10-21 12:41:59 +0000
@@ -615,14 +615,6 @@ MgmApiSession::getConfig(Parser_t::Conte
   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(node != 0){
     bool compatible;
     switch (m_mgmsrv.getNodeType(node)) {
@@ -648,16 +640,19 @@ MgmApiSession::getConfig(Parser_t::Conte
       return;
     }
   }  
-  
-  NdbMutex_Lock(m_mgmsrv.m_configMutex);
-  const ConfigValues * cfg = &conf->m_configValues->m_config;
-  
-  UtilBuffer src;
-  cfg->pack(src);
-  NdbMutex_Unlock(m_mgmsrv.m_configMutex);
-  
-  char *tmp_str = (char *) malloc(base64_needed_encoded_length(src.length()));
-  (void) base64_encode(src.get_data(), src.length(), tmp_str);
+
+  UtilBuffer packed;
+  if (!m_mgmsrv.getPackedConfig(packed))
+  {
+    m_output->println("get config reply");
+    m_output->println("result: Could not fetch configuration");
+    m_output->println("");
+    return;
+  }
+
+  char *tmp_str =
+    (char *) malloc(base64_needed_encoded_length(packed.length()));
+  (void) base64_encode(packed.get_data(), packed.length(), tmp_str);
 
   SLEEP_ERROR_INSERTED(1);
 
@@ -2027,6 +2022,13 @@ done:
   m_output->println("");
 }
 
+#if 0
+  // TODO Magnus, "get variables"
+  ndbout_c("NdbConfig_get_path(0): %s", NdbConfig_get_path(0));
+  ndbout_c("opt_ndb_connectstring: %s", opt_ndb_connectstring);
+  ndbout_c("NDB_CONNECTSTRING: %s", getenv("NDB_CONNECTSTRING"));
+  ndbout_c("datadir: %s", opts.datadir);
+#endif
 
 template class MutexVector<int>;
 template class Vector<ParserRow<MgmApiSession> const*>;

=== modified file 'storage/ndb/src/mgmsrv/main.cpp'
--- a/storage/ndb/src/mgmsrv/main.cpp	2008-10-03 08:22:13 +0000
+++ b/storage/ndb/src/mgmsrv/main.cpp	2008-10-21 12:41:59 +0000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2003 MySQL AB
+/* Copyright (C) 2003-2008 MySQL AB, 2008 Sun Microsystems, Inc.
 
    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
@@ -115,6 +115,10 @@ static struct my_option my_long_options[
     "Local bind address",
     (uchar**) &opts.bind_address, (uchar**) &opts.bind_address, 0,
     GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+  { "datadir", 256,
+    "Data directory for this node",
+    (uchar**) &opts.datadir, (uchar**) &opts.datadir, 0,
+    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 };
 
@@ -161,12 +165,14 @@ int main(int argc, char** argv)
     exit(1);
   }
 
-  if (opts.mycnf == 0 && opts.config_filename == 0)
-  {
-    MY_STAT buf;
-    if (my_stat("config.ini", &buf, MYF(0)) != NULL)
-      opts.config_filename = "config.ini";
-  }
+  /**
+     Install signal handler for SIGPIPE
+     Done in TransporterFacade as well.. what about Configretriever?
+   */
+#if !defined NDB_WIN32
+  signal(SIGPIPE, SIG_IGN);
+#endif
+
 start:
 
   g_eventLogger->info("NDB Cluster Management Server. %s", NDB_VERSION_STRING);
@@ -177,14 +183,6 @@ start:
     exit(1);
   }
 
-  /**
-     Install signal handler for SIGPIPE
-     Done in TransporterFacade as well.. what about Configretriever?
-   */
-#if !defined NDB_WIN32
-  signal(SIGPIPE, SIG_IGN);
-#endif
-
   /* Init mgm, load or fetch config */
   if (!mgm->init()) {
     delete mgm;

=== modified file 'storage/ndb/src/ndbapi/ClusterMgr.cpp'
--- a/storage/ndb/src/ndbapi/ClusterMgr.cpp	2008-08-21 06:33:53 +0000
+++ b/storage/ndb/src/ndbapi/ClusterMgr.cpp	2008-10-21 12:41:59 +0000
@@ -221,7 +221,6 @@ ClusterMgr::threadMain( ){
   NdbApiSignal signal(numberToRef(API_CLUSTERMGR, theFacade.ownId()));
   
   signal.theVerId_signalNumber   = GSN_API_REGREQ;
-  signal.theReceiversBlockNumber = QMGR;
   signal.theTrace                = 0;
   signal.theLength               = ApiRegReq::SignalLength;
 
@@ -280,6 +279,11 @@ ClusterMgr::threadMain( ){
 	  theNode.hbCounter = 0;
 	}
 
+        if(theNode.m_info.m_type == NodeInfo::MGM)
+          signal.theReceiversBlockNumber = API_CLUSTERMGR;
+        else
+          signal.theReceiversBlockNumber = QMGR;
+
 #ifdef DEBUG_REG
 	ndbout_c("ClusterMgr: Sending API_REGREQ to node %d", (int)nodeId);
 #endif
@@ -365,7 +369,8 @@ ClusterMgr::execAPI_REGREQ(const Uint32 
   conf->version = NDB_VERSION;
   conf->mysql_version = NDB_MYSQL_VERSION_D;
   conf->apiHeartbeatFrequency = node.hbFrequency;
-  theFacade.sendSignalUnCond(&signal, nodeId);
+  if (theFacade.sendSignalUnCond(&signal, nodeId) == SEND_OK)
+    node.m_api_reg_conf= true;
 }
 
 void

=== modified file 'storage/ndb/src/ndbapi/SignalSender.cpp'
--- a/storage/ndb/src/ndbapi/SignalSender.cpp	2008-09-17 13:23:21 +0000
+++ b/storage/ndb/src/ndbapi/SignalSender.cpp	2008-10-21 12:41:59 +0000
@@ -18,6 +18,7 @@
 #include <SignalLoggerManager.hpp>
 #include <signaldata/NFCompleteRep.hpp>
 #include <signaldata/NodeFailRep.hpp>
+#include <signaldata/TestOrd.hpp>
 
 
 SimpleSignal::SimpleSignal(bool dealloc){
@@ -141,6 +142,55 @@ SignalSender::getNoOfConnectedNodes() co
   return theFacade->theClusterMgr->getNoOfConnectedNodes();
 }
 
+
+void
+SignalSender::getNodes(NodeBitmask& mask,
+                       NodeInfo::NodeType type)
+{
+  mask.clear();
+  for(Uint32 i = 0; i < MAX_NODES; i++)
+  {
+    const ClusterMgr::Node& node= getNodeInfo(i);
+    if(!node.defined)
+      continue;
+    if(type == NodeInfo::INVALID || // INVALID -> add all nodes to mask
+       node.m_info.getType() == type)
+    {
+      mask.set(i);
+    }
+  }
+}
+
+
+NodeBitmask
+SignalSender::broadcastSignal(NodeBitmask mask,
+                              SimpleSignal& sig,
+                              Uint16 recBlock, Uint16 gsn,
+                              Uint32 len)
+{
+  sig.set(*this, TestOrd::TraceAPI, recBlock, gsn, len);
+
+  NodeBitmask result;
+  for(Uint32 i = 0; i < MAX_NODES; i++)
+  {
+    if(mask.get(i) && sendSignal(i, &sig) == SEND_OK)
+      result.set(i);
+  }
+  return result;
+}
+
+
+SendStatus
+SignalSender::sendSignal(Uint16 nodeId,
+                         SimpleSignal& sig,
+                         Uint16 recBlock, Uint16 gsn,
+                         Uint32 len)
+{
+  sig.set(*this, TestOrd::TraceAPI, recBlock, gsn, len);
+  return sendSignal(nodeId, &sig);
+}
+
+
 template<class T>
 SimpleSignal *
 SignalSender::waitFor(Uint32 timeOutMillis, T & t)

=== modified file 'storage/ndb/src/ndbapi/SignalSender.hpp'
--- a/storage/ndb/src/ndbapi/SignalSender.hpp	2008-09-17 13:23:21 +0000
+++ b/storage/ndb/src/ndbapi/SignalSender.hpp	2008-10-21 12:41:59 +0000
@@ -55,8 +55,19 @@ public:
   const ClusterMgr::Node &getNodeInfo(Uint16 nodeId) const;
   Uint32 getNoOfConnectedNodes() const;
 
+  /*
+    Return bitmask of all defined nodes of a certain type
+    returns all defined nodes by default.
+   */
+  void getNodes(NodeBitmask& mask,
+                NodeInfo::NodeType type = NodeInfo::INVALID);
+
   SendStatus sendSignal(Uint16 nodeId, const SimpleSignal *);
-  
+  SendStatus sendSignal(Uint16 nodeId, SimpleSignal& sig,
+                        Uint16 recBlock, Uint16 gsn, Uint32 len);
+  NodeBitmask broadcastSignal(NodeBitmask mask, SimpleSignal& sig,
+                              Uint16 recBlock, Uint16 gsn, Uint32 len);
+
   SimpleSignal * waitFor(Uint32 timeOutMillis = 0);
   SimpleSignal * waitFor(Uint16 nodeId, Uint32 timeOutMillis = 0);
   SimpleSignal * waitFor(Uint16 nodeId, Uint16 gsn, Uint32 timeOutMillis = 0);  

=== modified file 'storage/ndb/src/ndbapi/TransporterFacade.cpp'
--- a/storage/ndb/src/ndbapi/TransporterFacade.cpp	2008-09-10 00:32:18 +0000
+++ b/storage/ndb/src/ndbapi/TransporterFacade.cpp	2008-10-21 12:41:59 +0000
@@ -312,9 +312,16 @@ TransporterFacade::deliver_signal(Signal
        break;
 
      case GSN_API_REGCONF:
+     {
        clusterMgr->execAPI_REGCONF(theData);
+
+       // Distribute signal to all threads/blocks
+       NdbApiSignal tSignal(* header);
+       tSignal.setDataPtr(theData);
+       for_each(&tSignal, ptr);
        break;
-     
+     }
+
      case GSN_API_REGREF:
        clusterMgr->execAPI_REGREF(theData);
        break;
@@ -386,6 +393,16 @@ TransporterFacade::deliver_signal(Signal
        
      }
      return;
+  } else if (tRecBlockNo >= MIN_API_FIXED_BLOCK_NO &&
+             tRecBlockNo <= MAX_API_FIXED_BLOCK_NO) {
+    Uint32 dynamic= m_fixed2dynamic[tRecBlockNo - MIN_API_FIXED_BLOCK_NO];
+    oe = m_threads.get(dynamic);
+    if (oe.m_object != 0 && oe.m_executeFunction != 0) {
+      NdbApiSignal tmpSignal(*header);
+      NdbApiSignal * tSignal = &tmpSignal;
+      tSignal->setDataPtr(theData);
+      (* oe.m_executeFunction) (oe.m_object, tSignal, ptr);
+    }//if   
   } else {
     ; // Ignore all other block numbers.
     if(header->theVerId_signalNumber != GSN_API_REGREQ) {
@@ -701,6 +718,9 @@ TransporterFacade::TransporterFacade(Glo
   m_batch_size= DEF_BATCH_SIZE;
   m_max_trans_id = 0;
 
+  for (int i = 0; i < NO_API_FIXED_BLOCKS; i++)
+    m_fixed2dynamic[i]= RNIL;
+
   theClusterMgr = new ClusterMgr(* this);
 
 #ifdef API_TRACE
@@ -929,9 +949,20 @@ TransporterFacade::open(void* objRef, 
                         int blockNo)
 {
   DBUG_ENTER("TransporterFacade::open");
-  int r= m_threads.open(objRef, fun, statusFun, blockNo);
+  int r= m_threads.open(objRef, fun, statusFun);
   if (r < 0)
     DBUG_RETURN(r);
+
+  if (unlikely(blockNo != -1)){
+    // Using fixed block number, add fixed->dymamic mapping
+    Uint32 fixed_index= blockNo - MIN_API_FIXED_BLOCK_NO;
+
+    assert(blockNo >= MIN_API_FIXED_BLOCK_NO &&
+           fixed_index <= NO_API_FIXED_BLOCKS);
+
+    m_fixed2dynamic[fixed_index]= r;
+  }
+
 #if 1
   if (theOwnId > 0) {
     (*statusFun)(objRef, numberToRef(r, theOwnId), true, true);
@@ -1686,8 +1717,7 @@ TransporterFacade::ThreadData::expand(Ui
 int
 TransporterFacade::ThreadData::open(void* objRef, 
 				    ExecuteFunction fun, 
-				    NodeStatusFunction fun2,
-                                    int blockNo)
+				    NodeStatusFunction fun2)
 {
   Uint32 nextFree = m_firstFree;
 
@@ -1695,41 +1725,16 @@ TransporterFacade::ThreadData::open(void
     return -1;
   }
 
-  Object_Execute oe = { objRef , fun };
-
-  if (unlikely(blockNo >= 0)) {
-    // Open block with fixed number
-    Uint32 index= numberToIndex(blockNo);
-
-    if(index > m_statusNext.size()){
-      expand(index - m_statusNext.size());
-    }
-
-    m_use_cnt++;
-
-    // Single linked free list, relink the previous one that points to this
-    for(Uint32 i = 0; i < m_statusNext.size(); i++){
-      if (m_statusNext[i] == index){
-        m_statusNext[i]= m_statusNext[index];
-        break;
-      }
-    }
-
-    m_statusNext[index] = INACTIVE;
-    m_objectExecute[index] = oe;
-    m_statusFunction[index] = fun2;
-
-    return indexToNumber(index);
-  }
-
   if(nextFree == END_OF_LIST){
     expand(10);
     nextFree = m_firstFree;
   }
-  
+
   m_use_cnt++;
   m_firstFree = m_statusNext[nextFree];
 
+  Object_Execute oe = { objRef , fun };
+
   m_statusNext[nextFree] = INACTIVE;
   m_objectExecute[nextFree] = oe;
   m_statusFunction[nextFree] = fun2;
@@ -1950,6 +1955,18 @@ SignalSender::sendSignal(Uint16 nodeId, 
     signalLogger.flushSignalLog();
   }
 #endif
+
+  if (nodeId == theFacade->ownId())
+  {
+    SignalHeader tmp= s->header;
+    tmp.theSendersBlockRef = getOwnRef();
+    theFacade->deliver_signal(&tmp,
+                              1, // JBB
+                              (Uint32*)&s->theData[0],
+                              (LinearSectionPtr*)&s->ptr[0]);
+    return SEND_OK;
+  }
+
   assert(getNodeInfo(nodeId).m_api_reg_conf == true ||
          s->readSignalNumber() == GSN_API_REGREQ);
   

=== modified file 'storage/ndb/src/ndbapi/TransporterFacade.hpp'
--- a/storage/ndb/src/ndbapi/TransporterFacade.hpp	2008-08-26 14:05:01 +0000
+++ b/storage/ndb/src/ndbapi/TransporterFacade.hpp	2008-10-21 12:41:59 +0000
@@ -283,7 +283,7 @@ private:
     Vector<Object_Execute> m_objectExecute;
     Vector<NodeStatusFunction> m_statusFunction;
     
-    int open(void* objRef, ExecuteFunction, NodeStatusFunction, int);
+    int open(void* objRef, ExecuteFunction, NodeStatusFunction);
     int close(int number);
     void expand(Uint32 size);
 
@@ -303,7 +303,8 @@ private:
       return (m_statusNext[index] & (1 << 16)) != 0;
     }
   } m_threads;
-  
+
+  Uint32 m_fixed2dynamic[NO_API_FIXED_BLOCKS];
   Uint32 m_max_trans_id;
   Uint32 m_fragmented_signal_id;
 

=== modified file 'storage/ndb/test/run-test/Makefile.am'
--- a/storage/ndb/test/run-test/Makefile.am	2008-08-28 12:23:34 +0000
+++ b/storage/ndb/test/run-test/Makefile.am	2008-10-21 12:41:59 +0000
@@ -42,7 +42,6 @@ LDADD_LOC = $(top_builddir)/storage/ndb/
 atrt_CXXFLAGS = -I$(top_srcdir)/ndb/src/mgmapi \
                       -I$(top_srcdir)/ndb/src/mgmsrv \
                       -I$(top_srcdir)/ndb/include/mgmcommon \
-                      -DMYSQLCLUSTERDIR="\"\"" \
                  -DDEFAULT_PREFIX="\"$(prefix)\""
 
 atrt_LDFLAGS = -static @ndb_bin_am_ldflags@

Thread
bzr commit into mysql-5.1 branch (msvensson:2879) WL#4350Magnus Svensson21 Oct