blob: 298dac3bfe7438bb9ea5362f84a7af0965039753 [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 Feist8f2710a2018-05-09 17:18:55 -0700166 // get managed objects for all interfaces
167 for (const auto &conn : interfaceConnections)
168 {
169 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700170 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700171 interface](boost::system::error_code &ec,
172 const ManagedObjectType &managedInterface) {
173 if (ec)
174 {
175 std::cerr
176 << "error getting managed object for device "
177 << conn << "\n";
178 pendingProbes[interface].clear();
179 return;
180 }
181 for (auto &interfaceManagedObj : managedInterface)
182 {
183 auto ifaceObjFind =
184 interfaceManagedObj.second.find(interface);
185 if (ifaceObjFind !=
186 interfaceManagedObj.second.end())
187 {
188 std::vector<boost::container::flat_map<
189 std::string, BasicVariantType>>
190 &dbusObject = DBUS_PROBE_OBJECTS[interface];
191 dbusObject.emplace_back(ifaceObjFind->second);
192 }
193 }
194 pendingProbes[interface].clear();
195 },
196 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
197 "GetManagedObjects");
198 }
199 },
200 "xyz.openbmc_project.ObjectMapper",
201 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700202 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700203 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800204}
James Feist8f2710a2018-05-09 17:18:55 -0700205// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800206bool probeDbus(
207 const std::string &interface,
208 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700209 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800210 &devices,
211 bool &foundProbe)
212{
James Feist8f2710a2018-05-09 17:18:55 -0700213 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
214 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800215 if (dbusObject.empty())
216 {
James Feist8f2710a2018-05-09 17:18:55 -0700217 foundProbe = false;
218 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800219 }
220 foundProbe = true;
221
222 bool foundMatch = false;
223 for (auto &device : dbusObject)
224 {
225 bool deviceMatches = true;
226 for (auto &match : matches)
227 {
228 auto deviceValue = device.find(match.first);
229 if (deviceValue != device.end())
230 {
231 switch (match.second.type())
232 {
James Feist9eb0b582018-04-27 12:15:46 -0700233 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800234 {
James Feist9eb0b582018-04-27 12:15:46 -0700235 std::regex search(match.second.get<std::string>());
236 std::smatch match;
237
238 // convert value to string respresentation
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700239 std::string probeValue = variant_ns::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700240 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700241 if (!std::regex_search(probeValue, match, search))
242 {
243 deviceMatches = false;
244 break;
245 }
James Feist3cb5fec2018-01-23 14:41:51 -0800246 break;
247 }
James Feist9eb0b582018-04-27 12:15:46 -0700248 case nlohmann::json::value_t::boolean:
249 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800250 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700251 unsigned int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700252 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800253
James Feist9eb0b582018-04-27 12:15:46 -0700254 if (probeValue != match.second.get<unsigned int>())
255 {
256 deviceMatches = false;
257 }
258 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800259 }
James Feist9eb0b582018-04-27 12:15:46 -0700260 case nlohmann::json::value_t::number_integer:
261 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700262 int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700263 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800264
James Feist9eb0b582018-04-27 12:15:46 -0700265 if (probeValue != match.second.get<int>())
266 {
267 deviceMatches = false;
268 }
269 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800270 }
James Feist9eb0b582018-04-27 12:15:46 -0700271 case nlohmann::json::value_t::number_float:
272 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700273 float probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700274 VariantToFloatVisitor(), deviceValue->second);
275
276 if (probeValue != match.second.get<float>())
277 {
278 deviceMatches = false;
279 }
280 break;
281 }
James Feist3cb5fec2018-01-23 14:41:51 -0800282 }
283 }
284 else
285 {
286 deviceMatches = false;
287 break;
288 }
289 }
290 if (deviceMatches)
291 {
292 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700293 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800294 device));
295 foundMatch = true;
296 deviceMatches = false; // for next iteration
297 }
298 }
299 return foundMatch;
300}
301
302// default probe entry point, iterates a list looking for specific types to
303// call specific probe functions
304bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700305 const std::vector<std::string> &probeCommand,
306 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800307 &foundDevs)
308{
309 const static std::regex command(R"(\((.*)\))");
310 std::smatch match;
311 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700312 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800313 bool cur = true;
314 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
315
316 for (auto &probe : probeCommand)
317 {
318 bool foundProbe = false;
319 boost::container::flat_map<const char *, probe_type_codes,
320 cmp_str>::const_iterator probeType;
321
322 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
323 probeType++)
324 {
325 if (probe.find(probeType->first) != std::string::npos)
326 {
327 foundProbe = true;
328 break;
329 }
330 }
331 if (foundProbe)
332 {
333 switch (probeType->second)
334 {
James Feist9eb0b582018-04-27 12:15:46 -0700335 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800336 {
James Feist8f2710a2018-05-09 17:18:55 -0700337 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700338 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800339 }
James Feist9eb0b582018-04-27 12:15:46 -0700340 case probe_type_codes::TRUE_T:
341 {
James Feist8f2710a2018-05-09 17:18:55 -0700342 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700343 break;
344 }
345 case probe_type_codes::MATCH_ONE:
346 {
347 // set current value to last, this probe type shouldn't
348 // affect the outcome
349 cur = ret;
350 matchOne = true;
351 break;
352 }
353 /*case probe_type_codes::AND:
354 break;
355 case probe_type_codes::OR:
356 break;
357 // these are no-ops until the last command switch
358 */
359 case probe_type_codes::FOUND:
360 {
361 if (!std::regex_search(probe, match, command))
362 {
363 std::cerr << "found probe sytax error " << probe
364 << "\n";
365 return false;
366 }
367 std::string commandStr = *(match.begin() + 1);
368 boost::replace_all(commandStr, "'", "");
369 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
370 commandStr) != PASSED_PROBES.end());
371 break;
372 }
James Feist3cb5fec2018-01-23 14:41:51 -0800373 }
374 }
375 // look on dbus for object
376 else
377 {
378 if (!std::regex_search(probe, match, command))
379 {
380 std::cerr << "dbus probe sytax error " << probe << "\n";
381 return false;
382 }
383 std::string commandStr = *(match.begin() + 1);
384 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700385 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800386 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800387 auto json = nlohmann::json::parse(commandStr, nullptr, false);
388 if (json.is_discarded())
389 {
390 std::cerr << "dbus command sytax error " << commandStr << "\n";
391 return false;
392 }
393 // we can match any (string, variant) property. (string, string)
394 // does a regex
395 std::map<std::string, nlohmann::json> dbusProbeMap =
396 json.get<std::map<std::string, nlohmann::json>>();
397 auto findStart = probe.find("(");
398 if (findStart == std::string::npos)
399 {
400 return false;
401 }
402 std::string probeInterface = probe.substr(0, findStart);
403 cur =
404 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
405 }
406
407 // some functions like AND and OR only take affect after the
408 // fact
409 switch (lastCommand)
410 {
James Feist9eb0b582018-04-27 12:15:46 -0700411 case probe_type_codes::AND:
412 ret = cur && ret;
413 break;
414 case probe_type_codes::OR:
415 ret = cur || ret;
416 break;
417 default:
418 ret = cur;
419 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800420 }
421 lastCommand = probeType != PROBE_TYPES.end()
422 ? probeType->second
423 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800424 }
425
426 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800427 if (ret && foundDevs.size() == 0)
428 {
429 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700430 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800431 }
James Feist6bd2a022018-03-13 12:30:58 -0700432 if (matchOne && foundDevs.size() > 1)
433 {
434 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
435 }
James Feist3cb5fec2018-01-23 14:41:51 -0800436 return ret;
437}
James Feist8f2710a2018-05-09 17:18:55 -0700438// this class finds the needed dbus fields and on destruction runs the probe
439struct PerformProbe : std::enable_shared_from_this<PerformProbe>
440{
James Feist3cb5fec2018-01-23 14:41:51 -0800441
James Feist8f2710a2018-05-09 17:18:55 -0700442 PerformProbe(
443 const std::vector<std::string> &probeCommand,
444 std::function<void(std::vector<boost::container::flat_map<
445 std::string, BasicVariantType>> &)> &&callback) :
446 _probeCommand(probeCommand),
447 _callback(std::move(callback))
448 {
449 }
450 ~PerformProbe()
451 {
452 if (probe(_probeCommand, _foundDevs))
453 {
454 _callback(_foundDevs);
455 }
456 }
457 void run()
458 {
459 // parse out dbus probes by discarding other probe types
460 boost::container::flat_map<const char *, probe_type_codes,
461 cmp_str>::const_iterator probeType;
462
463 std::vector<std::string> dbusProbes;
464 for (std::string &probe : _probeCommand)
465 {
466 bool found = false;
467 boost::container::flat_map<const char *, probe_type_codes,
468 cmp_str>::const_iterator probeType;
469 for (probeType = PROBE_TYPES.begin();
470 probeType != PROBE_TYPES.end(); probeType++)
471 {
472 if (probe.find(probeType->first) != std::string::npos)
473 {
474 found = true;
475 break;
476 }
477 }
478 if (found)
479 {
480 continue;
481 }
482 // syntax requires probe before first open brace
483 auto findStart = probe.find("(");
484 std::string interface = probe.substr(0, findStart);
485
486 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
487 }
488 }
489 std::vector<std::string> _probeCommand;
490 std::function<void(
491 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
492 &)>
493 _callback;
494 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
495 _foundDevs;
496};
497
498// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700499bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800500{
Ed Tanous072e25d2018-12-16 21:45:20 -0800501 std::filesystem::create_directory(OUTPUT_DIR);
James Feist1b2e2242018-01-30 13:45:19 -0800502 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700503 if (!output.good())
504 {
505 return false;
506 }
James Feist1b2e2242018-01-30 13:45:19 -0800507 output << systemConfiguration.dump(4);
508 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700509 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700510}
James Feist1b2e2242018-01-30 13:45:19 -0800511
James Feist8f2710a2018-05-09 17:18:55 -0700512// template function to add array as dbus property
513template <typename PropertyType>
514void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700515 sdbusplus::asio::dbus_interface *iface,
516 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700517{
518 std::vector<PropertyType> values;
519 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800520 {
James Feist8f2710a2018-05-09 17:18:55 -0700521 auto ptr = property.get_ptr<const PropertyType *>();
522 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800523 {
James Feist8f2710a2018-05-09 17:18:55 -0700524 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800525 }
526 }
James Feistbb43d022018-06-12 15:44:33 -0700527 // todo(james), currently there are no reason to persist arrays, get around
528 // to it if needed
529
530 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800531}
James Feist97a63f12018-05-17 13:50:57 -0700532
533template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700534bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700535 nlohmann::json &systemConfiguration)
536{
537 try
538 {
539 nlohmann::json::json_pointer ptr(ptrStr);
540 nlohmann::json &ref = systemConfiguration[ptr];
541 ref = value;
542 return true;
543 }
544 catch (const std::out_of_range)
545 {
546 return false;
547 }
548}
James Feistbb43d022018-06-12 15:44:33 -0700549
550template <typename PropertyType>
551void addProperty(const std::string &propertyName, const PropertyType &value,
552 sdbusplus::asio::dbus_interface *iface,
553 nlohmann::json &systemConfiguration,
554 const std::string &jsonPointerString,
555 sdbusplus::asio::PropertyPermission permission)
556{
557 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
558 {
559 iface->register_property(propertyName, value);
560 return;
561 }
James Feist68500ff2018-08-08 15:40:42 -0700562 iface->register_property(
563 propertyName, value,
564 [&systemConfiguration,
565 jsonPointerString{std::string(jsonPointerString)}](
566 const PropertyType &newVal, PropertyType &val) {
567 val = newVal;
568 if (!setJsonFromPointer(jsonPointerString, val,
569 systemConfiguration))
570 {
571 std::cerr << "error setting json field\n";
572 return -1;
573 }
James Feistc6248a52018-08-14 10:09:45 -0700574 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700575 {
576 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700577 return -1;
578 }
579 return 1;
580 });
581}
582
583void createDeleteObjectMethod(
584 const std::string &jsonPointerPath,
585 const std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
586 sdbusplus::asio::object_server &objServer,
587 nlohmann::json &systemConfiguration)
588{
589 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
590 iface->register_method(
591 "Delete", [&objServer, &systemConfiguration, interface,
592 jsonPointerPath{std::string(jsonPointerPath)}]() {
593 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
594 interface.lock();
595 if (!iface)
596 {
597 // this technically can't happen as the pointer is pointing to
598 // us
599 throw DBusInternalError();
600 }
601 nlohmann::json::json_pointer ptr(jsonPointerPath);
602 if (!objServer.remove_interface(iface))
603 {
604 std::cerr << "Can't delete interface " << jsonPointerPath
605 << "\n";
606 throw DBusInternalError();
607 }
608 systemConfiguration[ptr] = nullptr;
609
610 if (!writeJsonFiles(systemConfiguration))
611 {
612 std::cerr << "error setting json file\n";
613 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700614 }
James Feistbb43d022018-06-12 15:44:33 -0700615 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700616 });
James Feistbb43d022018-06-12 15:44:33 -0700617}
618
James Feist1b2e2242018-01-30 13:45:19 -0800619// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700620void populateInterfaceFromJson(
621 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700622 std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
623 nlohmann::json &dict, sdbusplus::asio::object_server &objServer,
James Feistbb43d022018-06-12 15:44:33 -0700624 sdbusplus::asio::PropertyPermission permission =
625 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800626{
James Feist9eb0b582018-04-27 12:15:46 -0700627 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800628 {
James Feist8f2710a2018-05-09 17:18:55 -0700629 auto type = dictPair.value().type();
630 bool array = false;
631 if (dictPair.value().type() == nlohmann::json::value_t::array)
632 {
633 array = true;
634 if (!dictPair.value().size())
635 {
636 continue;
637 }
638 type = dictPair.value()[0].type();
639 bool isLegal = true;
640 for (const auto &arrayItem : dictPair.value())
641 {
642 if (arrayItem.type() != type)
643 {
644 isLegal = false;
645 break;
646 }
647 }
648 if (!isLegal)
649 {
650 std::cerr << "dbus format error" << dictPair.value() << "\n";
651 continue;
652 }
653 if (type == nlohmann::json::value_t::object)
654 {
655 continue; // handled elsewhere
656 }
657 }
James Feist97a63f12018-05-17 13:50:57 -0700658 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700659 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
660 {
661 // all setable numbers are doubles as it is difficult to always
662 // create a configuration file with all whole numbers as decimals
663 // i.e. 1.0
664 if (dictPair.value().is_number())
665 {
666 type = nlohmann::json::value_t::number_float;
667 }
668 }
669
James Feist8f2710a2018-05-09 17:18:55 -0700670 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800671 {
James Feist9eb0b582018-04-27 12:15:46 -0700672 case (nlohmann::json::value_t::boolean):
673 {
James Feist8f2710a2018-05-09 17:18:55 -0700674 if (array)
675 {
676 // todo: array of bool isn't detected correctly by
677 // sdbusplus, change it to numbers
678 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700679 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700680 }
James Feistbb43d022018-06-12 15:44:33 -0700681
James Feist97a63f12018-05-17 13:50:57 -0700682 else
683 {
James Feistbb43d022018-06-12 15:44:33 -0700684 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700685 iface.get(), systemConfiguration, key,
686 permission);
James Feist97a63f12018-05-17 13:50:57 -0700687 }
James Feist9eb0b582018-04-27 12:15:46 -0700688 break;
689 }
690 case (nlohmann::json::value_t::number_integer):
691 {
James Feist8f2710a2018-05-09 17:18:55 -0700692 if (array)
693 {
694 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700695 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700696 }
697 else
698 {
James Feistbb43d022018-06-12 15:44:33 -0700699 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700700 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700701 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700702 }
James Feist9eb0b582018-04-27 12:15:46 -0700703 break;
704 }
705 case (nlohmann::json::value_t::number_unsigned):
706 {
James Feist8f2710a2018-05-09 17:18:55 -0700707 if (array)
708 {
709 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700710 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700711 }
712 else
713 {
James Feistbb43d022018-06-12 15:44:33 -0700714 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700715 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700716 systemConfiguration, key,
717 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700718 }
James Feist9eb0b582018-04-27 12:15:46 -0700719 break;
720 }
721 case (nlohmann::json::value_t::number_float):
722 {
James Feist8f2710a2018-05-09 17:18:55 -0700723 if (array)
724 {
725 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700726 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700727 }
James Feistbb43d022018-06-12 15:44:33 -0700728
James Feist97a63f12018-05-17 13:50:57 -0700729 else
730 {
James Feistbb43d022018-06-12 15:44:33 -0700731 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700732 iface.get(), systemConfiguration, key,
733 permission);
James Feist97a63f12018-05-17 13:50:57 -0700734 }
James Feist9eb0b582018-04-27 12:15:46 -0700735 break;
736 }
737 case (nlohmann::json::value_t::string):
738 {
James Feist8f2710a2018-05-09 17:18:55 -0700739 if (array)
740 {
James Feistc6248a52018-08-14 10:09:45 -0700741 addArrayToDbus<std::string>(dictPair.key(),
742 dictPair.value(), iface.get(),
743 permission);
James Feist97a63f12018-05-17 13:50:57 -0700744 }
745 else
746 {
James Feistc6248a52018-08-14 10:09:45 -0700747 addProperty(
748 dictPair.key(), dictPair.value().get<std::string>(),
749 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700750 }
James Feist9eb0b582018-04-27 12:15:46 -0700751 break;
752 }
James Feist1b2e2242018-01-30 13:45:19 -0800753 }
754 }
James Feistc6248a52018-08-14 10:09:45 -0700755 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
756 {
757 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
758 systemConfiguration);
759 }
James Feist8f2710a2018-05-09 17:18:55 -0700760 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800761}
762
James Feistc6248a52018-08-14 10:09:45 -0700763sdbusplus::asio::PropertyPermission getPermission(const std::string &interface)
764{
765 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
766 interface) != settableInterfaces.end()
767 ? sdbusplus::asio::PropertyPermission::readWrite
768 : sdbusplus::asio::PropertyPermission::readOnly;
769}
770
James Feist68500ff2018-08-08 15:40:42 -0700771void createAddObjectMethod(const std::string &jsonPointerPath,
772 const std::string &path,
773 nlohmann::json &systemConfiguration,
774 sdbusplus::asio::object_server &objServer)
775{
776 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
777
778 iface->register_method(
779 "AddObject",
780 [&systemConfiguration, &objServer,
781 jsonPointerPath{std::string(jsonPointerPath)},
782 path{std::string(path)}](
783 const boost::container::flat_map<std::string, JsonVariantType>
784 &data) {
785 nlohmann::json::json_pointer ptr(jsonPointerPath);
786 nlohmann::json &base = systemConfiguration[ptr];
787 auto findExposes = base.find("Exposes");
788
789 if (findExposes == base.end())
790 {
791 throw std::invalid_argument("Entity must have children.");
792 }
793
794 // this will throw invalid-argument to sdbusplus if invalid json
795 nlohmann::json newData{};
796 for (const auto &item : data)
797 {
798 nlohmann::json &newJson = newData[item.first];
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700799 variant_ns::visit(
James Feist68500ff2018-08-08 15:40:42 -0700800 [&newJson](auto &&val) { newJson = std::move(val); },
801 item.second);
802 }
803
804 auto findName = newData.find("Name");
805 auto findType = newData.find("Type");
806 if (findName == newData.end() || findType == newData.end())
807 {
808 throw std::invalid_argument("AddObject missing Name or Type");
809 }
810 const std::string *type = findType->get_ptr<const std::string *>();
811 const std::string *name = findName->get_ptr<const std::string *>();
812 if (type == nullptr || name == nullptr)
813 {
814 throw std::invalid_argument("Type and Name must be a string.");
815 }
816
817 size_t lastIndex = 0;
818 // we add in the "exposes"
819 for (; lastIndex < findExposes->size(); lastIndex++)
820 {
821 if (findExposes->at(lastIndex)["Name"] == *name &&
822 findExposes->at(lastIndex)["Type"] == *type)
823 {
824 throw std::invalid_argument(
825 "Field already in JSON, not adding");
826 }
827 lastIndex++;
828 }
829
830 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
831 *type + ".json");
832 // todo(james) we might want to also make a list of 'can add'
833 // interfaces but for now I think the assumption if there is a
834 // schema avaliable that it is allowed to update is fine
835 if (!schemaFile.good())
836 {
837 throw std::invalid_argument(
838 "No schema avaliable, cannot validate.");
839 }
840 nlohmann::json schema =
841 nlohmann::json::parse(schemaFile, nullptr, false);
842 if (schema.is_discarded())
843 {
844 std::cerr << "Schema not legal" << *type << ".json\n";
845 throw DBusInternalError();
846 }
847 if (!validateJson(schema, newData))
848 {
849 throw std::invalid_argument("Data does not match schema");
850 }
851
852 if (!writeJsonFiles(systemConfiguration))
853 {
854 std::cerr << "Error writing json files\n";
855 throw DBusInternalError();
856 }
857 std::string dbusName = *name;
858
859 std::regex_replace(dbusName.begin(), dbusName.begin(),
860 dbusName.end(), ILLEGAL_DBUS_REGEX, "_");
861 auto iface = objServer.add_interface(
862 path + "/" + dbusName,
863 "xyz.openbmc_project.Configuration." + *type);
864 // permission is read-write, as since we just created it, must be
865 // runtime modifiable
866 populateInterfaceFromJson(
867 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700868 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700869 newData, objServer,
870 sdbusplus::asio::PropertyPermission::readWrite);
871 // todo(james) generate patch
872 findExposes->push_back(newData);
873 });
874 iface->initialize();
875}
876
James Feist97a63f12018-05-17 13:50:57 -0700877void postToDbus(const nlohmann::json &newConfiguration,
878 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700879 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800880
James Feist1b2e2242018-01-30 13:45:19 -0800881{
James Feist97a63f12018-05-17 13:50:57 -0700882 // iterate through boards
883 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800884 {
885 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700886 std::vector<std::string> path;
887 std::string jsonPointerPath = "/" + boardKey;
888 // loop through newConfiguration, but use values from system
889 // configuration to be able to modify via dbus later
890 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700891 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800892 std::string boardType;
893 if (findBoardType != boardValues.end() &&
894 findBoardType->type() == nlohmann::json::value_t::string)
895 {
896 boardType = findBoardType->get<std::string>();
897 std::regex_replace(boardType.begin(), boardType.begin(),
898 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
899 }
900 else
901 {
902 std::cerr << "Unable to find type for " << boardKey
903 << " reverting to Chassis.\n";
904 boardType = "Chassis";
905 }
James Feist11be6672018-04-06 14:05:32 -0700906 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800907
908 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
909 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700910 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
911 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800912
James Feist8f2710a2018-05-09 17:18:55 -0700913 auto inventoryIface = objServer.add_interface(
914 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700915
James Feist8f2710a2018-05-09 17:18:55 -0700916 auto boardIface = objServer.add_interface(
917 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700918
James Feist68500ff2018-08-08 15:40:42 -0700919 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
920 objServer);
921
James Feist97a63f12018-05-17 13:50:57 -0700922 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700923 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700924 jsonPointerPath += "/";
925 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700926 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700927 {
928 if (boardField.value().type() == nlohmann::json::value_t::object)
929 {
James Feist8f2710a2018-05-09 17:18:55 -0700930 auto iface =
931 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700932 populateInterfaceFromJson(systemConfiguration,
933 jsonPointerPath + boardField.key(),
934 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700935 }
936 }
James Feist97a63f12018-05-17 13:50:57 -0700937
James Feist1e3e6982018-08-03 16:09:28 -0700938 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800939 if (exposes == boardValues.end())
940 {
941 continue;
942 }
James Feist97a63f12018-05-17 13:50:57 -0700943 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700944 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700945
946 // store the board level pointer so we can modify it on the way down
947 std::string jsonPointerPathBoard = jsonPointerPath;
948 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800949 for (auto &item : *exposes)
950 {
James Feist97a63f12018-05-17 13:50:57 -0700951 exposesIndex++;
952 jsonPointerPath = jsonPointerPathBoard;
953 jsonPointerPath += std::to_string(exposesIndex);
954
James Feistd63d18a2018-07-19 15:23:45 -0700955 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800956 if (findName == item.end())
957 {
958 std::cerr << "cannot find name in field " << item << "\n";
959 continue;
960 }
James Feist1e3e6982018-08-03 16:09:28 -0700961 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800962 // if status is not found it is assumed to be status = 'okay'
963 if (findStatus != item.end())
964 {
965 if (*findStatus == "disabled")
966 {
967 continue;
968 }
969 }
James Feistd63d18a2018-07-19 15:23:45 -0700970 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800971 std::string itemType;
972 if (findType != item.end())
973 {
974 itemType = findType->get<std::string>();
975 std::regex_replace(itemType.begin(), itemType.begin(),
976 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
977 }
978 else
979 {
980 itemType = "unknown";
981 }
982 std::string itemName = findName->get<std::string>();
983 std::regex_replace(itemName.begin(), itemName.begin(),
984 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -0700985
James Feist8f2710a2018-05-09 17:18:55 -0700986 auto itemIface = objServer.add_interface(
987 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800988 "xyz.openbmc_project.Configuration." + itemType);
989
James Feist97a63f12018-05-17 13:50:57 -0700990 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700991 itemIface, item, objServer,
992 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800993
James Feist9eb0b582018-04-27 12:15:46 -0700994 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800995 {
James Feist97a63f12018-05-17 13:50:57 -0700996 jsonPointerPath = jsonPointerPathBoard +
997 std::to_string(exposesIndex) + "/" +
998 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -0800999 if (objectPair.value().type() ==
1000 nlohmann::json::value_t::object)
1001 {
James Feist8f2710a2018-05-09 17:18:55 -07001002 auto objectIface = objServer.add_interface(
1003 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001004 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001005 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001006
1007 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001008 systemConfiguration, jsonPointerPath, objectIface,
1009 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001010 }
1011 else if (objectPair.value().type() ==
1012 nlohmann::json::value_t::array)
1013 {
1014 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001015 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001016 {
James Feist8f2710a2018-05-09 17:18:55 -07001017 continue;
1018 }
1019 bool isLegal = true;
1020 auto type = objectPair.value()[0].type();
1021 if (type != nlohmann::json::value_t::object)
1022 {
1023 continue;
1024 }
1025
1026 // verify legal json
1027 for (const auto &arrayItem : objectPair.value())
1028 {
1029 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001030 {
James Feist8f2710a2018-05-09 17:18:55 -07001031 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001032 break;
1033 }
James Feist8f2710a2018-05-09 17:18:55 -07001034 }
1035 if (!isLegal)
1036 {
1037 std::cerr << "dbus format error" << objectPair.value()
1038 << "\n";
1039 break;
1040 }
1041
1042 for (auto &arrayItem : objectPair.value())
1043 {
James Feist97a63f12018-05-17 13:50:57 -07001044
James Feist8f2710a2018-05-09 17:18:55 -07001045 auto objectIface = objServer.add_interface(
1046 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001047 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001048 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001049 populateInterfaceFromJson(
1050 systemConfiguration,
1051 jsonPointerPath + "/" + std::to_string(index),
1052 objectIface, arrayItem, objServer,
1053 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001054 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001055 }
1056 }
1057 }
1058 }
1059 }
1060}
1061
1062// finds the template character (currently set to $) and replaces the value with
1063// the field found in a dbus object i.e. $ADDRESS would get populated with the
1064// ADDRESS field from a object on dbus
1065void templateCharReplace(
1066 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -07001067 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -08001068 &foundDevice,
1069 size_t &foundDeviceIdx)
1070{
James Feist11be6672018-04-06 14:05:32 -07001071 if (keyPair.value().type() == nlohmann::json::value_t::object)
1072 {
1073 for (auto nextLayer = keyPair.value().begin();
1074 nextLayer != keyPair.value().end(); nextLayer++)
1075 {
1076 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1077 }
1078 return;
1079 }
1080 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -08001081 {
1082 return;
1083 }
1084
1085 std::string value = keyPair.value();
1086 if (value.find(TEMPLATE_CHAR) != std::string::npos)
1087 {
1088 std::string templateValue = value;
1089
1090 templateValue.erase(0, 1); // remove template character
1091
1092 // special case index
1093 if ("index" == templateValue)
1094 {
1095 keyPair.value() = foundDeviceIdx;
1096 }
1097 else
1098 {
James Feist13b86d62018-05-29 11:24:35 -07001099 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -08001100 for (auto &foundDevicePair : foundDevice)
1101 {
1102 if (boost::iequals(foundDevicePair.first, templateValue))
1103 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -07001104 variant_ns::visit(
James Feist13b86d62018-05-29 11:24:35 -07001105 [&](auto &&val) { keyPair.value() = val; },
1106 foundDevicePair.second);
1107 found = true;
James Feist1b2e2242018-01-30 13:45:19 -08001108 break;
1109 }
1110 }
James Feist13b86d62018-05-29 11:24:35 -07001111 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -08001112 {
1113 std::cerr << "could not find symbol " << templateValue << "\n";
1114 }
James Feist1b2e2242018-01-30 13:45:19 -08001115 }
1116 }
James Feistc6090822019-01-04 16:02:48 -08001117
1118 // convert hex numbers to ints
James Feist28dc2da2018-10-15 14:47:42 -07001119 else if (boost::starts_with(value, "0x"))
1120 {
1121 try
1122 {
James Feistc6090822019-01-04 16:02:48 -08001123 size_t pos = 0;
1124 int64_t temp = std::stoul(value, &pos, 0);
1125 if (pos == value.size())
1126 {
1127 keyPair.value() = static_cast<uint64_t>(temp);
1128 }
James Feist28dc2da2018-10-15 14:47:42 -07001129 }
1130 catch (std::invalid_argument)
1131 {
1132 }
James Feistc6090822019-01-04 16:02:48 -08001133 catch (std::out_of_range)
1134 {
1135 }
James Feist28dc2da2018-10-15 14:47:42 -07001136 }
James Feist1b2e2242018-01-30 13:45:19 -08001137}
1138
James Feist8f2710a2018-05-09 17:18:55 -07001139// reads json files out of the filesystem
1140bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001141{
1142 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001143 std::vector<std::filesystem::path> jsonPaths;
1144 if (!findFiles(std::filesystem::path(configurationDirectory),
James Feista3c180a2018-08-09 16:06:04 -07001145 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001146 {
1147 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001148 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001149 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001150 }
James Feistb4383f42018-08-06 16:54:10 -07001151
1152 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1153 globalSchema);
1154 if (!schemaStream.good())
1155 {
1156 std::cerr
1157 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1158 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001159 return false;
James Feistb4383f42018-08-06 16:54:10 -07001160 }
1161 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1162 if (schema.is_discarded())
1163 {
1164 std::cerr
1165 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1166 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001167 return false;
James Feistb4383f42018-08-06 16:54:10 -07001168 }
1169
James Feist3cb5fec2018-01-23 14:41:51 -08001170 for (auto &jsonPath : jsonPaths)
1171 {
1172 std::ifstream jsonStream(jsonPath.c_str());
1173 if (!jsonStream.good())
1174 {
1175 std::cerr << "unable to open " << jsonPath.string() << "\n";
1176 continue;
1177 }
1178 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1179 if (data.is_discarded())
1180 {
1181 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1182 continue;
1183 }
James Feistb4383f42018-08-06 16:54:10 -07001184 if (!validateJson(schema, data))
1185 {
1186 std::cerr << "Error validating " << jsonPath.string() << "\n";
1187 continue;
1188 }
1189
James Feist3cb5fec2018-01-23 14:41:51 -08001190 if (data.type() == nlohmann::json::value_t::array)
1191 {
1192 for (auto &d : data)
1193 {
1194 configurations.emplace_back(d);
1195 }
1196 }
1197 else
1198 {
1199 configurations.emplace_back(data);
1200 }
1201 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001202 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001203}
James Feist3cb5fec2018-01-23 14:41:51 -08001204
James Feist8f2710a2018-05-09 17:18:55 -07001205struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001206{
James Feist75fdeeb2018-02-20 14:26:16 -08001207
James Feist8f2710a2018-05-09 17:18:55 -07001208 PerformScan(nlohmann::json &systemConfiguration,
1209 std::list<nlohmann::json> &configurations,
1210 std::function<void(void)> &&callback) :
1211 _systemConfiguration(systemConfiguration),
1212 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001213 {
James Feist8f2710a2018-05-09 17:18:55 -07001214 }
1215 void run()
1216 {
1217 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001218 {
James Feist1e3e6982018-08-03 16:09:28 -07001219 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001220 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001221
James Feist1b2e2242018-01-30 13:45:19 -08001222 nlohmann::json probeCommand;
1223 // check for poorly formatted fields, probe must be an array
1224 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001225 {
1226 std::cerr << "configuration file missing probe:\n " << *it
1227 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001228 it = _configurations.erase(it);
1229 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001230 }
James Feist1b2e2242018-01-30 13:45:19 -08001231 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001232 {
1233 probeCommand = nlohmann::json::array();
1234 probeCommand.push_back(*findProbe);
1235 }
1236 else
1237 {
1238 probeCommand = *findProbe;
1239 }
James Feist1b2e2242018-01-30 13:45:19 -08001240
1241 if (findName == it->end())
1242 {
1243 std::cerr << "configuration file missing name:\n " << *it
1244 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001245 it = _configurations.erase(it);
1246 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001247 }
James Feist8f2710a2018-05-09 17:18:55 -07001248 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001249
James Feist8f2710a2018-05-09 17:18:55 -07001250 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1251 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001252 {
James Feist8f2710a2018-05-09 17:18:55 -07001253 it = _configurations.erase(it);
1254 continue;
1255 }
1256 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001257
James Feist8f2710a2018-05-09 17:18:55 -07001258 // store reference to this to children to makes sure we don't get
1259 // destroyed too early
1260 auto thisRef = shared_from_this();
1261 auto p = std::make_shared<PerformProbe>(
1262 probeCommand,
1263 [&, record, name,
1264 thisRef](std::vector<boost::container::flat_map<
1265 std::string, BasicVariantType>> &foundDevices) {
1266 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001267
James Feist8f2710a2018-05-09 17:18:55 -07001268 PASSED_PROBES.push_back(name);
1269 size_t foundDeviceIdx = 0;
1270
James Feistbe5425f2018-06-08 10:30:55 -07001271 // insert into configuration temporarly to be able to
1272 // reference ourselves
1273 _systemConfiguration[name] = *record;
1274
James Feist8f2710a2018-05-09 17:18:55 -07001275 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001276 {
James Feist8f2710a2018-05-09 17:18:55 -07001277 for (auto keyPair = record->begin();
1278 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001279 {
James Feist1b2e2242018-01-30 13:45:19 -08001280 templateCharReplace(keyPair, foundDevice,
1281 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001282 }
James Feist1e3e6982018-08-03 16:09:28 -07001283 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001284 if (findExpose == record->end())
1285 {
1286 continue;
1287 }
1288 for (auto &expose : *findExpose)
1289 {
1290 for (auto keyPair = expose.begin();
1291 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001292 {
James Feist1b2e2242018-01-30 13:45:19 -08001293
James Feist8f2710a2018-05-09 17:18:55 -07001294 // fill in template characters with devices
1295 // found
1296 templateCharReplace(keyPair, foundDevice,
1297 foundDeviceIdx);
1298 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001299 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001300 {
1301 if (keyPair.value().type() !=
1302 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001303 {
James Feist8f2710a2018-05-09 17:18:55 -07001304 std::cerr << "bind_ value must be of "
1305 "type string "
1306 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001307 continue;
1308 }
James Feist8f2710a2018-05-09 17:18:55 -07001309 bool foundBind = false;
1310 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001311 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001312
James Feist8f2710a2018-05-09 17:18:55 -07001313 for (auto &configurationPair :
1314 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001315 {
James Feist1b2e2242018-01-30 13:45:19 -08001316
James Feist8f2710a2018-05-09 17:18:55 -07001317 auto configListFind =
1318 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001319 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001320
1321 if (configListFind ==
1322 configurationPair.value()
1323 .end() ||
1324 configListFind->type() !=
1325 nlohmann::json::value_t::array)
1326 {
1327 continue;
1328 }
1329 for (auto &exposedObject :
1330 *configListFind)
1331 {
1332 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001333 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001334 if (boost::iequals(
1335 foundObjectName,
1336 keyPair.value()
1337 .get<std::string>()))
1338 {
James Feist1e3e6982018-08-03 16:09:28 -07001339 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001340 "okay";
1341 expose[bind] = exposedObject;
1342
1343 foundBind = true;
1344 break;
1345 }
1346 }
1347 if (foundBind)
1348 {
James Feist3cb5fec2018-01-23 14:41:51 -08001349 break;
1350 }
1351 }
James Feist8f2710a2018-05-09 17:18:55 -07001352 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001353 {
James Feist8f2710a2018-05-09 17:18:55 -07001354 std::cerr << "configuration file "
1355 "dependency error, "
1356 "could not find bind "
1357 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001358 }
1359 }
1360 }
1361 }
1362 }
James Feistbe5425f2018-06-08 10:30:55 -07001363 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001364 _systemConfiguration[name] = *record;
1365 });
1366 p->run();
1367 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001368 }
1369 }
James Feist75fdeeb2018-02-20 14:26:16 -08001370
James Feist8f2710a2018-05-09 17:18:55 -07001371 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001372 {
James Feist8f2710a2018-05-09 17:18:55 -07001373 if (_passed)
1374 {
1375 auto nextScan = std::make_shared<PerformScan>(
1376 _systemConfiguration, _configurations, std::move(_callback));
1377 nextScan->run();
1378 }
1379 else
1380 {
1381 _callback();
1382 }
1383 }
1384 nlohmann::json &_systemConfiguration;
1385 std::list<nlohmann::json> _configurations;
1386 std::function<void(void)> _callback;
1387 std::vector<std::shared_ptr<PerformProbe>> _probes;
1388 bool _passed = false;
1389};
James Feistc95cb142018-02-26 10:41:42 -08001390
James Feist8f2710a2018-05-09 17:18:55 -07001391// main properties changed entry
1392void propertiesChangedCallback(
1393 boost::asio::io_service &io,
1394 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1395 nlohmann::json &systemConfiguration,
1396 sdbusplus::asio::object_server &objServer)
1397{
1398 static boost::asio::deadline_timer timer(io);
1399 timer.expires_from_now(boost::posix_time::seconds(1));
1400
1401 // setup an async wait as we normally get flooded with new requests
1402 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001403 if (ec == boost::asio::error::operation_aborted)
1404 {
1405 // we were cancelled
1406 return;
1407 }
1408 else if (ec)
1409 {
1410 std::cerr << "async wait error " << ec << "\n";
1411 return;
1412 }
1413
1414 nlohmann::json oldConfiguration = systemConfiguration;
1415 DBUS_PROBE_OBJECTS.clear();
1416
1417 std::list<nlohmann::json> configurations;
1418 if (!findJsonFiles(configurations))
1419 {
1420 std::cerr << "cannot find json files\n";
1421 return;
1422 }
1423
1424 auto perfScan = std::make_shared<PerformScan>(
1425 systemConfiguration, configurations, [&, oldConfiguration]() {
1426 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001427 for (auto it = newConfiguration.begin();
1428 it != newConfiguration.end();)
1429 {
1430 auto findKey = oldConfiguration.find(it.key());
1431 if (findKey != oldConfiguration.end())
1432 {
1433 it = newConfiguration.erase(it);
1434 }
1435 else
1436 {
1437 it++;
1438 }
1439 }
James Feist8f2710a2018-05-09 17:18:55 -07001440 registerCallbacks(io, dbusMatches, systemConfiguration,
1441 objServer);
1442 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001443 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001444
James Feistbb43d022018-06-12 15:44:33 -07001445 io.post([&]() {
1446 if (!writeJsonFiles(systemConfiguration))
1447 {
1448 std::cerr << "Error writing json files\n";
1449 }
1450 });
James Feist8f2710a2018-05-09 17:18:55 -07001451 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001452 postToDbus(newConfiguration, systemConfiguration,
1453 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001454 });
1455 });
1456 });
1457 perfScan->run();
1458 });
James Feist75fdeeb2018-02-20 14:26:16 -08001459}
1460
James Feist8f2710a2018-05-09 17:18:55 -07001461void registerCallbacks(boost::asio::io_service &io,
1462 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1463 nlohmann::json &systemConfiguration,
1464 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001465{
1466 static boost::container::flat_set<std::string> watchedObjects;
1467
1468 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1469 {
1470 auto findObject = watchedObjects.find(objectMap.first);
1471 if (findObject != watchedObjects.end())
1472 {
1473 continue;
1474 }
James Feist8f2710a2018-05-09 17:18:55 -07001475 std::function<void(sdbusplus::message::message & message)>
1476 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001477
James Feist8f2710a2018-05-09 17:18:55 -07001478 [&](sdbusplus::message::message &) {
1479 propertiesChangedCallback(io, dbusMatches,
1480 systemConfiguration, objServer);
1481 };
1482
1483 sdbusplus::bus::match::match match(
1484 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1485 "type='signal',member='PropertiesChanged',arg0='" +
1486 objectMap.first + "'",
1487 eventHandler);
1488 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001489 }
1490}
1491
1492int main(int argc, char **argv)
1493{
1494 // setup connection to dbus
1495 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001496 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001497 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001498
James Feist8f2710a2018-05-09 17:18:55 -07001499 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001500
James Feist8f2710a2018-05-09 17:18:55 -07001501 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1502 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1503 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001504
James Feist8f2710a2018-05-09 17:18:55 -07001505 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1506 objServer.add_interface("/xyz/openbmc_project/inventory",
1507 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001508
1509 // to keep reference to the match / filter objects so they don't get
1510 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001511 std::vector<sdbusplus::bus::match::match> dbusMatches;
1512
1513 nlohmann::json systemConfiguration = nlohmann::json::object();
1514
1515 inventoryIface->register_method(
1516 "Notify", [](const boost::container::flat_map<
1517 std::string,
1518 boost::container::flat_map<std::string, BasicVariantType>>
1519 &object) { return; });
1520 inventoryIface->initialize();
1521
1522 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001523#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001524 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001525#endif
James Feist8f2710a2018-05-09 17:18:55 -07001526 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1527 objServer);
1528 });
James Feist4131aea2018-03-09 09:47:30 -08001529
James Feistfd1264a2018-05-03 12:10:00 -07001530 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001531 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1532 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001533 });
James Feist8f2710a2018-05-09 17:18:55 -07001534 entityIface->initialize();
1535
James Feist1b2e2242018-01-30 13:45:19 -08001536 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001537
1538 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001539}