blob: 3704f841e0dbb20476c401a64744aed9d1ac95c2 [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 Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.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 Feistf5125b02019-06-06 11:27:43 -070023#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080024#include <boost/algorithm/string/predicate.hpp>
25#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070026#include <boost/algorithm/string/split.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <boost/container/flat_map.hpp>
28#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080029#include <boost/lexical_cast.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070031#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080032#include <fstream>
33#include <iostream>
34#include <nlohmann/json.hpp>
35#include <regex>
36#include <sdbusplus/asio/connection.hpp>
37#include <sdbusplus/asio/object_server.hpp>
38#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080039
James Feista465ccc2019-02-08 12:51:01 -080040constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
41constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070042constexpr const char* tempConfigDir = "/tmp/configuration/";
43constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
44constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080045constexpr const char* globalSchema = "global.json";
James Feistf5125b02019-06-06 11:27:43 -070046constexpr const char* templateChar = "$";
James Feist8f2710a2018-05-09 17:18:55 -070047constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080048
James Feistf1b14142019-04-10 15:22:09 -070049constexpr const bool DEBUG = false;
50
James Feist3cb5fec2018-01-23 14:41:51 -080051struct cmp_str
52{
James Feista465ccc2019-02-08 12:51:01 -080053 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080054 {
55 return std::strcmp(a, b) < 0;
56 }
57};
58
James Feist8f2710a2018-05-09 17:18:55 -070059struct PerformProbe;
60
James Feist3cb5fec2018-01-23 14:41:51 -080061// underscore T for collison with dbus c api
62enum class probe_type_codes
63{
64 FALSE_T,
65 TRUE_T,
66 AND,
67 OR,
James Feist6bd2a022018-03-13 12:30:58 -070068 FOUND,
69 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080070};
James Feista465ccc2019-02-08 12:51:01 -080071const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080072 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
73 {"TRUE", probe_type_codes::TRUE_T},
74 {"AND", probe_type_codes::AND},
75 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070076 {"FOUND", probe_type_codes::FOUND},
77 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080078
James Feist41334262019-03-25 13:30:20 -070079static constexpr std::array<const char*, 5> settableInterfaces = {
80 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070081using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080082 std::variant<std::vector<std::string>, std::vector<double>, std::string,
83 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
84 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070085using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080086 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
87 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070088
James Feist3cb5fec2018-01-23 14:41:51 -080089using GetSubTreeType = std::vector<
90 std::pair<std::string,
91 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
92
93using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070094 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080095 boost::container::flat_map<
96 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070097 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080098
99boost::container::flat_map<
100 std::string,
James Feist8f2710a2018-05-09 17:18:55 -0700101 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800102 DBUS_PROBE_OBJECTS;
103std::vector<std::string> PASSED_PROBES;
104
105// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700106std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700107static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800108
Johnathan Mantey2015f752019-03-26 15:22:31 -0700109const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
110const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800111
James Feista465ccc2019-02-08 12:51:01 -0800112void registerCallbacks(boost::asio::io_service& io,
113 std::vector<sdbusplus::bus::match::match>& dbusMatches,
114 nlohmann::json& systemConfiguration,
115 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800116
James Feist3cb5fec2018-01-23 14:41:51 -0800117// calls the mapper to find all exposed objects of an interface type
118// and creates a vector<flat_map> that contains all the key value pairs
119// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700120void findDbusObjects(std::shared_ptr<PerformProbe> probe,
121 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800122 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800123{
James Feist8f2710a2018-05-09 17:18:55 -0700124
125 // store reference to pending callbacks so we don't overwhelm services
126 static boost::container::flat_map<
127 std::string, std::vector<std::shared_ptr<PerformProbe>>>
128 pendingProbes;
129
130 if (DBUS_PROBE_OBJECTS[interface].size())
131 {
132 return;
133 }
134
135 // add shared_ptr to vector of Probes waiting for callback from a specific
136 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800137 std::array<const char*, 1> objects = {interface.c_str()};
138 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700139 pendingProbes[interface];
140 auto iter = pending.emplace(pending.end(), probe);
141 // only allow first call to run to not overwhelm processes
142 if (iter != pending.begin())
143 {
144 return;
145 }
146
James Feist3cb5fec2018-01-23 14:41:51 -0800147 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700148 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800149 [connection, interface, probe](boost::system::error_code& ec,
150 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700151 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700152 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700153 {
James Feist0de40152018-07-25 11:56:12 -0700154 pendingProbes[interface].clear();
155 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700156 {
James Feist0de40152018-07-25 11:56:12 -0700157 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700158 }
James Feist0de40152018-07-25 11:56:12 -0700159 std::cerr << "Error communicating to mapper.\n";
160
161 // if we can't communicate to the mapper something is very wrong
162 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700163 }
James Feist8f2710a2018-05-09 17:18:55 -0700164 else
James Feist3cb5fec2018-01-23 14:41:51 -0800165 {
James Feista465ccc2019-02-08 12:51:01 -0800166 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700167 {
James Feista465ccc2019-02-08 12:51:01 -0800168 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700169 {
James Feist0eb40352019-04-09 14:44:04 -0700170 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700171 }
172 }
James Feist3cb5fec2018-01-23 14:41:51 -0800173 }
James Feist63845bf2019-01-24 12:19:51 -0800174 if (interfaceConnections.empty())
175 {
176 pendingProbes[interface].clear();
177 return;
178 }
James Feist8f2710a2018-05-09 17:18:55 -0700179 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800180 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700181 {
182 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700183 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800184 interface](boost::system::error_code& ec,
185 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700186 if (ec)
187 {
188 std::cerr
189 << "error getting managed object for device "
190 << conn << "\n";
191 pendingProbes[interface].clear();
192 return;
193 }
James Feista465ccc2019-02-08 12:51:01 -0800194 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700195 {
196 auto ifaceObjFind =
197 interfaceManagedObj.second.find(interface);
198 if (ifaceObjFind !=
199 interfaceManagedObj.second.end())
200 {
201 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800202 std::string, BasicVariantType>>&
203 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700204 dbusObject.emplace_back(ifaceObjFind->second);
205 }
206 }
207 pendingProbes[interface].clear();
208 },
209 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
210 "GetManagedObjects");
211 }
212 },
213 "xyz.openbmc_project.ObjectMapper",
214 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700215 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700216 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800217}
James Feist8f2710a2018-05-09 17:18:55 -0700218// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800219bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800220 const std::string& interface,
221 const std::map<std::string, nlohmann::json>& matches,
James Feistf1b14142019-04-10 15:22:09 -0700222 std::vector<std::optional<
223 boost::container::flat_map<std::string, BasicVariantType>>>& devices,
James Feista465ccc2019-02-08 12:51:01 -0800224 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800225{
James Feista465ccc2019-02-08 12:51:01 -0800226 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
227 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800228 if (dbusObject.empty())
229 {
James Feist8f2710a2018-05-09 17:18:55 -0700230 foundProbe = false;
231 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800232 }
233 foundProbe = true;
234
235 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800236 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800237 {
238 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800239 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800240 {
241 auto deviceValue = device.find(match.first);
242 if (deviceValue != device.end())
243 {
244 switch (match.second.type())
245 {
James Feist9eb0b582018-04-27 12:15:46 -0700246 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800247 {
James Feist9eb0b582018-04-27 12:15:46 -0700248 std::regex search(match.second.get<std::string>());
249 std::smatch match;
250
251 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800252 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700253 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700254 if (!std::regex_search(probeValue, match, search))
255 {
256 deviceMatches = false;
257 break;
258 }
James Feist3cb5fec2018-01-23 14:41:51 -0800259 break;
260 }
James Feist9eb0b582018-04-27 12:15:46 -0700261 case nlohmann::json::value_t::boolean:
262 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800263 {
James Feista465ccc2019-02-08 12:51:01 -0800264 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700265 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800266
James Feist9eb0b582018-04-27 12:15:46 -0700267 if (probeValue != match.second.get<unsigned int>())
268 {
269 deviceMatches = false;
270 }
271 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800272 }
James Feist9eb0b582018-04-27 12:15:46 -0700273 case nlohmann::json::value_t::number_integer:
274 {
James Feista465ccc2019-02-08 12:51:01 -0800275 int probeValue = std::visit(VariantToIntVisitor(),
276 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800277
James Feist9eb0b582018-04-27 12:15:46 -0700278 if (probeValue != match.second.get<int>())
279 {
280 deviceMatches = false;
281 }
282 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800283 }
James Feist9eb0b582018-04-27 12:15:46 -0700284 case nlohmann::json::value_t::number_float:
285 {
James Feista465ccc2019-02-08 12:51:01 -0800286 float probeValue = std::visit(VariantToFloatVisitor(),
287 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700288
289 if (probeValue != match.second.get<float>())
290 {
291 deviceMatches = false;
292 }
293 break;
294 }
James Feist0eb40352019-04-09 14:44:04 -0700295 default:
296 {
297 std::cerr << "unexpected dbus probe type "
298 << match.second.type_name() << "\n";
299 }
James Feist3cb5fec2018-01-23 14:41:51 -0800300 }
301 }
302 else
303 {
304 deviceMatches = false;
305 break;
306 }
307 }
308 if (deviceMatches)
309 {
James Feistf5125b02019-06-06 11:27:43 -0700310 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800311 foundMatch = true;
312 deviceMatches = false; // for next iteration
313 }
314 }
315 return foundMatch;
316}
317
318// default probe entry point, iterates a list looking for specific types to
319// call specific probe functions
320bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800321 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700322 std::vector<std::optional<
323 boost::container::flat_map<std::string, BasicVariantType>>>& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800324{
325 const static std::regex command(R"(\((.*)\))");
326 std::smatch match;
327 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700328 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800329 bool cur = true;
330 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
331
James Feista465ccc2019-02-08 12:51:01 -0800332 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800333 {
334 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800335 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800336 cmp_str>::const_iterator probeType;
337
338 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
339 probeType++)
340 {
341 if (probe.find(probeType->first) != std::string::npos)
342 {
343 foundProbe = true;
344 break;
345 }
346 }
347 if (foundProbe)
348 {
349 switch (probeType->second)
350 {
James Feist9eb0b582018-04-27 12:15:46 -0700351 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800352 {
James Feist8f2710a2018-05-09 17:18:55 -0700353 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800354 }
James Feist9eb0b582018-04-27 12:15:46 -0700355 case probe_type_codes::TRUE_T:
356 {
James Feist8f2710a2018-05-09 17:18:55 -0700357 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700358 }
359 case probe_type_codes::MATCH_ONE:
360 {
361 // set current value to last, this probe type shouldn't
362 // affect the outcome
363 cur = ret;
364 matchOne = true;
365 break;
366 }
367 /*case probe_type_codes::AND:
368 break;
369 case probe_type_codes::OR:
370 break;
371 // these are no-ops until the last command switch
372 */
373 case probe_type_codes::FOUND:
374 {
375 if (!std::regex_search(probe, match, command))
376 {
James Feist0eb40352019-04-09 14:44:04 -0700377 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700378 << "\n";
379 return false;
380 }
381 std::string commandStr = *(match.begin() + 1);
382 boost::replace_all(commandStr, "'", "");
383 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
384 commandStr) != PASSED_PROBES.end());
385 break;
386 }
James Feist0eb40352019-04-09 14:44:04 -0700387 default:
388 {
389 break;
390 }
James Feist3cb5fec2018-01-23 14:41:51 -0800391 }
392 }
393 // look on dbus for object
394 else
395 {
396 if (!std::regex_search(probe, match, command))
397 {
James Feist0eb40352019-04-09 14:44:04 -0700398 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800399 return false;
400 }
401 std::string commandStr = *(match.begin() + 1);
402 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700403 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800404 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800405 auto json = nlohmann::json::parse(commandStr, nullptr, false);
406 if (json.is_discarded())
407 {
James Feist0eb40352019-04-09 14:44:04 -0700408 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800409 return false;
410 }
411 // we can match any (string, variant) property. (string, string)
412 // does a regex
413 std::map<std::string, nlohmann::json> dbusProbeMap =
414 json.get<std::map<std::string, nlohmann::json>>();
415 auto findStart = probe.find("(");
416 if (findStart == std::string::npos)
417 {
418 return false;
419 }
420 std::string probeInterface = probe.substr(0, findStart);
421 cur =
422 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
423 }
424
425 // some functions like AND and OR only take affect after the
426 // fact
427 switch (lastCommand)
428 {
James Feist9eb0b582018-04-27 12:15:46 -0700429 case probe_type_codes::AND:
430 ret = cur && ret;
431 break;
432 case probe_type_codes::OR:
433 ret = cur || ret;
434 break;
435 default:
436 ret = cur;
437 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800438 }
439 lastCommand = probeType != PROBE_TYPES.end()
440 ? probeType->second
441 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800442 }
443
444 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800445 if (ret && foundDevs.size() == 0)
446 {
James Feistf1b14142019-04-10 15:22:09 -0700447 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800448 }
James Feist0eb40352019-04-09 14:44:04 -0700449 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700450 {
James Feist0eb40352019-04-09 14:44:04 -0700451 // match one could match multiple dbus values, which means we don't care
452 // what one we found so we shouldn't be using template replace. return
453 // an empty one
454 foundDevs.clear();
James Feistf1b14142019-04-10 15:22:09 -0700455 foundDevs.emplace_back(std::nullopt);
James Feist6bd2a022018-03-13 12:30:58 -0700456 }
James Feist3cb5fec2018-01-23 14:41:51 -0800457 return ret;
458}
James Feist8f2710a2018-05-09 17:18:55 -0700459// this class finds the needed dbus fields and on destruction runs the probe
460struct PerformProbe : std::enable_shared_from_this<PerformProbe>
461{
James Feist3cb5fec2018-01-23 14:41:51 -0800462
James Feist8f2710a2018-05-09 17:18:55 -0700463 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800464 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700465 std::function<void(std::vector<std::optional<boost::container::flat_map<
466 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700467 _probeCommand(probeCommand),
468 _callback(std::move(callback))
469 {
470 }
471 ~PerformProbe()
472 {
James Feistf1b14142019-04-10 15:22:09 -0700473 std::vector<std::optional<
474 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700475 foundDevs;
476 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700477 {
James Feist0eb40352019-04-09 14:44:04 -0700478 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700479 }
480 }
481 void run()
482 {
483 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800484 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700485 cmp_str>::const_iterator probeType;
486
James Feista465ccc2019-02-08 12:51:01 -0800487 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700488 {
489 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800490 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700491 cmp_str>::const_iterator probeType;
492 for (probeType = PROBE_TYPES.begin();
493 probeType != PROBE_TYPES.end(); probeType++)
494 {
495 if (probe.find(probeType->first) != std::string::npos)
496 {
497 found = true;
498 break;
499 }
500 }
501 if (found)
502 {
503 continue;
504 }
505 // syntax requires probe before first open brace
506 auto findStart = probe.find("(");
507 std::string interface = probe.substr(0, findStart);
508
509 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
510 }
511 }
512 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700513 std::function<void(std::vector<std::optional<boost::container::flat_map<
514 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700515 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700516};
517
518// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800519bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800520{
James Feist1df06a42019-04-11 14:23:04 -0700521 std::filesystem::create_directory(configurationOutDir);
522 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700523 if (!output.good())
524 {
525 return false;
526 }
James Feist1b2e2242018-01-30 13:45:19 -0800527 output << systemConfiguration.dump(4);
528 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700529 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700530}
James Feist1b2e2242018-01-30 13:45:19 -0800531
James Feist97a63f12018-05-17 13:50:57 -0700532template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800533bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
534 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700535{
536 try
537 {
538 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800539 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700540 ref = value;
541 return true;
542 }
543 catch (const std::out_of_range)
544 {
545 return false;
546 }
547}
James Feistbb43d022018-06-12 15:44:33 -0700548
James Feistebcc26b2019-03-22 12:30:43 -0700549// template function to add array as dbus property
550template <typename PropertyType>
551void addArrayToDbus(const std::string& name, const nlohmann::json& array,
552 sdbusplus::asio::dbus_interface* iface,
553 sdbusplus::asio::PropertyPermission permission,
554 nlohmann::json& systemConfiguration,
555 const std::string& jsonPointerString)
556{
557 std::vector<PropertyType> values;
558 for (const auto& property : array)
559 {
560 auto ptr = property.get_ptr<const PropertyType*>();
561 if (ptr != nullptr)
562 {
563 values.emplace_back(*ptr);
564 }
565 }
566
567 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
568 {
569 iface->register_property(name, values);
570 }
571 else
572 {
573 iface->register_property(
574 name, values,
575 [&systemConfiguration,
576 jsonPointerString{std::string(jsonPointerString)}](
577 const std::vector<PropertyType>& newVal,
578 std::vector<PropertyType>& val) {
579 val = newVal;
580 if (!setJsonFromPointer(jsonPointerString, val,
581 systemConfiguration))
582 {
583 std::cerr << "error setting json field\n";
584 return -1;
585 }
586 if (!writeJsonFiles(systemConfiguration))
587 {
588 std::cerr << "error setting json file\n";
589 return -1;
590 }
591 return 1;
592 });
593 }
594}
595
James Feistbb43d022018-06-12 15:44:33 -0700596template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800597void addProperty(const std::string& propertyName, const PropertyType& value,
598 sdbusplus::asio::dbus_interface* iface,
599 nlohmann::json& systemConfiguration,
600 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700601 sdbusplus::asio::PropertyPermission permission)
602{
603 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
604 {
605 iface->register_property(propertyName, value);
606 return;
607 }
James Feist68500ff2018-08-08 15:40:42 -0700608 iface->register_property(
609 propertyName, value,
610 [&systemConfiguration,
611 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800612 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700613 val = newVal;
614 if (!setJsonFromPointer(jsonPointerString, val,
615 systemConfiguration))
616 {
617 std::cerr << "error setting json field\n";
618 return -1;
619 }
James Feistc6248a52018-08-14 10:09:45 -0700620 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700621 {
622 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700623 return -1;
624 }
625 return 1;
626 });
627}
628
629void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800630 const std::string& jsonPointerPath,
631 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
632 sdbusplus::asio::object_server& objServer,
633 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700634{
635 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
636 iface->register_method(
637 "Delete", [&objServer, &systemConfiguration, interface,
638 jsonPointerPath{std::string(jsonPointerPath)}]() {
639 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
640 interface.lock();
641 if (!iface)
642 {
643 // this technically can't happen as the pointer is pointing to
644 // us
645 throw DBusInternalError();
646 }
647 nlohmann::json::json_pointer ptr(jsonPointerPath);
648 if (!objServer.remove_interface(iface))
649 {
650 std::cerr << "Can't delete interface " << jsonPointerPath
651 << "\n";
652 throw DBusInternalError();
653 }
654 systemConfiguration[ptr] = nullptr;
655
656 if (!writeJsonFiles(systemConfiguration))
657 {
658 std::cerr << "error setting json file\n";
659 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700660 }
James Feistbb43d022018-06-12 15:44:33 -0700661 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700662 });
James Feistbb43d022018-06-12 15:44:33 -0700663}
664
James Feist1b2e2242018-01-30 13:45:19 -0800665// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700666void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800667 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
668 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
669 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700670 sdbusplus::asio::PropertyPermission permission =
671 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800672{
James Feista465ccc2019-02-08 12:51:01 -0800673 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800674 {
James Feist8f2710a2018-05-09 17:18:55 -0700675 auto type = dictPair.value().type();
676 bool array = false;
677 if (dictPair.value().type() == nlohmann::json::value_t::array)
678 {
679 array = true;
680 if (!dictPair.value().size())
681 {
682 continue;
683 }
684 type = dictPair.value()[0].type();
685 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800686 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700687 {
688 if (arrayItem.type() != type)
689 {
690 isLegal = false;
691 break;
692 }
693 }
694 if (!isLegal)
695 {
696 std::cerr << "dbus format error" << dictPair.value() << "\n";
697 continue;
698 }
James Feista218ddb2019-04-11 14:01:31 -0700699 }
700 if (type == nlohmann::json::value_t::object)
701 {
702 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700703 }
James Feist97a63f12018-05-17 13:50:57 -0700704 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700705 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
706 {
707 // all setable numbers are doubles as it is difficult to always
708 // create a configuration file with all whole numbers as decimals
709 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700710 if (array)
711 {
712 if (dictPair.value()[0].is_number())
713 {
714 type = nlohmann::json::value_t::number_float;
715 }
716 }
717 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700718 {
719 type = nlohmann::json::value_t::number_float;
720 }
721 }
722
James Feist8f2710a2018-05-09 17:18:55 -0700723 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800724 {
James Feist9eb0b582018-04-27 12:15:46 -0700725 case (nlohmann::json::value_t::boolean):
726 {
James Feist8f2710a2018-05-09 17:18:55 -0700727 if (array)
728 {
729 // todo: array of bool isn't detected correctly by
730 // sdbusplus, change it to numbers
731 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700732 iface.get(), permission,
733 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700734 }
James Feistbb43d022018-06-12 15:44:33 -0700735
James Feist97a63f12018-05-17 13:50:57 -0700736 else
737 {
James Feistbb43d022018-06-12 15:44:33 -0700738 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700739 iface.get(), systemConfiguration, key,
740 permission);
James Feist97a63f12018-05-17 13:50:57 -0700741 }
James Feist9eb0b582018-04-27 12:15:46 -0700742 break;
743 }
744 case (nlohmann::json::value_t::number_integer):
745 {
James Feist8f2710a2018-05-09 17:18:55 -0700746 if (array)
747 {
748 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700749 iface.get(), permission,
750 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700751 }
752 else
753 {
James Feistbb43d022018-06-12 15:44:33 -0700754 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700755 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700756 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700757 }
James Feist9eb0b582018-04-27 12:15:46 -0700758 break;
759 }
760 case (nlohmann::json::value_t::number_unsigned):
761 {
James Feist8f2710a2018-05-09 17:18:55 -0700762 if (array)
763 {
764 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700765 iface.get(), permission,
766 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700767 }
768 else
769 {
James Feistbb43d022018-06-12 15:44:33 -0700770 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700771 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700772 systemConfiguration, key,
773 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700774 }
James Feist9eb0b582018-04-27 12:15:46 -0700775 break;
776 }
777 case (nlohmann::json::value_t::number_float):
778 {
James Feist8f2710a2018-05-09 17:18:55 -0700779 if (array)
780 {
781 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700782 iface.get(), permission,
783 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700784 }
James Feistbb43d022018-06-12 15:44:33 -0700785
James Feist97a63f12018-05-17 13:50:57 -0700786 else
787 {
James Feistbb43d022018-06-12 15:44:33 -0700788 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700789 iface.get(), systemConfiguration, key,
790 permission);
James Feist97a63f12018-05-17 13:50:57 -0700791 }
James Feist9eb0b582018-04-27 12:15:46 -0700792 break;
793 }
794 case (nlohmann::json::value_t::string):
795 {
James Feist8f2710a2018-05-09 17:18:55 -0700796 if (array)
797 {
James Feistebcc26b2019-03-22 12:30:43 -0700798 addArrayToDbus<std::string>(
799 dictPair.key(), dictPair.value(), iface.get(),
800 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700801 }
802 else
803 {
James Feistc6248a52018-08-14 10:09:45 -0700804 addProperty(
805 dictPair.key(), dictPair.value().get<std::string>(),
806 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700807 }
James Feist9eb0b582018-04-27 12:15:46 -0700808 break;
809 }
James Feist0eb40352019-04-09 14:44:04 -0700810 default:
811 {
James Feista218ddb2019-04-11 14:01:31 -0700812 std::cerr << "Unexpected json type in system configuration "
813 << dictPair.key() << ": "
814 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700815 break;
816 }
James Feist1b2e2242018-01-30 13:45:19 -0800817 }
818 }
James Feistc6248a52018-08-14 10:09:45 -0700819 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
820 {
821 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
822 systemConfiguration);
823 }
James Feist8f2710a2018-05-09 17:18:55 -0700824 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800825}
826
James Feista465ccc2019-02-08 12:51:01 -0800827sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700828{
829 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
830 interface) != settableInterfaces.end()
831 ? sdbusplus::asio::PropertyPermission::readWrite
832 : sdbusplus::asio::PropertyPermission::readOnly;
833}
834
James Feista465ccc2019-02-08 12:51:01 -0800835void createAddObjectMethod(const std::string& jsonPointerPath,
836 const std::string& path,
837 nlohmann::json& systemConfiguration,
838 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700839{
840 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
841
842 iface->register_method(
843 "AddObject",
844 [&systemConfiguration, &objServer,
845 jsonPointerPath{std::string(jsonPointerPath)},
846 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800847 const boost::container::flat_map<std::string, JsonVariantType>&
848 data) {
James Feist68500ff2018-08-08 15:40:42 -0700849 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800850 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700851 auto findExposes = base.find("Exposes");
852
853 if (findExposes == base.end())
854 {
855 throw std::invalid_argument("Entity must have children.");
856 }
857
858 // this will throw invalid-argument to sdbusplus if invalid json
859 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800860 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700861 {
James Feista465ccc2019-02-08 12:51:01 -0800862 nlohmann::json& newJson = newData[item.first];
863 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
864 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700865 }
866
867 auto findName = newData.find("Name");
868 auto findType = newData.find("Type");
869 if (findName == newData.end() || findType == newData.end())
870 {
871 throw std::invalid_argument("AddObject missing Name or Type");
872 }
James Feista465ccc2019-02-08 12:51:01 -0800873 const std::string* type = findType->get_ptr<const std::string*>();
874 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700875 if (type == nullptr || name == nullptr)
876 {
877 throw std::invalid_argument("Type and Name must be a string.");
878 }
879
880 size_t lastIndex = 0;
881 // we add in the "exposes"
882 for (; lastIndex < findExposes->size(); lastIndex++)
883 {
884 if (findExposes->at(lastIndex)["Name"] == *name &&
885 findExposes->at(lastIndex)["Type"] == *type)
886 {
887 throw std::invalid_argument(
888 "Field already in JSON, not adding");
889 }
890 lastIndex++;
891 }
892
893 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
894 *type + ".json");
895 // todo(james) we might want to also make a list of 'can add'
896 // interfaces but for now I think the assumption if there is a
897 // schema avaliable that it is allowed to update is fine
898 if (!schemaFile.good())
899 {
900 throw std::invalid_argument(
901 "No schema avaliable, cannot validate.");
902 }
903 nlohmann::json schema =
904 nlohmann::json::parse(schemaFile, nullptr, false);
905 if (schema.is_discarded())
906 {
907 std::cerr << "Schema not legal" << *type << ".json\n";
908 throw DBusInternalError();
909 }
910 if (!validateJson(schema, newData))
911 {
912 throw std::invalid_argument("Data does not match schema");
913 }
914
James Feist16a02f22019-05-13 15:21:37 -0700915 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700916 if (!writeJsonFiles(systemConfiguration))
917 {
918 std::cerr << "Error writing json files\n";
919 throw DBusInternalError();
920 }
921 std::string dbusName = *name;
922
923 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700924 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700925 auto iface = objServer.add_interface(
926 path + "/" + dbusName,
927 "xyz.openbmc_project.Configuration." + *type);
928 // permission is read-write, as since we just created it, must be
929 // runtime modifiable
930 populateInterfaceFromJson(
931 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700932 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
933 iface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700934 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700935 });
936 iface->initialize();
937}
938
James Feista465ccc2019-02-08 12:51:01 -0800939void postToDbus(const nlohmann::json& newConfiguration,
940 nlohmann::json& systemConfiguration,
941 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800942
James Feist1b2e2242018-01-30 13:45:19 -0800943{
James Feist97a63f12018-05-17 13:50:57 -0700944 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800945 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800946 {
James Feistf1b14142019-04-10 15:22:09 -0700947 std::string boardKey = boardPair.value()["Name"];
948 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700949 // loop through newConfiguration, but use values from system
950 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700951 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700952 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800953 std::string boardType;
954 if (findBoardType != boardValues.end() &&
955 findBoardType->type() == nlohmann::json::value_t::string)
956 {
957 boardType = findBoardType->get<std::string>();
958 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700959 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800960 }
961 else
962 {
963 std::cerr << "Unable to find type for " << boardKey
964 << " reverting to Chassis.\n";
965 boardType = "Chassis";
966 }
James Feist11be6672018-04-06 14:05:32 -0700967 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800968
969 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700970 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700971 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
972 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800973
James Feist8f2710a2018-05-09 17:18:55 -0700974 auto inventoryIface = objServer.add_interface(
975 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700976
James Feist8f2710a2018-05-09 17:18:55 -0700977 auto boardIface = objServer.add_interface(
978 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700979
James Feist68500ff2018-08-08 15:40:42 -0700980 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
981 objServer);
982
James Feist97a63f12018-05-17 13:50:57 -0700983 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700984 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700985 jsonPointerPath += "/";
986 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800987 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700988 {
989 if (boardField.value().type() == nlohmann::json::value_t::object)
990 {
James Feist8f2710a2018-05-09 17:18:55 -0700991 auto iface =
992 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700993 populateInterfaceFromJson(systemConfiguration,
994 jsonPointerPath + boardField.key(),
995 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700996 }
997 }
James Feist97a63f12018-05-17 13:50:57 -0700998
James Feist1e3e6982018-08-03 16:09:28 -0700999 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001000 if (exposes == boardValues.end())
1001 {
1002 continue;
1003 }
James Feist97a63f12018-05-17 13:50:57 -07001004 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001005 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001006
1007 // store the board level pointer so we can modify it on the way down
1008 std::string jsonPointerPathBoard = jsonPointerPath;
1009 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001010 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001011 {
James Feist97a63f12018-05-17 13:50:57 -07001012 exposesIndex++;
1013 jsonPointerPath = jsonPointerPathBoard;
1014 jsonPointerPath += std::to_string(exposesIndex);
1015
James Feistd63d18a2018-07-19 15:23:45 -07001016 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001017 if (findName == item.end())
1018 {
1019 std::cerr << "cannot find name in field " << item << "\n";
1020 continue;
1021 }
James Feist1e3e6982018-08-03 16:09:28 -07001022 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001023 // if status is not found it is assumed to be status = 'okay'
1024 if (findStatus != item.end())
1025 {
1026 if (*findStatus == "disabled")
1027 {
1028 continue;
1029 }
1030 }
James Feistd63d18a2018-07-19 15:23:45 -07001031 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001032 std::string itemType;
1033 if (findType != item.end())
1034 {
1035 itemType = findType->get<std::string>();
1036 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001037 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1038 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001039 }
1040 else
1041 {
1042 itemType = "unknown";
1043 }
1044 std::string itemName = findName->get<std::string>();
1045 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001046 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001047
James Feist8f2710a2018-05-09 17:18:55 -07001048 auto itemIface = objServer.add_interface(
1049 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001050 "xyz.openbmc_project.Configuration." + itemType);
1051
James Feist97a63f12018-05-17 13:50:57 -07001052 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001053 itemIface, item, objServer,
1054 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001055
James Feista465ccc2019-02-08 12:51:01 -08001056 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001057 {
James Feist97a63f12018-05-17 13:50:57 -07001058 jsonPointerPath = jsonPointerPathBoard +
1059 std::to_string(exposesIndex) + "/" +
1060 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001061 if (objectPair.value().type() ==
1062 nlohmann::json::value_t::object)
1063 {
James Feist8f2710a2018-05-09 17:18:55 -07001064 auto objectIface = objServer.add_interface(
1065 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001066 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001067 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001068
1069 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001070 systemConfiguration, jsonPointerPath, objectIface,
1071 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001072 }
1073 else if (objectPair.value().type() ==
1074 nlohmann::json::value_t::array)
1075 {
1076 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001077 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001078 {
James Feist8f2710a2018-05-09 17:18:55 -07001079 continue;
1080 }
1081 bool isLegal = true;
1082 auto type = objectPair.value()[0].type();
1083 if (type != nlohmann::json::value_t::object)
1084 {
1085 continue;
1086 }
1087
1088 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001089 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001090 {
1091 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001092 {
James Feist8f2710a2018-05-09 17:18:55 -07001093 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001094 break;
1095 }
James Feist8f2710a2018-05-09 17:18:55 -07001096 }
1097 if (!isLegal)
1098 {
1099 std::cerr << "dbus format error" << objectPair.value()
1100 << "\n";
1101 break;
1102 }
1103
James Feista465ccc2019-02-08 12:51:01 -08001104 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001105 {
James Feist97a63f12018-05-17 13:50:57 -07001106
James Feist8f2710a2018-05-09 17:18:55 -07001107 auto objectIface = objServer.add_interface(
1108 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001109 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001110 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001111 populateInterfaceFromJson(
1112 systemConfiguration,
1113 jsonPointerPath + "/" + std::to_string(index),
1114 objectIface, arrayItem, objServer,
1115 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001116 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001117 }
1118 }
1119 }
1120 }
1121 }
1122}
1123
1124// finds the template character (currently set to $) and replaces the value with
1125// the field found in a dbus object i.e. $ADDRESS would get populated with the
1126// ADDRESS field from a object on dbus
1127void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001128 nlohmann::json::iterator& keyPair,
1129 const boost::container::flat_map<std::string, BasicVariantType>&
1130 foundDevice,
1131 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001132{
James Feist11be6672018-04-06 14:05:32 -07001133 if (keyPair.value().type() == nlohmann::json::value_t::object)
1134 {
1135 for (auto nextLayer = keyPair.value().begin();
1136 nextLayer != keyPair.value().end(); nextLayer++)
1137 {
1138 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1139 }
1140 return;
1141 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001142
1143 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1144 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001145 {
1146 return;
1147 }
1148
James Feistf5125b02019-06-06 11:27:43 -07001149 boost::replace_all(*strPtr, std::string(templateChar) + "index",
1150 std::to_string(foundDeviceIdx));
Ed Tanous12bc7932019-02-26 14:36:20 -08001151
Ed Tanous12bc7932019-02-26 14:36:20 -08001152 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001153 {
James Feistf5125b02019-06-06 11:27:43 -07001154 std::string templateName = templateChar + foundDevicePair.first;
1155 boost::iterator_range<std::string::const_iterator> find =
1156 boost::ifind_first(*strPtr, templateName);
1157 if (find)
James Feist1b2e2242018-01-30 13:45:19 -08001158 {
James Feistf5125b02019-06-06 11:27:43 -07001159 size_t start = find.begin() - strPtr->begin();
1160 // check for additional operations
1161 if (find.end() != strPtr->end())
1162 {
1163 // save the prefix
1164 std::string prefix = strPtr->substr(0, start);
1165
1166 // operate on the rest (+1 for trailing space)
1167 std::string end =
1168 strPtr->substr(start + templateName.size() + 1);
1169
1170 std::vector<std::string> split;
1171 boost::split(split, end, boost::is_any_of(" "));
1172
1173 // need at least 1 operation and number
1174 if (split.size() < 2)
1175 {
1176 std::cerr << "Syntax error on template replacement of "
1177 << *strPtr << "\n";
1178 for (const std::string& data : split)
1179 {
1180 std::cerr << data << " ";
1181 }
1182 std::cerr << "\n";
1183 continue;
1184 }
1185
1186 // we assume that the replacement is a number, because we can
1187 // only do math on numbers.. we might concatenate strings in the
1188 // future, but thats later
1189 int number =
1190 std::visit(VariantToIntVisitor(), foundDevicePair.second);
1191
1192 bool isOperator = true;
1193 TemplateOperation next = TemplateOperation::addition;
1194
1195 auto it = split.begin();
1196
1197 for (; it != split.end(); it++)
1198 {
1199 if (isOperator)
1200 {
1201 if (*it == "+")
1202 {
1203 next = TemplateOperation::addition;
1204 }
1205 else if (*it == "-")
1206 {
1207 next = TemplateOperation::subtraction;
1208 }
1209 else if (*it == "*")
1210 {
1211 next = TemplateOperation::multiplication;
1212 }
1213 else if (*it == R"(%)")
1214 {
1215 next = TemplateOperation::modulo;
1216 }
1217 else if (*it == R"(/)")
1218 {
1219 next = TemplateOperation::division;
1220 }
1221 else
1222 {
1223 break;
1224 }
1225 }
1226 else
1227 {
1228 int constant = 0;
1229 try
1230 {
1231 constant = std::stoi(*it);
1232 }
1233 catch (std::invalid_argument&)
1234 {
1235 std::cerr
1236 << "Parameter not supported for templates "
1237 << *it << "\n";
1238 continue;
1239 }
1240 switch (next)
1241 {
1242 case TemplateOperation::addition:
1243 {
1244 number += constant;
1245 break;
1246 }
1247 case TemplateOperation::subtraction:
1248 {
1249 number -= constant;
1250 break;
1251 }
1252 case TemplateOperation::multiplication:
1253 {
1254 number *= constant;
1255 break;
1256 }
1257 case TemplateOperation::division:
1258 {
1259 number /= constant;
1260 break;
1261 }
1262 case TemplateOperation::modulo:
1263 {
1264 number = number % constant;
1265 break;
1266 }
1267
1268 default:
1269 break;
1270 }
1271 }
1272 isOperator = !isOperator;
1273 }
1274 std::string result = prefix + std::to_string(number);
1275
1276 if (it != split.end())
1277 {
1278 for (; it != split.end(); it++)
1279 {
1280 result += " " + *it;
1281 }
1282 }
1283 keyPair.value() = result;
1284 }
1285 else
1286 {
1287 std::visit([&](auto&& val) { keyPair.value() = val; },
1288 foundDevicePair.second);
1289 }
1290
Ed Tanous12bc7932019-02-26 14:36:20 -08001291 // We probably just invalidated the pointer above, so set it to null
1292 strPtr = nullptr;
1293 break;
James Feist1b2e2242018-01-30 13:45:19 -08001294 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001295
1296 std::string probeValue =
1297 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1298 boost::replace_all(*strPtr, templateName, probeValue);
1299 }
1300
1301 strPtr = keyPair.value().get_ptr<std::string*>();
1302 if (strPtr == nullptr)
1303 {
1304 return;
James Feist1b2e2242018-01-30 13:45:19 -08001305 }
James Feistc6090822019-01-04 16:02:48 -08001306
1307 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001308 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001309 {
1310 try
1311 {
James Feistc6090822019-01-04 16:02:48 -08001312 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001313 int64_t temp = std::stoul(*strPtr, &pos, 0);
1314 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001315 {
1316 keyPair.value() = static_cast<uint64_t>(temp);
1317 }
James Feist28dc2da2018-10-15 14:47:42 -07001318 }
1319 catch (std::invalid_argument)
1320 {
1321 }
James Feistc6090822019-01-04 16:02:48 -08001322 catch (std::out_of_range)
1323 {
1324 }
James Feist28dc2da2018-10-15 14:47:42 -07001325 }
James Feistf5125b02019-06-06 11:27:43 -07001326 // non-hex numbers
1327 else
1328 {
1329 try
1330 {
1331 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
1332 keyPair.value() = temp;
1333 }
1334 catch (boost::bad_lexical_cast&)
1335 {
1336 }
1337 }
James Feist1b2e2242018-01-30 13:45:19 -08001338}
1339
James Feist8f2710a2018-05-09 17:18:55 -07001340// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001341bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001342{
1343 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001344 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001345 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1346 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001347 {
1348 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001349 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001350 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001351 }
James Feistb4383f42018-08-06 16:54:10 -07001352
1353 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1354 globalSchema);
1355 if (!schemaStream.good())
1356 {
1357 std::cerr
1358 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1359 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001360 return false;
James Feistb4383f42018-08-06 16:54:10 -07001361 }
1362 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1363 if (schema.is_discarded())
1364 {
1365 std::cerr
1366 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1367 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001368 return false;
James Feistb4383f42018-08-06 16:54:10 -07001369 }
1370
James Feista465ccc2019-02-08 12:51:01 -08001371 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001372 {
1373 std::ifstream jsonStream(jsonPath.c_str());
1374 if (!jsonStream.good())
1375 {
1376 std::cerr << "unable to open " << jsonPath.string() << "\n";
1377 continue;
1378 }
1379 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1380 if (data.is_discarded())
1381 {
1382 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1383 continue;
1384 }
James Feist8da99192019-01-24 08:20:16 -08001385 /*
1386 * todo(james): reenable this once less things are in flight
1387 *
James Feistb4383f42018-08-06 16:54:10 -07001388 if (!validateJson(schema, data))
1389 {
1390 std::cerr << "Error validating " << jsonPath.string() << "\n";
1391 continue;
1392 }
James Feist8da99192019-01-24 08:20:16 -08001393 */
James Feistb4383f42018-08-06 16:54:10 -07001394
James Feist3cb5fec2018-01-23 14:41:51 -08001395 if (data.type() == nlohmann::json::value_t::array)
1396 {
James Feista465ccc2019-02-08 12:51:01 -08001397 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001398 {
1399 configurations.emplace_back(d);
1400 }
1401 }
1402 else
1403 {
1404 configurations.emplace_back(data);
1405 }
1406 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001407 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001408}
James Feist3cb5fec2018-01-23 14:41:51 -08001409
James Feist8f2710a2018-05-09 17:18:55 -07001410struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001411{
James Feist75fdeeb2018-02-20 14:26:16 -08001412
James Feista465ccc2019-02-08 12:51:01 -08001413 PerformScan(nlohmann::json& systemConfiguration,
1414 std::list<nlohmann::json>& configurations,
1415 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001416 _systemConfiguration(systemConfiguration),
1417 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001418 {
James Feist8f2710a2018-05-09 17:18:55 -07001419 }
1420 void run()
1421 {
1422 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001423 {
James Feist1e3e6982018-08-03 16:09:28 -07001424 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001425 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001426
James Feist1b2e2242018-01-30 13:45:19 -08001427 nlohmann::json probeCommand;
1428 // check for poorly formatted fields, probe must be an array
1429 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001430 {
1431 std::cerr << "configuration file missing probe:\n " << *it
1432 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001433 it = _configurations.erase(it);
1434 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001435 }
James Feist1b2e2242018-01-30 13:45:19 -08001436 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001437 {
1438 probeCommand = nlohmann::json::array();
1439 probeCommand.push_back(*findProbe);
1440 }
1441 else
1442 {
1443 probeCommand = *findProbe;
1444 }
James Feist1b2e2242018-01-30 13:45:19 -08001445
1446 if (findName == it->end())
1447 {
1448 std::cerr << "configuration file missing name:\n " << *it
1449 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001450 it = _configurations.erase(it);
1451 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001452 }
James Feistf1b14142019-04-10 15:22:09 -07001453 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001454
James Feistf1b14142019-04-10 15:22:09 -07001455 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1456 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001457 {
James Feist8f2710a2018-05-09 17:18:55 -07001458 it = _configurations.erase(it);
1459 continue;
1460 }
James Feistf1b14142019-04-10 15:22:09 -07001461 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001462
James Feist8f2710a2018-05-09 17:18:55 -07001463 // store reference to this to children to makes sure we don't get
1464 // destroyed too early
1465 auto thisRef = shared_from_this();
1466 auto p = std::make_shared<PerformProbe>(
1467 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001468 [&, recordPtr, probeName,
1469 thisRef](std::vector<std::optional<boost::container::flat_map<
1470 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001471 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001472
James Feistf1b14142019-04-10 15:22:09 -07001473 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001474 size_t foundDeviceIdx = 0;
1475
James Feista465ccc2019-02-08 12:51:01 -08001476 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001477 {
James Feistf5125b02019-06-06 11:27:43 -07001478 nlohmann::json record = *recordPtr;
James Feistf1b14142019-04-10 15:22:09 -07001479 std::string recordName;
1480 size_t hash = 0;
1481 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001482 {
James Feistf1b14142019-04-10 15:22:09 -07001483 // use an array so alphabetical order from the
1484 // flat_map is maintained
1485 auto device = nlohmann::json::array();
1486 for (auto& devPair : *foundDevice)
1487 {
1488 device.push_back(devPair.first);
1489 std::visit(
1490 [&device](auto&& v) {
1491 device.push_back(v);
1492 },
1493 devPair.second);
1494 }
1495 hash = std::hash<std::string>{}(probeName +
1496 device.dump());
1497 // hashes are hard to distinguish, use the
1498 // non-hashed version if we want debug
1499 if constexpr (DEBUG)
1500 {
1501 recordName = probeName + device.dump();
1502 }
1503 else
1504 {
1505 recordName = std::to_string(hash);
1506 }
James Feist8f2710a2018-05-09 17:18:55 -07001507 }
James Feistf1b14142019-04-10 15:22:09 -07001508 else
1509 {
1510 recordName = probeName;
1511 }
1512
James Feist1df06a42019-04-11 14:23:04 -07001513 auto fromLastJson = lastJson.find(recordName);
1514 if (fromLastJson != lastJson.end())
1515 {
1516 // keep user changes
1517 _systemConfiguration[recordName] = *fromLastJson;
1518 continue;
1519 }
1520
James Feistf1b14142019-04-10 15:22:09 -07001521 // insert into configuration temporarily to be able to
1522 // reference ourselves
1523
1524 _systemConfiguration[recordName] = record;
1525
1526 if (foundDevice)
1527 {
1528 for (auto keyPair = record.begin();
1529 keyPair != record.end(); keyPair++)
1530 {
1531 templateCharReplace(keyPair, *foundDevice,
1532 foundDeviceIdx);
1533 }
1534 }
1535 auto findExpose = record.find("Exposes");
1536 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001537 {
1538 continue;
1539 }
James Feista465ccc2019-02-08 12:51:01 -08001540 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001541 {
1542 for (auto keyPair = expose.begin();
1543 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001544 {
James Feist1b2e2242018-01-30 13:45:19 -08001545
James Feist8f2710a2018-05-09 17:18:55 -07001546 // fill in template characters with devices
1547 // found
James Feistf1b14142019-04-10 15:22:09 -07001548 if (foundDevice)
1549 {
1550 templateCharReplace(keyPair, *foundDevice,
1551 foundDeviceIdx);
1552 }
James Feist8f2710a2018-05-09 17:18:55 -07001553 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001554 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001555 {
1556 if (keyPair.value().type() !=
1557 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001558 {
James Feist8f2710a2018-05-09 17:18:55 -07001559 std::cerr << "bind_ value must be of "
1560 "type string "
1561 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001562 continue;
1563 }
James Feist8f2710a2018-05-09 17:18:55 -07001564 bool foundBind = false;
1565 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001566 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001567
James Feista465ccc2019-02-08 12:51:01 -08001568 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001569 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001570 {
James Feist1b2e2242018-01-30 13:45:19 -08001571
James Feist8f2710a2018-05-09 17:18:55 -07001572 auto configListFind =
1573 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001574 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001575
1576 if (configListFind ==
1577 configurationPair.value()
1578 .end() ||
1579 configListFind->type() !=
1580 nlohmann::json::value_t::array)
1581 {
1582 continue;
1583 }
James Feista465ccc2019-02-08 12:51:01 -08001584 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001585 *configListFind)
1586 {
1587 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001588 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001589 if (boost::iequals(
1590 foundObjectName,
1591 keyPair.value()
1592 .get<std::string>()))
1593 {
James Feist1e3e6982018-08-03 16:09:28 -07001594 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001595 "okay";
1596 expose[bind] = exposedObject;
1597
1598 foundBind = true;
1599 break;
1600 }
1601 }
1602 if (foundBind)
1603 {
James Feist3cb5fec2018-01-23 14:41:51 -08001604 break;
1605 }
1606 }
James Feist8f2710a2018-05-09 17:18:55 -07001607 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001608 {
James Feist8f2710a2018-05-09 17:18:55 -07001609 std::cerr << "configuration file "
1610 "dependency error, "
1611 "could not find bind "
1612 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001613 }
1614 }
1615 }
1616 }
James Feistf1b14142019-04-10 15:22:09 -07001617 // overwrite ourselves with cleaned up version
1618 _systemConfiguration[recordName] = record;
James Feist1a996582019-05-14 15:10:06 -07001619
1620 logDeviceAdded(record);
James Feist1df06a42019-04-11 14:23:04 -07001621
James Feistf1b14142019-04-10 15:22:09 -07001622 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001623 }
James Feist8f2710a2018-05-09 17:18:55 -07001624 });
1625 p->run();
1626 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001627 }
1628 }
James Feist75fdeeb2018-02-20 14:26:16 -08001629
James Feist8f2710a2018-05-09 17:18:55 -07001630 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001631 {
James Feist8f2710a2018-05-09 17:18:55 -07001632 if (_passed)
1633 {
1634 auto nextScan = std::make_shared<PerformScan>(
1635 _systemConfiguration, _configurations, std::move(_callback));
1636 nextScan->run();
1637 }
1638 else
1639 {
1640 _callback();
1641 }
1642 }
James Feista465ccc2019-02-08 12:51:01 -08001643 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001644 std::list<nlohmann::json> _configurations;
1645 std::function<void(void)> _callback;
1646 std::vector<std::shared_ptr<PerformProbe>> _probes;
1647 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001648 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001649};
James Feistc95cb142018-02-26 10:41:42 -08001650
James Feist1df06a42019-04-11 14:23:04 -07001651void startRemovedTimer(boost::asio::deadline_timer& timer,
1652 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1653 nlohmann::json& systemConfiguration)
1654{
1655 static bool scannedPowerOff = false;
1656 static bool scannedPowerOn = false;
1657
1658 if (scannedPowerOn)
1659 {
1660 return;
1661 }
1662
1663 if (!isPowerOn() && scannedPowerOff)
1664 {
1665 return;
1666 }
1667
1668 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001669 timer.async_wait(
1670 [&systemConfiguration](const boost::system::error_code& ec) {
1671 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001672 {
James Feist1a996582019-05-14 15:10:06 -07001673 // we were cancelled
1674 return;
1675 }
1676
1677 bool powerOff = !isPowerOn();
1678 for (const auto& item : lastJson.items())
1679 {
1680 if (systemConfiguration.find(item.key()) ==
1681 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001682 {
James Feist1a996582019-05-14 15:10:06 -07001683 bool isDetectedPowerOn = false;
1684 auto powerState = item.value().find("PowerState");
1685 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001686 {
James Feist1a996582019-05-14 15:10:06 -07001687 auto ptr = powerState->get_ptr<const std::string*>();
1688 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001689 {
James Feist1a996582019-05-14 15:10:06 -07001690 if (*ptr == "On" || *ptr == "BiosPost")
1691 {
1692 isDetectedPowerOn = true;
1693 }
James Feist1df06a42019-04-11 14:23:04 -07001694 }
1695 }
James Feist1a996582019-05-14 15:10:06 -07001696 if (powerOff && isDetectedPowerOn)
1697 {
1698 // power not on yet, don't know if it's there or not
1699 continue;
1700 }
1701 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1702 {
1703 // already logged it when power was off
1704 continue;
1705 }
James Feist1df06a42019-04-11 14:23:04 -07001706
James Feist1a996582019-05-14 15:10:06 -07001707 logDeviceRemoved(item.value());
1708 }
James Feist1df06a42019-04-11 14:23:04 -07001709 }
James Feist1a996582019-05-14 15:10:06 -07001710 scannedPowerOff = true;
1711 if (!powerOff)
1712 {
1713 scannedPowerOn = true;
1714 }
1715 });
James Feist1df06a42019-04-11 14:23:04 -07001716}
1717
James Feist8f2710a2018-05-09 17:18:55 -07001718// main properties changed entry
1719void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001720 boost::asio::io_service& io,
1721 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1722 nlohmann::json& systemConfiguration,
1723 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001724{
1725 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001726 static bool timerRunning;
1727
1728 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001729 timer.expires_from_now(boost::posix_time::seconds(1));
1730
1731 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001732 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001733 if (ec == boost::asio::error::operation_aborted)
1734 {
1735 // we were cancelled
1736 return;
1737 }
1738 else if (ec)
1739 {
1740 std::cerr << "async wait error " << ec << "\n";
1741 return;
1742 }
James Feist1df06a42019-04-11 14:23:04 -07001743 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001744
1745 nlohmann::json oldConfiguration = systemConfiguration;
1746 DBUS_PROBE_OBJECTS.clear();
1747
1748 std::list<nlohmann::json> configurations;
1749 if (!findJsonFiles(configurations))
1750 {
1751 std::cerr << "cannot find json files\n";
1752 return;
1753 }
1754
1755 auto perfScan = std::make_shared<PerformScan>(
1756 systemConfiguration, configurations, [&, oldConfiguration]() {
1757 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001758 for (auto it = newConfiguration.begin();
1759 it != newConfiguration.end();)
1760 {
1761 auto findKey = oldConfiguration.find(it.key());
1762 if (findKey != oldConfiguration.end())
1763 {
1764 it = newConfiguration.erase(it);
1765 }
1766 else
1767 {
1768 it++;
1769 }
1770 }
James Feist8f2710a2018-05-09 17:18:55 -07001771 registerCallbacks(io, dbusMatches, systemConfiguration,
1772 objServer);
1773 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001774 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001775
James Feistbb43d022018-06-12 15:44:33 -07001776 io.post([&]() {
1777 if (!writeJsonFiles(systemConfiguration))
1778 {
1779 std::cerr << "Error writing json files\n";
1780 }
1781 });
James Feist8f2710a2018-05-09 17:18:55 -07001782 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001783 postToDbus(newConfiguration, systemConfiguration,
1784 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001785 if (!timerRunning)
1786 {
1787 startRemovedTimer(timer, dbusMatches,
1788 systemConfiguration);
1789 }
James Feist8f2710a2018-05-09 17:18:55 -07001790 });
1791 });
1792 });
1793 perfScan->run();
1794 });
James Feist75fdeeb2018-02-20 14:26:16 -08001795}
1796
James Feista465ccc2019-02-08 12:51:01 -08001797void registerCallbacks(boost::asio::io_service& io,
1798 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1799 nlohmann::json& systemConfiguration,
1800 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001801{
1802 static boost::container::flat_set<std::string> watchedObjects;
1803
James Feista465ccc2019-02-08 12:51:01 -08001804 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001805 {
1806 auto findObject = watchedObjects.find(objectMap.first);
1807 if (findObject != watchedObjects.end())
1808 {
1809 continue;
1810 }
James Feist8f2710a2018-05-09 17:18:55 -07001811 std::function<void(sdbusplus::message::message & message)>
1812 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001813
James Feista465ccc2019-02-08 12:51:01 -08001814 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001815 propertiesChangedCallback(io, dbusMatches,
1816 systemConfiguration, objServer);
1817 };
1818
1819 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001820 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001821 "type='signal',member='PropertiesChanged',arg0='" +
1822 objectMap.first + "'",
1823 eventHandler);
1824 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001825 }
1826}
1827
James Feista465ccc2019-02-08 12:51:01 -08001828int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001829{
1830 // setup connection to dbus
1831 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001832 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001833 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001834
James Feist8f2710a2018-05-09 17:18:55 -07001835 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001836
James Feist8f2710a2018-05-09 17:18:55 -07001837 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1838 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1839 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001840
James Feist8f2710a2018-05-09 17:18:55 -07001841 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1842 objServer.add_interface("/xyz/openbmc_project/inventory",
1843 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001844
1845 // to keep reference to the match / filter objects so they don't get
1846 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001847 std::vector<sdbusplus::bus::match::match> dbusMatches;
1848
1849 nlohmann::json systemConfiguration = nlohmann::json::object();
1850
1851 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001852 "Notify",
1853 [](const boost::container::flat_map<
1854 std::string,
1855 boost::container::flat_map<std::string, BasicVariantType>>&
1856 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001857 inventoryIface->initialize();
1858
1859 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001860#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001861 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001862#endif
James Feist8f2710a2018-05-09 17:18:55 -07001863 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1864 objServer);
1865 });
James Feist4131aea2018-03-09 09:47:30 -08001866
James Feistfd1264a2018-05-03 12:10:00 -07001867 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001868 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1869 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001870 });
James Feist8f2710a2018-05-09 17:18:55 -07001871 entityIface->initialize();
1872
James Feist1df06a42019-04-11 14:23:04 -07001873 if (fwVersionIsSame())
1874 {
1875 if (std::filesystem::is_regular_file(currentConfiguration))
1876 {
1877 // this file could just be deleted, but it's nice for debug
1878 std::filesystem::create_directory(tempConfigDir);
1879 std::filesystem::remove(lastConfiguration);
1880 std::filesystem::copy(currentConfiguration, lastConfiguration);
1881 std::filesystem::remove(currentConfiguration);
1882
1883 std::ifstream jsonStream(lastConfiguration);
1884 if (jsonStream.good())
1885 {
1886 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1887 if (data.is_discarded())
1888 {
1889 std::cerr << "syntax error in " << lastConfiguration
1890 << "\n";
1891 }
1892 else
1893 {
1894 lastJson = std::move(data);
1895 }
1896 }
1897 else
1898 {
1899 std::cerr << "unable to open " << lastConfiguration << "\n";
1900 }
1901 }
1902 }
1903 else
1904 {
1905 // not an error, just logging at this level to make it in the journal
1906 std::cerr << "Clearing previous configuration\n";
1907 std::filesystem::remove(currentConfiguration);
1908 }
1909
1910 // some boards only show up after power is on, we want to not say they are
1911 // removed until the same state happens
1912 setupPowerMatch(SYSTEM_BUS);
1913
James Feist1b2e2242018-01-30 13:45:19 -08001914 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001915
1916 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001917}