blob: 4e7ea9d7b5cc2c2b5c560f1588eaba895cdd744e [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 Feist98132792019-07-09 13:29:09 -0700184 interface](boost::system::error_code& errc,
James Feista465ccc2019-02-08 12:51:01 -0800185 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700186 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700187 {
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>());
James Feist98132792019-07-09 13:29:09 -0700249 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700250
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 Feist98132792019-07-09 13:29:09 -0700254 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700255 {
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;
James Feist54a0dca2019-06-26 10:34:54 -0700331 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800332
James Feista465ccc2019-02-08 12:51:01 -0800333 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800334 {
335 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800336 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800337 cmp_str>::const_iterator probeType;
338
339 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
340 probeType++)
341 {
342 if (probe.find(probeType->first) != std::string::npos)
343 {
344 foundProbe = true;
345 break;
346 }
347 }
348 if (foundProbe)
349 {
350 switch (probeType->second)
351 {
James Feist9eb0b582018-04-27 12:15:46 -0700352 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800353 {
James Feiste31e00a2019-07-24 10:45:43 -0700354 cur = false;
355 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800356 }
James Feist9eb0b582018-04-27 12:15:46 -0700357 case probe_type_codes::TRUE_T:
358 {
James Feiste31e00a2019-07-24 10:45:43 -0700359 cur = true;
360 break;
James Feist9eb0b582018-04-27 12:15:46 -0700361 }
362 case probe_type_codes::MATCH_ONE:
363 {
364 // set current value to last, this probe type shouldn't
365 // affect the outcome
366 cur = ret;
367 matchOne = true;
368 break;
369 }
370 /*case probe_type_codes::AND:
371 break;
372 case probe_type_codes::OR:
373 break;
374 // these are no-ops until the last command switch
375 */
376 case probe_type_codes::FOUND:
377 {
378 if (!std::regex_search(probe, match, command))
379 {
James Feist0eb40352019-04-09 14:44:04 -0700380 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700381 << "\n";
382 return false;
383 }
384 std::string commandStr = *(match.begin() + 1);
385 boost::replace_all(commandStr, "'", "");
386 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
387 commandStr) != PASSED_PROBES.end());
388 break;
389 }
James Feist0eb40352019-04-09 14:44:04 -0700390 default:
391 {
392 break;
393 }
James Feist3cb5fec2018-01-23 14:41:51 -0800394 }
395 }
396 // look on dbus for object
397 else
398 {
399 if (!std::regex_search(probe, match, command))
400 {
James Feist0eb40352019-04-09 14:44:04 -0700401 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800402 return false;
403 }
404 std::string commandStr = *(match.begin() + 1);
405 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700406 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800407 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800408 auto json = nlohmann::json::parse(commandStr, nullptr, false);
409 if (json.is_discarded())
410 {
James Feist0eb40352019-04-09 14:44:04 -0700411 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800412 return false;
413 }
414 // we can match any (string, variant) property. (string, string)
415 // does a regex
416 std::map<std::string, nlohmann::json> dbusProbeMap =
417 json.get<std::map<std::string, nlohmann::json>>();
418 auto findStart = probe.find("(");
419 if (findStart == std::string::npos)
420 {
421 return false;
422 }
423 std::string probeInterface = probe.substr(0, findStart);
424 cur =
425 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
426 }
427
428 // some functions like AND and OR only take affect after the
429 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700430 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800431 {
James Feist54a0dca2019-06-26 10:34:54 -0700432 ret = cur && ret;
433 }
434 else if (lastCommand == probe_type_codes::OR)
435 {
436 ret = cur || ret;
437 }
438
439 if (first)
440 {
441 ret = cur;
442 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
444 lastCommand = probeType != PROBE_TYPES.end()
445 ? probeType->second
446 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800447 }
448
449 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800450 if (ret && foundDevs.size() == 0)
451 {
James Feistf1b14142019-04-10 15:22:09 -0700452 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800453 }
James Feist0eb40352019-04-09 14:44:04 -0700454 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700455 {
James Feist71f295f2019-06-20 13:35:12 -0700456 // match the last one
457 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700458 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700459
460 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700461 }
James Feist3cb5fec2018-01-23 14:41:51 -0800462 return ret;
463}
James Feist8f2710a2018-05-09 17:18:55 -0700464// this class finds the needed dbus fields and on destruction runs the probe
465struct PerformProbe : std::enable_shared_from_this<PerformProbe>
466{
James Feist3cb5fec2018-01-23 14:41:51 -0800467
James Feist8f2710a2018-05-09 17:18:55 -0700468 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800469 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700470 std::function<void(std::vector<std::optional<boost::container::flat_map<
471 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700472 _probeCommand(probeCommand),
473 _callback(std::move(callback))
474 {
475 }
476 ~PerformProbe()
477 {
James Feistf1b14142019-04-10 15:22:09 -0700478 std::vector<std::optional<
479 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700480 foundDevs;
481 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700482 {
James Feist0eb40352019-04-09 14:44:04 -0700483 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700484 }
485 }
486 void run()
487 {
488 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700489
James Feista465ccc2019-02-08 12:51:01 -0800490 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700491 {
492 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800493 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700494 cmp_str>::const_iterator probeType;
495 for (probeType = PROBE_TYPES.begin();
496 probeType != PROBE_TYPES.end(); probeType++)
497 {
498 if (probe.find(probeType->first) != std::string::npos)
499 {
500 found = true;
501 break;
502 }
503 }
504 if (found)
505 {
506 continue;
507 }
508 // syntax requires probe before first open brace
509 auto findStart = probe.find("(");
510 std::string interface = probe.substr(0, findStart);
511
512 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
513 }
514 }
515 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700516 std::function<void(std::vector<std::optional<boost::container::flat_map<
517 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700518 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700519};
520
521// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800522bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800523{
James Feist1df06a42019-04-11 14:23:04 -0700524 std::filesystem::create_directory(configurationOutDir);
525 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700526 if (!output.good())
527 {
528 return false;
529 }
James Feist1b2e2242018-01-30 13:45:19 -0800530 output << systemConfiguration.dump(4);
531 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700532 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700533}
James Feist1b2e2242018-01-30 13:45:19 -0800534
James Feist97a63f12018-05-17 13:50:57 -0700535template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800536bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
537 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700538{
539 try
540 {
541 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800542 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700543 ref = value;
544 return true;
545 }
James Feist98132792019-07-09 13:29:09 -0700546 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700547 {
548 return false;
549 }
550}
James Feistbb43d022018-06-12 15:44:33 -0700551
James Feistebcc26b2019-03-22 12:30:43 -0700552// template function to add array as dbus property
553template <typename PropertyType>
554void addArrayToDbus(const std::string& name, const nlohmann::json& array,
555 sdbusplus::asio::dbus_interface* iface,
556 sdbusplus::asio::PropertyPermission permission,
557 nlohmann::json& systemConfiguration,
558 const std::string& jsonPointerString)
559{
560 std::vector<PropertyType> values;
561 for (const auto& property : array)
562 {
563 auto ptr = property.get_ptr<const PropertyType*>();
564 if (ptr != nullptr)
565 {
566 values.emplace_back(*ptr);
567 }
568 }
569
570 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
571 {
572 iface->register_property(name, values);
573 }
574 else
575 {
576 iface->register_property(
577 name, values,
578 [&systemConfiguration,
579 jsonPointerString{std::string(jsonPointerString)}](
580 const std::vector<PropertyType>& newVal,
581 std::vector<PropertyType>& val) {
582 val = newVal;
583 if (!setJsonFromPointer(jsonPointerString, val,
584 systemConfiguration))
585 {
586 std::cerr << "error setting json field\n";
587 return -1;
588 }
589 if (!writeJsonFiles(systemConfiguration))
590 {
591 std::cerr << "error setting json file\n";
592 return -1;
593 }
594 return 1;
595 });
596 }
597}
598
James Feistbb43d022018-06-12 15:44:33 -0700599template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800600void addProperty(const std::string& propertyName, const PropertyType& value,
601 sdbusplus::asio::dbus_interface* iface,
602 nlohmann::json& systemConfiguration,
603 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700604 sdbusplus::asio::PropertyPermission permission)
605{
606 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
607 {
608 iface->register_property(propertyName, value);
609 return;
610 }
James Feist68500ff2018-08-08 15:40:42 -0700611 iface->register_property(
612 propertyName, value,
613 [&systemConfiguration,
614 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800615 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700616 val = newVal;
617 if (!setJsonFromPointer(jsonPointerString, val,
618 systemConfiguration))
619 {
620 std::cerr << "error setting json field\n";
621 return -1;
622 }
James Feistc6248a52018-08-14 10:09:45 -0700623 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700624 {
625 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700626 return -1;
627 }
628 return 1;
629 });
630}
631
632void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800633 const std::string& jsonPointerPath,
634 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
635 sdbusplus::asio::object_server& objServer,
636 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700637{
638 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
639 iface->register_method(
640 "Delete", [&objServer, &systemConfiguration, interface,
641 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700642 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700643 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700644 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700645 {
646 // this technically can't happen as the pointer is pointing to
647 // us
648 throw DBusInternalError();
649 }
650 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700651 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700652 {
653 std::cerr << "Can't delete interface " << jsonPointerPath
654 << "\n";
655 throw DBusInternalError();
656 }
657 systemConfiguration[ptr] = nullptr;
658
659 if (!writeJsonFiles(systemConfiguration))
660 {
661 std::cerr << "error setting json file\n";
662 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700663 }
James Feistbb43d022018-06-12 15:44:33 -0700664 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700665 });
James Feistbb43d022018-06-12 15:44:33 -0700666}
667
James Feist1b2e2242018-01-30 13:45:19 -0800668// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700669void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800670 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
671 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
672 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700673 sdbusplus::asio::PropertyPermission permission =
674 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800675{
James Feista465ccc2019-02-08 12:51:01 -0800676 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800677 {
James Feist8f2710a2018-05-09 17:18:55 -0700678 auto type = dictPair.value().type();
679 bool array = false;
680 if (dictPair.value().type() == nlohmann::json::value_t::array)
681 {
682 array = true;
683 if (!dictPair.value().size())
684 {
685 continue;
686 }
687 type = dictPair.value()[0].type();
688 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800689 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700690 {
691 if (arrayItem.type() != type)
692 {
693 isLegal = false;
694 break;
695 }
696 }
697 if (!isLegal)
698 {
699 std::cerr << "dbus format error" << dictPair.value() << "\n";
700 continue;
701 }
James Feista218ddb2019-04-11 14:01:31 -0700702 }
703 if (type == nlohmann::json::value_t::object)
704 {
705 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700706 }
James Feist97a63f12018-05-17 13:50:57 -0700707 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700708 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
709 {
710 // all setable numbers are doubles as it is difficult to always
711 // create a configuration file with all whole numbers as decimals
712 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700713 if (array)
714 {
715 if (dictPair.value()[0].is_number())
716 {
717 type = nlohmann::json::value_t::number_float;
718 }
719 }
720 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700721 {
722 type = nlohmann::json::value_t::number_float;
723 }
724 }
725
James Feist8f2710a2018-05-09 17:18:55 -0700726 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800727 {
James Feist9eb0b582018-04-27 12:15:46 -0700728 case (nlohmann::json::value_t::boolean):
729 {
James Feist8f2710a2018-05-09 17:18:55 -0700730 if (array)
731 {
732 // todo: array of bool isn't detected correctly by
733 // sdbusplus, change it to numbers
734 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700735 iface.get(), permission,
736 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700737 }
James Feistbb43d022018-06-12 15:44:33 -0700738
James Feist97a63f12018-05-17 13:50:57 -0700739 else
740 {
James Feistbb43d022018-06-12 15:44:33 -0700741 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700742 iface.get(), systemConfiguration, key,
743 permission);
James Feist97a63f12018-05-17 13:50:57 -0700744 }
James Feist9eb0b582018-04-27 12:15:46 -0700745 break;
746 }
747 case (nlohmann::json::value_t::number_integer):
748 {
James Feist8f2710a2018-05-09 17:18:55 -0700749 if (array)
750 {
751 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700752 iface.get(), permission,
753 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700754 }
755 else
756 {
James Feistbb43d022018-06-12 15:44:33 -0700757 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700758 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700759 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_unsigned):
764 {
James Feist8f2710a2018-05-09 17:18:55 -0700765 if (array)
766 {
767 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700768 iface.get(), permission,
769 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700770 }
771 else
772 {
James Feistbb43d022018-06-12 15:44:33 -0700773 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700774 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700775 systemConfiguration, key,
776 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700777 }
James Feist9eb0b582018-04-27 12:15:46 -0700778 break;
779 }
780 case (nlohmann::json::value_t::number_float):
781 {
James Feist8f2710a2018-05-09 17:18:55 -0700782 if (array)
783 {
784 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700785 iface.get(), permission,
786 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700787 }
James Feistbb43d022018-06-12 15:44:33 -0700788
James Feist97a63f12018-05-17 13:50:57 -0700789 else
790 {
James Feistbb43d022018-06-12 15:44:33 -0700791 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700792 iface.get(), systemConfiguration, key,
793 permission);
James Feist97a63f12018-05-17 13:50:57 -0700794 }
James Feist9eb0b582018-04-27 12:15:46 -0700795 break;
796 }
797 case (nlohmann::json::value_t::string):
798 {
James Feist8f2710a2018-05-09 17:18:55 -0700799 if (array)
800 {
James Feistebcc26b2019-03-22 12:30:43 -0700801 addArrayToDbus<std::string>(
802 dictPair.key(), dictPair.value(), iface.get(),
803 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700804 }
805 else
806 {
James Feistc6248a52018-08-14 10:09:45 -0700807 addProperty(
808 dictPair.key(), dictPair.value().get<std::string>(),
809 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700810 }
James Feist9eb0b582018-04-27 12:15:46 -0700811 break;
812 }
James Feist0eb40352019-04-09 14:44:04 -0700813 default:
814 {
James Feista218ddb2019-04-11 14:01:31 -0700815 std::cerr << "Unexpected json type in system configuration "
816 << dictPair.key() << ": "
817 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700818 break;
819 }
James Feist1b2e2242018-01-30 13:45:19 -0800820 }
821 }
James Feistc6248a52018-08-14 10:09:45 -0700822 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
823 {
824 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
825 systemConfiguration);
826 }
James Feist8f2710a2018-05-09 17:18:55 -0700827 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800828}
829
James Feista465ccc2019-02-08 12:51:01 -0800830sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700831{
832 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
833 interface) != settableInterfaces.end()
834 ? sdbusplus::asio::PropertyPermission::readWrite
835 : sdbusplus::asio::PropertyPermission::readOnly;
836}
837
James Feista465ccc2019-02-08 12:51:01 -0800838void createAddObjectMethod(const std::string& jsonPointerPath,
839 const std::string& path,
840 nlohmann::json& systemConfiguration,
841 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700842{
843 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
844
845 iface->register_method(
846 "AddObject",
847 [&systemConfiguration, &objServer,
848 jsonPointerPath{std::string(jsonPointerPath)},
849 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800850 const boost::container::flat_map<std::string, JsonVariantType>&
851 data) {
James Feist68500ff2018-08-08 15:40:42 -0700852 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800853 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700854 auto findExposes = base.find("Exposes");
855
856 if (findExposes == base.end())
857 {
858 throw std::invalid_argument("Entity must have children.");
859 }
860
861 // this will throw invalid-argument to sdbusplus if invalid json
862 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800863 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700864 {
James Feista465ccc2019-02-08 12:51:01 -0800865 nlohmann::json& newJson = newData[item.first];
866 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
867 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700868 }
869
870 auto findName = newData.find("Name");
871 auto findType = newData.find("Type");
872 if (findName == newData.end() || findType == newData.end())
873 {
874 throw std::invalid_argument("AddObject missing Name or Type");
875 }
James Feista465ccc2019-02-08 12:51:01 -0800876 const std::string* type = findType->get_ptr<const std::string*>();
877 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700878 if (type == nullptr || name == nullptr)
879 {
880 throw std::invalid_argument("Type and Name must be a string.");
881 }
882
883 size_t lastIndex = 0;
884 // we add in the "exposes"
885 for (; lastIndex < findExposes->size(); lastIndex++)
886 {
887 if (findExposes->at(lastIndex)["Name"] == *name &&
888 findExposes->at(lastIndex)["Type"] == *type)
889 {
890 throw std::invalid_argument(
891 "Field already in JSON, not adding");
892 }
893 lastIndex++;
894 }
895
896 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
897 *type + ".json");
898 // todo(james) we might want to also make a list of 'can add'
899 // interfaces but for now I think the assumption if there is a
900 // schema avaliable that it is allowed to update is fine
901 if (!schemaFile.good())
902 {
903 throw std::invalid_argument(
904 "No schema avaliable, cannot validate.");
905 }
906 nlohmann::json schema =
907 nlohmann::json::parse(schemaFile, nullptr, false);
908 if (schema.is_discarded())
909 {
910 std::cerr << "Schema not legal" << *type << ".json\n";
911 throw DBusInternalError();
912 }
913 if (!validateJson(schema, newData))
914 {
915 throw std::invalid_argument("Data does not match schema");
916 }
917
James Feist16a02f22019-05-13 15:21:37 -0700918 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700919 if (!writeJsonFiles(systemConfiguration))
920 {
921 std::cerr << "Error writing json files\n";
922 throw DBusInternalError();
923 }
924 std::string dbusName = *name;
925
926 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700927 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist98132792019-07-09 13:29:09 -0700928 auto interface = objServer.add_interface(
James Feist68500ff2018-08-08 15:40:42 -0700929 path + "/" + dbusName,
930 "xyz.openbmc_project.Configuration." + *type);
931 // permission is read-write, as since we just created it, must be
932 // runtime modifiable
933 populateInterfaceFromJson(
934 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700935 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700936 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700937 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700938 });
939 iface->initialize();
940}
941
James Feista465ccc2019-02-08 12:51:01 -0800942void postToDbus(const nlohmann::json& newConfiguration,
943 nlohmann::json& systemConfiguration,
944 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800945
James Feist1b2e2242018-01-30 13:45:19 -0800946{
James Feist97a63f12018-05-17 13:50:57 -0700947 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800948 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800949 {
James Feistf1b14142019-04-10 15:22:09 -0700950 std::string boardKey = boardPair.value()["Name"];
951 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700952 // loop through newConfiguration, but use values from system
953 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700954 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700955 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800956 std::string boardType;
957 if (findBoardType != boardValues.end() &&
958 findBoardType->type() == nlohmann::json::value_t::string)
959 {
960 boardType = findBoardType->get<std::string>();
961 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700962 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800963 }
964 else
965 {
966 std::cerr << "Unable to find type for " << boardKey
967 << " reverting to Chassis.\n";
968 boardType = "Chassis";
969 }
James Feist11be6672018-04-06 14:05:32 -0700970 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800971
972 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700973 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700974 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
975 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800976
James Feist8f2710a2018-05-09 17:18:55 -0700977 auto inventoryIface = objServer.add_interface(
978 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700979
James Feist8f2710a2018-05-09 17:18:55 -0700980 auto boardIface = objServer.add_interface(
981 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700982
James Feist68500ff2018-08-08 15:40:42 -0700983 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
984 objServer);
985
James Feist97a63f12018-05-17 13:50:57 -0700986 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700987 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700988 jsonPointerPath += "/";
989 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800990 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700991 {
992 if (boardField.value().type() == nlohmann::json::value_t::object)
993 {
James Feist8f2710a2018-05-09 17:18:55 -0700994 auto iface =
995 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700996 populateInterfaceFromJson(systemConfiguration,
997 jsonPointerPath + boardField.key(),
998 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700999 }
1000 }
James Feist97a63f12018-05-17 13:50:57 -07001001
James Feist1e3e6982018-08-03 16:09:28 -07001002 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001003 if (exposes == boardValues.end())
1004 {
1005 continue;
1006 }
James Feist97a63f12018-05-17 13:50:57 -07001007 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001008 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001009
1010 // store the board level pointer so we can modify it on the way down
1011 std::string jsonPointerPathBoard = jsonPointerPath;
1012 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001013 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001014 {
James Feist97a63f12018-05-17 13:50:57 -07001015 exposesIndex++;
1016 jsonPointerPath = jsonPointerPathBoard;
1017 jsonPointerPath += std::to_string(exposesIndex);
1018
James Feistd63d18a2018-07-19 15:23:45 -07001019 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001020 if (findName == item.end())
1021 {
1022 std::cerr << "cannot find name in field " << item << "\n";
1023 continue;
1024 }
James Feist1e3e6982018-08-03 16:09:28 -07001025 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001026 // if status is not found it is assumed to be status = 'okay'
1027 if (findStatus != item.end())
1028 {
1029 if (*findStatus == "disabled")
1030 {
1031 continue;
1032 }
1033 }
James Feistd63d18a2018-07-19 15:23:45 -07001034 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001035 std::string itemType;
1036 if (findType != item.end())
1037 {
1038 itemType = findType->get<std::string>();
1039 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001040 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1041 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001042 }
1043 else
1044 {
1045 itemType = "unknown";
1046 }
1047 std::string itemName = findName->get<std::string>();
1048 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001049 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001050
James Feist8f2710a2018-05-09 17:18:55 -07001051 auto itemIface = objServer.add_interface(
1052 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001053 "xyz.openbmc_project.Configuration." + itemType);
1054
James Feist97a63f12018-05-17 13:50:57 -07001055 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001056 itemIface, item, objServer,
1057 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001058
James Feista465ccc2019-02-08 12:51:01 -08001059 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001060 {
James Feist97a63f12018-05-17 13:50:57 -07001061 jsonPointerPath = jsonPointerPathBoard +
1062 std::to_string(exposesIndex) + "/" +
1063 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001064 if (objectPair.value().type() ==
1065 nlohmann::json::value_t::object)
1066 {
James Feist8f2710a2018-05-09 17:18:55 -07001067 auto objectIface = objServer.add_interface(
1068 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001069 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001070 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001071
1072 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001073 systemConfiguration, jsonPointerPath, objectIface,
1074 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001075 }
1076 else if (objectPair.value().type() ==
1077 nlohmann::json::value_t::array)
1078 {
1079 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001080 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001081 {
James Feist8f2710a2018-05-09 17:18:55 -07001082 continue;
1083 }
1084 bool isLegal = true;
1085 auto type = objectPair.value()[0].type();
1086 if (type != nlohmann::json::value_t::object)
1087 {
1088 continue;
1089 }
1090
1091 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001092 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001093 {
1094 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001095 {
James Feist8f2710a2018-05-09 17:18:55 -07001096 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001097 break;
1098 }
James Feist8f2710a2018-05-09 17:18:55 -07001099 }
1100 if (!isLegal)
1101 {
1102 std::cerr << "dbus format error" << objectPair.value()
1103 << "\n";
1104 break;
1105 }
1106
James Feista465ccc2019-02-08 12:51:01 -08001107 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001108 {
James Feist97a63f12018-05-17 13:50:57 -07001109
James Feist8f2710a2018-05-09 17:18:55 -07001110 auto objectIface = objServer.add_interface(
1111 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001112 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001113 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001114 populateInterfaceFromJson(
1115 systemConfiguration,
1116 jsonPointerPath + "/" + std::to_string(index),
1117 objectIface, arrayItem, objServer,
1118 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001119 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001120 }
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127// finds the template character (currently set to $) and replaces the value with
1128// the field found in a dbus object i.e. $ADDRESS would get populated with the
1129// ADDRESS field from a object on dbus
1130void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001131 nlohmann::json::iterator& keyPair,
1132 const boost::container::flat_map<std::string, BasicVariantType>&
1133 foundDevice,
1134 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001135{
James Feist665267c2019-06-24 15:24:17 -07001136 if (keyPair.value().type() == nlohmann::json::value_t::object ||
1137 keyPair.value().type() == nlohmann::json::value_t::array)
James Feist11be6672018-04-06 14:05:32 -07001138 {
1139 for (auto nextLayer = keyPair.value().begin();
1140 nextLayer != keyPair.value().end(); nextLayer++)
1141 {
1142 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1143 }
1144 return;
1145 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001146
1147 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1148 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001149 {
1150 return;
1151 }
1152
James Feistf5125b02019-06-06 11:27:43 -07001153 boost::replace_all(*strPtr, std::string(templateChar) + "index",
1154 std::to_string(foundDeviceIdx));
Ed Tanous12bc7932019-02-26 14:36:20 -08001155
Ed Tanous12bc7932019-02-26 14:36:20 -08001156 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001157 {
James Feistf5125b02019-06-06 11:27:43 -07001158 std::string templateName = templateChar + foundDevicePair.first;
1159 boost::iterator_range<std::string::const_iterator> find =
1160 boost::ifind_first(*strPtr, templateName);
1161 if (find)
James Feist1b2e2242018-01-30 13:45:19 -08001162 {
James Feistf5125b02019-06-06 11:27:43 -07001163 size_t start = find.begin() - strPtr->begin();
1164 // check for additional operations
Patrick Venture4d988822019-08-09 11:25:19 -07001165 if (find.end() == strPtr->end())
James Feistf5125b02019-06-06 11:27:43 -07001166 {
1167 std::visit([&](auto&& val) { keyPair.value() = val; },
1168 foundDevicePair.second);
James Feist27e75832019-07-08 11:52:15 -07001169 return;
James Feistf5125b02019-06-06 11:27:43 -07001170 }
1171
Patrick Venture4d988822019-08-09 11:25:19 -07001172 // save the prefix
1173 std::string prefix = strPtr->substr(0, start);
1174
1175 // operate on the rest (+1 for trailing space)
1176 std::string end = strPtr->substr(start + templateName.size() + 1);
1177
1178 std::vector<std::string> split;
1179 boost::split(split, end, boost::is_any_of(" "));
1180
1181 // need at least 1 operation and number
1182 if (split.size() < 2)
1183 {
1184 std::cerr << "Syntax error on template replacement of "
1185 << *strPtr << "\n";
1186 for (const std::string& data : split)
1187 {
1188 std::cerr << data << " ";
1189 }
1190 std::cerr << "\n";
1191 continue;
1192 }
1193
1194 // we assume that the replacement is a number, because we can
1195 // only do math on numbers.. we might concatenate strings in the
1196 // future, but thats later
1197 int number =
1198 std::visit(VariantToIntVisitor(), foundDevicePair.second);
1199
1200 bool isOperator = true;
1201 TemplateOperation next = TemplateOperation::addition;
1202
1203 auto it = split.begin();
1204
1205 for (; it != split.end(); it++)
1206 {
1207 if (isOperator)
1208 {
1209 if (*it == "+")
1210 {
1211 next = TemplateOperation::addition;
1212 }
1213 else if (*it == "-")
1214 {
1215 next = TemplateOperation::subtraction;
1216 }
1217 else if (*it == "*")
1218 {
1219 next = TemplateOperation::multiplication;
1220 }
1221 else if (*it == R"(%)")
1222 {
1223 next = TemplateOperation::modulo;
1224 }
1225 else if (*it == R"(/)")
1226 {
1227 next = TemplateOperation::division;
1228 }
1229 else
1230 {
1231 break;
1232 }
1233 }
1234 else
1235 {
1236 int constant = 0;
1237 try
1238 {
1239 constant = std::stoi(*it);
1240 }
1241 catch (std::invalid_argument&)
1242 {
1243 std::cerr << "Parameter not supported for templates "
1244 << *it << "\n";
1245 continue;
1246 }
1247 switch (next)
1248 {
1249 case TemplateOperation::addition:
1250 {
1251 number += constant;
1252 break;
1253 }
1254 case TemplateOperation::subtraction:
1255 {
1256 number -= constant;
1257 break;
1258 }
1259 case TemplateOperation::multiplication:
1260 {
1261 number *= constant;
1262 break;
1263 }
1264 case TemplateOperation::division:
1265 {
1266 number /= constant;
1267 break;
1268 }
1269 case TemplateOperation::modulo:
1270 {
1271 number = number % constant;
1272 break;
1273 }
1274
1275 default:
1276 break;
1277 }
1278 }
1279 isOperator = !isOperator;
1280 }
1281 std::string result = prefix + std::to_string(number);
1282
1283 if (it != split.end())
1284 {
1285 for (; it != split.end(); it++)
1286 {
1287 result += " " + *it;
1288 }
1289 }
1290 keyPair.value() = result;
1291
Ed Tanous12bc7932019-02-26 14:36:20 -08001292 // We probably just invalidated the pointer above, so set it to null
1293 strPtr = nullptr;
1294 break;
James Feist1b2e2242018-01-30 13:45:19 -08001295 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001296 }
1297
1298 strPtr = keyPair.value().get_ptr<std::string*>();
1299 if (strPtr == nullptr)
1300 {
1301 return;
James Feist1b2e2242018-01-30 13:45:19 -08001302 }
James Feistc6090822019-01-04 16:02:48 -08001303
1304 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001305 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001306 {
1307 try
1308 {
James Feistc6090822019-01-04 16:02:48 -08001309 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001310 int64_t temp = std::stoul(*strPtr, &pos, 0);
1311 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001312 {
1313 keyPair.value() = static_cast<uint64_t>(temp);
1314 }
James Feist28dc2da2018-10-15 14:47:42 -07001315 }
James Feist98132792019-07-09 13:29:09 -07001316 catch (std::invalid_argument&)
James Feist28dc2da2018-10-15 14:47:42 -07001317 {
1318 }
James Feist98132792019-07-09 13:29:09 -07001319 catch (std::out_of_range&)
James Feistc6090822019-01-04 16:02:48 -08001320 {
1321 }
James Feist28dc2da2018-10-15 14:47:42 -07001322 }
James Feistf5125b02019-06-06 11:27:43 -07001323 // non-hex numbers
1324 else
1325 {
1326 try
1327 {
1328 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
1329 keyPair.value() = temp;
1330 }
1331 catch (boost::bad_lexical_cast&)
1332 {
1333 }
1334 }
James Feist1b2e2242018-01-30 13:45:19 -08001335}
1336
James Feist8f2710a2018-05-09 17:18:55 -07001337// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001338bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001339{
1340 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001341 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001342 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1343 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001344 {
1345 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001346 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001347 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001348 }
James Feistb4383f42018-08-06 16:54:10 -07001349
1350 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1351 globalSchema);
1352 if (!schemaStream.good())
1353 {
1354 std::cerr
1355 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1356 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001357 return false;
James Feistb4383f42018-08-06 16:54:10 -07001358 }
1359 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1360 if (schema.is_discarded())
1361 {
1362 std::cerr
1363 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1364 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001365 return false;
James Feistb4383f42018-08-06 16:54:10 -07001366 }
1367
James Feista465ccc2019-02-08 12:51:01 -08001368 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001369 {
1370 std::ifstream jsonStream(jsonPath.c_str());
1371 if (!jsonStream.good())
1372 {
1373 std::cerr << "unable to open " << jsonPath.string() << "\n";
1374 continue;
1375 }
1376 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1377 if (data.is_discarded())
1378 {
1379 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1380 continue;
1381 }
James Feist8da99192019-01-24 08:20:16 -08001382 /*
1383 * todo(james): reenable this once less things are in flight
1384 *
James Feistb4383f42018-08-06 16:54:10 -07001385 if (!validateJson(schema, data))
1386 {
1387 std::cerr << "Error validating " << jsonPath.string() << "\n";
1388 continue;
1389 }
James Feist8da99192019-01-24 08:20:16 -08001390 */
James Feistb4383f42018-08-06 16:54:10 -07001391
James Feist3cb5fec2018-01-23 14:41:51 -08001392 if (data.type() == nlohmann::json::value_t::array)
1393 {
James Feista465ccc2019-02-08 12:51:01 -08001394 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001395 {
1396 configurations.emplace_back(d);
1397 }
1398 }
1399 else
1400 {
1401 configurations.emplace_back(data);
1402 }
1403 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001404 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001405}
James Feist3cb5fec2018-01-23 14:41:51 -08001406
James Feist8f2710a2018-05-09 17:18:55 -07001407struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001408{
James Feist75fdeeb2018-02-20 14:26:16 -08001409
James Feista465ccc2019-02-08 12:51:01 -08001410 PerformScan(nlohmann::json& systemConfiguration,
1411 std::list<nlohmann::json>& configurations,
1412 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001413 _systemConfiguration(systemConfiguration),
1414 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001415 {
James Feist8f2710a2018-05-09 17:18:55 -07001416 }
1417 void run()
1418 {
1419 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001420 {
James Feist1e3e6982018-08-03 16:09:28 -07001421 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001422 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001423
James Feist1b2e2242018-01-30 13:45:19 -08001424 nlohmann::json probeCommand;
1425 // check for poorly formatted fields, probe must be an array
1426 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001427 {
1428 std::cerr << "configuration file missing probe:\n " << *it
1429 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001430 it = _configurations.erase(it);
1431 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001432 }
James Feist1b2e2242018-01-30 13:45:19 -08001433 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001434 {
1435 probeCommand = nlohmann::json::array();
1436 probeCommand.push_back(*findProbe);
1437 }
1438 else
1439 {
1440 probeCommand = *findProbe;
1441 }
James Feist1b2e2242018-01-30 13:45:19 -08001442
1443 if (findName == it->end())
1444 {
1445 std::cerr << "configuration file missing name:\n " << *it
1446 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001447 it = _configurations.erase(it);
1448 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001449 }
James Feistf1b14142019-04-10 15:22:09 -07001450 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001451
James Feistf1b14142019-04-10 15:22:09 -07001452 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1453 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001454 {
James Feist8f2710a2018-05-09 17:18:55 -07001455 it = _configurations.erase(it);
1456 continue;
1457 }
James Feistf1b14142019-04-10 15:22:09 -07001458 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001459
James Feist8f2710a2018-05-09 17:18:55 -07001460 // store reference to this to children to makes sure we don't get
1461 // destroyed too early
1462 auto thisRef = shared_from_this();
1463 auto p = std::make_shared<PerformProbe>(
1464 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001465 [&, recordPtr, probeName,
1466 thisRef](std::vector<std::optional<boost::container::flat_map<
1467 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001468 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001469
James Feistf1b14142019-04-10 15:22:09 -07001470 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001471 size_t foundDeviceIdx = 0;
1472
James Feista465ccc2019-02-08 12:51:01 -08001473 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001474 {
James Feistf5125b02019-06-06 11:27:43 -07001475 nlohmann::json record = *recordPtr;
James Feistf1b14142019-04-10 15:22:09 -07001476 std::string recordName;
1477 size_t hash = 0;
1478 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001479 {
James Feistf1b14142019-04-10 15:22:09 -07001480 // use an array so alphabetical order from the
1481 // flat_map is maintained
1482 auto device = nlohmann::json::array();
1483 for (auto& devPair : *foundDevice)
1484 {
1485 device.push_back(devPair.first);
1486 std::visit(
1487 [&device](auto&& v) {
1488 device.push_back(v);
1489 },
1490 devPair.second);
1491 }
1492 hash = std::hash<std::string>{}(probeName +
1493 device.dump());
1494 // hashes are hard to distinguish, use the
1495 // non-hashed version if we want debug
1496 if constexpr (DEBUG)
1497 {
1498 recordName = probeName + device.dump();
1499 }
1500 else
1501 {
1502 recordName = std::to_string(hash);
1503 }
James Feist8f2710a2018-05-09 17:18:55 -07001504 }
James Feistf1b14142019-04-10 15:22:09 -07001505 else
1506 {
1507 recordName = probeName;
1508 }
1509
James Feist1df06a42019-04-11 14:23:04 -07001510 auto fromLastJson = lastJson.find(recordName);
1511 if (fromLastJson != lastJson.end())
1512 {
1513 // keep user changes
1514 _systemConfiguration[recordName] = *fromLastJson;
1515 continue;
1516 }
1517
James Feistf1b14142019-04-10 15:22:09 -07001518 // insert into configuration temporarily to be able to
1519 // reference ourselves
1520
1521 _systemConfiguration[recordName] = record;
1522
1523 if (foundDevice)
1524 {
1525 for (auto keyPair = record.begin();
1526 keyPair != record.end(); keyPair++)
1527 {
1528 templateCharReplace(keyPair, *foundDevice,
1529 foundDeviceIdx);
1530 }
1531 }
1532 auto findExpose = record.find("Exposes");
1533 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001534 {
1535 continue;
1536 }
James Feista465ccc2019-02-08 12:51:01 -08001537 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001538 {
1539 for (auto keyPair = expose.begin();
1540 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001541 {
James Feist1b2e2242018-01-30 13:45:19 -08001542
James Feist8f2710a2018-05-09 17:18:55 -07001543 // fill in template characters with devices
1544 // found
James Feistf1b14142019-04-10 15:22:09 -07001545 if (foundDevice)
1546 {
1547 templateCharReplace(keyPair, *foundDevice,
1548 foundDeviceIdx);
1549 }
James Feist8f2710a2018-05-09 17:18:55 -07001550 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001551 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001552 {
1553 if (keyPair.value().type() !=
1554 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001555 {
James Feist8f2710a2018-05-09 17:18:55 -07001556 std::cerr << "bind_ value must be of "
1557 "type string "
1558 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001559 continue;
1560 }
James Feist8f2710a2018-05-09 17:18:55 -07001561 bool foundBind = false;
1562 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001563 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001564
James Feista465ccc2019-02-08 12:51:01 -08001565 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001566 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001567 {
James Feist1b2e2242018-01-30 13:45:19 -08001568
James Feist8f2710a2018-05-09 17:18:55 -07001569 auto configListFind =
1570 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001571 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001572
1573 if (configListFind ==
1574 configurationPair.value()
1575 .end() ||
1576 configListFind->type() !=
1577 nlohmann::json::value_t::array)
1578 {
1579 continue;
1580 }
James Feista465ccc2019-02-08 12:51:01 -08001581 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001582 *configListFind)
1583 {
1584 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001585 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001586 if (boost::iequals(
1587 foundObjectName,
1588 keyPair.value()
1589 .get<std::string>()))
1590 {
James Feist1e3e6982018-08-03 16:09:28 -07001591 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001592 "okay";
1593 expose[bind] = exposedObject;
1594
1595 foundBind = true;
1596 break;
1597 }
1598 }
1599 if (foundBind)
1600 {
James Feist3cb5fec2018-01-23 14:41:51 -08001601 break;
1602 }
1603 }
James Feist8f2710a2018-05-09 17:18:55 -07001604 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001605 {
James Feist8f2710a2018-05-09 17:18:55 -07001606 std::cerr << "configuration file "
1607 "dependency error, "
1608 "could not find bind "
1609 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001610 }
1611 }
1612 }
1613 }
James Feistf1b14142019-04-10 15:22:09 -07001614 // overwrite ourselves with cleaned up version
1615 _systemConfiguration[recordName] = record;
James Feist1a996582019-05-14 15:10:06 -07001616
1617 logDeviceAdded(record);
James Feist1df06a42019-04-11 14:23:04 -07001618
James Feistf1b14142019-04-10 15:22:09 -07001619 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001620 }
James Feist8f2710a2018-05-09 17:18:55 -07001621 });
1622 p->run();
1623 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001624 }
1625 }
James Feist75fdeeb2018-02-20 14:26:16 -08001626
James Feist8f2710a2018-05-09 17:18:55 -07001627 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001628 {
James Feist8f2710a2018-05-09 17:18:55 -07001629 if (_passed)
1630 {
1631 auto nextScan = std::make_shared<PerformScan>(
1632 _systemConfiguration, _configurations, std::move(_callback));
1633 nextScan->run();
1634 }
1635 else
1636 {
1637 _callback();
1638 }
1639 }
James Feista465ccc2019-02-08 12:51:01 -08001640 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001641 std::list<nlohmann::json> _configurations;
1642 std::function<void(void)> _callback;
1643 std::vector<std::shared_ptr<PerformProbe>> _probes;
1644 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001645 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001646};
James Feistc95cb142018-02-26 10:41:42 -08001647
James Feist1df06a42019-04-11 14:23:04 -07001648void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001649 nlohmann::json& systemConfiguration)
1650{
1651 static bool scannedPowerOff = false;
1652 static bool scannedPowerOn = false;
1653
James Feistfb00f392019-06-25 14:16:48 -07001654 if (systemConfiguration.empty() || lastJson.empty())
1655 {
1656 return; // not ready yet
1657 }
James Feist1df06a42019-04-11 14:23:04 -07001658 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 {
James Feist98132792019-07-09 13:29:09 -07001787 startRemovedTimer(timer, systemConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001788 }
James Feist8f2710a2018-05-09 17:18:55 -07001789 });
1790 });
1791 });
1792 perfScan->run();
1793 });
James Feist75fdeeb2018-02-20 14:26:16 -08001794}
1795
James Feista465ccc2019-02-08 12:51:01 -08001796void registerCallbacks(boost::asio::io_service& io,
1797 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1798 nlohmann::json& systemConfiguration,
1799 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001800{
1801 static boost::container::flat_set<std::string> watchedObjects;
1802
James Feista465ccc2019-02-08 12:51:01 -08001803 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001804 {
1805 auto findObject = watchedObjects.find(objectMap.first);
1806 if (findObject != watchedObjects.end())
1807 {
1808 continue;
1809 }
James Feist8f2710a2018-05-09 17:18:55 -07001810 std::function<void(sdbusplus::message::message & message)>
1811 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001812
James Feista465ccc2019-02-08 12:51:01 -08001813 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001814 propertiesChangedCallback(io, dbusMatches,
1815 systemConfiguration, objServer);
1816 };
1817
1818 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001819 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001820 "type='signal',member='PropertiesChanged',arg0='" +
1821 objectMap.first + "'",
1822 eventHandler);
1823 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001824 }
1825}
1826
James Feist98132792019-07-09 13:29:09 -07001827int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001828{
1829 // setup connection to dbus
1830 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001831 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001832 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001833
James Feist8f2710a2018-05-09 17:18:55 -07001834 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001835
James Feist8f2710a2018-05-09 17:18:55 -07001836 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1837 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1838 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001839
James Feist8f2710a2018-05-09 17:18:55 -07001840 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1841 objServer.add_interface("/xyz/openbmc_project/inventory",
1842 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001843
1844 // to keep reference to the match / filter objects so they don't get
1845 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001846 std::vector<sdbusplus::bus::match::match> dbusMatches;
1847
1848 nlohmann::json systemConfiguration = nlohmann::json::object();
1849
1850 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001851 "Notify",
1852 [](const boost::container::flat_map<
1853 std::string,
James Feist98132792019-07-09 13:29:09 -07001854 boost::container::flat_map<std::string, BasicVariantType>>&) {
1855 return;
1856 });
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}