command.cc 10.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
17
#include <auth/command.h>
#include <auth/auth_log.h>
#include <auth/auth_srv.h>
18
19

#include <cc/data.h>
20
#include <datasrc/client_list.h>
21
#include <config/ccsession.h>
22
23
#include <exceptions/exceptions.h>
#include <dns/rrclass.h>
24

25
26
27
28
29
30
31
#include <string>

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>

#include <sys/types.h>
#include <unistd.h>
32
33

using boost::scoped_ptr;
34
35
using namespace isc::auth;
using namespace isc::config;
36
37
using namespace isc::data;
using namespace isc::datasrc;
38
39
using namespace isc::dns;
using namespace std;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

namespace {
/// An exception that is thrown if an error occurs while handling a command
/// on an \c AuthSrv object.
///
/// Currently it's only used internally, since \c execAuthServerCommand()
/// (which is the only interface to this module) catches all \c isc::
/// exceptions and converts them.
class AuthCommandError : public isc::Exception {
public:
    AuthCommandError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

/// An abstract base class that represents a single command identifier
/// for an \c AuthSrv object.
///
/// Each of derived classes of \c AuthCommand, which are hidden inside the
/// implementation, corresponds to a command executed on \c AuthSrv, such as
/// "shutdown".  The derived class is responsible to execute the corresponding
/// command with the given command arguments (if any) in its \c exec()
/// method.
///
/// In the initial implementation the existence of the command classes is
/// hidden inside the implementation since the only public interface is
/// \c execAuthServerCommand(), which does not expose this class.
/// In future, we may want to make this framework more dynamic, i.e.,
/// registering specific derived classes run time outside of this
/// implementation.  If and when that happens the definition of the abstract
/// class will be published.
class AuthCommand {
    ///
    /// \name Constructors and Destructor
    ///
    /// Note: The copy constructor and the assignment operator are
    /// intentionally defined as private to make it explicit that this is a
    /// pure base class.
    //@{
private:
    AuthCommand(const AuthCommand& source);
    AuthCommand& operator=(const AuthCommand& source);
protected:
    /// \brief The default constructor.
    ///
    /// This is intentionally defined as \c protected as this base class should
    /// never be instantiated (except as part of a derived class).
    AuthCommand() {}
public:
    /// The destructor.
    virtual ~AuthCommand() {}
    //@}

    /// Execute a single control command.
    ///
    /// Specific derived methods can throw exceptions.  When called via
    /// \c execAuthServerCommand(), all BIND 10 exceptions are caught
    /// and converted into an error code.
    /// The derived method may also throw an exception of class
    /// \c AuthCommandError when it encounters an internal error, such as
    /// semantics error on the command arguments.
    ///
101
102
103
104
105
106
107
    /// This method should return the execution result in the form of
    /// \c ConstElementPtr.  It will be transparently used as the return
    /// value from the command handler called from the corresponding
    /// \c CCSession object.  For a successful completion of the command,
    /// it should suffice to return the return value of
    /// \c isc::config::createAnswer() with no argument.
    ///
108
109
    /// \param server The \c AuthSrv object on which the command is executed.
    /// \param args Command specific argument.
110
    /// \return Command execution result.
111
112
    virtual ConstElementPtr exec(AuthSrv& server,
                                 isc::data::ConstElementPtr args) = 0;
113
114
};

115
116
// Handle the "shutdown" command. An optional parameter "pid" is used to
// see if it is really for our instance.
117
118
class ShutdownCommand : public AuthCommand {
public:
119
120
121
    virtual ConstElementPtr exec(AuthSrv& server,
                                 isc::data::ConstElementPtr args)
    {
122
        // Is the pid argument provided?
123
        if (args && args->contains("pid")) {
124
            // If it is, we check it is the same as our PID
125
126
127
128
129
130
131
132
133
134
135
136

            // 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.
137
                return (createAnswer());
138
139
            }
        }
140
        LOG_DEBUG(auth_logger, DBG_AUTH_SHUT, AUTH_SHUTDOWN);
141
        server.stop();
142
        return (createAnswer());
143
144
145
    }
};

146
147
// Handle the "getstats" command.  The argument is a list.
class GetStatsCommand : public AuthCommand {
148
public:
149
    virtual ConstElementPtr exec(AuthSrv& server, isc::data::ConstElementPtr) {
150
        return (createAnswer(0, server.getStatistics()));
151
152
153
    }
};

154
155
class StartDDNSForwarderCommand : public AuthCommand {
public:
156
157
    virtual ConstElementPtr exec(AuthSrv& server,
                                 isc::data::ConstElementPtr) {
158
        server.createDDNSForwarder();
159
        return (createAnswer());
160
161
162
163
164
    }
};

class StopDDNSForwarderCommand : public AuthCommand {
public:
165
166
    virtual ConstElementPtr exec(AuthSrv& server,
                                 isc::data::ConstElementPtr) {
167
        server.destroyDDNSForwarder();
168
        return (createAnswer());
169
170
171
    }
};

172
173
174
// Handle the "loadzone" command.
class LoadZoneCommand : public AuthCommand {
public:
175
176
177
    virtual ConstElementPtr exec(AuthSrv& server,
                                 isc::data::ConstElementPtr args)
    {
178
179
180
181
182
        if (args == NULL) {
            isc_throw(AuthCommandError, "Null argument");
        }

        ConstElementPtr class_elem = args->get("class");
183
184
        RRClass zone_class(class_elem ? RRClass(class_elem->stringValue()) :
            RRClass::IN());
185
186
187
188
189

        ConstElementPtr origin_elem = args->get("origin");
        if (!origin_elem) {
            isc_throw(AuthCommandError, "Zone origin is missing");
        }
190
        Name origin(origin_elem->stringValue());
191

192
        const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
193
            list(server.getClientList(zone_class));
194

195
196
        if (!list) {
            isc_throw(AuthCommandError, "There's no client list for "
197
                      "class " << zone_class);
198
199
        }

200
        switch (list->reload(origin)) {
201
202
203
            case ConfigurableClientList::ZONE_RELOADED:
                // Everything worked fine.
                LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
204
                    .arg(zone_class).arg(origin);
205
                return (createAnswer());
206
            case ConfigurableClientList::ZONE_NOT_FOUND:
207
208
                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
                          zone_class << " was not found in any configured "
209
210
                          "data source. Configure it first.");
            case ConfigurableClientList::ZONE_NOT_CACHED:
211
212
                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
                          zone_class << " is not served from memory, but "
Jeremy C. Reed's avatar
Jeremy C. Reed committed
213
                          "directly from the data source. It is not possible "
214
                          "to reload it into memory. Configure it to be cached "
215
216
217
218
219
                          "first.");
            case ConfigurableClientList::CACHE_DISABLED:
                // This is an internal error. Auth server must have the cache
                // enabled.
                isc_throw(isc::Unexpected, "Cache disabled in client list of "
220
                          "class " << zone_class);
221
        }
222
        return (createAnswer());
223
224
225
226
227
228
229
230
231
232
    }
};

// The factory of command objects.
AuthCommand*
createAuthCommand(const string& command_id) {
    // For the initial implementation we use a naive if-else blocks
    // (see also createAuthConfigParser())
    if (command_id == "shutdown") {
        return (new ShutdownCommand());
233
234
    } else if (command_id == "getstats") {
        return (new GetStatsCommand());
235
236
    } else if (command_id == "loadzone") {
        return (new LoadZoneCommand());
237
238
239
240
    } else if (command_id == "start_ddns_forwarder") {
        return (new StartDDNSForwarderCommand());
    } else if (command_id == "stop_ddns_forwarder") {
        return (new StopDDNSForwarderCommand());
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
    } else if (false && command_id == "_throw_exception") {
        // This is for testing purpose only and should not appear in the
        // actual configuration syntax.
        // XXX: ModuleCCSession doesn't seem to validate commands (unlike
        // config), so we should disable this case for now.
        throw runtime_error("throwing for test");
    }

    isc_throw(AuthCommandError, "Unknown command identifier: " << command_id);
}
} // end of unnamed namespace

ConstElementPtr
execAuthServerCommand(AuthSrv& server, const string& command_id,
                      ConstElementPtr args)
{
257
    LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_RECEIVED_COMMAND).arg(command_id);
258
    try {
259
260
        return (scoped_ptr<AuthCommand>(
                    createAuthCommand(command_id))->exec(server, args));
261
    } catch (const isc::Exception& ex) {
262
263
        LOG_ERROR(auth_logger, AUTH_COMMAND_FAILED).arg(command_id)
                                                   .arg(ex.what());
264
265
266
        return (createAnswer(1, ex.what()));
    }
}