Kea  1.9.9-git
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 
10 #include <cc/command_interpreter.h>
11 #include <cc/data.h>
12 #include <string>
13 #include <set>
14 
15 using namespace std;
16 
17 using isc::data::Element;
21 
22 namespace isc {
23 namespace config {
24 
25 const char *CONTROL_COMMAND = "command";
26 const char *CONTROL_RESULT = "result";
27 const char *CONTROL_TEXT = "text";
28 const char *CONTROL_ARGUMENTS = "arguments";
29 const char *CONTROL_SERVICE = "service";
30 
31 // Full version, with status, text and arguments
33 createAnswer(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 
58 createAnswer(const int status_code, const std::string& text) {
59  return (createAnswer(status_code, text, ElementPtr()));
60 }
61 
63 createAnswer(const int status_code, const ConstElementPtr& arg) {
64  return (createAnswer(status_code, "", arg));
65 }
66 
68 parseAnswer(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 
99 std::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 
137 createCommand(const std::string& command) {
138  return (createCommand(command, ElementPtr(), ""));
139 }
140 
142 createCommand(const std::string& command, ConstElementPtr arg) {
143  return (createCommand(command, arg, ""));
144 }
145 
147 createCommand(const std::string& command, const std::string& service) {
148  return (createCommand(command, ElementPtr(), service));
149 }
150 
152 createCommand(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 
169 std::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 
204 std::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 }
ConstElementPtr createAnswer(const int status_code, const ConstElementPtr &arg)
const char * CONTROL_RESULT
String used for result, i.e. integer status ("result")
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
A standard Data module exception that is thrown if a parse error is encountered when constructing an ...
Definition: data.h:43
const char * CONTROL_ARGUMENTS
String used for arguments map ("arguments")
std::string parseCommandWithArgs(ConstElementPtr &arg, ConstElementPtr command)
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
STL namespace.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
std::string answerToText(const ConstElementPtr &msg)
const char * CONTROL_TEXT
String used for storing textual description ("text")
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
A standard control channel exception that is thrown if a function is there is a problem with one of t...
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Defines the logger used by the top-level component of kea-dhcp-ddns.
std::string parseCommand(ConstElementPtr &arg, ConstElementPtr command)
This file contains several functions and constants that are used for handling commands and responses ...
const char * CONTROL_COMMAND
String used for commands ("command")
const char * CONTROL_SERVICE
String used for service list ("service")
ConstElementPtr combineCommandsLists(const ConstElementPtr &response1, const ConstElementPtr &response2)
The Element class represents a piece of data, used by the command channel and configuration parts...
Definition: data.h:66
ConstElementPtr createCommand(const std::string &command, ConstElementPtr arg, const std::string &service)