blob: 35fadb939f582dc53ed32ed57b51ec3deaee809b [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
James Feista465ccc2019-02-08 12:51:01 -080017#include "filesystem.hpp"
18
James Feistc95cb142018-02-26 10:41:42 -080019#include <Overlay.hpp>
James Feista465ccc2019-02-08 12:51:01 -080020#include <Utils.hpp>
21#include <VariantVisitors.hpp>
James Feist11be6672018-04-06 14:05:32 -070022#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080023#include <boost/algorithm/string/predicate.hpp>
24#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/container/flat_map.hpp>
26#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080027#include <boost/lexical_cast.hpp>
28#include <fstream>
29#include <iostream>
30#include <nlohmann/json.hpp>
31#include <regex>
32#include <sdbusplus/asio/connection.hpp>
33#include <sdbusplus/asio/object_server.hpp>
34#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080035
James Feista465ccc2019-02-08 12:51:01 -080036constexpr const char* OUTPUT_DIR = "/var/configuration/";
37constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
38constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
39constexpr const char* globalSchema = "global.json";
40constexpr const char* TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080041constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070042constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080043constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080044
James Feist3cb5fec2018-01-23 14:41:51 -080045struct cmp_str
46{
James Feista465ccc2019-02-08 12:51:01 -080047 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080048 {
49 return std::strcmp(a, b) < 0;
50 }
51};
52
James Feist8f2710a2018-05-09 17:18:55 -070053struct PerformProbe;
54
James Feist3cb5fec2018-01-23 14:41:51 -080055// underscore T for collison with dbus c api
56enum class probe_type_codes
57{
58 FALSE_T,
59 TRUE_T,
60 AND,
61 OR,
James Feist6bd2a022018-03-13 12:30:58 -070062 FOUND,
63 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080064};
James Feista465ccc2019-02-08 12:51:01 -080065const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080066 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
67 {"TRUE", probe_type_codes::TRUE_T},
68 {"AND", probe_type_codes::AND},
69 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070070 {"FOUND", probe_type_codes::FOUND},
71 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080072
James Feist41334262019-03-25 13:30:20 -070073static constexpr std::array<const char*, 5> settableInterfaces = {
74 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070075using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080076 std::variant<std::vector<std::string>, std::vector<double>, std::string,
77 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
78 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070079using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080080 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
81 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070082
James Feist3cb5fec2018-01-23 14:41:51 -080083using GetSubTreeType = std::vector<
84 std::pair<std::string,
85 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
86
87using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070088 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080089 boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080092
93boost::container::flat_map<
94 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070095 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080096 DBUS_PROBE_OBJECTS;
97std::vector<std::string> PASSED_PROBES;
98
99// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700100std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -0800101
Johnathan Mantey2015f752019-03-26 15:22:31 -0700102const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
103const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800104
James Feista465ccc2019-02-08 12:51:01 -0800105void registerCallbacks(boost::asio::io_service& io,
106 std::vector<sdbusplus::bus::match::match>& dbusMatches,
107 nlohmann::json& systemConfiguration,
108 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800109
James Feist3cb5fec2018-01-23 14:41:51 -0800110// calls the mapper to find all exposed objects of an interface type
111// and creates a vector<flat_map> that contains all the key value pairs
112// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700113void findDbusObjects(std::shared_ptr<PerformProbe> probe,
114 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800115 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800116{
James Feist8f2710a2018-05-09 17:18:55 -0700117
118 // store reference to pending callbacks so we don't overwhelm services
119 static boost::container::flat_map<
120 std::string, std::vector<std::shared_ptr<PerformProbe>>>
121 pendingProbes;
122
123 if (DBUS_PROBE_OBJECTS[interface].size())
124 {
125 return;
126 }
127
128 // add shared_ptr to vector of Probes waiting for callback from a specific
129 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800130 std::array<const char*, 1> objects = {interface.c_str()};
131 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700132 pendingProbes[interface];
133 auto iter = pending.emplace(pending.end(), probe);
134 // only allow first call to run to not overwhelm processes
135 if (iter != pending.begin())
136 {
137 return;
138 }
139
James Feist3cb5fec2018-01-23 14:41:51 -0800140 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700141 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800142 [connection, interface, probe](boost::system::error_code& ec,
143 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700144 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700145 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700146 {
James Feist0de40152018-07-25 11:56:12 -0700147 pendingProbes[interface].clear();
148 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700149 {
James Feist0de40152018-07-25 11:56:12 -0700150 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700151 }
James Feist0de40152018-07-25 11:56:12 -0700152 std::cerr << "Error communicating to mapper.\n";
153
154 // if we can't communicate to the mapper something is very wrong
155 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700156 }
James Feist8f2710a2018-05-09 17:18:55 -0700157 else
James Feist3cb5fec2018-01-23 14:41:51 -0800158 {
James Feista465ccc2019-02-08 12:51:01 -0800159 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700160 {
James Feista465ccc2019-02-08 12:51:01 -0800161 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700162 {
163 auto insertPair =
164 interfaceConnections.insert(connPair.first);
165 }
166 }
James Feist3cb5fec2018-01-23 14:41:51 -0800167 }
James Feist63845bf2019-01-24 12:19:51 -0800168 if (interfaceConnections.empty())
169 {
170 pendingProbes[interface].clear();
171 return;
172 }
James Feist8f2710a2018-05-09 17:18:55 -0700173 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800174 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700175 {
176 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700177 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800178 interface](boost::system::error_code& ec,
179 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700180 if (ec)
181 {
182 std::cerr
183 << "error getting managed object for device "
184 << conn << "\n";
185 pendingProbes[interface].clear();
186 return;
187 }
James Feista465ccc2019-02-08 12:51:01 -0800188 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700189 {
190 auto ifaceObjFind =
191 interfaceManagedObj.second.find(interface);
192 if (ifaceObjFind !=
193 interfaceManagedObj.second.end())
194 {
195 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800196 std::string, BasicVariantType>>&
197 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700198 dbusObject.emplace_back(ifaceObjFind->second);
199 }
200 }
201 pendingProbes[interface].clear();
202 },
203 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
204 "GetManagedObjects");
205 }
206 },
207 "xyz.openbmc_project.ObjectMapper",
208 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700209 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700210 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800211}
James Feist8f2710a2018-05-09 17:18:55 -0700212// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800213bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800214 const std::string& interface,
215 const std::map<std::string, nlohmann::json>& matches,
216 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
217 devices,
218 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800219{
James Feista465ccc2019-02-08 12:51:01 -0800220 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
221 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800222 if (dbusObject.empty())
223 {
James Feist8f2710a2018-05-09 17:18:55 -0700224 foundProbe = false;
225 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800226 }
227 foundProbe = true;
228
229 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800230 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800231 {
232 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800233 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800234 {
235 auto deviceValue = device.find(match.first);
236 if (deviceValue != device.end())
237 {
238 switch (match.second.type())
239 {
James Feist9eb0b582018-04-27 12:15:46 -0700240 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800241 {
James Feist9eb0b582018-04-27 12:15:46 -0700242 std::regex search(match.second.get<std::string>());
243 std::smatch match;
244
245 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800246 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700247 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700248 if (!std::regex_search(probeValue, match, search))
249 {
250 deviceMatches = false;
251 break;
252 }
James Feist3cb5fec2018-01-23 14:41:51 -0800253 break;
254 }
James Feist9eb0b582018-04-27 12:15:46 -0700255 case nlohmann::json::value_t::boolean:
256 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800257 {
James Feista465ccc2019-02-08 12:51:01 -0800258 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700259 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800260
James Feist9eb0b582018-04-27 12:15:46 -0700261 if (probeValue != match.second.get<unsigned int>())
262 {
263 deviceMatches = false;
264 }
265 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800266 }
James Feist9eb0b582018-04-27 12:15:46 -0700267 case nlohmann::json::value_t::number_integer:
268 {
James Feista465ccc2019-02-08 12:51:01 -0800269 int probeValue = std::visit(VariantToIntVisitor(),
270 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800271
James Feist9eb0b582018-04-27 12:15:46 -0700272 if (probeValue != match.second.get<int>())
273 {
274 deviceMatches = false;
275 }
276 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800277 }
James Feist9eb0b582018-04-27 12:15:46 -0700278 case nlohmann::json::value_t::number_float:
279 {
James Feista465ccc2019-02-08 12:51:01 -0800280 float probeValue = std::visit(VariantToFloatVisitor(),
281 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700282
283 if (probeValue != match.second.get<float>())
284 {
285 deviceMatches = false;
286 }
287 break;
288 }
James Feist3cb5fec2018-01-23 14:41:51 -0800289 }
290 }
291 else
292 {
293 deviceMatches = false;
294 break;
295 }
296 }
297 if (deviceMatches)
298 {
299 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700300 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800301 device));
302 foundMatch = true;
303 deviceMatches = false; // for next iteration
304 }
305 }
306 return foundMatch;
307}
308
309// default probe entry point, iterates a list looking for specific types to
310// call specific probe functions
311bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800312 const std::vector<std::string>& probeCommand,
313 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
314 foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800315{
316 const static std::regex command(R"(\((.*)\))");
317 std::smatch match;
318 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700319 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800320 bool cur = true;
321 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
322
James Feista465ccc2019-02-08 12:51:01 -0800323 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800324 {
325 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800326 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800327 cmp_str>::const_iterator probeType;
328
329 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
330 probeType++)
331 {
332 if (probe.find(probeType->first) != std::string::npos)
333 {
334 foundProbe = true;
335 break;
336 }
337 }
338 if (foundProbe)
339 {
340 switch (probeType->second)
341 {
James Feist9eb0b582018-04-27 12:15:46 -0700342 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800343 {
James Feist8f2710a2018-05-09 17:18:55 -0700344 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700345 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800346 }
James Feist9eb0b582018-04-27 12:15:46 -0700347 case probe_type_codes::TRUE_T:
348 {
James Feist8f2710a2018-05-09 17:18:55 -0700349 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700350 break;
351 }
352 case probe_type_codes::MATCH_ONE:
353 {
354 // set current value to last, this probe type shouldn't
355 // affect the outcome
356 cur = ret;
357 matchOne = true;
358 break;
359 }
360 /*case probe_type_codes::AND:
361 break;
362 case probe_type_codes::OR:
363 break;
364 // these are no-ops until the last command switch
365 */
366 case probe_type_codes::FOUND:
367 {
368 if (!std::regex_search(probe, match, command))
369 {
370 std::cerr << "found probe sytax error " << probe
371 << "\n";
372 return false;
373 }
374 std::string commandStr = *(match.begin() + 1);
375 boost::replace_all(commandStr, "'", "");
376 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
377 commandStr) != PASSED_PROBES.end());
378 break;
379 }
James Feist3cb5fec2018-01-23 14:41:51 -0800380 }
381 }
382 // look on dbus for object
383 else
384 {
385 if (!std::regex_search(probe, match, command))
386 {
387 std::cerr << "dbus probe sytax error " << probe << "\n";
388 return false;
389 }
390 std::string commandStr = *(match.begin() + 1);
391 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700392 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800393 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800394 auto json = nlohmann::json::parse(commandStr, nullptr, false);
395 if (json.is_discarded())
396 {
397 std::cerr << "dbus command sytax error " << commandStr << "\n";
398 return false;
399 }
400 // we can match any (string, variant) property. (string, string)
401 // does a regex
402 std::map<std::string, nlohmann::json> dbusProbeMap =
403 json.get<std::map<std::string, nlohmann::json>>();
404 auto findStart = probe.find("(");
405 if (findStart == std::string::npos)
406 {
407 return false;
408 }
409 std::string probeInterface = probe.substr(0, findStart);
410 cur =
411 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
412 }
413
414 // some functions like AND and OR only take affect after the
415 // fact
416 switch (lastCommand)
417 {
James Feist9eb0b582018-04-27 12:15:46 -0700418 case probe_type_codes::AND:
419 ret = cur && ret;
420 break;
421 case probe_type_codes::OR:
422 ret = cur || ret;
423 break;
424 default:
425 ret = cur;
426 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800427 }
428 lastCommand = probeType != PROBE_TYPES.end()
429 ? probeType->second
430 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800431 }
432
433 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800434 if (ret && foundDevs.size() == 0)
435 {
436 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700437 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800438 }
James Feist6bd2a022018-03-13 12:30:58 -0700439 if (matchOne && foundDevs.size() > 1)
440 {
441 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
442 }
James Feist3cb5fec2018-01-23 14:41:51 -0800443 return ret;
444}
James Feist8f2710a2018-05-09 17:18:55 -0700445// this class finds the needed dbus fields and on destruction runs the probe
446struct PerformProbe : std::enable_shared_from_this<PerformProbe>
447{
James Feist3cb5fec2018-01-23 14:41:51 -0800448
James Feist8f2710a2018-05-09 17:18:55 -0700449 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800450 const std::vector<std::string>& probeCommand,
James Feist8f2710a2018-05-09 17:18:55 -0700451 std::function<void(std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800452 std::string, BasicVariantType>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700453 _probeCommand(probeCommand),
454 _callback(std::move(callback))
455 {
456 }
457 ~PerformProbe()
458 {
459 if (probe(_probeCommand, _foundDevs))
460 {
461 _callback(_foundDevs);
462 }
463 }
464 void run()
465 {
466 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800467 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700468 cmp_str>::const_iterator probeType;
469
470 std::vector<std::string> dbusProbes;
James Feista465ccc2019-02-08 12:51:01 -0800471 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700472 {
473 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800474 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700475 cmp_str>::const_iterator probeType;
476 for (probeType = PROBE_TYPES.begin();
477 probeType != PROBE_TYPES.end(); probeType++)
478 {
479 if (probe.find(probeType->first) != std::string::npos)
480 {
481 found = true;
482 break;
483 }
484 }
485 if (found)
486 {
487 continue;
488 }
489 // syntax requires probe before first open brace
490 auto findStart = probe.find("(");
491 std::string interface = probe.substr(0, findStart);
492
493 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
494 }
495 }
496 std::vector<std::string> _probeCommand;
James Feista465ccc2019-02-08 12:51:01 -0800497 std::function<void(std::vector<boost::container::flat_map<
498 std::string, BasicVariantType>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700499 _callback;
500 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
501 _foundDevs;
502};
503
504// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800505bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800506{
Ed Tanous072e25d2018-12-16 21:45:20 -0800507 std::filesystem::create_directory(OUTPUT_DIR);
James Feist1b2e2242018-01-30 13:45:19 -0800508 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700509 if (!output.good())
510 {
511 return false;
512 }
James Feist1b2e2242018-01-30 13:45:19 -0800513 output << systemConfiguration.dump(4);
514 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700515 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700516}
James Feist1b2e2242018-01-30 13:45:19 -0800517
James Feist97a63f12018-05-17 13:50:57 -0700518template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800519bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
520 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700521{
522 try
523 {
524 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800525 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700526 ref = value;
527 return true;
528 }
529 catch (const std::out_of_range)
530 {
531 return false;
532 }
533}
James Feistbb43d022018-06-12 15:44:33 -0700534
James Feistebcc26b2019-03-22 12:30:43 -0700535// template function to add array as dbus property
536template <typename PropertyType>
537void addArrayToDbus(const std::string& name, const nlohmann::json& array,
538 sdbusplus::asio::dbus_interface* iface,
539 sdbusplus::asio::PropertyPermission permission,
540 nlohmann::json& systemConfiguration,
541 const std::string& jsonPointerString)
542{
543 std::vector<PropertyType> values;
544 for (const auto& property : array)
545 {
546 auto ptr = property.get_ptr<const PropertyType*>();
547 if (ptr != nullptr)
548 {
549 values.emplace_back(*ptr);
550 }
551 }
552
553 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
554 {
555 iface->register_property(name, values);
556 }
557 else
558 {
559 iface->register_property(
560 name, values,
561 [&systemConfiguration,
562 jsonPointerString{std::string(jsonPointerString)}](
563 const std::vector<PropertyType>& newVal,
564 std::vector<PropertyType>& val) {
565 val = newVal;
566 if (!setJsonFromPointer(jsonPointerString, val,
567 systemConfiguration))
568 {
569 std::cerr << "error setting json field\n";
570 return -1;
571 }
572 if (!writeJsonFiles(systemConfiguration))
573 {
574 std::cerr << "error setting json file\n";
575 return -1;
576 }
577 return 1;
578 });
579 }
580}
581
James Feistbb43d022018-06-12 15:44:33 -0700582template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800583void addProperty(const std::string& propertyName, const PropertyType& value,
584 sdbusplus::asio::dbus_interface* iface,
585 nlohmann::json& systemConfiguration,
586 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700587 sdbusplus::asio::PropertyPermission permission)
588{
589 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
590 {
591 iface->register_property(propertyName, value);
592 return;
593 }
James Feist68500ff2018-08-08 15:40:42 -0700594 iface->register_property(
595 propertyName, value,
596 [&systemConfiguration,
597 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800598 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700599 val = newVal;
600 if (!setJsonFromPointer(jsonPointerString, val,
601 systemConfiguration))
602 {
603 std::cerr << "error setting json field\n";
604 return -1;
605 }
James Feistc6248a52018-08-14 10:09:45 -0700606 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700607 {
608 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700609 return -1;
610 }
611 return 1;
612 });
613}
614
615void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800616 const std::string& jsonPointerPath,
617 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
618 sdbusplus::asio::object_server& objServer,
619 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700620{
621 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
622 iface->register_method(
623 "Delete", [&objServer, &systemConfiguration, interface,
624 jsonPointerPath{std::string(jsonPointerPath)}]() {
625 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
626 interface.lock();
627 if (!iface)
628 {
629 // this technically can't happen as the pointer is pointing to
630 // us
631 throw DBusInternalError();
632 }
633 nlohmann::json::json_pointer ptr(jsonPointerPath);
634 if (!objServer.remove_interface(iface))
635 {
636 std::cerr << "Can't delete interface " << jsonPointerPath
637 << "\n";
638 throw DBusInternalError();
639 }
640 systemConfiguration[ptr] = nullptr;
641
642 if (!writeJsonFiles(systemConfiguration))
643 {
644 std::cerr << "error setting json file\n";
645 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700646 }
James Feistbb43d022018-06-12 15:44:33 -0700647 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700648 });
James Feistbb43d022018-06-12 15:44:33 -0700649}
650
James Feist1b2e2242018-01-30 13:45:19 -0800651// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700652void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800653 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
654 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
655 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700656 sdbusplus::asio::PropertyPermission permission =
657 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800658{
James Feista465ccc2019-02-08 12:51:01 -0800659 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800660 {
James Feist8f2710a2018-05-09 17:18:55 -0700661 auto type = dictPair.value().type();
662 bool array = false;
663 if (dictPair.value().type() == nlohmann::json::value_t::array)
664 {
665 array = true;
666 if (!dictPair.value().size())
667 {
668 continue;
669 }
670 type = dictPair.value()[0].type();
671 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800672 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700673 {
674 if (arrayItem.type() != type)
675 {
676 isLegal = false;
677 break;
678 }
679 }
680 if (!isLegal)
681 {
682 std::cerr << "dbus format error" << dictPair.value() << "\n";
683 continue;
684 }
685 if (type == nlohmann::json::value_t::object)
686 {
687 continue; // handled elsewhere
688 }
689 }
James Feist97a63f12018-05-17 13:50:57 -0700690 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700691 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
692 {
693 // all setable numbers are doubles as it is difficult to always
694 // create a configuration file with all whole numbers as decimals
695 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700696 if (array)
697 {
698 if (dictPair.value()[0].is_number())
699 {
700 type = nlohmann::json::value_t::number_float;
701 }
702 }
703 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700704 {
705 type = nlohmann::json::value_t::number_float;
706 }
707 }
708
James Feist8f2710a2018-05-09 17:18:55 -0700709 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800710 {
James Feist9eb0b582018-04-27 12:15:46 -0700711 case (nlohmann::json::value_t::boolean):
712 {
James Feist8f2710a2018-05-09 17:18:55 -0700713 if (array)
714 {
715 // todo: array of bool isn't detected correctly by
716 // sdbusplus, change it to numbers
717 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700718 iface.get(), permission,
719 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700720 }
James Feistbb43d022018-06-12 15:44:33 -0700721
James Feist97a63f12018-05-17 13:50:57 -0700722 else
723 {
James Feistbb43d022018-06-12 15:44:33 -0700724 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700725 iface.get(), systemConfiguration, key,
726 permission);
James Feist97a63f12018-05-17 13:50:57 -0700727 }
James Feist9eb0b582018-04-27 12:15:46 -0700728 break;
729 }
730 case (nlohmann::json::value_t::number_integer):
731 {
James Feist8f2710a2018-05-09 17:18:55 -0700732 if (array)
733 {
734 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700735 iface.get(), permission,
736 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700737 }
738 else
739 {
James Feistbb43d022018-06-12 15:44:33 -0700740 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700741 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700742 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700743 }
James Feist9eb0b582018-04-27 12:15:46 -0700744 break;
745 }
746 case (nlohmann::json::value_t::number_unsigned):
747 {
James Feist8f2710a2018-05-09 17:18:55 -0700748 if (array)
749 {
750 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700751 iface.get(), permission,
752 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700753 }
754 else
755 {
James Feistbb43d022018-06-12 15:44:33 -0700756 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700757 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700758 systemConfiguration, key,
759 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700760 }
James Feist9eb0b582018-04-27 12:15:46 -0700761 break;
762 }
763 case (nlohmann::json::value_t::number_float):
764 {
James Feist8f2710a2018-05-09 17:18:55 -0700765 if (array)
766 {
767 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700768 iface.get(), permission,
769 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700770 }
James Feistbb43d022018-06-12 15:44:33 -0700771
James Feist97a63f12018-05-17 13:50:57 -0700772 else
773 {
James Feistbb43d022018-06-12 15:44:33 -0700774 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700775 iface.get(), systemConfiguration, key,
776 permission);
James Feist97a63f12018-05-17 13:50:57 -0700777 }
James Feist9eb0b582018-04-27 12:15:46 -0700778 break;
779 }
780 case (nlohmann::json::value_t::string):
781 {
James Feist8f2710a2018-05-09 17:18:55 -0700782 if (array)
783 {
James Feistebcc26b2019-03-22 12:30:43 -0700784 addArrayToDbus<std::string>(
785 dictPair.key(), dictPair.value(), iface.get(),
786 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700787 }
788 else
789 {
James Feistc6248a52018-08-14 10:09:45 -0700790 addProperty(
791 dictPair.key(), dictPair.value().get<std::string>(),
792 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700793 }
James Feist9eb0b582018-04-27 12:15:46 -0700794 break;
795 }
James Feist1b2e2242018-01-30 13:45:19 -0800796 }
797 }
James Feistc6248a52018-08-14 10:09:45 -0700798 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
799 {
800 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
801 systemConfiguration);
802 }
James Feist8f2710a2018-05-09 17:18:55 -0700803 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800804}
805
James Feista465ccc2019-02-08 12:51:01 -0800806sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700807{
808 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
809 interface) != settableInterfaces.end()
810 ? sdbusplus::asio::PropertyPermission::readWrite
811 : sdbusplus::asio::PropertyPermission::readOnly;
812}
813
James Feista465ccc2019-02-08 12:51:01 -0800814void createAddObjectMethod(const std::string& jsonPointerPath,
815 const std::string& path,
816 nlohmann::json& systemConfiguration,
817 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700818{
819 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
820
821 iface->register_method(
822 "AddObject",
823 [&systemConfiguration, &objServer,
824 jsonPointerPath{std::string(jsonPointerPath)},
825 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800826 const boost::container::flat_map<std::string, JsonVariantType>&
827 data) {
James Feist68500ff2018-08-08 15:40:42 -0700828 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800829 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700830 auto findExposes = base.find("Exposes");
831
832 if (findExposes == base.end())
833 {
834 throw std::invalid_argument("Entity must have children.");
835 }
836
837 // this will throw invalid-argument to sdbusplus if invalid json
838 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800839 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700840 {
James Feista465ccc2019-02-08 12:51:01 -0800841 nlohmann::json& newJson = newData[item.first];
842 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
843 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700844 }
845
846 auto findName = newData.find("Name");
847 auto findType = newData.find("Type");
848 if (findName == newData.end() || findType == newData.end())
849 {
850 throw std::invalid_argument("AddObject missing Name or Type");
851 }
James Feista465ccc2019-02-08 12:51:01 -0800852 const std::string* type = findType->get_ptr<const std::string*>();
853 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700854 if (type == nullptr || name == nullptr)
855 {
856 throw std::invalid_argument("Type and Name must be a string.");
857 }
858
859 size_t lastIndex = 0;
860 // we add in the "exposes"
861 for (; lastIndex < findExposes->size(); lastIndex++)
862 {
863 if (findExposes->at(lastIndex)["Name"] == *name &&
864 findExposes->at(lastIndex)["Type"] == *type)
865 {
866 throw std::invalid_argument(
867 "Field already in JSON, not adding");
868 }
869 lastIndex++;
870 }
871
872 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
873 *type + ".json");
874 // todo(james) we might want to also make a list of 'can add'
875 // interfaces but for now I think the assumption if there is a
876 // schema avaliable that it is allowed to update is fine
877 if (!schemaFile.good())
878 {
879 throw std::invalid_argument(
880 "No schema avaliable, cannot validate.");
881 }
882 nlohmann::json schema =
883 nlohmann::json::parse(schemaFile, nullptr, false);
884 if (schema.is_discarded())
885 {
886 std::cerr << "Schema not legal" << *type << ".json\n";
887 throw DBusInternalError();
888 }
889 if (!validateJson(schema, newData))
890 {
891 throw std::invalid_argument("Data does not match schema");
892 }
893
894 if (!writeJsonFiles(systemConfiguration))
895 {
896 std::cerr << "Error writing json files\n";
897 throw DBusInternalError();
898 }
899 std::string dbusName = *name;
900
901 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700902 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700903 auto iface = objServer.add_interface(
904 path + "/" + dbusName,
905 "xyz.openbmc_project.Configuration." + *type);
906 // permission is read-write, as since we just created it, must be
907 // runtime modifiable
908 populateInterfaceFromJson(
909 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700910 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700911 newData, objServer,
912 sdbusplus::asio::PropertyPermission::readWrite);
913 // todo(james) generate patch
914 findExposes->push_back(newData);
915 });
916 iface->initialize();
917}
918
James Feista465ccc2019-02-08 12:51:01 -0800919void postToDbus(const nlohmann::json& newConfiguration,
920 nlohmann::json& systemConfiguration,
921 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800922
James Feist1b2e2242018-01-30 13:45:19 -0800923{
James Feist97a63f12018-05-17 13:50:57 -0700924 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800925 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800926 {
927 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700928 std::vector<std::string> path;
929 std::string jsonPointerPath = "/" + boardKey;
930 // loop through newConfiguration, but use values from system
931 // configuration to be able to modify via dbus later
932 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700933 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800934 std::string boardType;
935 if (findBoardType != boardValues.end() &&
936 findBoardType->type() == nlohmann::json::value_t::string)
937 {
938 boardType = findBoardType->get<std::string>();
939 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700940 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800941 }
942 else
943 {
944 std::cerr << "Unable to find type for " << boardKey
945 << " reverting to Chassis.\n";
946 boardType = "Chassis";
947 }
James Feist11be6672018-04-06 14:05:32 -0700948 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800949
950 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700951 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700952 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
953 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800954
James Feist8f2710a2018-05-09 17:18:55 -0700955 auto inventoryIface = objServer.add_interface(
956 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700957
James Feist8f2710a2018-05-09 17:18:55 -0700958 auto boardIface = objServer.add_interface(
959 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700960
James Feist68500ff2018-08-08 15:40:42 -0700961 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
962 objServer);
963
James Feist97a63f12018-05-17 13:50:57 -0700964 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700965 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700966 jsonPointerPath += "/";
967 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800968 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700969 {
970 if (boardField.value().type() == nlohmann::json::value_t::object)
971 {
James Feist8f2710a2018-05-09 17:18:55 -0700972 auto iface =
973 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700974 populateInterfaceFromJson(systemConfiguration,
975 jsonPointerPath + boardField.key(),
976 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700977 }
978 }
James Feist97a63f12018-05-17 13:50:57 -0700979
James Feist1e3e6982018-08-03 16:09:28 -0700980 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800981 if (exposes == boardValues.end())
982 {
983 continue;
984 }
James Feist97a63f12018-05-17 13:50:57 -0700985 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700986 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700987
988 // store the board level pointer so we can modify it on the way down
989 std::string jsonPointerPathBoard = jsonPointerPath;
990 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -0800991 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -0800992 {
James Feist97a63f12018-05-17 13:50:57 -0700993 exposesIndex++;
994 jsonPointerPath = jsonPointerPathBoard;
995 jsonPointerPath += std::to_string(exposesIndex);
996
James Feistd63d18a2018-07-19 15:23:45 -0700997 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800998 if (findName == item.end())
999 {
1000 std::cerr << "cannot find name in field " << item << "\n";
1001 continue;
1002 }
James Feist1e3e6982018-08-03 16:09:28 -07001003 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001004 // if status is not found it is assumed to be status = 'okay'
1005 if (findStatus != item.end())
1006 {
1007 if (*findStatus == "disabled")
1008 {
1009 continue;
1010 }
1011 }
James Feistd63d18a2018-07-19 15:23:45 -07001012 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001013 std::string itemType;
1014 if (findType != item.end())
1015 {
1016 itemType = findType->get<std::string>();
1017 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001018 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1019 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001020 }
1021 else
1022 {
1023 itemType = "unknown";
1024 }
1025 std::string itemName = findName->get<std::string>();
1026 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001027 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001028
James Feist8f2710a2018-05-09 17:18:55 -07001029 auto itemIface = objServer.add_interface(
1030 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001031 "xyz.openbmc_project.Configuration." + itemType);
1032
James Feist97a63f12018-05-17 13:50:57 -07001033 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001034 itemIface, item, objServer,
1035 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001036
James Feista465ccc2019-02-08 12:51:01 -08001037 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001038 {
James Feist97a63f12018-05-17 13:50:57 -07001039 jsonPointerPath = jsonPointerPathBoard +
1040 std::to_string(exposesIndex) + "/" +
1041 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001042 if (objectPair.value().type() ==
1043 nlohmann::json::value_t::object)
1044 {
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 Feist8f2710a2018-05-09 17:18:55 -07001048 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001049
1050 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001051 systemConfiguration, jsonPointerPath, objectIface,
1052 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001053 }
1054 else if (objectPair.value().type() ==
1055 nlohmann::json::value_t::array)
1056 {
1057 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001058 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001059 {
James Feist8f2710a2018-05-09 17:18:55 -07001060 continue;
1061 }
1062 bool isLegal = true;
1063 auto type = objectPair.value()[0].type();
1064 if (type != nlohmann::json::value_t::object)
1065 {
1066 continue;
1067 }
1068
1069 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001070 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001071 {
1072 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001073 {
James Feist8f2710a2018-05-09 17:18:55 -07001074 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001075 break;
1076 }
James Feist8f2710a2018-05-09 17:18:55 -07001077 }
1078 if (!isLegal)
1079 {
1080 std::cerr << "dbus format error" << objectPair.value()
1081 << "\n";
1082 break;
1083 }
1084
James Feista465ccc2019-02-08 12:51:01 -08001085 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001086 {
James Feist97a63f12018-05-17 13:50:57 -07001087
James Feist8f2710a2018-05-09 17:18:55 -07001088 auto objectIface = objServer.add_interface(
1089 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001090 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001091 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001092 populateInterfaceFromJson(
1093 systemConfiguration,
1094 jsonPointerPath + "/" + std::to_string(index),
1095 objectIface, arrayItem, objServer,
1096 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001097 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001098 }
1099 }
1100 }
1101 }
1102 }
1103}
1104
1105// finds the template character (currently set to $) and replaces the value with
1106// the field found in a dbus object i.e. $ADDRESS would get populated with the
1107// ADDRESS field from a object on dbus
1108void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001109 nlohmann::json::iterator& keyPair,
1110 const boost::container::flat_map<std::string, BasicVariantType>&
1111 foundDevice,
1112 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001113{
James Feist11be6672018-04-06 14:05:32 -07001114 if (keyPair.value().type() == nlohmann::json::value_t::object)
1115 {
1116 for (auto nextLayer = keyPair.value().begin();
1117 nextLayer != keyPair.value().end(); nextLayer++)
1118 {
1119 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1120 }
1121 return;
1122 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001123
1124 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1125 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001126 {
1127 return;
1128 }
1129
Ed Tanous12bc7932019-02-26 14:36:20 -08001130 boost::replace_all(*strPtr, "$index", std::to_string(foundDeviceIdx));
1131
1132 std::size_t templateIndex = 0;
1133
1134 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001135 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001136 std::string templateName = "$" + foundDevicePair.first;
1137 if (boost::iequals(*strPtr, templateName))
James Feist1b2e2242018-01-30 13:45:19 -08001138 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001139 std::visit([&](auto&& val) { keyPair.value() = val; },
1140 foundDevicePair.second);
1141 // We probably just invalidated the pointer above, so set it to null
1142 strPtr = nullptr;
1143 break;
James Feist1b2e2242018-01-30 13:45:19 -08001144 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001145
1146 std::string probeValue =
1147 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1148 boost::replace_all(*strPtr, templateName, probeValue);
1149 }
1150
1151 strPtr = keyPair.value().get_ptr<std::string*>();
1152 if (strPtr == nullptr)
1153 {
1154 return;
James Feist1b2e2242018-01-30 13:45:19 -08001155 }
James Feistc6090822019-01-04 16:02:48 -08001156
1157 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001158 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001159 {
1160 try
1161 {
James Feistc6090822019-01-04 16:02:48 -08001162 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001163 int64_t temp = std::stoul(*strPtr, &pos, 0);
1164 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001165 {
1166 keyPair.value() = static_cast<uint64_t>(temp);
1167 }
James Feist28dc2da2018-10-15 14:47:42 -07001168 }
1169 catch (std::invalid_argument)
1170 {
1171 }
James Feistc6090822019-01-04 16:02:48 -08001172 catch (std::out_of_range)
1173 {
1174 }
James Feist28dc2da2018-10-15 14:47:42 -07001175 }
James Feist1b2e2242018-01-30 13:45:19 -08001176}
1177
James Feist8f2710a2018-05-09 17:18:55 -07001178// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001179bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001180{
1181 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001182 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001183 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1184 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001185 {
1186 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001187 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001188 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001189 }
James Feistb4383f42018-08-06 16:54:10 -07001190
1191 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1192 globalSchema);
1193 if (!schemaStream.good())
1194 {
1195 std::cerr
1196 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1197 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001198 return false;
James Feistb4383f42018-08-06 16:54:10 -07001199 }
1200 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1201 if (schema.is_discarded())
1202 {
1203 std::cerr
1204 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1205 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001206 return false;
James Feistb4383f42018-08-06 16:54:10 -07001207 }
1208
James Feista465ccc2019-02-08 12:51:01 -08001209 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001210 {
1211 std::ifstream jsonStream(jsonPath.c_str());
1212 if (!jsonStream.good())
1213 {
1214 std::cerr << "unable to open " << jsonPath.string() << "\n";
1215 continue;
1216 }
1217 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1218 if (data.is_discarded())
1219 {
1220 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1221 continue;
1222 }
James Feist8da99192019-01-24 08:20:16 -08001223 /*
1224 * todo(james): reenable this once less things are in flight
1225 *
James Feistb4383f42018-08-06 16:54:10 -07001226 if (!validateJson(schema, data))
1227 {
1228 std::cerr << "Error validating " << jsonPath.string() << "\n";
1229 continue;
1230 }
James Feist8da99192019-01-24 08:20:16 -08001231 */
James Feistb4383f42018-08-06 16:54:10 -07001232
James Feist3cb5fec2018-01-23 14:41:51 -08001233 if (data.type() == nlohmann::json::value_t::array)
1234 {
James Feista465ccc2019-02-08 12:51:01 -08001235 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001236 {
1237 configurations.emplace_back(d);
1238 }
1239 }
1240 else
1241 {
1242 configurations.emplace_back(data);
1243 }
1244 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001245 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001246}
James Feist3cb5fec2018-01-23 14:41:51 -08001247
James Feist8f2710a2018-05-09 17:18:55 -07001248struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001249{
James Feist75fdeeb2018-02-20 14:26:16 -08001250
James Feista465ccc2019-02-08 12:51:01 -08001251 PerformScan(nlohmann::json& systemConfiguration,
1252 std::list<nlohmann::json>& configurations,
1253 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001254 _systemConfiguration(systemConfiguration),
1255 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001256 {
James Feist8f2710a2018-05-09 17:18:55 -07001257 }
1258 void run()
1259 {
1260 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001261 {
James Feist1e3e6982018-08-03 16:09:28 -07001262 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001263 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001264
James Feist1b2e2242018-01-30 13:45:19 -08001265 nlohmann::json probeCommand;
1266 // check for poorly formatted fields, probe must be an array
1267 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001268 {
1269 std::cerr << "configuration file missing probe:\n " << *it
1270 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001271 it = _configurations.erase(it);
1272 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001273 }
James Feist1b2e2242018-01-30 13:45:19 -08001274 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001275 {
1276 probeCommand = nlohmann::json::array();
1277 probeCommand.push_back(*findProbe);
1278 }
1279 else
1280 {
1281 probeCommand = *findProbe;
1282 }
James Feist1b2e2242018-01-30 13:45:19 -08001283
1284 if (findName == it->end())
1285 {
1286 std::cerr << "configuration file missing name:\n " << *it
1287 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001288 it = _configurations.erase(it);
1289 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001290 }
James Feist8f2710a2018-05-09 17:18:55 -07001291 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001292
James Feist8f2710a2018-05-09 17:18:55 -07001293 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1294 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001295 {
James Feist8f2710a2018-05-09 17:18:55 -07001296 it = _configurations.erase(it);
1297 continue;
1298 }
James Feista465ccc2019-02-08 12:51:01 -08001299 nlohmann::json* record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001300
James Feist8f2710a2018-05-09 17:18:55 -07001301 // store reference to this to children to makes sure we don't get
1302 // destroyed too early
1303 auto thisRef = shared_from_this();
1304 auto p = std::make_shared<PerformProbe>(
1305 probeCommand,
1306 [&, record, name,
1307 thisRef](std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -08001308 std::string, BasicVariantType>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001309 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001310
James Feist8f2710a2018-05-09 17:18:55 -07001311 PASSED_PROBES.push_back(name);
1312 size_t foundDeviceIdx = 0;
1313
James Feistbe5425f2018-06-08 10:30:55 -07001314 // insert into configuration temporarly to be able to
1315 // reference ourselves
1316 _systemConfiguration[name] = *record;
1317
James Feista465ccc2019-02-08 12:51:01 -08001318 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001319 {
James Feist8f2710a2018-05-09 17:18:55 -07001320 for (auto keyPair = record->begin();
1321 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001322 {
James Feist1b2e2242018-01-30 13:45:19 -08001323 templateCharReplace(keyPair, foundDevice,
1324 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001325 }
James Feist1e3e6982018-08-03 16:09:28 -07001326 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001327 if (findExpose == record->end())
1328 {
1329 continue;
1330 }
James Feista465ccc2019-02-08 12:51:01 -08001331 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001332 {
1333 for (auto keyPair = expose.begin();
1334 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001335 {
James Feist1b2e2242018-01-30 13:45:19 -08001336
James Feist8f2710a2018-05-09 17:18:55 -07001337 // fill in template characters with devices
1338 // found
1339 templateCharReplace(keyPair, foundDevice,
1340 foundDeviceIdx);
1341 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001342 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001343 {
1344 if (keyPair.value().type() !=
1345 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001346 {
James Feist8f2710a2018-05-09 17:18:55 -07001347 std::cerr << "bind_ value must be of "
1348 "type string "
1349 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001350 continue;
1351 }
James Feist8f2710a2018-05-09 17:18:55 -07001352 bool foundBind = false;
1353 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001354 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001355
James Feista465ccc2019-02-08 12:51:01 -08001356 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001357 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001358 {
James Feist1b2e2242018-01-30 13:45:19 -08001359
James Feist8f2710a2018-05-09 17:18:55 -07001360 auto configListFind =
1361 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001362 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001363
1364 if (configListFind ==
1365 configurationPair.value()
1366 .end() ||
1367 configListFind->type() !=
1368 nlohmann::json::value_t::array)
1369 {
1370 continue;
1371 }
James Feista465ccc2019-02-08 12:51:01 -08001372 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001373 *configListFind)
1374 {
1375 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001376 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001377 if (boost::iequals(
1378 foundObjectName,
1379 keyPair.value()
1380 .get<std::string>()))
1381 {
James Feist1e3e6982018-08-03 16:09:28 -07001382 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001383 "okay";
1384 expose[bind] = exposedObject;
1385
1386 foundBind = true;
1387 break;
1388 }
1389 }
1390 if (foundBind)
1391 {
James Feist3cb5fec2018-01-23 14:41:51 -08001392 break;
1393 }
1394 }
James Feist8f2710a2018-05-09 17:18:55 -07001395 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001396 {
James Feist8f2710a2018-05-09 17:18:55 -07001397 std::cerr << "configuration file "
1398 "dependency error, "
1399 "could not find bind "
1400 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001401 }
1402 }
1403 }
1404 }
1405 }
James Feistbe5425f2018-06-08 10:30:55 -07001406 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001407 _systemConfiguration[name] = *record;
1408 });
1409 p->run();
1410 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001411 }
1412 }
James Feist75fdeeb2018-02-20 14:26:16 -08001413
James Feist8f2710a2018-05-09 17:18:55 -07001414 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001415 {
James Feist8f2710a2018-05-09 17:18:55 -07001416 if (_passed)
1417 {
1418 auto nextScan = std::make_shared<PerformScan>(
1419 _systemConfiguration, _configurations, std::move(_callback));
1420 nextScan->run();
1421 }
1422 else
1423 {
1424 _callback();
1425 }
1426 }
James Feista465ccc2019-02-08 12:51:01 -08001427 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001428 std::list<nlohmann::json> _configurations;
1429 std::function<void(void)> _callback;
1430 std::vector<std::shared_ptr<PerformProbe>> _probes;
1431 bool _passed = false;
1432};
James Feistc95cb142018-02-26 10:41:42 -08001433
James Feist8f2710a2018-05-09 17:18:55 -07001434// main properties changed entry
1435void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001436 boost::asio::io_service& io,
1437 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1438 nlohmann::json& systemConfiguration,
1439 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001440{
1441 static boost::asio::deadline_timer timer(io);
1442 timer.expires_from_now(boost::posix_time::seconds(1));
1443
1444 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001445 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001446 if (ec == boost::asio::error::operation_aborted)
1447 {
1448 // we were cancelled
1449 return;
1450 }
1451 else if (ec)
1452 {
1453 std::cerr << "async wait error " << ec << "\n";
1454 return;
1455 }
1456
1457 nlohmann::json oldConfiguration = systemConfiguration;
1458 DBUS_PROBE_OBJECTS.clear();
1459
1460 std::list<nlohmann::json> configurations;
1461 if (!findJsonFiles(configurations))
1462 {
1463 std::cerr << "cannot find json files\n";
1464 return;
1465 }
1466
1467 auto perfScan = std::make_shared<PerformScan>(
1468 systemConfiguration, configurations, [&, oldConfiguration]() {
1469 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001470 for (auto it = newConfiguration.begin();
1471 it != newConfiguration.end();)
1472 {
1473 auto findKey = oldConfiguration.find(it.key());
1474 if (findKey != oldConfiguration.end())
1475 {
1476 it = newConfiguration.erase(it);
1477 }
1478 else
1479 {
1480 it++;
1481 }
1482 }
James Feist8f2710a2018-05-09 17:18:55 -07001483 registerCallbacks(io, dbusMatches, systemConfiguration,
1484 objServer);
1485 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001486 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001487
James Feistbb43d022018-06-12 15:44:33 -07001488 io.post([&]() {
1489 if (!writeJsonFiles(systemConfiguration))
1490 {
1491 std::cerr << "Error writing json files\n";
1492 }
1493 });
James Feist8f2710a2018-05-09 17:18:55 -07001494 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001495 postToDbus(newConfiguration, systemConfiguration,
1496 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001497 });
1498 });
1499 });
1500 perfScan->run();
1501 });
James Feist75fdeeb2018-02-20 14:26:16 -08001502}
1503
James Feista465ccc2019-02-08 12:51:01 -08001504void registerCallbacks(boost::asio::io_service& io,
1505 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1506 nlohmann::json& systemConfiguration,
1507 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001508{
1509 static boost::container::flat_set<std::string> watchedObjects;
1510
James Feista465ccc2019-02-08 12:51:01 -08001511 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001512 {
1513 auto findObject = watchedObjects.find(objectMap.first);
1514 if (findObject != watchedObjects.end())
1515 {
1516 continue;
1517 }
James Feist8f2710a2018-05-09 17:18:55 -07001518 std::function<void(sdbusplus::message::message & message)>
1519 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001520
James Feista465ccc2019-02-08 12:51:01 -08001521 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001522 propertiesChangedCallback(io, dbusMatches,
1523 systemConfiguration, objServer);
1524 };
1525
1526 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001527 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001528 "type='signal',member='PropertiesChanged',arg0='" +
1529 objectMap.first + "'",
1530 eventHandler);
1531 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001532 }
1533}
1534
James Feista465ccc2019-02-08 12:51:01 -08001535int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001536{
1537 // setup connection to dbus
1538 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001539 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001540 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001541
James Feist8f2710a2018-05-09 17:18:55 -07001542 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001543
James Feist8f2710a2018-05-09 17:18:55 -07001544 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1545 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1546 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001547
James Feist8f2710a2018-05-09 17:18:55 -07001548 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1549 objServer.add_interface("/xyz/openbmc_project/inventory",
1550 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001551
1552 // to keep reference to the match / filter objects so they don't get
1553 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001554 std::vector<sdbusplus::bus::match::match> dbusMatches;
1555
1556 nlohmann::json systemConfiguration = nlohmann::json::object();
1557
1558 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001559 "Notify",
1560 [](const boost::container::flat_map<
1561 std::string,
1562 boost::container::flat_map<std::string, BasicVariantType>>&
1563 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001564 inventoryIface->initialize();
1565
1566 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001567#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001568 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001569#endif
James Feist8f2710a2018-05-09 17:18:55 -07001570 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1571 objServer);
1572 });
James Feist4131aea2018-03-09 09:47:30 -08001573
James Feistfd1264a2018-05-03 12:10:00 -07001574 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001575 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1576 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001577 });
James Feist8f2710a2018-05-09 17:18:55 -07001578 entityIface->initialize();
1579
James Feist1b2e2242018-01-30 13:45:19 -08001580 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001581
1582 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001583}