Michal 'vorner' Vaner 13 years ago
parent
commit
17f7af0d8a

File diff suppressed because it is too large
+ 55 - 39
doc/guide/bind10-guide.html


+ 18 - 1
doc/guide/bind10-guide.xml

@@ -913,7 +913,24 @@ address, but the usual ones don't." mean? -->
           In short, you should think twice before disabling something here.
           In short, you should think twice before disabling something here.
         </para>
         </para>
       </note>
       </note>
-
+      <para>
+        It is possible to start some components multiple times (currently
+        <command>b10-auth</command> and <command>b10-resolzer</command>).
+        You might want to do that to gain more performance (each one uses only
+        single core). Just put multiple entries under different names, like
+        this, with the same config:
+        <screen>&gt; <userinput>config add Boss/components b10-resolver-2</userinput>
+&gt; <userinput>config set Boss/components/b10-resolver-2/special resolver</userinput>
+&gt; <userinput>config set Boss/components/b10-resolver-2/kind needed</userinput>
+&gt; <userinput>config commit</userinput></screen>
+      </para>
+      <para>
+        However, this is work in progress and the support is not yet complete.
+        For example, each resolver will have its own cache, each authoritative
+        server will keep its own copy of in-memory data and there could be
+        problems with locking the sqlite database, if used. The configuration
+        might be changed to something more convenient in future.
+      </para>
     </section>
     </section>
 
 
   </chapter>
   </chapter>

+ 7 - 1
src/bin/auth/auth.spec.pre.in

@@ -97,7 +97,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down authoritative DNS server",
         "command_description": "Shut down authoritative DNS server",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       },
       },
       {
       {
         "command_name": "sendstats",
         "command_name": "sendstats",

+ 2 - 0
src/bin/auth/auth_log.h

@@ -29,6 +29,8 @@ namespace auth {
 
 
 // Debug messages indicating normal startup are logged at this debug level.
 // Debug messages indicating normal startup are logged at this debug level.
 const int DBG_AUTH_START = DBGLVL_START_SHUT;
 const int DBG_AUTH_START = DBGLVL_START_SHUT;
+// Debug messages upon shutdown
+const int DBG_AUTH_SHUT = DBGLVL_START_SHUT;
 
 
 // Debug level used to log setting information (such as configuration changes).
 // Debug level used to log setting information (such as configuration changes).
 const int DBG_AUTH_OPS = DBGLVL_COMMAND;
 const int DBG_AUTH_OPS = DBGLVL_COMMAND;

+ 4 - 0
src/bin/auth/auth_messages.mes

@@ -192,6 +192,10 @@ reason for the failure is included in the message.
 Initialization of the authoritative server has completed successfully
 Initialization of the authoritative server has completed successfully
 and it is entering the main loop, waiting for queries to arrive.
 and it is entering the main loop, waiting for queries to arrive.
 
 
+% AUTH_SHUTDOWN asked to stop, doing so
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
+
 % AUTH_SQLITE3 nothing to do for loading sqlite3
 % AUTH_SQLITE3 nothing to do for loading sqlite3
 This is a debug message indicating that the authoritative server has
 This is a debug message indicating that the authoritative server has
 found that the data source it is loading is an SQLite3 data source,
 found that the data source it is loading is an SQLite3 data source,

+ 1 - 0
src/bin/auth/auth_srv.h

@@ -344,6 +344,7 @@ public:
     /// \param type Type of a counter to get the value of
     /// \param type Type of a counter to get the value of
     ///
     ///
     /// \return the value of the counter.
     /// \return the value of the counter.
+
     uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
     uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
 
 
     /// \brief Get the value of per Opcode counter in the Auth Counters.
     /// \brief Get the value of per Opcode counter in the Auth Counters.

+ 33 - 14
src/bin/auth/command.cc

@@ -12,24 +12,23 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include <string>
-
-#include <boost/scoped_ptr.hpp>
-#include <boost/shared_ptr.hpp>
+#include <auth/command.h>
+#include <auth/auth_log.h>
+#include <auth/auth_srv.h>
 
 
+#include <cc/data.h>
+#include <datasrc/memory_datasrc.h>
+#include <config/ccsession.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
-
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
 
 
-#include <cc/data.h>
-
-#include <datasrc/memory_datasrc.h>
+#include <string>
 
 
-#include <config/ccsession.h>
+#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 
 
-#include <auth/auth_log.h>
-#include <auth/auth_srv.h>
-#include <auth/command.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 
 using boost::scoped_ptr;
 using boost::scoped_ptr;
 using namespace isc::auth;
 using namespace isc::auth;
@@ -104,10 +103,30 @@ public:
     virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) = 0;
     virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) = 0;
 };
 };
 
 
-// Handle the "shutdown" command.  No argument is assumed.
+// Handle the "shutdown" command. An optional parameter "pid" is used to
+// see if it is really for our instance.
 class ShutdownCommand : public AuthCommand {
 class ShutdownCommand : public AuthCommand {
 public:
 public:
-    virtual void exec(AuthSrv& server, isc::data::ConstElementPtr) {
+    virtual void exec(AuthSrv& server, isc::data::ConstElementPtr args) {
+        // Is the pid argument provided?
+        if (args && args->contains("pid")) {
+            // If it is, we check it is the same as our PID
+
+            // This might throw in case the type is not an int, but that's
+            // OK, as it'll get converted to an error on higher level.
+            const int pid(args->get("pid")->intValue());
+            const pid_t my_pid(getpid());
+            if (my_pid != pid) {
+                // It is not for us
+                //
+                // Note that this is completely expected situation, if
+                // there are multiple instances of the server running and
+                // another instance is being shut down, we get the message
+                // too, due to the multicast nature of our message bus.
+                return;
+            }
+        }
+        LOG_DEBUG(auth_logger, DBG_AUTH_SHUT, AUTH_SHUTDOWN);
         server.stop();
         server.stop();
     }
     }
 };
 };

+ 152 - 88
src/bin/auth/tests/command_unittest.cc

@@ -14,14 +14,9 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <cassert>
-#include <cstdlib>
-#include <string>
-#include <stdexcept>
-
-#include <boost/bind.hpp>
-
-#include <gtest/gtest.h>
+#include <auth/auth_srv.h>
+#include <auth/auth_config.h>
+#include <auth/command.h>
 
 
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <dns/rrclass.h>
@@ -33,14 +28,22 @@
 
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/memory_datasrc.h>
 
 
-#include <auth/auth_srv.h>
-#include <auth/auth_config.h>
-#include <auth/command.h>
-
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
 
 
 #include <testutils/mockups.h>
 #include <testutils/mockups.h>
 
 
+#include <cassert>
+#include <cstdlib>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+
+#include <gtest/gtest.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
 using namespace std;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::data;
@@ -50,58 +53,119 @@ using namespace isc::config;
 namespace {
 namespace {
 class AuthCommandTest : public ::testing::Test {
 class AuthCommandTest : public ::testing::Test {
 protected:
 protected:
-    AuthCommandTest() : server(false, xfrout), rcode(-1) {
-        server.setStatisticsSession(&statistics_session);
+    AuthCommandTest() :
+        server_(false, xfrout_),
+        rcode_(-1),
+        expect_rcode_(0),
+        itimer_(server_.getIOService())
+    {
+        server_.setStatisticsSession(&statistics_session_);
     }
     }
     void checkAnswer(const int expected_code) {
     void checkAnswer(const int expected_code) {
-        parseAnswer(rcode, result);
-        EXPECT_EQ(expected_code, rcode);
+        parseAnswer(rcode_, result_);
+        EXPECT_EQ(expected_code, rcode_);
     }
     }
-    MockSession statistics_session;
-    MockXfroutClient xfrout;
-    AuthSrv server;
-    ConstElementPtr result;
-    int rcode;
+    MockSession statistics_session_;
+    MockXfroutClient xfrout_;
+    AuthSrv server_;
+    ConstElementPtr result_;
+    // The shutdown command parameter
+    ConstElementPtr param_;
+    int rcode_, expect_rcode_;
+    isc::asiolink::IntervalTimer itimer_;
 public:
 public:
     void stopServer();          // need to be public for boost::bind
     void stopServer();          // need to be public for boost::bind
+    void dontStopServer();          // need to be public for boost::bind
 };
 };
 
 
 TEST_F(AuthCommandTest, unknownCommand) {
 TEST_F(AuthCommandTest, unknownCommand) {
-    result = execAuthServerCommand(server, "no_such_command",
-                                   ConstElementPtr());
-    parseAnswer(rcode, result);
-    EXPECT_EQ(1, rcode);
+    result_ = execAuthServerCommand(server_, "no_such_command",
+                                    ConstElementPtr());
+    parseAnswer(rcode_, result_);
+    EXPECT_EQ(1, rcode_);
 }
 }
 
 
 TEST_F(AuthCommandTest, DISABLED_unexpectedException) {
 TEST_F(AuthCommandTest, DISABLED_unexpectedException) {
     // execAuthServerCommand() won't catch standard exceptions.
     // execAuthServerCommand() won't catch standard exceptions.
     // Skip this test for now: ModuleCCSession doesn't seem to validate
     // Skip this test for now: ModuleCCSession doesn't seem to validate
     // commands.
     // commands.
-    EXPECT_THROW(execAuthServerCommand(server, "_throw_exception",
+    EXPECT_THROW(execAuthServerCommand(server_, "_throw_exception",
                                        ConstElementPtr()),
                                        ConstElementPtr()),
                  runtime_error);
                  runtime_error);
 }
 }
 
 
 TEST_F(AuthCommandTest, sendStatistics) {
 TEST_F(AuthCommandTest, sendStatistics) {
-    result = execAuthServerCommand(server, "sendstats", ConstElementPtr());
+    result_ = execAuthServerCommand(server_, "sendstats", ConstElementPtr());
     // Just check some message has been sent.  Detailed tests specific to
     // Just check some message has been sent.  Detailed tests specific to
     // statistics are done in its own tests.
     // statistics are done in its own tests.
-    EXPECT_EQ("Stats", statistics_session.getMessageDest());
+    EXPECT_EQ("Stats", statistics_session_.getMessageDest());
     checkAnswer(0);
     checkAnswer(0);
 }
 }
 
 
 void
 void
 AuthCommandTest::stopServer() {
 AuthCommandTest::stopServer() {
-    result = execAuthServerCommand(server, "shutdown", ConstElementPtr());
-    parseAnswer(rcode, result);
-    assert(rcode == 0); // make sure the test stops when something is wrong
+    result_ = execAuthServerCommand(server_, "shutdown", param_);
+    parseAnswer(rcode_, result_);
+    assert(rcode_ == 0); // make sure the test stops when something is wrong
 }
 }
 
 
 TEST_F(AuthCommandTest, shutdown) {
 TEST_F(AuthCommandTest, shutdown) {
-    isc::asiolink::IntervalTimer itimer(server.getIOService());
-    itimer.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
-    server.getIOService().run();
-    EXPECT_EQ(0, rcode);
+    // Param defaults to empty/null pointer on creation
+    itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+    server_.getIOService().run();
+    EXPECT_EQ(0, rcode_);
+}
+
+TEST_F(AuthCommandTest, shutdownCorrectPID) {
+    // Put the pid parameter there
+    const pid_t pid(getpid());
+    ElementPtr param(new isc::data::MapElement());
+    param->set("pid", ConstElementPtr(new isc::data::IntElement(pid)));
+    param_ = param;
+    // With the correct PID, it should act exactly the same as in case
+    // of no parameter
+    itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+    server_.getIOService().run();
+    EXPECT_EQ(0, rcode_);
+}
+
+// This is like stopServer, but the server should not stop after the
+// command, it should be running
+void
+AuthCommandTest::dontStopServer() {
+    result_ = execAuthServerCommand(server_, "shutdown", param_);
+    parseAnswer(rcode_, result_);
+    EXPECT_EQ(expect_rcode_, rcode_);
+    rcode_ = -1;
+    // We run the stopServer now, to really stop the server.
+    // If it had stopped already, it won't be run and the rcode -1 will
+    // be left here.
+    param_ = ConstElementPtr();
+    itimer_.cancel();
+    itimer_.setup(boost::bind(&AuthCommandTest::stopServer, this), 1);
+}
+
+// If we provide something not an int, the PID is not really specified, so
+// act as if nothing came.
+TEST_F(AuthCommandTest, shutdownNotInt) {
+    // Put the pid parameter there
+    ElementPtr param(new isc::data::MapElement());
+    param->set("pid", ConstElementPtr(new isc::data::StringElement("pid")));
+    param_ = param;
+    expect_rcode_ = 1;
+    // It should reject to stop if the PID is not an int.
+    itimer_.setup(boost::bind(&AuthCommandTest::dontStopServer, this), 1);
+    server_.getIOService().run();
+    EXPECT_EQ(0, rcode_);
+}
+
+TEST_F(AuthCommandTest, shutdownIncorrectPID) {
+    // The PID = 0 should be taken by init, so we are not init and the
+    // PID should be different
+    param_ = Element::fromJSON("{\"pid\": 0}");
+    itimer_.setup(boost::bind(&AuthCommandTest::dontStopServer, this), 1);
+    server_.getIOService().run();
+    EXPECT_EQ(0, rcode_);
 }
 }
 
 
 // A helper function commonly used for the "loadzone" command tests.
 // A helper function commonly used for the "loadzone" command tests.
@@ -165,7 +229,7 @@ newZoneChecks(AuthSrv& server) {
 }
 }
 
 
 TEST_F(AuthCommandTest, loadZone) {
 TEST_F(AuthCommandTest, loadZone) {
-    configureZones(server);
+    configureZones(server_);
 
 
     ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
     ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
                         "/test1-new.zone.in "
                         "/test1-new.zone.in "
@@ -174,118 +238,118 @@ TEST_F(AuthCommandTest, loadZone) {
                         "/test2-new.zone.in "
                         "/test2-new.zone.in "
                         TEST_DATA_BUILDDIR "/test2.zone.copied"));
                         TEST_DATA_BUILDDIR "/test2.zone.copied"));
 
 
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\"}"));
     checkAnswer(0);
     checkAnswer(0);
-    newZoneChecks(server);
+    newZoneChecks(server_);
 }
 }
 
 
 TEST_F(AuthCommandTest, loadBrokenZone) {
 TEST_F(AuthCommandTest, loadBrokenZone) {
-    configureZones(server);
+    configureZones(server_);
 
 
     ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
     ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
                         "/test1-broken.zone.in "
                         "/test1-broken.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\"}"));
     checkAnswer(1);
     checkAnswer(1);
-    zoneChecks(server);     // zone shouldn't be replaced
+    zoneChecks(server_);     // zone shouldn't be replaced
 }
 }
 
 
 TEST_F(AuthCommandTest, loadUnreadableZone) {
 TEST_F(AuthCommandTest, loadUnreadableZone) {
-    configureZones(server);
+    configureZones(server_);
 
 
     // install the zone file as unreadable
     // install the zone file as unreadable
     ASSERT_EQ(0, system(INSTALL_PROG " -m 000 " TEST_DATA_DIR
     ASSERT_EQ(0, system(INSTALL_PROG " -m 000 " TEST_DATA_DIR
                         "/test1.zone.in "
                         "/test1.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\"}"));
     checkAnswer(1);
     checkAnswer(1);
-    zoneChecks(server);     // zone shouldn't be replaced
+    zoneChecks(server_);     // zone shouldn't be replaced
 }
 }
 
 
 TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
 TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
     // try to execute load command without configuring the zone beforehand.
     // try to execute load command without configuring the zone beforehand.
     // it should fail.
     // it should fail.
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\"}"));
     checkAnswer(1);
     checkAnswer(1);
 }
 }
 
 
 TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
 TEST_F(AuthCommandTest, loadSqlite3DataSrc) {
     // For sqlite3 data source we don't have to do anything (the data source
     // For sqlite3 data source we don't have to do anything (the data source
     // (re)loads itself automatically)
     // (re)loads itself automatically)
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"datasrc\": \"sqlite3\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"datasrc\": \"sqlite3\"}"));
     checkAnswer(0);
     checkAnswer(0);
 }
 }
 
 
 TEST_F(AuthCommandTest, loadZoneInvalidParams) {
 TEST_F(AuthCommandTest, loadZoneInvalidParams) {
-    configureZones(server);
+    configureZones(server_);
 
 
     // null arg
     // null arg
-    result = execAuthServerCommand(server, "loadzone", ElementPtr());
+    result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
     checkAnswer(1);
     checkAnswer(1);
 
 
     // zone class is bogus
     // zone class is bogus
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"class\": \"no_such_class\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"class\": \"no_such_class\"}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"class\": 1}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"class\": 1}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // unsupported zone class
     // unsupported zone class
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"class\": \"CH\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"class\": \"CH\"}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // unsupported data source class
     // unsupported data source class
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"datasrc\": \"not supported\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"datasrc\": \"not supported\"}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // data source is bogus
     // data source is bogus
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"test1.example\","
-                                       " \"datasrc\": 0}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"test1.example\","
+                                        " \"datasrc\": 0}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // origin is missing
     // origin is missing
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON("{}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON("{}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // zone doesn't exist in the data source
     // zone doesn't exist in the data source
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON("{\"origin\": \"xx\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON("{\"origin\": \"xx\"}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
     // origin is bogus
     // origin is bogus
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON(
-                                       "{\"origin\": \"...\"}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON(
+                                        "{\"origin\": \"...\"}"));
     checkAnswer(1);
     checkAnswer(1);
 
 
-    result = execAuthServerCommand(server, "loadzone",
-                                   Element::fromJSON("{\"origin\": 10}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+                                    Element::fromJSON("{\"origin\": 10}"));
     checkAnswer(1);
     checkAnswer(1);
 }
 }
 }
 }

+ 14 - 7
src/bin/bind10/bind10_src.py.in

@@ -193,9 +193,13 @@ class BoB:
         self.nocache = nocache
         self.nocache = nocache
         self.component_config = {}
         self.component_config = {}
         # Some time in future, it may happen that a single component has
         # Some time in future, it may happen that a single component has
-        # multple processes. If so happens, name "components" may be
-        # inapropriate. But as the code isn't probably completely ready
-        # for it, we leave it at components for now.
+        # multple processes (like a pipeline-like component). If so happens,
+        # name "components" may be inapropriate. But as the code isn't probably
+        # completely ready for it, we leave it at components for now. We also
+        # want to support multiple instances of a single component. If it turns
+        # out that we'll have a single component with multiple same processes
+        # or if we start multiple components with the same configuration (we do
+        # this now, but it might change) is an open question.
         self.components = {}
         self.components = {}
         # Simply list of components that died and need to wait for a
         # Simply list of components that died and need to wait for a
         # restart. Components manage their own restart schedule now
         # restart. Components manage their own restart schedule now
@@ -649,14 +653,17 @@ class BoB:
         self.__started = True
         self.__started = True
         return None
         return None
 
 
-    def stop_process(self, process, recipient):
+    def stop_process(self, process, recipient, pid):
         """
         """
         Stop the given process, friendly-like. The process is the name it has
         Stop the given process, friendly-like. The process is the name it has
-        (in logs, etc), the recipient is the address on msgq.
+        (in logs, etc), the recipient is the address on msgq. The pid is the
+        pid of the process (if we have multiple processes of the same name,
+        it might want to choose if it is for this one).
         """
         """
         logger.info(BIND10_STOP_PROCESS, process)
         logger.info(BIND10_STOP_PROCESS, process)
-        self.cc_session.group_sendmsg({'command': ['shutdown']}, recipient,
-            recipient)
+        self.cc_session.group_sendmsg(isc.config.ccsession.
+                                      create_command('shutdown', {'pid': pid}),
+                                      recipient, recipient)
 
 
     def component_shutdown(self, exitcode=0):
     def component_shutdown(self, exitcode=0):
         """
         """

+ 17 - 1
src/bin/bind10/tests/bind10_test.py.in

@@ -460,6 +460,22 @@ class TestBoB(unittest.TestCase):
         # The drop_socket is not tested here, but in TestCacheCommands.
         # The drop_socket is not tested here, but in TestCacheCommands.
         # It needs the cache mocks to be in place and they are there.
         # It needs the cache mocks to be in place and they are there.
 
 
+    def test_stop_process(self):
+        """
+        Test checking the stop_process method sends the right message over
+        the message bus.
+        """
+        class DummySession():
+            def group_sendmsg(self, msg, group, instance="*"):
+                (self.msg, self.group, self.instance) = (msg, group, instance)
+        bob = BoB()
+        bob.cc_session = DummySession()
+        bob.stop_process('process', 'address', 42)
+        self.assertEqual('address', bob.cc_session.group)
+        self.assertEqual('address', bob.cc_session.instance)
+        self.assertEqual({'command': ['shutdown', {'pid': 42}]},
+                         bob.cc_session.msg)
+
 # Class for testing the BoB without actually starting processes.
 # Class for testing the BoB without actually starting processes.
 # This is used for testing the start/stop components routines and
 # This is used for testing the start/stop components routines and
 # the BoB commands.
 # the BoB commands.
@@ -598,7 +614,7 @@ class MockBob(BoB):
         procinfo.pid = 14
         procinfo.pid = 14
         return procinfo
         return procinfo
 
 
-    def stop_process(self, process, recipient):
+    def stop_process(self, process, recipient, pid):
         procmap = { 'b10-auth': self.stop_auth,
         procmap = { 'b10-auth': self.stop_auth,
                     'b10-resolver': self.stop_resolver,
                     'b10-resolver': self.stop_resolver,
                     'b10-xfrout': self.stop_xfrout,
                     'b10-xfrout': self.stop_xfrout,

+ 7 - 1
src/bin/cmdctl/cmdctl.spec.pre.in

@@ -31,7 +31,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "shutdown cmdctl",
         "command_description": "shutdown cmdctl",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       }
       }
     ]
     ]
   }
   }

+ 7 - 1
src/bin/ddns/ddns.spec

@@ -34,7 +34,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down DDNS",
         "command_description": "Shut down DDNS",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       }
       }
     ]
     ]
   }
   }

+ 42 - 24
src/bin/resolver/main.cc

@@ -14,18 +14,10 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <string>
-#include <iostream>
-
-#include <boost/foreach.hpp>
+#include <resolver/spec_config.h>
+#include <resolver/resolver.h>
+#include "resolver_log.h"
+#include "common.h"
 
 
 #include <asiodns/asiodns.h>
 #include <asiodns/asiodns.h>
 #include <asiolink/asiolink.h>
 #include <asiolink/asiolink.h>
@@ -47,16 +39,26 @@
 
 
 #include <auth/common.h>
 #include <auth/common.h>
 
 
-#include <resolver/spec_config.h>
-#include <resolver/resolver.h>
-
 #include <cache/resolver_cache.h>
 #include <cache/resolver_cache.h>
 #include <nsas/nameserver_address_store.h>
 #include <nsas/nameserver_address_store.h>
 
 
 #include <log/logger_support.h>
 #include <log/logger_support.h>
 #include <log/logger_level.h>
 #include <log/logger_level.h>
 #include "resolver_log.h"
 #include "resolver_log.h"
-#include "common.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <string>
+#include <iostream>
+
+#include <boost/foreach.hpp>
 
 
 using namespace std;
 using namespace std;
 using namespace isc::cc;
 using namespace isc::cc;
@@ -81,15 +83,31 @@ ConstElementPtr
 my_command_handler(const string& command, ConstElementPtr args) {
 my_command_handler(const string& command, ConstElementPtr args) {
     ConstElementPtr answer = createAnswer();
     ConstElementPtr answer = createAnswer();
 
 
-    if (command == "print_message") {
-        LOG_INFO(resolver_logger, RESOLVER_PRINT_COMMAND).arg(args);
-        /* let's add that message to our answer as well */
-        answer = createAnswer(0, args);
-    } else if (command == "shutdown") {
-        io_service.stop();
-    }
+    try {
+        if (command == "print_message") {
+            LOG_INFO(resolver_logger, RESOLVER_PRINT_COMMAND).arg(args);
+            /* let's add that message to our answer as well */
+            answer = createAnswer(0, args);
+        } else if (command == "shutdown") {
+            // Is the pid argument provided?
+            if (args && args->contains("pid")) {
+                // If it is, we check it is the same as our PID
+                const int pid(args->get("pid")->intValue());
+                const pid_t my_pid(getpid());
+                if (my_pid != pid) {
+                    // It is not for us (this is expected, see auth/command.cc
+                    // and the ShutdownCommand there).
+                    return (answer);
+                }
+            }
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SHUTDOWN);
+            io_service.stop();
+        }
 
 
-    return (answer);
+        return (answer);
+    } catch (const std::exception& e) {
+        return (createAnswer(1, e.what()));
+    }
 }
 }
 
 
 void
 void

+ 7 - 1
src/bin/resolver/resolver.spec.pre.in

@@ -154,7 +154,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down recursive DNS server",
         "command_description": "Shut down recursive DNS server",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       }
       }
     ]
     ]
   }
   }

+ 4 - 0
src/bin/resolver/resolver_messages.mes

@@ -246,3 +246,7 @@ RESOLVER_QUERY_REJECTED case, the server does not return any response.
 The log message shows the query in the form of <query name>/<query
 The log message shows the query in the form of <query name>/<query
 type>/<query class>, and the client that sends the query in the form of
 type>/<query class>, and the client that sends the query in the form of
 <Source IP address>#<source port>.
 <Source IP address>#<source port>.
+
+% RESOLVER_SHUTDOWN asked to shut down, doing so
+A debug message noting that the server was asked to terminate and is
+complying to the request.

+ 7 - 1
src/bin/stats/stats-httpd.spec

@@ -47,7 +47,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down the stats httpd",
         "command_description": "Shut down the stats httpd",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       }
       }
     ]
     ]
   }
   }

+ 3 - 1
src/bin/stats/stats.py.in

@@ -304,9 +304,11 @@ class Stats:
         return isc.config.create_answer(
         return isc.config.create_answer(
             0, "Stats is up. (PID " + str(os.getpid()) + ")")
             0, "Stats is up. (PID " + str(os.getpid()) + ")")
 
 
-    def command_shutdown(self):
+    def command_shutdown(self, pid=None):
         """
         """
         handle shutdown command
         handle shutdown command
+
+        The pid argument is ignored, it is here to match the signature.
         """
         """
         logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
         logger.info(STATS_RECEIVED_SHUTDOWN_COMMAND)
         self.running = False
         self.running = False

+ 7 - 1
src/bin/stats/stats.spec

@@ -12,7 +12,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down the stats module",
         "command_description": "Shut down the stats module",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       },
       },
       {
       {
         "command_name": "show",
         "command_name": "show",

+ 7 - 1
src/bin/xfrin/xfrin.spec

@@ -86,7 +86,13 @@
       {
       {
         "command_name": "shutdown",
         "command_name": "shutdown",
         "command_description": "Shut down xfrin module",
         "command_description": "Shut down xfrin module",
-        "command_args": []
+        "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
       }
       }
     ]
     ]
   }
   }

+ 7 - 1
src/bin/xfrout/xfrout.spec.pre.in

@@ -106,7 +106,13 @@
         {
         {
           "command_name": "shutdown",
           "command_name": "shutdown",
           "command_description": "Shut down Xfrout",
           "command_description": "Shut down Xfrout",
-          "command_args": []
+          "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
         }
         }
       ]
       ]
   }
   }

+ 7 - 1
src/bin/zonemgr/zonemgr.spec.pre.in

@@ -63,7 +63,13 @@
         {
         {
           "command_name": "shutdown",
           "command_name": "shutdown",
           "command_description": "Shut down Zonemgr",
           "command_description": "Shut down Zonemgr",
-          "command_args": []
+          "command_args": [
+          {
+            "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        ]
         }
         }
       ]
       ]
   }
   }

+ 4 - 4
src/lib/python/isc/bind10/component.py

@@ -22,9 +22,9 @@ Dependencies between them are not yet handled. It might turn out they are
 needed, in that case they will be added sometime in future.
 needed, in that case they will be added sometime in future.
 
 
 This framework allows for a single process to be started multiple times (by
 This framework allows for a single process to be started multiple times (by
-specifying multiple components with the same configuration). However, the rest
-of the system might not handle such situation well, so until it is made so,
-it would be better to start each process at most once.
+specifying multiple components with the same configuration). We might want
+to add a more convenient support (like providing a count argument to the
+configuration). This is yet to be designed.
 """
 """
 
 
 import isc.log
 import isc.log
@@ -408,7 +408,7 @@ class Component(BaseComponent):
         self._boss.register_process(self.pid(), self)
         self._boss.register_process(self.pid(), self)
 
 
     def _stop_internal(self):
     def _stop_internal(self):
-        self._boss.stop_process(self._process, self._address)
+        self._boss.stop_process(self._process, self._address, self.pid())
         # TODO Some way to wait for the process that doesn't want to
         # TODO Some way to wait for the process that doesn't want to
         # terminate and kill it would prove nice (or add it to boss somewhere?)
         # terminate and kill it would prove nice (or add it to boss somewhere?)
 
 

+ 5 - 3
src/lib/python/isc/bind10/tests/component_test.py

@@ -553,11 +553,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
         self.assertEqual(42, component.pid())
         self.assertEqual(42, component.pid())
         self.assertEqual(component, self.__registered_processes.get(42))
         self.assertEqual(component, self.__registered_processes.get(42))
 
 
-    def stop_process(self, process, address):
+    def stop_process(self, process, address, pid):
         """
         """
         Part of pretending to be boss.
         Part of pretending to be boss.
         """
         """
-        self.__stop_process_params = (process, address)
+        self.__stop_process_params = (process, address, pid)
 
 
     def start_simple(self, process):
     def start_simple(self, process):
         """
         """
@@ -573,9 +573,11 @@ class ComponentTests(BossUtils, unittest.TestCase):
         component.start()
         component.start()
         self.assertTrue(component.running())
         self.assertTrue(component.running())
         self.assertEqual('component', self.__start_simple_params)
         self.assertEqual('component', self.__start_simple_params)
+        component.pid = lambda: 42
         component.stop()
         component.stop()
         self.assertFalse(component.running())
         self.assertFalse(component.running())
-        self.assertEqual(('component', 'Address'), self.__stop_process_params)
+        self.assertEqual(('component', 'Address', 42),
+                         self.__stop_process_params)
 
 
     def test_component_kill(self):
     def test_component_kill(self):
         """
         """