blob: 77d4387e849b546c52a2e04b3306509ea45d3657 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <Utils.hpp>
James Feistc95cb142018-02-26 10:41:42 -080018#include <Overlay.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080019#include <nlohmann/json.hpp>
20#include <fstream>
21#include <regex>
James Feist8f2710a2018-05-09 17:18:55 -070022#include <iostream>
23#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist11be6672018-04-06 14:05:32 -070025#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080028#include <boost/lexical_cast.hpp>
29#include <boost/container/flat_map.hpp>
30#include <boost/container/flat_set.hpp>
31#include <VariantVisitors.hpp>
Ed Tanous072e25d2018-12-16 21:45:20 -080032#include "filesystem.hpp"
James Feist3cb5fec2018-01-23 14:41:51 -080033
34constexpr const char *OUTPUT_DIR = "/var/configuration/";
James Feistce4367c2018-10-16 09:19:57 -070035constexpr const char *configurationDirectory = PACKAGE_DIR "configurations";
36constexpr const char *schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feistb4383f42018-08-06 16:54:10 -070037constexpr const char *globalSchema = "global.json";
James Feist3cb5fec2018-01-23 14:41:51 -080038constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080039constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070040constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080041constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080042
James Feistee0de612018-10-12 11:15:46 -070043namespace variant_ns = sdbusplus::message::variant_ns;
James Feist3cb5fec2018-01-23 14:41:51 -080044struct cmp_str
45{
46 bool operator()(const char *a, const char *b) const
47 {
48 return std::strcmp(a, b) < 0;
49 }
50};
51
James Feist8f2710a2018-05-09 17:18:55 -070052struct PerformProbe;
53
James Feist3cb5fec2018-01-23 14:41:51 -080054// underscore T for collison with dbus c api
55enum class probe_type_codes
56{
57 FALSE_T,
58 TRUE_T,
59 AND,
60 OR,
James Feist6bd2a022018-03-13 12:30:58 -070061 FOUND,
62 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080063};
64const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
65 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
66 {"TRUE", probe_type_codes::TRUE_T},
67 {"AND", probe_type_codes::AND},
68 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070069 {"FOUND", probe_type_codes::FOUND},
70 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080071
James Feist3cab7872018-12-11 15:20:00 -080072static constexpr std::array<const char *, 4> settableInterfaces = {
73 "Thresholds", "Pid", "Pid.Zone", "Stepwise"};
James Feist68500ff2018-08-08 15:40:42 -070074using JsonVariantType =
75 sdbusplus::message::variant<std::vector<std::string>, std::string, int64_t,
76 uint64_t, double, int32_t, uint32_t, int16_t,
77 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070078using BasicVariantType =
79 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
80 uint32_t, int16_t, uint16_t, uint8_t, bool>;
81
James Feist3cb5fec2018-01-23 14:41:51 -080082using GetSubTreeType = std::vector<
83 std::pair<std::string,
84 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
85
86using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070087 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080088 boost::container::flat_map<
89 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070090 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080091
92boost::container::flat_map<
93 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070094 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080095 DBUS_PROBE_OBJECTS;
96std::vector<std::string> PASSED_PROBES;
97
98// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -070099std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -0800100
James Feista6750242018-07-16 14:12:27 -0700101const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -0800102
James Feist8f2710a2018-05-09 17:18:55 -0700103void registerCallbacks(boost::asio::io_service &io,
104 std::vector<sdbusplus::bus::match::match> &dbusMatches,
105 nlohmann::json &systemConfiguration,
106 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800107
James Feist3cb5fec2018-01-23 14:41:51 -0800108// calls the mapper to find all exposed objects of an interface type
109// and creates a vector<flat_map> that contains all the key value pairs
110// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700111void findDbusObjects(std::shared_ptr<PerformProbe> probe,
112 std::shared_ptr<sdbusplus::asio::connection> connection,
113 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800114{
James Feist8f2710a2018-05-09 17:18:55 -0700115
116 // store reference to pending callbacks so we don't overwhelm services
117 static boost::container::flat_map<
118 std::string, std::vector<std::shared_ptr<PerformProbe>>>
119 pendingProbes;
120
121 if (DBUS_PROBE_OBJECTS[interface].size())
122 {
123 return;
124 }
125
126 // add shared_ptr to vector of Probes waiting for callback from a specific
127 // interface to keep alive while waiting for response
128 std::array<const char *, 1> objects = {interface.c_str()};
129 std::vector<std::shared_ptr<PerformProbe>> &pending =
130 pendingProbes[interface];
131 auto iter = pending.emplace(pending.end(), probe);
132 // only allow first call to run to not overwhelm processes
133 if (iter != pending.begin())
134 {
135 return;
136 }
137
James Feist3cb5fec2018-01-23 14:41:51 -0800138 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700139 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700140 [connection, interface, probe](boost::system::error_code &ec,
141 const GetSubTreeType &interfaceSubtree) {
142 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700143 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700144 {
James Feist0de40152018-07-25 11:56:12 -0700145 pendingProbes[interface].clear();
146 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700147 {
James Feist0de40152018-07-25 11:56:12 -0700148 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700149 }
James Feist0de40152018-07-25 11:56:12 -0700150 std::cerr << "Error communicating to mapper.\n";
151
152 // if we can't communicate to the mapper something is very wrong
153 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700154 }
James Feist8f2710a2018-05-09 17:18:55 -0700155 else
James Feist3cb5fec2018-01-23 14:41:51 -0800156 {
James Feist8f2710a2018-05-09 17:18:55 -0700157 for (auto &object : interfaceSubtree)
158 {
159 for (auto &connPair : object.second)
160 {
161 auto insertPair =
162 interfaceConnections.insert(connPair.first);
163 }
164 }
James Feist3cb5fec2018-01-23 14:41:51 -0800165 }
James Feist63845bf2019-01-24 12:19:51 -0800166 if (interfaceConnections.empty())
167 {
168 pendingProbes[interface].clear();
169 return;
170 }
James Feist8f2710a2018-05-09 17:18:55 -0700171 // get managed objects for all interfaces
172 for (const auto &conn : interfaceConnections)
173 {
174 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700175 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700176 interface](boost::system::error_code &ec,
177 const ManagedObjectType &managedInterface) {
178 if (ec)
179 {
180 std::cerr
181 << "error getting managed object for device "
182 << conn << "\n";
183 pendingProbes[interface].clear();
184 return;
185 }
186 for (auto &interfaceManagedObj : managedInterface)
187 {
188 auto ifaceObjFind =
189 interfaceManagedObj.second.find(interface);
190 if (ifaceObjFind !=
191 interfaceManagedObj.second.end())
192 {
193 std::vector<boost::container::flat_map<
194 std::string, BasicVariantType>>
195 &dbusObject = DBUS_PROBE_OBJECTS[interface];
196 dbusObject.emplace_back(ifaceObjFind->second);
197 }
198 }
199 pendingProbes[interface].clear();
200 },
201 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
202 "GetManagedObjects");
203 }
204 },
205 "xyz.openbmc_project.ObjectMapper",
206 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700207 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700208 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800209}
James Feist8f2710a2018-05-09 17:18:55 -0700210// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800211bool probeDbus(
212 const std::string &interface,
213 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700214 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800215 &devices,
216 bool &foundProbe)
217{
James Feist8f2710a2018-05-09 17:18:55 -0700218 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
219 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800220 if (dbusObject.empty())
221 {
James Feist8f2710a2018-05-09 17:18:55 -0700222 foundProbe = false;
223 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800224 }
225 foundProbe = true;
226
227 bool foundMatch = false;
228 for (auto &device : dbusObject)
229 {
230 bool deviceMatches = true;
231 for (auto &match : matches)
232 {
233 auto deviceValue = device.find(match.first);
234 if (deviceValue != device.end())
235 {
236 switch (match.second.type())
237 {
James Feist9eb0b582018-04-27 12:15:46 -0700238 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800239 {
James Feist9eb0b582018-04-27 12:15:46 -0700240 std::regex search(match.second.get<std::string>());
241 std::smatch match;
242
243 // convert value to string respresentation
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700244 std::string probeValue = variant_ns::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700245 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700246 if (!std::regex_search(probeValue, match, search))
247 {
248 deviceMatches = false;
249 break;
250 }
James Feist3cb5fec2018-01-23 14:41:51 -0800251 break;
252 }
James Feist9eb0b582018-04-27 12:15:46 -0700253 case nlohmann::json::value_t::boolean:
254 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800255 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700256 unsigned int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700257 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800258
James Feist9eb0b582018-04-27 12:15:46 -0700259 if (probeValue != match.second.get<unsigned int>())
260 {
261 deviceMatches = false;
262 }
263 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800264 }
James Feist9eb0b582018-04-27 12:15:46 -0700265 case nlohmann::json::value_t::number_integer:
266 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700267 int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700268 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800269
James Feist9eb0b582018-04-27 12:15:46 -0700270 if (probeValue != match.second.get<int>())
271 {
272 deviceMatches = false;
273 }
274 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800275 }
James Feist9eb0b582018-04-27 12:15:46 -0700276 case nlohmann::json::value_t::number_float:
277 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700278 float probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700279 VariantToFloatVisitor(), deviceValue->second);
280
281 if (probeValue != match.second.get<float>())
282 {
283 deviceMatches = false;
284 }
285 break;
286 }
James Feist3cb5fec2018-01-23 14:41:51 -0800287 }
288 }
289 else
290 {
291 deviceMatches = false;
292 break;
293 }
294 }
295 if (deviceMatches)
296 {
297 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700298 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800299 device));
300 foundMatch = true;
301 deviceMatches = false; // for next iteration
302 }
303 }
304 return foundMatch;
305}
306
307// default probe entry point, iterates a list looking for specific types to
308// call specific probe functions
309bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700310 const std::vector<std::string> &probeCommand,
311 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800312 &foundDevs)
313{
314 const static std::regex command(R"(\((.*)\))");
315 std::smatch match;
316 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700317 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800318 bool cur = true;
319 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
320
321 for (auto &probe : probeCommand)
322 {
323 bool foundProbe = false;
324 boost::container::flat_map<const char *, probe_type_codes,
325 cmp_str>::const_iterator probeType;
326
327 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
328 probeType++)
329 {
330 if (probe.find(probeType->first) != std::string::npos)
331 {
332 foundProbe = true;
333 break;
334 }
335 }
336 if (foundProbe)
337 {
338 switch (probeType->second)
339 {
James Feist9eb0b582018-04-27 12:15:46 -0700340 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800341 {
James Feist8f2710a2018-05-09 17:18:55 -0700342 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700343 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800344 }
James Feist9eb0b582018-04-27 12:15:46 -0700345 case probe_type_codes::TRUE_T:
346 {
James Feist8f2710a2018-05-09 17:18:55 -0700347 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700348 break;
349 }
350 case probe_type_codes::MATCH_ONE:
351 {
352 // set current value to last, this probe type shouldn't
353 // affect the outcome
354 cur = ret;
355 matchOne = true;
356 break;
357 }
358 /*case probe_type_codes::AND:
359 break;
360 case probe_type_codes::OR:
361 break;
362 // these are no-ops until the last command switch
363 */
364 case probe_type_codes::FOUND:
365 {
366 if (!std::regex_search(probe, match, command))
367 {
368 std::cerr << "found probe sytax error " << probe
369 << "\n";
370 return false;
371 }
372 std::string commandStr = *(match.begin() + 1);
373 boost::replace_all(commandStr, "'", "");
374 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
375 commandStr) != PASSED_PROBES.end());
376 break;
377 }
James Feist3cb5fec2018-01-23 14:41:51 -0800378 }
379 }
380 // look on dbus for object
381 else
382 {
383 if (!std::regex_search(probe, match, command))
384 {
385 std::cerr << "dbus probe sytax error " << probe << "\n";
386 return false;
387 }
388 std::string commandStr = *(match.begin() + 1);
389 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700390 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800391 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800392 auto json = nlohmann::json::parse(commandStr, nullptr, false);
393 if (json.is_discarded())
394 {
395 std::cerr << "dbus command sytax error " << commandStr << "\n";
396 return false;
397 }
398 // we can match any (string, variant) property. (string, string)
399 // does a regex
400 std::map<std::string, nlohmann::json> dbusProbeMap =
401 json.get<std::map<std::string, nlohmann::json>>();
402 auto findStart = probe.find("(");
403 if (findStart == std::string::npos)
404 {
405 return false;
406 }
407 std::string probeInterface = probe.substr(0, findStart);
408 cur =
409 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
410 }
411
412 // some functions like AND and OR only take affect after the
413 // fact
414 switch (lastCommand)
415 {
James Feist9eb0b582018-04-27 12:15:46 -0700416 case probe_type_codes::AND:
417 ret = cur && ret;
418 break;
419 case probe_type_codes::OR:
420 ret = cur || ret;
421 break;
422 default:
423 ret = cur;
424 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800425 }
426 lastCommand = probeType != PROBE_TYPES.end()
427 ? probeType->second
428 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800429 }
430
431 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800432 if (ret && foundDevs.size() == 0)
433 {
434 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700435 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800436 }
James Feist6bd2a022018-03-13 12:30:58 -0700437 if (matchOne && foundDevs.size() > 1)
438 {
439 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
440 }
James Feist3cb5fec2018-01-23 14:41:51 -0800441 return ret;
442}
James Feist8f2710a2018-05-09 17:18:55 -0700443// this class finds the needed dbus fields and on destruction runs the probe
444struct PerformProbe : std::enable_shared_from_this<PerformProbe>
445{
James Feist3cb5fec2018-01-23 14:41:51 -0800446
James Feist8f2710a2018-05-09 17:18:55 -0700447 PerformProbe(
448 const std::vector<std::string> &probeCommand,
449 std::function<void(std::vector<boost::container::flat_map<
450 std::string, BasicVariantType>> &)> &&callback) :
451 _probeCommand(probeCommand),
452 _callback(std::move(callback))
453 {
454 }
455 ~PerformProbe()
456 {
457 if (probe(_probeCommand, _foundDevs))
458 {
459 _callback(_foundDevs);
460 }
461 }
462 void run()
463 {
464 // parse out dbus probes by discarding other probe types
465 boost::container::flat_map<const char *, probe_type_codes,
466 cmp_str>::const_iterator probeType;
467
468 std::vector<std::string> dbusProbes;
469 for (std::string &probe : _probeCommand)
470 {
471 bool found = false;
472 boost::container::flat_map<const char *, probe_type_codes,
473 cmp_str>::const_iterator probeType;
474 for (probeType = PROBE_TYPES.begin();
475 probeType != PROBE_TYPES.end(); probeType++)
476 {
477 if (probe.find(probeType->first) != std::string::npos)
478 {
479 found = true;
480 break;
481 }
482 }
483 if (found)
484 {
485 continue;
486 }
487 // syntax requires probe before first open brace
488 auto findStart = probe.find("(");
489 std::string interface = probe.substr(0, findStart);
490
491 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
492 }
493 }
494 std::vector<std::string> _probeCommand;
495 std::function<void(
496 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
497 &)>
498 _callback;
499 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
500 _foundDevs;
501};
502
503// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700504bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800505{
Ed Tanous072e25d2018-12-16 21:45:20 -0800506 std::filesystem::create_directory(OUTPUT_DIR);
James Feist1b2e2242018-01-30 13:45:19 -0800507 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700508 if (!output.good())
509 {
510 return false;
511 }
James Feist1b2e2242018-01-30 13:45:19 -0800512 output << systemConfiguration.dump(4);
513 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700514 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700515}
James Feist1b2e2242018-01-30 13:45:19 -0800516
James Feist8f2710a2018-05-09 17:18:55 -0700517// template function to add array as dbus property
518template <typename PropertyType>
519void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700520 sdbusplus::asio::dbus_interface *iface,
521 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700522{
523 std::vector<PropertyType> values;
524 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800525 {
James Feist8f2710a2018-05-09 17:18:55 -0700526 auto ptr = property.get_ptr<const PropertyType *>();
527 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800528 {
James Feist8f2710a2018-05-09 17:18:55 -0700529 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800530 }
531 }
James Feistbb43d022018-06-12 15:44:33 -0700532 // todo(james), currently there are no reason to persist arrays, get around
533 // to it if needed
534
535 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800536}
James Feist97a63f12018-05-17 13:50:57 -0700537
538template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700539bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700540 nlohmann::json &systemConfiguration)
541{
542 try
543 {
544 nlohmann::json::json_pointer ptr(ptrStr);
545 nlohmann::json &ref = systemConfiguration[ptr];
546 ref = value;
547 return true;
548 }
549 catch (const std::out_of_range)
550 {
551 return false;
552 }
553}
James Feistbb43d022018-06-12 15:44:33 -0700554
555template <typename PropertyType>
556void addProperty(const std::string &propertyName, const PropertyType &value,
557 sdbusplus::asio::dbus_interface *iface,
558 nlohmann::json &systemConfiguration,
559 const std::string &jsonPointerString,
560 sdbusplus::asio::PropertyPermission permission)
561{
562 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
563 {
564 iface->register_property(propertyName, value);
565 return;
566 }
James Feist68500ff2018-08-08 15:40:42 -0700567 iface->register_property(
568 propertyName, value,
569 [&systemConfiguration,
570 jsonPointerString{std::string(jsonPointerString)}](
571 const PropertyType &newVal, PropertyType &val) {
572 val = newVal;
573 if (!setJsonFromPointer(jsonPointerString, val,
574 systemConfiguration))
575 {
576 std::cerr << "error setting json field\n";
577 return -1;
578 }
James Feistc6248a52018-08-14 10:09:45 -0700579 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700580 {
581 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700582 return -1;
583 }
584 return 1;
585 });
586}
587
588void createDeleteObjectMethod(
589 const std::string &jsonPointerPath,
590 const std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
591 sdbusplus::asio::object_server &objServer,
592 nlohmann::json &systemConfiguration)
593{
594 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
595 iface->register_method(
596 "Delete", [&objServer, &systemConfiguration, interface,
597 jsonPointerPath{std::string(jsonPointerPath)}]() {
598 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
599 interface.lock();
600 if (!iface)
601 {
602 // this technically can't happen as the pointer is pointing to
603 // us
604 throw DBusInternalError();
605 }
606 nlohmann::json::json_pointer ptr(jsonPointerPath);
607 if (!objServer.remove_interface(iface))
608 {
609 std::cerr << "Can't delete interface " << jsonPointerPath
610 << "\n";
611 throw DBusInternalError();
612 }
613 systemConfiguration[ptr] = nullptr;
614
615 if (!writeJsonFiles(systemConfiguration))
616 {
617 std::cerr << "error setting json file\n";
618 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700619 }
James Feistbb43d022018-06-12 15:44:33 -0700620 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700621 });
James Feistbb43d022018-06-12 15:44:33 -0700622}
623
James Feist1b2e2242018-01-30 13:45:19 -0800624// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700625void populateInterfaceFromJson(
626 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700627 std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
628 nlohmann::json &dict, sdbusplus::asio::object_server &objServer,
James Feistbb43d022018-06-12 15:44:33 -0700629 sdbusplus::asio::PropertyPermission permission =
630 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800631{
James Feist9eb0b582018-04-27 12:15:46 -0700632 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800633 {
James Feist8f2710a2018-05-09 17:18:55 -0700634 auto type = dictPair.value().type();
635 bool array = false;
636 if (dictPair.value().type() == nlohmann::json::value_t::array)
637 {
638 array = true;
639 if (!dictPair.value().size())
640 {
641 continue;
642 }
643 type = dictPair.value()[0].type();
644 bool isLegal = true;
645 for (const auto &arrayItem : dictPair.value())
646 {
647 if (arrayItem.type() != type)
648 {
649 isLegal = false;
650 break;
651 }
652 }
653 if (!isLegal)
654 {
655 std::cerr << "dbus format error" << dictPair.value() << "\n";
656 continue;
657 }
658 if (type == nlohmann::json::value_t::object)
659 {
660 continue; // handled elsewhere
661 }
662 }
James Feist97a63f12018-05-17 13:50:57 -0700663 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700664 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
665 {
666 // all setable numbers are doubles as it is difficult to always
667 // create a configuration file with all whole numbers as decimals
668 // i.e. 1.0
669 if (dictPair.value().is_number())
670 {
671 type = nlohmann::json::value_t::number_float;
672 }
673 }
674
James Feist8f2710a2018-05-09 17:18:55 -0700675 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800676 {
James Feist9eb0b582018-04-27 12:15:46 -0700677 case (nlohmann::json::value_t::boolean):
678 {
James Feist8f2710a2018-05-09 17:18:55 -0700679 if (array)
680 {
681 // todo: array of bool isn't detected correctly by
682 // sdbusplus, change it to numbers
683 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700684 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700685 }
James Feistbb43d022018-06-12 15:44:33 -0700686
James Feist97a63f12018-05-17 13:50:57 -0700687 else
688 {
James Feistbb43d022018-06-12 15:44:33 -0700689 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700690 iface.get(), systemConfiguration, key,
691 permission);
James Feist97a63f12018-05-17 13:50:57 -0700692 }
James Feist9eb0b582018-04-27 12:15:46 -0700693 break;
694 }
695 case (nlohmann::json::value_t::number_integer):
696 {
James Feist8f2710a2018-05-09 17:18:55 -0700697 if (array)
698 {
699 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700700 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700701 }
702 else
703 {
James Feistbb43d022018-06-12 15:44:33 -0700704 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700705 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700706 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700707 }
James Feist9eb0b582018-04-27 12:15:46 -0700708 break;
709 }
710 case (nlohmann::json::value_t::number_unsigned):
711 {
James Feist8f2710a2018-05-09 17:18:55 -0700712 if (array)
713 {
714 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700715 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700716 }
717 else
718 {
James Feistbb43d022018-06-12 15:44:33 -0700719 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700720 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700721 systemConfiguration, key,
722 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700723 }
James Feist9eb0b582018-04-27 12:15:46 -0700724 break;
725 }
726 case (nlohmann::json::value_t::number_float):
727 {
James Feist8f2710a2018-05-09 17:18:55 -0700728 if (array)
729 {
730 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700731 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700732 }
James Feistbb43d022018-06-12 15:44:33 -0700733
James Feist97a63f12018-05-17 13:50:57 -0700734 else
735 {
James Feistbb43d022018-06-12 15:44:33 -0700736 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700737 iface.get(), systemConfiguration, key,
738 permission);
James Feist97a63f12018-05-17 13:50:57 -0700739 }
James Feist9eb0b582018-04-27 12:15:46 -0700740 break;
741 }
742 case (nlohmann::json::value_t::string):
743 {
James Feist8f2710a2018-05-09 17:18:55 -0700744 if (array)
745 {
James Feistc6248a52018-08-14 10:09:45 -0700746 addArrayToDbus<std::string>(dictPair.key(),
747 dictPair.value(), iface.get(),
748 permission);
James Feist97a63f12018-05-17 13:50:57 -0700749 }
750 else
751 {
James Feistc6248a52018-08-14 10:09:45 -0700752 addProperty(
753 dictPair.key(), dictPair.value().get<std::string>(),
754 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700755 }
James Feist9eb0b582018-04-27 12:15:46 -0700756 break;
757 }
James Feist1b2e2242018-01-30 13:45:19 -0800758 }
759 }
James Feistc6248a52018-08-14 10:09:45 -0700760 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
761 {
762 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
763 systemConfiguration);
764 }
James Feist8f2710a2018-05-09 17:18:55 -0700765 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800766}
767
James Feistc6248a52018-08-14 10:09:45 -0700768sdbusplus::asio::PropertyPermission getPermission(const std::string &interface)
769{
770 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
771 interface) != settableInterfaces.end()
772 ? sdbusplus::asio::PropertyPermission::readWrite
773 : sdbusplus::asio::PropertyPermission::readOnly;
774}
775
James Feist68500ff2018-08-08 15:40:42 -0700776void createAddObjectMethod(const std::string &jsonPointerPath,
777 const std::string &path,
778 nlohmann::json &systemConfiguration,
779 sdbusplus::asio::object_server &objServer)
780{
781 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
782
783 iface->register_method(
784 "AddObject",
785 [&systemConfiguration, &objServer,
786 jsonPointerPath{std::string(jsonPointerPath)},
787 path{std::string(path)}](
788 const boost::container::flat_map<std::string, JsonVariantType>
789 &data) {
790 nlohmann::json::json_pointer ptr(jsonPointerPath);
791 nlohmann::json &base = systemConfiguration[ptr];
792 auto findExposes = base.find("Exposes");
793
794 if (findExposes == base.end())
795 {
796 throw std::invalid_argument("Entity must have children.");
797 }
798
799 // this will throw invalid-argument to sdbusplus if invalid json
800 nlohmann::json newData{};
801 for (const auto &item : data)
802 {
803 nlohmann::json &newJson = newData[item.first];
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700804 variant_ns::visit(
James Feist68500ff2018-08-08 15:40:42 -0700805 [&newJson](auto &&val) { newJson = std::move(val); },
806 item.second);
807 }
808
809 auto findName = newData.find("Name");
810 auto findType = newData.find("Type");
811 if (findName == newData.end() || findType == newData.end())
812 {
813 throw std::invalid_argument("AddObject missing Name or Type");
814 }
815 const std::string *type = findType->get_ptr<const std::string *>();
816 const std::string *name = findName->get_ptr<const std::string *>();
817 if (type == nullptr || name == nullptr)
818 {
819 throw std::invalid_argument("Type and Name must be a string.");
820 }
821
822 size_t lastIndex = 0;
823 // we add in the "exposes"
824 for (; lastIndex < findExposes->size(); lastIndex++)
825 {
826 if (findExposes->at(lastIndex)["Name"] == *name &&
827 findExposes->at(lastIndex)["Type"] == *type)
828 {
829 throw std::invalid_argument(
830 "Field already in JSON, not adding");
831 }
832 lastIndex++;
833 }
834
835 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
836 *type + ".json");
837 // todo(james) we might want to also make a list of 'can add'
838 // interfaces but for now I think the assumption if there is a
839 // schema avaliable that it is allowed to update is fine
840 if (!schemaFile.good())
841 {
842 throw std::invalid_argument(
843 "No schema avaliable, cannot validate.");
844 }
845 nlohmann::json schema =
846 nlohmann::json::parse(schemaFile, nullptr, false);
847 if (schema.is_discarded())
848 {
849 std::cerr << "Schema not legal" << *type << ".json\n";
850 throw DBusInternalError();
851 }
852 if (!validateJson(schema, newData))
853 {
854 throw std::invalid_argument("Data does not match schema");
855 }
856
857 if (!writeJsonFiles(systemConfiguration))
858 {
859 std::cerr << "Error writing json files\n";
860 throw DBusInternalError();
861 }
862 std::string dbusName = *name;
863
864 std::regex_replace(dbusName.begin(), dbusName.begin(),
865 dbusName.end(), ILLEGAL_DBUS_REGEX, "_");
866 auto iface = objServer.add_interface(
867 path + "/" + dbusName,
868 "xyz.openbmc_project.Configuration." + *type);
869 // permission is read-write, as since we just created it, must be
870 // runtime modifiable
871 populateInterfaceFromJson(
872 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700873 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700874 newData, objServer,
875 sdbusplus::asio::PropertyPermission::readWrite);
876 // todo(james) generate patch
877 findExposes->push_back(newData);
878 });
879 iface->initialize();
880}
881
James Feist97a63f12018-05-17 13:50:57 -0700882void postToDbus(const nlohmann::json &newConfiguration,
883 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700884 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800885
James Feist1b2e2242018-01-30 13:45:19 -0800886{
James Feist97a63f12018-05-17 13:50:57 -0700887 // iterate through boards
888 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800889 {
890 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700891 std::vector<std::string> path;
892 std::string jsonPointerPath = "/" + boardKey;
893 // loop through newConfiguration, but use values from system
894 // configuration to be able to modify via dbus later
895 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700896 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800897 std::string boardType;
898 if (findBoardType != boardValues.end() &&
899 findBoardType->type() == nlohmann::json::value_t::string)
900 {
901 boardType = findBoardType->get<std::string>();
902 std::regex_replace(boardType.begin(), boardType.begin(),
903 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
904 }
905 else
906 {
907 std::cerr << "Unable to find type for " << boardKey
908 << " reverting to Chassis.\n";
909 boardType = "Chassis";
910 }
James Feist11be6672018-04-06 14:05:32 -0700911 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800912
913 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
914 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700915 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
916 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800917
James Feist8f2710a2018-05-09 17:18:55 -0700918 auto inventoryIface = objServer.add_interface(
919 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700920
James Feist8f2710a2018-05-09 17:18:55 -0700921 auto boardIface = objServer.add_interface(
922 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700923
James Feist68500ff2018-08-08 15:40:42 -0700924 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
925 objServer);
926
James Feist97a63f12018-05-17 13:50:57 -0700927 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700928 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700929 jsonPointerPath += "/";
930 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700931 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700932 {
933 if (boardField.value().type() == nlohmann::json::value_t::object)
934 {
James Feist8f2710a2018-05-09 17:18:55 -0700935 auto iface =
936 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700937 populateInterfaceFromJson(systemConfiguration,
938 jsonPointerPath + boardField.key(),
939 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700940 }
941 }
James Feist97a63f12018-05-17 13:50:57 -0700942
James Feist1e3e6982018-08-03 16:09:28 -0700943 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800944 if (exposes == boardValues.end())
945 {
946 continue;
947 }
James Feist97a63f12018-05-17 13:50:57 -0700948 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700949 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700950
951 // store the board level pointer so we can modify it on the way down
952 std::string jsonPointerPathBoard = jsonPointerPath;
953 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800954 for (auto &item : *exposes)
955 {
James Feist97a63f12018-05-17 13:50:57 -0700956 exposesIndex++;
957 jsonPointerPath = jsonPointerPathBoard;
958 jsonPointerPath += std::to_string(exposesIndex);
959
James Feistd63d18a2018-07-19 15:23:45 -0700960 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800961 if (findName == item.end())
962 {
963 std::cerr << "cannot find name in field " << item << "\n";
964 continue;
965 }
James Feist1e3e6982018-08-03 16:09:28 -0700966 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800967 // if status is not found it is assumed to be status = 'okay'
968 if (findStatus != item.end())
969 {
970 if (*findStatus == "disabled")
971 {
972 continue;
973 }
974 }
James Feistd63d18a2018-07-19 15:23:45 -0700975 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800976 std::string itemType;
977 if (findType != item.end())
978 {
979 itemType = findType->get<std::string>();
980 std::regex_replace(itemType.begin(), itemType.begin(),
981 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
982 }
983 else
984 {
985 itemType = "unknown";
986 }
987 std::string itemName = findName->get<std::string>();
988 std::regex_replace(itemName.begin(), itemName.begin(),
989 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -0700990
James Feist8f2710a2018-05-09 17:18:55 -0700991 auto itemIface = objServer.add_interface(
992 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800993 "xyz.openbmc_project.Configuration." + itemType);
994
James Feist97a63f12018-05-17 13:50:57 -0700995 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700996 itemIface, item, objServer,
997 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800998
James Feist9eb0b582018-04-27 12:15:46 -0700999 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001000 {
James Feist97a63f12018-05-17 13:50:57 -07001001 jsonPointerPath = jsonPointerPathBoard +
1002 std::to_string(exposesIndex) + "/" +
1003 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001004 if (objectPair.value().type() ==
1005 nlohmann::json::value_t::object)
1006 {
James Feist8f2710a2018-05-09 17:18:55 -07001007 auto objectIface = objServer.add_interface(
1008 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001009 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001010 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001011
1012 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001013 systemConfiguration, jsonPointerPath, objectIface,
1014 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001015 }
1016 else if (objectPair.value().type() ==
1017 nlohmann::json::value_t::array)
1018 {
1019 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001020 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001021 {
James Feist8f2710a2018-05-09 17:18:55 -07001022 continue;
1023 }
1024 bool isLegal = true;
1025 auto type = objectPair.value()[0].type();
1026 if (type != nlohmann::json::value_t::object)
1027 {
1028 continue;
1029 }
1030
1031 // verify legal json
1032 for (const auto &arrayItem : objectPair.value())
1033 {
1034 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001035 {
James Feist8f2710a2018-05-09 17:18:55 -07001036 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001037 break;
1038 }
James Feist8f2710a2018-05-09 17:18:55 -07001039 }
1040 if (!isLegal)
1041 {
1042 std::cerr << "dbus format error" << objectPair.value()
1043 << "\n";
1044 break;
1045 }
1046
1047 for (auto &arrayItem : objectPair.value())
1048 {
James Feist97a63f12018-05-17 13:50:57 -07001049
James Feist8f2710a2018-05-09 17:18:55 -07001050 auto objectIface = objServer.add_interface(
1051 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001052 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001053 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001054 populateInterfaceFromJson(
1055 systemConfiguration,
1056 jsonPointerPath + "/" + std::to_string(index),
1057 objectIface, arrayItem, objServer,
1058 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001059 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001060 }
1061 }
1062 }
1063 }
1064 }
1065}
1066
1067// finds the template character (currently set to $) and replaces the value with
1068// the field found in a dbus object i.e. $ADDRESS would get populated with the
1069// ADDRESS field from a object on dbus
1070void templateCharReplace(
1071 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -07001072 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -08001073 &foundDevice,
1074 size_t &foundDeviceIdx)
1075{
James Feist11be6672018-04-06 14:05:32 -07001076 if (keyPair.value().type() == nlohmann::json::value_t::object)
1077 {
1078 for (auto nextLayer = keyPair.value().begin();
1079 nextLayer != keyPair.value().end(); nextLayer++)
1080 {
1081 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1082 }
1083 return;
1084 }
1085 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -08001086 {
1087 return;
1088 }
1089
1090 std::string value = keyPair.value();
1091 if (value.find(TEMPLATE_CHAR) != std::string::npos)
1092 {
1093 std::string templateValue = value;
1094
1095 templateValue.erase(0, 1); // remove template character
1096
1097 // special case index
1098 if ("index" == templateValue)
1099 {
1100 keyPair.value() = foundDeviceIdx;
1101 }
1102 else
1103 {
James Feist13b86d62018-05-29 11:24:35 -07001104 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -08001105 for (auto &foundDevicePair : foundDevice)
1106 {
1107 if (boost::iequals(foundDevicePair.first, templateValue))
1108 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -07001109 variant_ns::visit(
James Feist13b86d62018-05-29 11:24:35 -07001110 [&](auto &&val) { keyPair.value() = val; },
1111 foundDevicePair.second);
1112 found = true;
James Feist1b2e2242018-01-30 13:45:19 -08001113 break;
1114 }
1115 }
James Feist13b86d62018-05-29 11:24:35 -07001116 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -08001117 {
1118 std::cerr << "could not find symbol " << templateValue << "\n";
1119 }
James Feist1b2e2242018-01-30 13:45:19 -08001120 }
1121 }
James Feistc6090822019-01-04 16:02:48 -08001122
1123 // convert hex numbers to ints
James Feist28dc2da2018-10-15 14:47:42 -07001124 else if (boost::starts_with(value, "0x"))
1125 {
1126 try
1127 {
James Feistc6090822019-01-04 16:02:48 -08001128 size_t pos = 0;
1129 int64_t temp = std::stoul(value, &pos, 0);
1130 if (pos == value.size())
1131 {
1132 keyPair.value() = static_cast<uint64_t>(temp);
1133 }
James Feist28dc2da2018-10-15 14:47:42 -07001134 }
1135 catch (std::invalid_argument)
1136 {
1137 }
James Feistc6090822019-01-04 16:02:48 -08001138 catch (std::out_of_range)
1139 {
1140 }
James Feist28dc2da2018-10-15 14:47:42 -07001141 }
James Feist1b2e2242018-01-30 13:45:19 -08001142}
1143
James Feist8f2710a2018-05-09 17:18:55 -07001144// reads json files out of the filesystem
1145bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001146{
1147 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001148 std::vector<std::filesystem::path> jsonPaths;
1149 if (!findFiles(std::filesystem::path(configurationDirectory),
James Feista3c180a2018-08-09 16:06:04 -07001150 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001151 {
1152 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001153 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001154 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001155 }
James Feistb4383f42018-08-06 16:54:10 -07001156
1157 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1158 globalSchema);
1159 if (!schemaStream.good())
1160 {
1161 std::cerr
1162 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1163 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001164 return false;
James Feistb4383f42018-08-06 16:54:10 -07001165 }
1166 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1167 if (schema.is_discarded())
1168 {
1169 std::cerr
1170 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1171 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001172 return false;
James Feistb4383f42018-08-06 16:54:10 -07001173 }
1174
James Feist3cb5fec2018-01-23 14:41:51 -08001175 for (auto &jsonPath : jsonPaths)
1176 {
1177 std::ifstream jsonStream(jsonPath.c_str());
1178 if (!jsonStream.good())
1179 {
1180 std::cerr << "unable to open " << jsonPath.string() << "\n";
1181 continue;
1182 }
1183 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1184 if (data.is_discarded())
1185 {
1186 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1187 continue;
1188 }
James Feist8da99192019-01-24 08:20:16 -08001189 /*
1190 * todo(james): reenable this once less things are in flight
1191 *
James Feistb4383f42018-08-06 16:54:10 -07001192 if (!validateJson(schema, data))
1193 {
1194 std::cerr << "Error validating " << jsonPath.string() << "\n";
1195 continue;
1196 }
James Feist8da99192019-01-24 08:20:16 -08001197 */
James Feistb4383f42018-08-06 16:54:10 -07001198
James Feist3cb5fec2018-01-23 14:41:51 -08001199 if (data.type() == nlohmann::json::value_t::array)
1200 {
1201 for (auto &d : data)
1202 {
1203 configurations.emplace_back(d);
1204 }
1205 }
1206 else
1207 {
1208 configurations.emplace_back(data);
1209 }
1210 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001211 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001212}
James Feist3cb5fec2018-01-23 14:41:51 -08001213
James Feist8f2710a2018-05-09 17:18:55 -07001214struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001215{
James Feist75fdeeb2018-02-20 14:26:16 -08001216
James Feist8f2710a2018-05-09 17:18:55 -07001217 PerformScan(nlohmann::json &systemConfiguration,
1218 std::list<nlohmann::json> &configurations,
1219 std::function<void(void)> &&callback) :
1220 _systemConfiguration(systemConfiguration),
1221 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001222 {
James Feist8f2710a2018-05-09 17:18:55 -07001223 }
1224 void run()
1225 {
1226 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001227 {
James Feist1e3e6982018-08-03 16:09:28 -07001228 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001229 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001230
James Feist1b2e2242018-01-30 13:45:19 -08001231 nlohmann::json probeCommand;
1232 // check for poorly formatted fields, probe must be an array
1233 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001234 {
1235 std::cerr << "configuration file missing probe:\n " << *it
1236 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001237 it = _configurations.erase(it);
1238 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001239 }
James Feist1b2e2242018-01-30 13:45:19 -08001240 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001241 {
1242 probeCommand = nlohmann::json::array();
1243 probeCommand.push_back(*findProbe);
1244 }
1245 else
1246 {
1247 probeCommand = *findProbe;
1248 }
James Feist1b2e2242018-01-30 13:45:19 -08001249
1250 if (findName == it->end())
1251 {
1252 std::cerr << "configuration file missing name:\n " << *it
1253 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001254 it = _configurations.erase(it);
1255 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001256 }
James Feist8f2710a2018-05-09 17:18:55 -07001257 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001258
James Feist8f2710a2018-05-09 17:18:55 -07001259 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1260 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001261 {
James Feist8f2710a2018-05-09 17:18:55 -07001262 it = _configurations.erase(it);
1263 continue;
1264 }
1265 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001266
James Feist8f2710a2018-05-09 17:18:55 -07001267 // store reference to this to children to makes sure we don't get
1268 // destroyed too early
1269 auto thisRef = shared_from_this();
1270 auto p = std::make_shared<PerformProbe>(
1271 probeCommand,
1272 [&, record, name,
1273 thisRef](std::vector<boost::container::flat_map<
1274 std::string, BasicVariantType>> &foundDevices) {
1275 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001276
James Feist8f2710a2018-05-09 17:18:55 -07001277 PASSED_PROBES.push_back(name);
1278 size_t foundDeviceIdx = 0;
1279
James Feistbe5425f2018-06-08 10:30:55 -07001280 // insert into configuration temporarly to be able to
1281 // reference ourselves
1282 _systemConfiguration[name] = *record;
1283
James Feist8f2710a2018-05-09 17:18:55 -07001284 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001285 {
James Feist8f2710a2018-05-09 17:18:55 -07001286 for (auto keyPair = record->begin();
1287 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001288 {
James Feist1b2e2242018-01-30 13:45:19 -08001289 templateCharReplace(keyPair, foundDevice,
1290 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001291 }
James Feist1e3e6982018-08-03 16:09:28 -07001292 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001293 if (findExpose == record->end())
1294 {
1295 continue;
1296 }
1297 for (auto &expose : *findExpose)
1298 {
1299 for (auto keyPair = expose.begin();
1300 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001301 {
James Feist1b2e2242018-01-30 13:45:19 -08001302
James Feist8f2710a2018-05-09 17:18:55 -07001303 // fill in template characters with devices
1304 // found
1305 templateCharReplace(keyPair, foundDevice,
1306 foundDeviceIdx);
1307 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001308 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001309 {
1310 if (keyPair.value().type() !=
1311 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001312 {
James Feist8f2710a2018-05-09 17:18:55 -07001313 std::cerr << "bind_ value must be of "
1314 "type string "
1315 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001316 continue;
1317 }
James Feist8f2710a2018-05-09 17:18:55 -07001318 bool foundBind = false;
1319 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001320 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001321
James Feist8f2710a2018-05-09 17:18:55 -07001322 for (auto &configurationPair :
1323 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001324 {
James Feist1b2e2242018-01-30 13:45:19 -08001325
James Feist8f2710a2018-05-09 17:18:55 -07001326 auto configListFind =
1327 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001328 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001329
1330 if (configListFind ==
1331 configurationPair.value()
1332 .end() ||
1333 configListFind->type() !=
1334 nlohmann::json::value_t::array)
1335 {
1336 continue;
1337 }
1338 for (auto &exposedObject :
1339 *configListFind)
1340 {
1341 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001342 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001343 if (boost::iequals(
1344 foundObjectName,
1345 keyPair.value()
1346 .get<std::string>()))
1347 {
James Feist1e3e6982018-08-03 16:09:28 -07001348 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001349 "okay";
1350 expose[bind] = exposedObject;
1351
1352 foundBind = true;
1353 break;
1354 }
1355 }
1356 if (foundBind)
1357 {
James Feist3cb5fec2018-01-23 14:41:51 -08001358 break;
1359 }
1360 }
James Feist8f2710a2018-05-09 17:18:55 -07001361 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001362 {
James Feist8f2710a2018-05-09 17:18:55 -07001363 std::cerr << "configuration file "
1364 "dependency error, "
1365 "could not find bind "
1366 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001367 }
1368 }
1369 }
1370 }
1371 }
James Feistbe5425f2018-06-08 10:30:55 -07001372 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001373 _systemConfiguration[name] = *record;
1374 });
1375 p->run();
1376 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001377 }
1378 }
James Feist75fdeeb2018-02-20 14:26:16 -08001379
James Feist8f2710a2018-05-09 17:18:55 -07001380 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001381 {
James Feist8f2710a2018-05-09 17:18:55 -07001382 if (_passed)
1383 {
1384 auto nextScan = std::make_shared<PerformScan>(
1385 _systemConfiguration, _configurations, std::move(_callback));
1386 nextScan->run();
1387 }
1388 else
1389 {
1390 _callback();
1391 }
1392 }
1393 nlohmann::json &_systemConfiguration;
1394 std::list<nlohmann::json> _configurations;
1395 std::function<void(void)> _callback;
1396 std::vector<std::shared_ptr<PerformProbe>> _probes;
1397 bool _passed = false;
1398};
James Feistc95cb142018-02-26 10:41:42 -08001399
James Feist8f2710a2018-05-09 17:18:55 -07001400// main properties changed entry
1401void propertiesChangedCallback(
1402 boost::asio::io_service &io,
1403 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1404 nlohmann::json &systemConfiguration,
1405 sdbusplus::asio::object_server &objServer)
1406{
1407 static boost::asio::deadline_timer timer(io);
1408 timer.expires_from_now(boost::posix_time::seconds(1));
1409
1410 // setup an async wait as we normally get flooded with new requests
1411 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001412 if (ec == boost::asio::error::operation_aborted)
1413 {
1414 // we were cancelled
1415 return;
1416 }
1417 else if (ec)
1418 {
1419 std::cerr << "async wait error " << ec << "\n";
1420 return;
1421 }
1422
1423 nlohmann::json oldConfiguration = systemConfiguration;
1424 DBUS_PROBE_OBJECTS.clear();
1425
1426 std::list<nlohmann::json> configurations;
1427 if (!findJsonFiles(configurations))
1428 {
1429 std::cerr << "cannot find json files\n";
1430 return;
1431 }
1432
1433 auto perfScan = std::make_shared<PerformScan>(
1434 systemConfiguration, configurations, [&, oldConfiguration]() {
1435 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001436 for (auto it = newConfiguration.begin();
1437 it != newConfiguration.end();)
1438 {
1439 auto findKey = oldConfiguration.find(it.key());
1440 if (findKey != oldConfiguration.end())
1441 {
1442 it = newConfiguration.erase(it);
1443 }
1444 else
1445 {
1446 it++;
1447 }
1448 }
James Feist8f2710a2018-05-09 17:18:55 -07001449 registerCallbacks(io, dbusMatches, systemConfiguration,
1450 objServer);
1451 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001452 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001453
James Feistbb43d022018-06-12 15:44:33 -07001454 io.post([&]() {
1455 if (!writeJsonFiles(systemConfiguration))
1456 {
1457 std::cerr << "Error writing json files\n";
1458 }
1459 });
James Feist8f2710a2018-05-09 17:18:55 -07001460 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001461 postToDbus(newConfiguration, systemConfiguration,
1462 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001463 });
1464 });
1465 });
1466 perfScan->run();
1467 });
James Feist75fdeeb2018-02-20 14:26:16 -08001468}
1469
James Feist8f2710a2018-05-09 17:18:55 -07001470void registerCallbacks(boost::asio::io_service &io,
1471 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1472 nlohmann::json &systemConfiguration,
1473 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001474{
1475 static boost::container::flat_set<std::string> watchedObjects;
1476
1477 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1478 {
1479 auto findObject = watchedObjects.find(objectMap.first);
1480 if (findObject != watchedObjects.end())
1481 {
1482 continue;
1483 }
James Feist8f2710a2018-05-09 17:18:55 -07001484 std::function<void(sdbusplus::message::message & message)>
1485 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001486
James Feist8f2710a2018-05-09 17:18:55 -07001487 [&](sdbusplus::message::message &) {
1488 propertiesChangedCallback(io, dbusMatches,
1489 systemConfiguration, objServer);
1490 };
1491
1492 sdbusplus::bus::match::match match(
1493 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1494 "type='signal',member='PropertiesChanged',arg0='" +
1495 objectMap.first + "'",
1496 eventHandler);
1497 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001498 }
1499}
1500
1501int main(int argc, char **argv)
1502{
1503 // setup connection to dbus
1504 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001505 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001506 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001507
James Feist8f2710a2018-05-09 17:18:55 -07001508 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001509
James Feist8f2710a2018-05-09 17:18:55 -07001510 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1511 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1512 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001513
James Feist8f2710a2018-05-09 17:18:55 -07001514 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1515 objServer.add_interface("/xyz/openbmc_project/inventory",
1516 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001517
1518 // to keep reference to the match / filter objects so they don't get
1519 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001520 std::vector<sdbusplus::bus::match::match> dbusMatches;
1521
1522 nlohmann::json systemConfiguration = nlohmann::json::object();
1523
1524 inventoryIface->register_method(
1525 "Notify", [](const boost::container::flat_map<
1526 std::string,
1527 boost::container::flat_map<std::string, BasicVariantType>>
1528 &object) { return; });
1529 inventoryIface->initialize();
1530
1531 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001532#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001533 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001534#endif
James Feist8f2710a2018-05-09 17:18:55 -07001535 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1536 objServer);
1537 });
James Feist4131aea2018-03-09 09:47:30 -08001538
James Feistfd1264a2018-05-03 12:10:00 -07001539 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001540 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1541 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001542 });
James Feist8f2710a2018-05-09 17:18:55 -07001543 entityIface->initialize();
1544
James Feist1b2e2242018-01-30 13:45:19 -08001545 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001546
1547 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001548}