Kea 2.6.2
command_interpreter.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2024 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
41 ElementPtr result = Element::create(status_code);
43
44 if (!text.empty()) {
46 }
47 if (arg) {
48 answer->set(CONTROL_ARGUMENTS, arg);
49 }
50 return (answer);
51}
52
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
69parseAnswerText(int &rcode, const ConstElementPtr& msg) {
70 if (!msg) {
71 isc_throw(CtrlChannelError, "invalid answer: no answer specified");
72 }
73 if (msg->getType() != Element::map) {
74 isc_throw(CtrlChannelError, "invalid answer: expected toplevel entry to be a map, got "
75 << Element::typeToName(msg->getType()) << " instead");
76 }
77 if (!msg->contains(CONTROL_RESULT)) {
79 "invalid answer: does not contain mandatory '" << CONTROL_RESULT << "'");
80 }
81
83 if (result->getType() != Element::integer) {
84 isc_throw(CtrlChannelError, "invalid answer: expected '" << CONTROL_RESULT
85 << "' to be an integer, got "
86 << Element::typeToName(result->getType()) << " instead");
87 }
88
89 rcode = result->intValue();
90
91 // If there are arguments, return them.
92 return (msg->get(CONTROL_TEXT));
93}
94
96parseAnswer(int &rcode, const ConstElementPtr& msg) {
97 if (!msg) {
98 isc_throw(CtrlChannelError, "invalid answer: no answer specified");
99 }
100 if (msg->getType() != Element::map) {
101 isc_throw(CtrlChannelError, "invalid answer: expected toplevel entry to be a map, got "
102 << Element::typeToName(msg->getType()) << " instead");
103 }
104 if (!msg->contains(CONTROL_RESULT)) {
106 "invalid answer: does not contain mandatory '" << CONTROL_RESULT << "'");
107 }
108
110 if (result->getType() != Element::integer) {
111 isc_throw(CtrlChannelError, "invalid answer: expected '" << CONTROL_RESULT
112 << "' to be an integer, got "
113 << Element::typeToName(result->getType()) << " instead");
114 }
115
116 rcode = result->intValue();
117
118 // If there are arguments, return them.
119 ConstElementPtr args = msg->get(CONTROL_ARGUMENTS);
120 if (args) {
121 return (args);
122 }
123
124 // There are no arguments, let's try to return just the text status
125 return (msg->get(CONTROL_TEXT));
126}
127
128
129std::string
131 if (!msg) {
132 isc_throw(CtrlChannelError, "invalid answer: no answer specified");
133 }
134 if (msg->getType() != Element::map) {
135 isc_throw(CtrlChannelError, "invalid answer: expected toplevel entry to be a map, got "
136 << Element::typeToName(msg->getType()) << " instead");
137 }
138 if (!msg->contains(CONTROL_RESULT)) {
140 "invalid answer: does not contain mandatory '" << CONTROL_RESULT << "'");
141 }
142
144 if (result->getType() != Element::integer) {
145 isc_throw(CtrlChannelError, "invalid answer: expected '" << CONTROL_RESULT
146 << "' to be an integer, got " << Element::typeToName(result->getType())
147 << " instead");
148 }
149
150 stringstream txt;
151 int rcode = result->intValue();
152 if (rcode == 0) {
153 txt << "success(0)";
154 } else {
155 txt << "failure(" << rcode << ")";
156 }
157
158 // Was any text provided? If yes, include it.
159 ConstElementPtr txt_elem = msg->get(CONTROL_TEXT);
160 if (txt_elem) {
161 txt << ", text=" << txt_elem->stringValue();
162 }
163
164 return (txt.str());
165}
166
168createCommand(const std::string& command) {
169 return (createCommand(command, ElementPtr(), ""));
170}
171
173createCommand(const std::string& command, ConstElementPtr arg) {
174 return (createCommand(command, arg, ""));
175}
176
178createCommand(const std::string& command, const std::string& service) {
179 return (createCommand(command, ElementPtr(), service));
180}
181
183createCommand(const std::string& command,
184 ConstElementPtr arg,
185 const std::string& service) {
187 ElementPtr cmd = Element::create(command);
188 query->set(CONTROL_COMMAND, cmd);
189 if (arg) {
190 query->set(CONTROL_ARGUMENTS, arg);
191 }
192 if (!service.empty()) {
193 ElementPtr services = Element::createList();
194 services->add(Element::create(service));
195 query->set(CONTROL_SERVICE, services);
196 }
197 return (query);
198}
199
200std::string
202 if (!command) {
203 isc_throw(CtrlChannelError, "invalid command: no command specified");
204 }
205 if (command->getType() != Element::map) {
206 isc_throw(CtrlChannelError, "invalid command: expected toplevel entry to be a map, got "
207 << Element::typeToName(command->getType()) << " instead");
208 }
209 if (!command->contains(CONTROL_COMMAND)) {
211 "invalid command: does not contain mandatory '" << CONTROL_COMMAND << "'");
212 }
213
214 // Make sure that all specified parameters are supported.
215 auto const& command_params = command->mapValue();
216 for (auto const& param : command_params) {
217 if ((param.first != CONTROL_COMMAND) &&
218 (param.first != CONTROL_ARGUMENTS) &&
219 (param.first != CONTROL_SERVICE) &&
220 (param.first != CONTROL_REMOTE_ADDRESS)) {
222 "invalid command: unsupported parameter '" << param.first << "'");
223 }
224 }
225
226 ConstElementPtr cmd = command->get(CONTROL_COMMAND);
227 if (cmd->getType() != Element::string) {
228 isc_throw(CtrlChannelError, "invalid command: expected '"
229 << CONTROL_COMMAND << "' to be a string, got "
230 << Element::typeToName(command->getType()) << " instead");
231 }
232
233 arg = command->get(CONTROL_ARGUMENTS);
234
235 return (cmd->stringValue());
236}
237
238std::string
240 std::string command_name = parseCommand(arg, command);
241
242 // This function requires arguments within the command.
243 if (!arg) {
245 "invalid command '" << command_name << "': no arguments specified");
246 }
247
248 // Arguments must be a map.
249 if (arg->getType() != Element::map) {
251 "invalid command '" << command_name << "': expected '"
252 << CONTROL_ARGUMENTS << "' to be a map, got "
253 << Element::typeToName(arg->getType()) << " instead");
254 }
255
256 // At least one argument is required.
257 if (arg->size() == 0) {
259 "invalid command '" << command_name << "': '"
260 << CONTROL_ARGUMENTS << "' is empty");
261 }
262
263 return (command_name);
264}
265
268 const ConstElementPtr& response2) {
269 // Usually when this method is called there should be two non-null
270 // responses. If there is just a single response, return this
271 // response.
272 if (!response1 && response2) {
273 return (response2);
274
275 } else if (response1 && !response2) {
276 return (response1);
277
278 } else if (!response1 && !response2) {
279 return (ConstElementPtr());
280
281 } else {
282 // Both responses are non-null so we need to combine the lists
283 // of supported commands if the status codes are 0.
284 int status_code;
285 ConstElementPtr args1 = parseAnswer(status_code, response1);
286 if (status_code != 0) {
287 return (response1);
288 }
289
290 ConstElementPtr args2 = parseAnswer(status_code, response2);
291 if (status_code != 0) {
292 return (response2);
293 }
294
295 const std::vector<ElementPtr> vec1 = args1->listValue();
296 const std::vector<ElementPtr> vec2 = args2->listValue();
297
298 // Storing command names in a set guarantees that the non-unique
299 // command names are aggregated.
300 std::set<std::string> combined_set;
301 for (auto const& v : vec1) {
302 combined_set.insert(v->stringValue());
303 }
304 for (auto const& v : vec2) {
305 combined_set.insert(v->stringValue());
306 }
307
308 // Create a combined list of commands.
309 ElementPtr combined_list = Element::createList();
310 for (auto const& s : combined_set) {
311 combined_list->add(Element::create(s));
312 }
313 return (createAnswer(CONTROL_RESULT_SUCCESS, combined_list));
314 }
315}
316
317} // namespace config
318} // namespace isc
when the call the UDPServer carries on at the same position As a result
Definition asiodns.dox:16
it forwards queries to a single upstream resolver and passes the answers back to the client It is constructed with the address of the forward server Queries are initiated with the question to ask the forward a buffer into which to write the answer
Definition asiodns.dox:60
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
@ map
Definition data.h:147
@ integer
Definition data.h:140
@ string
Definition data.h:144
static std::string typeToName(Element::types type)
Returns the name of the given type as a string.
Definition data.cc:651
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
static ElementPtr createList(const Position &pos=ZERO_POSITION())
Creates an empty ListElement type ElementPtr.
Definition data.cc:299
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:72
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition data.h:49
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 answerToText(const ConstElementPtr &msg)
Converts answer to printable text.
const char * CONTROL_ARGUMENTS
String used for arguments map ("arguments")
ConstElementPtr parseAnswerText(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns text status.
const char * CONTROL_TEXT
String used for storing textual description ("text")
const char * CONTROL_COMMAND
String used for commands ("command")
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
ConstElementPtr createCommand(const std::string &command)
Creates a standard command message with no argument (of the form { "command": "my_command" }...
ConstElementPtr combineCommandsLists(const ConstElementPtr &response1, const ConstElementPtr &response2)
Combines lists of commands carried in two responses.
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
Parses the given command into a string containing the actual command and an ElementPtr containing the...
const char * CONTROL_SERVICE
String used for service list ("service")
std::string parseCommandWithArgs(ConstElementPtr &arg, ConstElementPtr command)
Parses the given command into a string containing the command name and an ElementPtr containing the m...
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
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.
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
Defines the logger used by the top-level component of kea-lfc.