1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config/command_mgr.h>
#include <config/command_socket_factory.h>
#include <cc/data.h>
#include <cc/command_interpreter.h>
#include <dhcp/iface_mgr.h>
#include <config/config_log.h>
#include <boost/bind.hpp>
using namespace isc::data;
namespace isc {
namespace config {
CommandMgr::CommandMgr() {
registerCommand("list-commands",
boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2));
}
CommandSocketPtr
CommandMgr::openCommandSocket(const isc::data::ConstElementPtr& socket_info) {
if (socket_) {
isc_throw(SocketError, "There is already a control socket open");
}
socket_ = CommandSocketFactory::create(socket_info);
return (socket_);
}
void CommandMgr::closeCommandSocket() {
// First, let's close the socket for incoming new connections.
if (socket_) {
socket_->close();
socket_.reset();
}
// Now let's close all existing connections that we may have.
for (std::list<CommandSocketPtr>::iterator conn = connections_.begin();
conn != connections_.end(); ++conn) {
(*conn)->close();
}
connections_.clear();
}
void CommandMgr::addConnection(const CommandSocketPtr& conn) {
connections_.push_back(conn);
}
bool CommandMgr::closeConnection(int fd) {
// Let's iterate over all currently registered connections.
for (std::list<CommandSocketPtr>::iterator conn = connections_.begin();
conn != connections_.end(); ++conn) {
// If found, close it.
if ((*conn)->getFD() == fd) {
(*conn)->close();
connections_.erase(conn);
return (true);
}
}
return (false);
}
CommandMgr&
CommandMgr::instance() {
static CommandMgr cmd_mgr;
return (cmd_mgr);
}
void CommandMgr::registerCommand(const std::string& cmd, CommandHandler handler) {
if (!handler) {
isc_throw(InvalidCommandHandler, "Specified command handler is NULL");
}
HandlerContainer::const_iterator it = handlers_.find(cmd);
if (it != handlers_.end()) {
isc_throw(InvalidCommandName, "Handler for command '" << cmd
<< "' is already installed.");
}
handlers_.insert(make_pair(cmd, handler));
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_REGISTERED).arg(cmd);
}
void CommandMgr::deregisterCommand(const std::string& cmd) {
if (cmd == "list-commands") {
isc_throw(InvalidCommandName,
"Can't uninstall internal command 'list-commands'");
}
HandlerContainer::iterator it = handlers_.find(cmd);
if (it == handlers_.end()) {
isc_throw(InvalidCommandName, "Handler for command '" << cmd
<< "' not found.");
}
handlers_.erase(it);
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_DEREGISTERED).arg(cmd);
}
void CommandMgr::deregisterAll() {
// No need to log anything here. deregisterAll is not used in production
// code, just in tests.
handlers_.clear();
registerCommand("list-commands",
boost::bind(&CommandMgr::listCommandsHandler, this, _1, _2));
}
void
CommandMgr::commandReader(int sockfd) {
/// @todo: We do not handle commands that are larger than 64K.
// We should not expect commands bigger than 64K.
char buf[65536];
memset(buf, 0, sizeof(buf));
ConstElementPtr cmd, rsp;
// Read incoming data.
int rval = read(sockfd, buf, sizeof(buf));
if (rval < 0) {
// Read failed
LOG_ERROR(command_logger, COMMAND_SOCKET_READ_FAIL).arg(rval).arg(sockfd);
/// @todo: Should we close the connection, similar to what is already
/// being done for rval == 0?
return;
} else if (rval == 0) {
// Remove it from the active connections list.
instance().closeConnection(sockfd);
return;
}
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_SOCKET_READ).arg(rval).arg(sockfd);
// Ok, we received something. Let's see if we can make any sense of it.
try {
// Try to interpret it as JSON.
std::string sbuf(buf, static_cast<size_t>(rval));
cmd = Element::fromJSON(sbuf, true);
// If successful, then process it as a command.
rsp = CommandMgr::instance().processCommand(cmd);
} catch (const Exception& ex) {
LOG_WARN(command_logger, COMMAND_PROCESS_ERROR1).arg(ex.what());
rsp = createAnswer(CONTROL_RESULT_ERROR, std::string(ex.what()));
}
if (!rsp) {
LOG_WARN(command_logger, COMMAND_RESPONSE_ERROR);
return;
}
// Let's convert JSON response to text. Note that at this stage
// the rsp pointer is always set.
std::string txt = rsp->str();
size_t len = txt.length();
if (len > 65535) {
// Hmm, our response is too large. Let's send the first
// 64KB and hope for the best.
LOG_ERROR(command_logger, COMMAND_SOCKET_RESPONSE_TOOLARGE).arg(len);
len = 65535;
}
// Send the data back over socket.
rval = write(sockfd, txt.c_str(), len);
LOG_DEBUG(command_logger, DBG_COMMAND, COMMAND_SOCKET_WRITE).arg(len).arg(sockfd);
if (rval < 0) {
// Response transmission failed. Since the response failed, it doesn't
// make sense to send any status codes. Let's log it and be done with
// it.
LOG_ERROR(command_logger, COMMAND_SOCKET_WRITE_FAIL).arg(len).arg(sockfd);
}
}
isc::data::ConstElementPtr
CommandMgr::processCommand(const isc::data::ConstElementPtr& cmd) {
if (!cmd) {
return (createAnswer(CONTROL_RESULT_ERROR,
"Command processing failed: NULL command parameter"));
}
try {
ConstElementPtr arg;
std::string name = parseCommand(arg, cmd);
LOG_INFO(command_logger, COMMAND_RECEIVED).arg(name);
HandlerContainer::const_iterator it = handlers_.find(name);
if (it == handlers_.end()) {
// Ok, there's no such command.
return (createAnswer(CONTROL_RESULT_ERROR,
"'" + name + "' command not supported."));
}
// Call the actual handler and return whatever it returned
return (it->second(name, arg));
} catch (const Exception& e) {
LOG_WARN(command_logger, COMMAND_PROCESS_ERROR2).arg(e.what());
return (createAnswer(CONTROL_RESULT_ERROR,
std::string("Error during command processing:")
+ e.what()));
}
}
isc::data::ConstElementPtr
CommandMgr::listCommandsHandler(const std::string& name,
const isc::data::ConstElementPtr& params) {
using namespace isc::data;
ElementPtr commands = Element::createList();
for (HandlerContainer::const_iterator it = handlers_.begin();
it != handlers_.end(); ++it) {
commands->add(Element::create(it->first));
}
return (createAnswer(CONTROL_RESULT_SUCCESS, commands));
}
}; // end of isc::config
}; // end of isc
|