Kea 2.2.0
command_interpreter.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2022 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";
30const char *CONTROL_REMOTE_ADDRESS = "remote-address";
31
32// Full version, with status, text and arguments
34createAnswer(const int status_code, const std::string& text,
35 const ConstElementPtr& arg) {
36 if (status_code != 0 && text.empty()) {
37 isc_throw(CtrlChannelError, "Text has to be provided for status_code != 0");
38 }
39
40 ElementPtr answer = Element::createMap();
41 ElementPtr result = Element::create(status_code);
42 answer->set(CONTROL_RESULT, result);
43
44 if (!text.empty()) {
45 answer->set(CONTROL_TEXT, Element::create(text));
46 }
47 if (arg) {
48 answer->set(CONTROL_ARGUMENTS, arg);
49 }
50 return (answer);
51}
52
55 return (createAnswer(0, string(""), ConstElementPtr()));
56}
57
59createAnswer(const int status_code, const std::string& text) {
60 return (createAnswer(status_code, text, ElementPtr()));
61}
62
64createAnswer(const int status_code, const ConstElementPtr& arg) {
65 return (createAnswer(status_code, "", arg));
66}
67
69parseAnswer(int &rcode, const ConstElementPtr& msg) {
70 if (!msg) {
71 isc_throw(CtrlChannelError, "No answer specified");
72 }
73 if (msg->getType() != Element::map) {
75 "Invalid answer Element specified, expected map");
76 }
77 if (!msg->contains(CONTROL_RESULT)) {
79 "Invalid answer specified, does not contain mandatory 'result'");
80 }
81
82 ConstElementPtr result = msg->get(CONTROL_RESULT);
83 if (result->getType() != Element::integer) {
85 "Result element in answer message is not a string");
86 }
87
88 rcode = result->intValue();
89
90 // If there are arguments, return them.
91 ConstElementPtr args = msg->get(CONTROL_ARGUMENTS);
92 if (args) {
93 return (args);
94 }
95
96 // There are no arguments, let's try to return just the text status
97 return (msg->get(CONTROL_TEXT));
98}
99
100std::string
102 if (!msg) {
103 isc_throw(CtrlChannelError, "No answer specified");
104 }
105 if (msg->getType() != Element::map) {
107 "Invalid answer Element specified, expected map");
108 }
109 if (!msg->contains(CONTROL_RESULT)) {
111 "Invalid answer specified, does not contain mandatory 'result'");
112 }
113
114 ConstElementPtr result = msg->get(CONTROL_RESULT);
115 if (result->getType() != Element::integer) {
117 "Result element in answer message is not a string");
118 }
119
120 stringstream txt;
121 int rcode = result->intValue();
122 if (rcode == 0) {
123 txt << "success(0)";
124 } else {
125 txt << "failure(" << rcode << ")";
126 }
127
128 // Was any text provided? If yes, include it.
129 ConstElementPtr txt_elem = msg->get(CONTROL_TEXT);
130 if (txt_elem) {
131 txt << ", text=" << txt_elem->stringValue();
132 }
133
134 return (txt.str());
135}
136
138createCommand(const std::string& command) {
139 return (createCommand(command, ElementPtr(), ""));
140}
141
143createCommand(const std::string& command, ConstElementPtr arg) {
144 return (createCommand(command, arg, ""));
145}
146
148createCommand(const std::string& command, const std::string& service) {
149 return (createCommand(command, ElementPtr(), service));
150}
151
153createCommand(const std::string& command,
154 ConstElementPtr arg,
155 const std::string& service) {
156 ElementPtr query = Element::createMap();
157 ElementPtr cmd = Element::create(command);
158 query->set(CONTROL_COMMAND, cmd);
159 if (arg) {
160 query->set(CONTROL_ARGUMENTS, arg);
161 }
162 if (!service.empty()) {
163 ElementPtr services = Element::createList();
164 services->add(Element::create(service));
165 query->set(CONTROL_SERVICE, services);
166 }
167 return (query);
168}
169
170std::string
172 if (!command) {
173 isc_throw(CtrlChannelError, "No command specified");
174 }
175 if (command->getType() != Element::map) {
176 isc_throw(CtrlChannelError, "Invalid command Element specified, expected map");
177 }
178 if (!command->contains(CONTROL_COMMAND)) {
180 "Invalid answer specified, does not contain mandatory 'command'");
181 }
182
183 // Make sure that all specified parameters are supported.
184 auto command_params = command->mapValue();
185 for (auto param : command_params) {
186 if ((param.first != CONTROL_COMMAND) &&
187 (param.first != CONTROL_ARGUMENTS) &&
188 (param.first != CONTROL_SERVICE) &&
189 (param.first != CONTROL_REMOTE_ADDRESS)) {
190 isc_throw(CtrlChannelError, "Received command contains unsupported "
191 "parameter '" << param.first << "'");
192 }
193 }
194
195 ConstElementPtr cmd = command->get(CONTROL_COMMAND);
196 if (cmd->getType() != Element::string) {
198 "'command' element in command message is not a string");
199 }
200
201 arg = command->get(CONTROL_ARGUMENTS);
202
203 return (cmd->stringValue());
204}
205
206std::string
208 std::string command_name = parseCommand(arg, command);
209
210 // This function requires arguments within the command.
211 if (!arg) {
213 "no arguments specified for the '" << command_name
214 << "' command");
215 }
216
217 // Arguments must be a map.
218 if (arg->getType() != Element::map) {
219 isc_throw(CtrlChannelError, "arguments specified for the '" << command_name
220 << "' command are not a map");
221 }
222
223 // At least one argument is required.
224 if (arg->size() == 0) {
225 isc_throw(CtrlChannelError, "arguments must not be empty for "
226 "the '" << command_name << "' command");
227 }
228
229 return (command_name);
230}
231
234 const ConstElementPtr& response2) {
235 // Usually when this method is called there should be two non-null
236 // responses. If there is just a single response, return this
237 // response.
238 if (!response1 && response2) {
239 return (response2);
240
241 } else if (response1 && !response2) {
242 return (response1);
243
244 } else if (!response1 && !response2) {
245 return (ConstElementPtr());
246
247 } else {
248 // Both responses are non-null so we need to combine the lists
249 // of supported commands if the status codes are 0.
250 int status_code;
251 ConstElementPtr args1 = parseAnswer(status_code, response1);
252 if (status_code != 0) {
253 return (response1);
254 }
255
256 ConstElementPtr args2 = parseAnswer(status_code, response2);
257 if (status_code != 0) {
258 return (response2);
259 }
260
261 const std::vector<ElementPtr> vec1 = args1->listValue();
262 const std::vector<ElementPtr> vec2 = args2->listValue();
263
264 // Storing command names in a set guarantees that the non-unique
265 // command names are aggregated.
266 std::set<std::string> combined_set;
267 for (auto v = vec1.cbegin(); v != vec1.cend(); ++v) {
268 combined_set.insert((*v)->stringValue());
269 }
270 for (auto v = vec2.cbegin(); v != vec2.cend(); ++v) {
271 combined_set.insert((*v)->stringValue());
272 }
273
274 // Create a combined list of commands.
275 ElementPtr combined_list = Element::createList();
276 for (auto s = combined_set.cbegin(); s != combined_set.cend(); ++s) {
277 combined_list->add(Element::create(*s));
278 }
279 return (createAnswer(CONTROL_RESULT_SUCCESS, combined_list));
280 }
281}
282
283}
284}
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_REMOTE_ADDRESS
String used for remote address ("remote-address")
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.