Kea 1.9.11
command_interpreter.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2018 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11#include <cc/data.h>
12#include <string>
13#include <set>
14
15using namespace std;
16
21
22namespace isc {
23namespace config {
24
25const char *CONTROL_COMMAND = "command";
26const char *CONTROL_RESULT = "result";
27const char *CONTROL_TEXT = "text";
28const char *CONTROL_ARGUMENTS = "arguments";
29const char *CONTROL_SERVICE = "service";
30
31// Full version, with status, text and arguments
33createAnswer(const int status_code, const std::string& text,
34 const ConstElementPtr& arg) {
35 if (status_code != 0 && text.empty()) {
36 isc_throw(CtrlChannelError, "Text has to be provided for status_code != 0");
37 }
38
39 ElementPtr answer = Element::createMap();
40 ElementPtr result = Element::create(status_code);
41 answer->set(CONTROL_RESULT, result);
42
43 if (!text.empty()) {
44 answer->set(CONTROL_TEXT, Element::create(text));
45 }
46 if (arg) {
47 answer->set(CONTROL_ARGUMENTS, arg);
48 }
49 return (answer);
50}
51
54 return (createAnswer(0, string(""), ConstElementPtr()));
55}
56
58createAnswer(const int status_code, const std::string& text) {
59 return (createAnswer(status_code, text, ElementPtr()));
60}
61
63createAnswer(const int status_code, const ConstElementPtr& arg) {
64 return (createAnswer(status_code, "", arg));
65}
66
68parseAnswer(int &rcode, const ConstElementPtr& msg) {
69 if (!msg) {
70 isc_throw(CtrlChannelError, "No answer specified");
71 }
72 if (msg->getType() != Element::map) {
74 "Invalid answer Element specified, expected map");
75 }
76 if (!msg->contains(CONTROL_RESULT)) {
78 "Invalid answer specified, does not contain mandatory 'result'");
79 }
80
81 ConstElementPtr result = msg->get(CONTROL_RESULT);
82 if (result->getType() != Element::integer) {
84 "Result element in answer message is not a string");
85 }
86
87 rcode = result->intValue();
88
89 // If there are arguments, return them.
90 ConstElementPtr args = msg->get(CONTROL_ARGUMENTS);
91 if (args) {
92 return (args);
93 }
94
95 // There are no arguments, let's try to return just the text status
96 return (msg->get(CONTROL_TEXT));
97}
98
99std::string
101 if (!msg) {
102 isc_throw(CtrlChannelError, "No answer specified");
103 }
104 if (msg->getType() != Element::map) {
106 "Invalid answer Element specified, expected map");
107 }
108 if (!msg->contains(CONTROL_RESULT)) {
110 "Invalid answer specified, does not contain mandatory 'result'");
111 }
112
113 ConstElementPtr result = msg->get(CONTROL_RESULT);
114 if (result->getType() != Element::integer) {
116 "Result element in answer message is not a string");
117 }
118
119 stringstream txt;
120 int rcode = result->intValue();
121 if (rcode == 0) {
122 txt << "success(0)";
123 } else {
124 txt << "failure(" << rcode << ")";
125 }
126
127 // Was any text provided? If yes, include it.
128 ConstElementPtr txt_elem = msg->get(CONTROL_TEXT);
129 if (txt_elem) {
130 txt << ", text=" << txt_elem->stringValue();
131 }
132
133 return (txt.str());
134}
135
137createCommand(const std::string& command) {
138 return (createCommand(command, ElementPtr(), ""));
139}
140
142createCommand(const std::string& command, ConstElementPtr arg) {
143 return (createCommand(command, arg, ""));
144}
145
147createCommand(const std::string& command, const std::string& service) {
148 return (createCommand(command, ElementPtr(), service));
149}
150
152createCommand(const std::string& command,
153 ConstElementPtr arg,
154 const std::string& service) {
155 ElementPtr query = Element::createMap();
156 ElementPtr cmd = Element::create(command);
157 query->set(CONTROL_COMMAND, cmd);
158 if (arg) {
159 query->set(CONTROL_ARGUMENTS, arg);
160 }
161 if (!service.empty()) {
162 ElementPtr services = Element::createList();
163 services->add(Element::create(service));
164 query->set(CONTROL_SERVICE, services);
165 }
166 return (query);
167}
168
169std::string
171 if (!command) {
172 isc_throw(CtrlChannelError, "No command specified");
173 }
174 if (command->getType() != Element::map) {
175 isc_throw(CtrlChannelError, "Invalid command Element specified, expected map");
176 }
177 if (!command->contains(CONTROL_COMMAND)) {
179 "Invalid answer specified, does not contain mandatory 'command'");
180 }
181
182 // Make sure that all specified parameters are supported.
183 auto command_params = command->mapValue();
184 for (auto param : command_params) {
185 if ((param.first != CONTROL_COMMAND) &&
186 (param.first != CONTROL_ARGUMENTS) &&
187 (param.first != CONTROL_SERVICE)) {
188 isc_throw(CtrlChannelError, "Received command contains unsupported "
189 "parameter '" << param.first << "'");
190 }
191 }
192
193 ConstElementPtr cmd = command->get(CONTROL_COMMAND);
194 if (cmd->getType() != Element::string) {
196 "'command' element in command message is not a string");
197 }
198
199 arg = command->get(CONTROL_ARGUMENTS);
200
201 return (cmd->stringValue());
202}
203
204std::string
206 std::string command_name = parseCommand(arg, command);
207
208 // This function requires arguments within the command.
209 if (!arg) {
211 "no arguments specified for the '" << command_name
212 << "' command");
213 }
214
215 // Arguments must be a map.
216 if (arg->getType() != Element::map) {
217 isc_throw(CtrlChannelError, "arguments specified for the '" << command_name
218 << "' command are not a map");
219 }
220
221 // At least one argument is required.
222 if (arg->size() == 0) {
223 isc_throw(CtrlChannelError, "arguments must not be empty for "
224 "the '" << command_name << "' command");
225 }
226
227 return (command_name);
228}
229
232 const ConstElementPtr& response2) {
233 // Usually when this method is called there should be two non-null
234 // responses. If there is just a single response, return this
235 // response.
236 if (!response1 && response2) {
237 return (response2);
238
239 } else if (response1 && !response2) {
240 return (response1);
241
242 } else if (!response1 && !response2) {
243 return (ConstElementPtr());
244
245 } else {
246 // Both responses are non-null so we need to combine the lists
247 // of supported commands if the status codes are 0.
248 int status_code;
249 ConstElementPtr args1 = parseAnswer(status_code, response1);
250 if (status_code != 0) {
251 return (response1);
252 }
253
254 ConstElementPtr args2 = parseAnswer(status_code, response2);
255 if (status_code != 0) {
256 return (response2);
257 }
258
259 const std::vector<ElementPtr> vec1 = args1->listValue();
260 const std::vector<ElementPtr> vec2 = args2->listValue();
261
262 // Storing command names in a set guarantees that the non-unique
263 // command names are aggregated.
264 std::set<std::string> combined_set;
265 for (auto v = vec1.cbegin(); v != vec1.cend(); ++v) {
266 combined_set.insert((*v)->stringValue());
267 }
268 for (auto v = vec2.cbegin(); v != vec2.cend(); ++v) {
269 combined_set.insert((*v)->stringValue());
270 }
271
272 // Create a combined list of commands.
273 ElementPtr combined_list = Element::createList();
274 for (auto s = combined_set.cbegin(); s != combined_set.cend(); ++s) {
275 combined_list->add(Element::create(*s));
276 }
277 return (createAnswer(CONTROL_RESULT_SUCCESS, combined_list));
278 }
279}
280
281}
282}
A standard control channel exception that is thrown if a function is there is a problem with one of t...
The Element class represents a piece of data, used by the command channel and configuration parts.
Definition: data.h:70
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:47
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::string parseCommandWithArgs(ConstElementPtr &arg, ConstElementPtr command)
const char * CONTROL_ARGUMENTS
String used for arguments map ("arguments")
const char * CONTROL_TEXT
String used for storing textual description ("text")
const char * CONTROL_COMMAND
String used for commands ("command")
ConstElementPtr createCommand(const std::string &command)
Creates a standard command message with no argument (of the form { "command": "my_command" })
const char * CONTROL_SERVICE
String used for service list ("service")
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
ConstElementPtr combineCommandsLists(const ConstElementPtr &response1, const ConstElementPtr &response2)
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
const char * CONTROL_RESULT
String used for result, i.e. integer status ("result")
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
std::string answerToText(const ConstElementPtr &msg)
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:27
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
Defines the logger used by the top-level component of kea-lfc.