blob: bd8ccc7bc20d60bfb922f35e6f5a769617589423 [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 Feist3cb5fec2018-01-23 14:41:51 -080023#include <boost/algorithm/string/predicate.hpp>
24#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <boost/container/flat_map.hpp>
26#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080027#include <boost/lexical_cast.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070028#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080029#include <fstream>
30#include <iostream>
31#include <nlohmann/json.hpp>
32#include <regex>
33#include <sdbusplus/asio/connection.hpp>
34#include <sdbusplus/asio/object_server.hpp>
35#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080036
James Feista465ccc2019-02-08 12:51:01 -080037constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
38constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070039constexpr const char* tempConfigDir = "/tmp/configuration/";
40constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
41constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080042constexpr const char* globalSchema = "global.json";
43constexpr const char* TEMPLATE_CHAR = "$";
James Feist8f2710a2018-05-09 17:18:55 -070044constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080045
James Feistf1b14142019-04-10 15:22:09 -070046constexpr const bool DEBUG = false;
47
James Feist3cb5fec2018-01-23 14:41:51 -080048struct cmp_str
49{
James Feista465ccc2019-02-08 12:51:01 -080050 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080051 {
52 return std::strcmp(a, b) < 0;
53 }
54};
55
James Feist8f2710a2018-05-09 17:18:55 -070056struct PerformProbe;
57
James Feist3cb5fec2018-01-23 14:41:51 -080058// underscore T for collison with dbus c api
59enum class probe_type_codes
60{
61 FALSE_T,
62 TRUE_T,
63 AND,
64 OR,
James Feist6bd2a022018-03-13 12:30:58 -070065 FOUND,
66 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080067};
James Feista465ccc2019-02-08 12:51:01 -080068const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080069 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
70 {"TRUE", probe_type_codes::TRUE_T},
71 {"AND", probe_type_codes::AND},
72 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070073 {"FOUND", probe_type_codes::FOUND},
74 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080075
James Feist41334262019-03-25 13:30:20 -070076static constexpr std::array<const char*, 5> settableInterfaces = {
77 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070078using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080079 std::variant<std::vector<std::string>, std::vector<double>, std::string,
80 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
81 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070082using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080083 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
84 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070085
James Feist3cb5fec2018-01-23 14:41:51 -080086using GetSubTreeType = std::vector<
87 std::pair<std::string,
88 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
89
90using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070091 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080092 boost::container::flat_map<
93 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070094 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080095
96boost::container::flat_map<
97 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070098 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080099 DBUS_PROBE_OBJECTS;
100std::vector<std::string> PASSED_PROBES;
101
102// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700103std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700104static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800105
Johnathan Mantey2015f752019-03-26 15:22:31 -0700106const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
107const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800108
James Feista465ccc2019-02-08 12:51:01 -0800109void registerCallbacks(boost::asio::io_service& io,
110 std::vector<sdbusplus::bus::match::match>& dbusMatches,
111 nlohmann::json& systemConfiguration,
112 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800113
James Feist3cb5fec2018-01-23 14:41:51 -0800114// calls the mapper to find all exposed objects of an interface type
115// and creates a vector<flat_map> that contains all the key value pairs
116// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700117void findDbusObjects(std::shared_ptr<PerformProbe> probe,
118 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800119 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800120{
James Feist8f2710a2018-05-09 17:18:55 -0700121
122 // store reference to pending callbacks so we don't overwhelm services
123 static boost::container::flat_map<
124 std::string, std::vector<std::shared_ptr<PerformProbe>>>
125 pendingProbes;
126
127 if (DBUS_PROBE_OBJECTS[interface].size())
128 {
129 return;
130 }
131
132 // add shared_ptr to vector of Probes waiting for callback from a specific
133 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800134 std::array<const char*, 1> objects = {interface.c_str()};
135 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700136 pendingProbes[interface];
137 auto iter = pending.emplace(pending.end(), probe);
138 // only allow first call to run to not overwhelm processes
139 if (iter != pending.begin())
140 {
141 return;
142 }
143
James Feist3cb5fec2018-01-23 14:41:51 -0800144 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700145 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800146 [connection, interface, probe](boost::system::error_code& ec,
147 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700148 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700149 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700150 {
James Feist0de40152018-07-25 11:56:12 -0700151 pendingProbes[interface].clear();
152 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700153 {
James Feist0de40152018-07-25 11:56:12 -0700154 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700155 }
James Feist0de40152018-07-25 11:56:12 -0700156 std::cerr << "Error communicating to mapper.\n";
157
158 // if we can't communicate to the mapper something is very wrong
159 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700160 }
James Feist8f2710a2018-05-09 17:18:55 -0700161 else
James Feist3cb5fec2018-01-23 14:41:51 -0800162 {
James Feista465ccc2019-02-08 12:51:01 -0800163 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700164 {
James Feista465ccc2019-02-08 12:51:01 -0800165 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700166 {
James Feist0eb40352019-04-09 14:44:04 -0700167 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700168 }
169 }
James Feist3cb5fec2018-01-23 14:41:51 -0800170 }
James Feist63845bf2019-01-24 12:19:51 -0800171 if (interfaceConnections.empty())
172 {
173 pendingProbes[interface].clear();
174 return;
175 }
James Feist8f2710a2018-05-09 17:18:55 -0700176 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800177 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700178 {
179 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700180 [conn,
James Feista465ccc2019-02-08 12:51:01 -0800181 interface](boost::system::error_code& ec,
182 const ManagedObjectType& managedInterface) {
James Feist8f2710a2018-05-09 17:18:55 -0700183 if (ec)
184 {
185 std::cerr
186 << "error getting managed object for device "
187 << conn << "\n";
188 pendingProbes[interface].clear();
189 return;
190 }
James Feista465ccc2019-02-08 12:51:01 -0800191 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700192 {
193 auto ifaceObjFind =
194 interfaceManagedObj.second.find(interface);
195 if (ifaceObjFind !=
196 interfaceManagedObj.second.end())
197 {
198 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800199 std::string, BasicVariantType>>&
200 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700201 dbusObject.emplace_back(ifaceObjFind->second);
202 }
203 }
204 pendingProbes[interface].clear();
205 },
206 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
207 "GetManagedObjects");
208 }
209 },
210 "xyz.openbmc_project.ObjectMapper",
211 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700212 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700213 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800214}
James Feist8f2710a2018-05-09 17:18:55 -0700215// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800216bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800217 const std::string& interface,
218 const std::map<std::string, nlohmann::json>& matches,
James Feistf1b14142019-04-10 15:22:09 -0700219 std::vector<std::optional<
220 boost::container::flat_map<std::string, BasicVariantType>>>& devices,
James Feista465ccc2019-02-08 12:51:01 -0800221 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800222{
James Feista465ccc2019-02-08 12:51:01 -0800223 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
224 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800225 if (dbusObject.empty())
226 {
James Feist8f2710a2018-05-09 17:18:55 -0700227 foundProbe = false;
228 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800229 }
230 foundProbe = true;
231
232 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800233 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800234 {
235 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800236 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800237 {
238 auto deviceValue = device.find(match.first);
239 if (deviceValue != device.end())
240 {
241 switch (match.second.type())
242 {
James Feist9eb0b582018-04-27 12:15:46 -0700243 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800244 {
James Feist9eb0b582018-04-27 12:15:46 -0700245 std::regex search(match.second.get<std::string>());
246 std::smatch match;
247
248 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800249 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700250 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700251 if (!std::regex_search(probeValue, match, search))
252 {
253 deviceMatches = false;
254 break;
255 }
James Feist3cb5fec2018-01-23 14:41:51 -0800256 break;
257 }
James Feist9eb0b582018-04-27 12:15:46 -0700258 case nlohmann::json::value_t::boolean:
259 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800260 {
James Feista465ccc2019-02-08 12:51:01 -0800261 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700262 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800263
James Feist9eb0b582018-04-27 12:15:46 -0700264 if (probeValue != match.second.get<unsigned int>())
265 {
266 deviceMatches = false;
267 }
268 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800269 }
James Feist9eb0b582018-04-27 12:15:46 -0700270 case nlohmann::json::value_t::number_integer:
271 {
James Feista465ccc2019-02-08 12:51:01 -0800272 int probeValue = std::visit(VariantToIntVisitor(),
273 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800274
James Feist9eb0b582018-04-27 12:15:46 -0700275 if (probeValue != match.second.get<int>())
276 {
277 deviceMatches = false;
278 }
279 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800280 }
James Feist9eb0b582018-04-27 12:15:46 -0700281 case nlohmann::json::value_t::number_float:
282 {
James Feista465ccc2019-02-08 12:51:01 -0800283 float probeValue = std::visit(VariantToFloatVisitor(),
284 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700285
286 if (probeValue != match.second.get<float>())
287 {
288 deviceMatches = false;
289 }
290 break;
291 }
James Feist0eb40352019-04-09 14:44:04 -0700292 default:
293 {
294 std::cerr << "unexpected dbus probe type "
295 << match.second.type_name() << "\n";
296 }
James Feist3cb5fec2018-01-23 14:41:51 -0800297 }
298 }
299 else
300 {
301 deviceMatches = false;
302 break;
303 }
304 }
305 if (deviceMatches)
306 {
307 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700308 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800309 device));
310 foundMatch = true;
311 deviceMatches = false; // for next iteration
312 }
313 }
314 return foundMatch;
315}
316
317// default probe entry point, iterates a list looking for specific types to
318// call specific probe functions
319bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800320 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700321 std::vector<std::optional<
322 boost::container::flat_map<std::string, BasicVariantType>>>& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800323{
324 const static std::regex command(R"(\((.*)\))");
325 std::smatch match;
326 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700327 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800328 bool cur = true;
329 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
330
James Feista465ccc2019-02-08 12:51:01 -0800331 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800332 {
333 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800334 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800335 cmp_str>::const_iterator probeType;
336
337 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
338 probeType++)
339 {
340 if (probe.find(probeType->first) != std::string::npos)
341 {
342 foundProbe = true;
343 break;
344 }
345 }
346 if (foundProbe)
347 {
348 switch (probeType->second)
349 {
James Feist9eb0b582018-04-27 12:15:46 -0700350 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800351 {
James Feist8f2710a2018-05-09 17:18:55 -0700352 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800353 }
James Feist9eb0b582018-04-27 12:15:46 -0700354 case probe_type_codes::TRUE_T:
355 {
James Feist8f2710a2018-05-09 17:18:55 -0700356 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700357 }
358 case probe_type_codes::MATCH_ONE:
359 {
360 // set current value to last, this probe type shouldn't
361 // affect the outcome
362 cur = ret;
363 matchOne = true;
364 break;
365 }
366 /*case probe_type_codes::AND:
367 break;
368 case probe_type_codes::OR:
369 break;
370 // these are no-ops until the last command switch
371 */
372 case probe_type_codes::FOUND:
373 {
374 if (!std::regex_search(probe, match, command))
375 {
James Feist0eb40352019-04-09 14:44:04 -0700376 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700377 << "\n";
378 return false;
379 }
380 std::string commandStr = *(match.begin() + 1);
381 boost::replace_all(commandStr, "'", "");
382 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
383 commandStr) != PASSED_PROBES.end());
384 break;
385 }
James Feist0eb40352019-04-09 14:44:04 -0700386 default:
387 {
388 break;
389 }
James Feist3cb5fec2018-01-23 14:41:51 -0800390 }
391 }
392 // look on dbus for object
393 else
394 {
395 if (!std::regex_search(probe, match, command))
396 {
James Feist0eb40352019-04-09 14:44:04 -0700397 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800398 return false;
399 }
400 std::string commandStr = *(match.begin() + 1);
401 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700402 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800403 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800404 auto json = nlohmann::json::parse(commandStr, nullptr, false);
405 if (json.is_discarded())
406 {
James Feist0eb40352019-04-09 14:44:04 -0700407 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800408 return false;
409 }
410 // we can match any (string, variant) property. (string, string)
411 // does a regex
412 std::map<std::string, nlohmann::json> dbusProbeMap =
413 json.get<std::map<std::string, nlohmann::json>>();
414 auto findStart = probe.find("(");
415 if (findStart == std::string::npos)
416 {
417 return false;
418 }
419 std::string probeInterface = probe.substr(0, findStart);
420 cur =
421 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
422 }
423
424 // some functions like AND and OR only take affect after the
425 // fact
426 switch (lastCommand)
427 {
James Feist9eb0b582018-04-27 12:15:46 -0700428 case probe_type_codes::AND:
429 ret = cur && ret;
430 break;
431 case probe_type_codes::OR:
432 ret = cur || ret;
433 break;
434 default:
435 ret = cur;
436 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800437 }
438 lastCommand = probeType != PROBE_TYPES.end()
439 ? probeType->second
440 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800441 }
442
443 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800444 if (ret && foundDevs.size() == 0)
445 {
James Feistf1b14142019-04-10 15:22:09 -0700446 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800447 }
James Feist0eb40352019-04-09 14:44:04 -0700448 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700449 {
James Feist0eb40352019-04-09 14:44:04 -0700450 // match one could match multiple dbus values, which means we don't care
451 // what one we found so we shouldn't be using template replace. return
452 // an empty one
453 foundDevs.clear();
James Feistf1b14142019-04-10 15:22:09 -0700454 foundDevs.emplace_back(std::nullopt);
James Feist6bd2a022018-03-13 12:30:58 -0700455 }
James Feist3cb5fec2018-01-23 14:41:51 -0800456 return ret;
457}
James Feist8f2710a2018-05-09 17:18:55 -0700458// this class finds the needed dbus fields and on destruction runs the probe
459struct PerformProbe : std::enable_shared_from_this<PerformProbe>
460{
James Feist3cb5fec2018-01-23 14:41:51 -0800461
James Feist8f2710a2018-05-09 17:18:55 -0700462 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800463 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700464 std::function<void(std::vector<std::optional<boost::container::flat_map<
465 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700466 _probeCommand(probeCommand),
467 _callback(std::move(callback))
468 {
469 }
470 ~PerformProbe()
471 {
James Feistf1b14142019-04-10 15:22:09 -0700472 std::vector<std::optional<
473 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700474 foundDevs;
475 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700476 {
James Feist0eb40352019-04-09 14:44:04 -0700477 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700478 }
479 }
480 void run()
481 {
482 // parse out dbus probes by discarding other probe types
James Feista465ccc2019-02-08 12:51:01 -0800483 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700484 cmp_str>::const_iterator probeType;
485
James Feista465ccc2019-02-08 12:51:01 -0800486 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700487 {
488 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800489 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700490 cmp_str>::const_iterator probeType;
491 for (probeType = PROBE_TYPES.begin();
492 probeType != PROBE_TYPES.end(); probeType++)
493 {
494 if (probe.find(probeType->first) != std::string::npos)
495 {
496 found = true;
497 break;
498 }
499 }
500 if (found)
501 {
502 continue;
503 }
504 // syntax requires probe before first open brace
505 auto findStart = probe.find("(");
506 std::string interface = probe.substr(0, findStart);
507
508 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
509 }
510 }
511 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700512 std::function<void(std::vector<std::optional<boost::container::flat_map<
513 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700514 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700515};
516
517// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800518bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800519{
James Feist1df06a42019-04-11 14:23:04 -0700520 std::filesystem::create_directory(configurationOutDir);
521 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700522 if (!output.good())
523 {
524 return false;
525 }
James Feist1b2e2242018-01-30 13:45:19 -0800526 output << systemConfiguration.dump(4);
527 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700528 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700529}
James Feist1b2e2242018-01-30 13:45:19 -0800530
James Feist97a63f12018-05-17 13:50:57 -0700531template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800532bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
533 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700534{
535 try
536 {
537 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800538 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700539 ref = value;
540 return true;
541 }
542 catch (const std::out_of_range)
543 {
544 return false;
545 }
546}
James Feistbb43d022018-06-12 15:44:33 -0700547
James Feistebcc26b2019-03-22 12:30:43 -0700548// template function to add array as dbus property
549template <typename PropertyType>
550void addArrayToDbus(const std::string& name, const nlohmann::json& array,
551 sdbusplus::asio::dbus_interface* iface,
552 sdbusplus::asio::PropertyPermission permission,
553 nlohmann::json& systemConfiguration,
554 const std::string& jsonPointerString)
555{
556 std::vector<PropertyType> values;
557 for (const auto& property : array)
558 {
559 auto ptr = property.get_ptr<const PropertyType*>();
560 if (ptr != nullptr)
561 {
562 values.emplace_back(*ptr);
563 }
564 }
565
566 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
567 {
568 iface->register_property(name, values);
569 }
570 else
571 {
572 iface->register_property(
573 name, values,
574 [&systemConfiguration,
575 jsonPointerString{std::string(jsonPointerString)}](
576 const std::vector<PropertyType>& newVal,
577 std::vector<PropertyType>& val) {
578 val = newVal;
579 if (!setJsonFromPointer(jsonPointerString, val,
580 systemConfiguration))
581 {
582 std::cerr << "error setting json field\n";
583 return -1;
584 }
585 if (!writeJsonFiles(systemConfiguration))
586 {
587 std::cerr << "error setting json file\n";
588 return -1;
589 }
590 return 1;
591 });
592 }
593}
594
James Feistbb43d022018-06-12 15:44:33 -0700595template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800596void addProperty(const std::string& propertyName, const PropertyType& value,
597 sdbusplus::asio::dbus_interface* iface,
598 nlohmann::json& systemConfiguration,
599 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700600 sdbusplus::asio::PropertyPermission permission)
601{
602 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
603 {
604 iface->register_property(propertyName, value);
605 return;
606 }
James Feist68500ff2018-08-08 15:40:42 -0700607 iface->register_property(
608 propertyName, value,
609 [&systemConfiguration,
610 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800611 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700612 val = newVal;
613 if (!setJsonFromPointer(jsonPointerString, val,
614 systemConfiguration))
615 {
616 std::cerr << "error setting json field\n";
617 return -1;
618 }
James Feistc6248a52018-08-14 10:09:45 -0700619 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700620 {
621 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700622 return -1;
623 }
624 return 1;
625 });
626}
627
628void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800629 const std::string& jsonPointerPath,
630 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
631 sdbusplus::asio::object_server& objServer,
632 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700633{
634 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
635 iface->register_method(
636 "Delete", [&objServer, &systemConfiguration, interface,
637 jsonPointerPath{std::string(jsonPointerPath)}]() {
638 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
639 interface.lock();
640 if (!iface)
641 {
642 // this technically can't happen as the pointer is pointing to
643 // us
644 throw DBusInternalError();
645 }
646 nlohmann::json::json_pointer ptr(jsonPointerPath);
647 if (!objServer.remove_interface(iface))
648 {
649 std::cerr << "Can't delete interface " << jsonPointerPath
650 << "\n";
651 throw DBusInternalError();
652 }
653 systemConfiguration[ptr] = nullptr;
654
655 if (!writeJsonFiles(systemConfiguration))
656 {
657 std::cerr << "error setting json file\n";
658 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700659 }
James Feistbb43d022018-06-12 15:44:33 -0700660 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700661 });
James Feistbb43d022018-06-12 15:44:33 -0700662}
663
James Feist1b2e2242018-01-30 13:45:19 -0800664// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700665void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800666 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
667 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
668 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700669 sdbusplus::asio::PropertyPermission permission =
670 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800671{
James Feista465ccc2019-02-08 12:51:01 -0800672 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800673 {
James Feist8f2710a2018-05-09 17:18:55 -0700674 auto type = dictPair.value().type();
675 bool array = false;
676 if (dictPair.value().type() == nlohmann::json::value_t::array)
677 {
678 array = true;
679 if (!dictPair.value().size())
680 {
681 continue;
682 }
683 type = dictPair.value()[0].type();
684 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800685 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700686 {
687 if (arrayItem.type() != type)
688 {
689 isLegal = false;
690 break;
691 }
692 }
693 if (!isLegal)
694 {
695 std::cerr << "dbus format error" << dictPair.value() << "\n";
696 continue;
697 }
James Feista218ddb2019-04-11 14:01:31 -0700698 }
699 if (type == nlohmann::json::value_t::object)
700 {
701 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700702 }
James Feist97a63f12018-05-17 13:50:57 -0700703 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700704 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
705 {
706 // all setable numbers are doubles as it is difficult to always
707 // create a configuration file with all whole numbers as decimals
708 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700709 if (array)
710 {
711 if (dictPair.value()[0].is_number())
712 {
713 type = nlohmann::json::value_t::number_float;
714 }
715 }
716 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700717 {
718 type = nlohmann::json::value_t::number_float;
719 }
720 }
721
James Feist8f2710a2018-05-09 17:18:55 -0700722 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800723 {
James Feist9eb0b582018-04-27 12:15:46 -0700724 case (nlohmann::json::value_t::boolean):
725 {
James Feist8f2710a2018-05-09 17:18:55 -0700726 if (array)
727 {
728 // todo: array of bool isn't detected correctly by
729 // sdbusplus, change it to numbers
730 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700731 iface.get(), permission,
732 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700733 }
James Feistbb43d022018-06-12 15:44:33 -0700734
James Feist97a63f12018-05-17 13:50:57 -0700735 else
736 {
James Feistbb43d022018-06-12 15:44:33 -0700737 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700738 iface.get(), systemConfiguration, key,
739 permission);
James Feist97a63f12018-05-17 13:50:57 -0700740 }
James Feist9eb0b582018-04-27 12:15:46 -0700741 break;
742 }
743 case (nlohmann::json::value_t::number_integer):
744 {
James Feist8f2710a2018-05-09 17:18:55 -0700745 if (array)
746 {
747 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700748 iface.get(), permission,
749 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700750 }
751 else
752 {
James Feistbb43d022018-06-12 15:44:33 -0700753 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700754 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700755 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700756 }
James Feist9eb0b582018-04-27 12:15:46 -0700757 break;
758 }
759 case (nlohmann::json::value_t::number_unsigned):
760 {
James Feist8f2710a2018-05-09 17:18:55 -0700761 if (array)
762 {
763 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700764 iface.get(), permission,
765 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700766 }
767 else
768 {
James Feistbb43d022018-06-12 15:44:33 -0700769 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700770 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700771 systemConfiguration, key,
772 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700773 }
James Feist9eb0b582018-04-27 12:15:46 -0700774 break;
775 }
776 case (nlohmann::json::value_t::number_float):
777 {
James Feist8f2710a2018-05-09 17:18:55 -0700778 if (array)
779 {
780 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700781 iface.get(), permission,
782 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700783 }
James Feistbb43d022018-06-12 15:44:33 -0700784
James Feist97a63f12018-05-17 13:50:57 -0700785 else
786 {
James Feistbb43d022018-06-12 15:44:33 -0700787 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700788 iface.get(), systemConfiguration, key,
789 permission);
James Feist97a63f12018-05-17 13:50:57 -0700790 }
James Feist9eb0b582018-04-27 12:15:46 -0700791 break;
792 }
793 case (nlohmann::json::value_t::string):
794 {
James Feist8f2710a2018-05-09 17:18:55 -0700795 if (array)
796 {
James Feistebcc26b2019-03-22 12:30:43 -0700797 addArrayToDbus<std::string>(
798 dictPair.key(), dictPair.value(), iface.get(),
799 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700800 }
801 else
802 {
James Feistc6248a52018-08-14 10:09:45 -0700803 addProperty(
804 dictPair.key(), dictPair.value().get<std::string>(),
805 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700806 }
James Feist9eb0b582018-04-27 12:15:46 -0700807 break;
808 }
James Feist0eb40352019-04-09 14:44:04 -0700809 default:
810 {
James Feista218ddb2019-04-11 14:01:31 -0700811 std::cerr << "Unexpected json type in system configuration "
812 << dictPair.key() << ": "
813 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700814 break;
815 }
James Feist1b2e2242018-01-30 13:45:19 -0800816 }
817 }
James Feistc6248a52018-08-14 10:09:45 -0700818 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
819 {
820 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
821 systemConfiguration);
822 }
James Feist8f2710a2018-05-09 17:18:55 -0700823 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800824}
825
James Feista465ccc2019-02-08 12:51:01 -0800826sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700827{
828 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
829 interface) != settableInterfaces.end()
830 ? sdbusplus::asio::PropertyPermission::readWrite
831 : sdbusplus::asio::PropertyPermission::readOnly;
832}
833
James Feista465ccc2019-02-08 12:51:01 -0800834void createAddObjectMethod(const std::string& jsonPointerPath,
835 const std::string& path,
836 nlohmann::json& systemConfiguration,
837 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700838{
839 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
840
841 iface->register_method(
842 "AddObject",
843 [&systemConfiguration, &objServer,
844 jsonPointerPath{std::string(jsonPointerPath)},
845 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800846 const boost::container::flat_map<std::string, JsonVariantType>&
847 data) {
James Feist68500ff2018-08-08 15:40:42 -0700848 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800849 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700850 auto findExposes = base.find("Exposes");
851
852 if (findExposes == base.end())
853 {
854 throw std::invalid_argument("Entity must have children.");
855 }
856
857 // this will throw invalid-argument to sdbusplus if invalid json
858 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800859 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700860 {
James Feista465ccc2019-02-08 12:51:01 -0800861 nlohmann::json& newJson = newData[item.first];
862 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
863 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700864 }
865
866 auto findName = newData.find("Name");
867 auto findType = newData.find("Type");
868 if (findName == newData.end() || findType == newData.end())
869 {
870 throw std::invalid_argument("AddObject missing Name or Type");
871 }
James Feista465ccc2019-02-08 12:51:01 -0800872 const std::string* type = findType->get_ptr<const std::string*>();
873 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700874 if (type == nullptr || name == nullptr)
875 {
876 throw std::invalid_argument("Type and Name must be a string.");
877 }
878
879 size_t lastIndex = 0;
880 // we add in the "exposes"
881 for (; lastIndex < findExposes->size(); lastIndex++)
882 {
883 if (findExposes->at(lastIndex)["Name"] == *name &&
884 findExposes->at(lastIndex)["Type"] == *type)
885 {
886 throw std::invalid_argument(
887 "Field already in JSON, not adding");
888 }
889 lastIndex++;
890 }
891
892 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
893 *type + ".json");
894 // todo(james) we might want to also make a list of 'can add'
895 // interfaces but for now I think the assumption if there is a
896 // schema avaliable that it is allowed to update is fine
897 if (!schemaFile.good())
898 {
899 throw std::invalid_argument(
900 "No schema avaliable, cannot validate.");
901 }
902 nlohmann::json schema =
903 nlohmann::json::parse(schemaFile, nullptr, false);
904 if (schema.is_discarded())
905 {
906 std::cerr << "Schema not legal" << *type << ".json\n";
907 throw DBusInternalError();
908 }
909 if (!validateJson(schema, newData))
910 {
911 throw std::invalid_argument("Data does not match schema");
912 }
913
James Feist16a02f22019-05-13 15:21:37 -0700914 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700915 if (!writeJsonFiles(systemConfiguration))
916 {
917 std::cerr << "Error writing json files\n";
918 throw DBusInternalError();
919 }
920 std::string dbusName = *name;
921
922 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700923 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist68500ff2018-08-08 15:40:42 -0700924 auto iface = objServer.add_interface(
925 path + "/" + dbusName,
926 "xyz.openbmc_project.Configuration." + *type);
927 // permission is read-write, as since we just created it, must be
928 // runtime modifiable
929 populateInterfaceFromJson(
930 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700931 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
932 iface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700933 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700934 });
935 iface->initialize();
936}
937
James Feista465ccc2019-02-08 12:51:01 -0800938void postToDbus(const nlohmann::json& newConfiguration,
939 nlohmann::json& systemConfiguration,
940 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800941
James Feist1b2e2242018-01-30 13:45:19 -0800942{
James Feist97a63f12018-05-17 13:50:57 -0700943 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800944 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800945 {
James Feistf1b14142019-04-10 15:22:09 -0700946 std::string boardKey = boardPair.value()["Name"];
947 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700948 // loop through newConfiguration, but use values from system
949 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700950 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700951 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800952 std::string boardType;
953 if (findBoardType != boardValues.end() &&
954 findBoardType->type() == nlohmann::json::value_t::string)
955 {
956 boardType = findBoardType->get<std::string>();
957 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700958 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800959 }
960 else
961 {
962 std::cerr << "Unable to find type for " << boardKey
963 << " reverting to Chassis.\n";
964 boardType = "Chassis";
965 }
James Feist11be6672018-04-06 14:05:32 -0700966 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800967
968 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700969 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700970 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
971 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800972
James Feist8f2710a2018-05-09 17:18:55 -0700973 auto inventoryIface = objServer.add_interface(
974 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700975
James Feist8f2710a2018-05-09 17:18:55 -0700976 auto boardIface = objServer.add_interface(
977 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700978
James Feist68500ff2018-08-08 15:40:42 -0700979 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
980 objServer);
981
James Feist97a63f12018-05-17 13:50:57 -0700982 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700983 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700984 jsonPointerPath += "/";
985 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800986 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700987 {
988 if (boardField.value().type() == nlohmann::json::value_t::object)
989 {
James Feist8f2710a2018-05-09 17:18:55 -0700990 auto iface =
991 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700992 populateInterfaceFromJson(systemConfiguration,
993 jsonPointerPath + boardField.key(),
994 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700995 }
996 }
James Feist97a63f12018-05-17 13:50:57 -0700997
James Feist1e3e6982018-08-03 16:09:28 -0700998 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800999 if (exposes == boardValues.end())
1000 {
1001 continue;
1002 }
James Feist97a63f12018-05-17 13:50:57 -07001003 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001004 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001005
1006 // store the board level pointer so we can modify it on the way down
1007 std::string jsonPointerPathBoard = jsonPointerPath;
1008 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001009 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001010 {
James Feist97a63f12018-05-17 13:50:57 -07001011 exposesIndex++;
1012 jsonPointerPath = jsonPointerPathBoard;
1013 jsonPointerPath += std::to_string(exposesIndex);
1014
James Feistd63d18a2018-07-19 15:23:45 -07001015 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001016 if (findName == item.end())
1017 {
1018 std::cerr << "cannot find name in field " << item << "\n";
1019 continue;
1020 }
James Feist1e3e6982018-08-03 16:09:28 -07001021 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001022 // if status is not found it is assumed to be status = 'okay'
1023 if (findStatus != item.end())
1024 {
1025 if (*findStatus == "disabled")
1026 {
1027 continue;
1028 }
1029 }
James Feistd63d18a2018-07-19 15:23:45 -07001030 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001031 std::string itemType;
1032 if (findType != item.end())
1033 {
1034 itemType = findType->get<std::string>();
1035 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001036 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1037 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001038 }
1039 else
1040 {
1041 itemType = "unknown";
1042 }
1043 std::string itemName = findName->get<std::string>();
1044 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001045 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001046
James Feist8f2710a2018-05-09 17:18:55 -07001047 auto itemIface = objServer.add_interface(
1048 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001049 "xyz.openbmc_project.Configuration." + itemType);
1050
James Feist97a63f12018-05-17 13:50:57 -07001051 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001052 itemIface, item, objServer,
1053 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001054
James Feista465ccc2019-02-08 12:51:01 -08001055 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001056 {
James Feist97a63f12018-05-17 13:50:57 -07001057 jsonPointerPath = jsonPointerPathBoard +
1058 std::to_string(exposesIndex) + "/" +
1059 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001060 if (objectPair.value().type() ==
1061 nlohmann::json::value_t::object)
1062 {
James Feist8f2710a2018-05-09 17:18:55 -07001063 auto objectIface = objServer.add_interface(
1064 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001065 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001066 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001067
1068 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001069 systemConfiguration, jsonPointerPath, objectIface,
1070 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001071 }
1072 else if (objectPair.value().type() ==
1073 nlohmann::json::value_t::array)
1074 {
1075 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001076 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001077 {
James Feist8f2710a2018-05-09 17:18:55 -07001078 continue;
1079 }
1080 bool isLegal = true;
1081 auto type = objectPair.value()[0].type();
1082 if (type != nlohmann::json::value_t::object)
1083 {
1084 continue;
1085 }
1086
1087 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001088 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001089 {
1090 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001091 {
James Feist8f2710a2018-05-09 17:18:55 -07001092 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001093 break;
1094 }
James Feist8f2710a2018-05-09 17:18:55 -07001095 }
1096 if (!isLegal)
1097 {
1098 std::cerr << "dbus format error" << objectPair.value()
1099 << "\n";
1100 break;
1101 }
1102
James Feista465ccc2019-02-08 12:51:01 -08001103 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001104 {
James Feist97a63f12018-05-17 13:50:57 -07001105
James Feist8f2710a2018-05-09 17:18:55 -07001106 auto objectIface = objServer.add_interface(
1107 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001108 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001109 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001110 populateInterfaceFromJson(
1111 systemConfiguration,
1112 jsonPointerPath + "/" + std::to_string(index),
1113 objectIface, arrayItem, objServer,
1114 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001115 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001116 }
1117 }
1118 }
1119 }
1120 }
1121}
1122
1123// finds the template character (currently set to $) and replaces the value with
1124// the field found in a dbus object i.e. $ADDRESS would get populated with the
1125// ADDRESS field from a object on dbus
1126void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001127 nlohmann::json::iterator& keyPair,
1128 const boost::container::flat_map<std::string, BasicVariantType>&
1129 foundDevice,
1130 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001131{
James Feist11be6672018-04-06 14:05:32 -07001132 if (keyPair.value().type() == nlohmann::json::value_t::object)
1133 {
1134 for (auto nextLayer = keyPair.value().begin();
1135 nextLayer != keyPair.value().end(); nextLayer++)
1136 {
1137 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1138 }
1139 return;
1140 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001141
1142 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1143 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001144 {
1145 return;
1146 }
1147
Ed Tanous12bc7932019-02-26 14:36:20 -08001148 boost::replace_all(*strPtr, "$index", std::to_string(foundDeviceIdx));
1149
Ed Tanous12bc7932019-02-26 14:36:20 -08001150 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001151 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001152 std::string templateName = "$" + foundDevicePair.first;
1153 if (boost::iequals(*strPtr, templateName))
James Feist1b2e2242018-01-30 13:45:19 -08001154 {
Ed Tanous12bc7932019-02-26 14:36:20 -08001155 std::visit([&](auto&& val) { keyPair.value() = val; },
1156 foundDevicePair.second);
1157 // We probably just invalidated the pointer above, so set it to null
1158 strPtr = nullptr;
1159 break;
James Feist1b2e2242018-01-30 13:45:19 -08001160 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001161
1162 std::string probeValue =
1163 std::visit(VariantToStringVisitor(), foundDevicePair.second);
1164 boost::replace_all(*strPtr, templateName, probeValue);
1165 }
1166
1167 strPtr = keyPair.value().get_ptr<std::string*>();
1168 if (strPtr == nullptr)
1169 {
1170 return;
James Feist1b2e2242018-01-30 13:45:19 -08001171 }
James Feistc6090822019-01-04 16:02:48 -08001172
1173 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001174 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001175 {
1176 try
1177 {
James Feistc6090822019-01-04 16:02:48 -08001178 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001179 int64_t temp = std::stoul(*strPtr, &pos, 0);
1180 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001181 {
1182 keyPair.value() = static_cast<uint64_t>(temp);
1183 }
James Feist28dc2da2018-10-15 14:47:42 -07001184 }
1185 catch (std::invalid_argument)
1186 {
1187 }
James Feistc6090822019-01-04 16:02:48 -08001188 catch (std::out_of_range)
1189 {
1190 }
James Feist28dc2da2018-10-15 14:47:42 -07001191 }
James Feist1b2e2242018-01-30 13:45:19 -08001192}
1193
James Feist8f2710a2018-05-09 17:18:55 -07001194// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001195bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001196{
1197 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001198 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001199 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1200 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001201 {
1202 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001203 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001204 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001205 }
James Feistb4383f42018-08-06 16:54:10 -07001206
1207 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1208 globalSchema);
1209 if (!schemaStream.good())
1210 {
1211 std::cerr
1212 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1213 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001214 return false;
James Feistb4383f42018-08-06 16:54:10 -07001215 }
1216 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1217 if (schema.is_discarded())
1218 {
1219 std::cerr
1220 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1221 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001222 return false;
James Feistb4383f42018-08-06 16:54:10 -07001223 }
1224
James Feista465ccc2019-02-08 12:51:01 -08001225 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001226 {
1227 std::ifstream jsonStream(jsonPath.c_str());
1228 if (!jsonStream.good())
1229 {
1230 std::cerr << "unable to open " << jsonPath.string() << "\n";
1231 continue;
1232 }
1233 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1234 if (data.is_discarded())
1235 {
1236 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1237 continue;
1238 }
James Feist8da99192019-01-24 08:20:16 -08001239 /*
1240 * todo(james): reenable this once less things are in flight
1241 *
James Feistb4383f42018-08-06 16:54:10 -07001242 if (!validateJson(schema, data))
1243 {
1244 std::cerr << "Error validating " << jsonPath.string() << "\n";
1245 continue;
1246 }
James Feist8da99192019-01-24 08:20:16 -08001247 */
James Feistb4383f42018-08-06 16:54:10 -07001248
James Feist3cb5fec2018-01-23 14:41:51 -08001249 if (data.type() == nlohmann::json::value_t::array)
1250 {
James Feista465ccc2019-02-08 12:51:01 -08001251 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001252 {
1253 configurations.emplace_back(d);
1254 }
1255 }
1256 else
1257 {
1258 configurations.emplace_back(data);
1259 }
1260 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001261 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001262}
James Feist3cb5fec2018-01-23 14:41:51 -08001263
James Feist8f2710a2018-05-09 17:18:55 -07001264struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001265{
James Feist75fdeeb2018-02-20 14:26:16 -08001266
James Feista465ccc2019-02-08 12:51:01 -08001267 PerformScan(nlohmann::json& systemConfiguration,
1268 std::list<nlohmann::json>& configurations,
1269 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001270 _systemConfiguration(systemConfiguration),
1271 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001272 {
James Feist8f2710a2018-05-09 17:18:55 -07001273 }
1274 void run()
1275 {
1276 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001277 {
James Feist1e3e6982018-08-03 16:09:28 -07001278 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001279 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001280
James Feist1b2e2242018-01-30 13:45:19 -08001281 nlohmann::json probeCommand;
1282 // check for poorly formatted fields, probe must be an array
1283 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001284 {
1285 std::cerr << "configuration file missing probe:\n " << *it
1286 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001287 it = _configurations.erase(it);
1288 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001289 }
James Feist1b2e2242018-01-30 13:45:19 -08001290 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001291 {
1292 probeCommand = nlohmann::json::array();
1293 probeCommand.push_back(*findProbe);
1294 }
1295 else
1296 {
1297 probeCommand = *findProbe;
1298 }
James Feist1b2e2242018-01-30 13:45:19 -08001299
1300 if (findName == it->end())
1301 {
1302 std::cerr << "configuration file missing name:\n " << *it
1303 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001304 it = _configurations.erase(it);
1305 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001306 }
James Feistf1b14142019-04-10 15:22:09 -07001307 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001308
James Feistf1b14142019-04-10 15:22:09 -07001309 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1310 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001311 {
James Feist8f2710a2018-05-09 17:18:55 -07001312 it = _configurations.erase(it);
1313 continue;
1314 }
James Feistf1b14142019-04-10 15:22:09 -07001315 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001316
James Feist8f2710a2018-05-09 17:18:55 -07001317 // store reference to this to children to makes sure we don't get
1318 // destroyed too early
1319 auto thisRef = shared_from_this();
1320 auto p = std::make_shared<PerformProbe>(
1321 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001322 [&, recordPtr, probeName,
1323 thisRef](std::vector<std::optional<boost::container::flat_map<
1324 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001325 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001326
James Feistf1b14142019-04-10 15:22:09 -07001327 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001328 size_t foundDeviceIdx = 0;
1329
James Feistf1b14142019-04-10 15:22:09 -07001330 nlohmann::json record = *recordPtr;
James Feistbe5425f2018-06-08 10:30:55 -07001331
James Feista465ccc2019-02-08 12:51:01 -08001332 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001333 {
James Feistf1b14142019-04-10 15:22:09 -07001334 std::string recordName;
1335 size_t hash = 0;
1336 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001337 {
James Feistf1b14142019-04-10 15:22:09 -07001338 // use an array so alphabetical order from the
1339 // flat_map is maintained
1340 auto device = nlohmann::json::array();
1341 for (auto& devPair : *foundDevice)
1342 {
1343 device.push_back(devPair.first);
1344 std::visit(
1345 [&device](auto&& v) {
1346 device.push_back(v);
1347 },
1348 devPair.second);
1349 }
1350 hash = std::hash<std::string>{}(probeName +
1351 device.dump());
1352 // hashes are hard to distinguish, use the
1353 // non-hashed version if we want debug
1354 if constexpr (DEBUG)
1355 {
1356 recordName = probeName + device.dump();
1357 }
1358 else
1359 {
1360 recordName = std::to_string(hash);
1361 }
James Feist8f2710a2018-05-09 17:18:55 -07001362 }
James Feistf1b14142019-04-10 15:22:09 -07001363 else
1364 {
1365 recordName = probeName;
1366 }
1367
James Feist1df06a42019-04-11 14:23:04 -07001368 auto fromLastJson = lastJson.find(recordName);
1369 if (fromLastJson != lastJson.end())
1370 {
1371 // keep user changes
1372 _systemConfiguration[recordName] = *fromLastJson;
1373 continue;
1374 }
1375
James Feistf1b14142019-04-10 15:22:09 -07001376 // insert into configuration temporarily to be able to
1377 // reference ourselves
1378
1379 _systemConfiguration[recordName] = record;
1380
1381 if (foundDevice)
1382 {
1383 for (auto keyPair = record.begin();
1384 keyPair != record.end(); keyPair++)
1385 {
1386 templateCharReplace(keyPair, *foundDevice,
1387 foundDeviceIdx);
1388 }
1389 }
1390 auto findExpose = record.find("Exposes");
1391 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001392 {
1393 continue;
1394 }
James Feista465ccc2019-02-08 12:51:01 -08001395 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001396 {
1397 for (auto keyPair = expose.begin();
1398 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001399 {
James Feist1b2e2242018-01-30 13:45:19 -08001400
James Feist8f2710a2018-05-09 17:18:55 -07001401 // fill in template characters with devices
1402 // found
James Feistf1b14142019-04-10 15:22:09 -07001403 if (foundDevice)
1404 {
1405 templateCharReplace(keyPair, *foundDevice,
1406 foundDeviceIdx);
1407 }
James Feist8f2710a2018-05-09 17:18:55 -07001408 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001409 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001410 {
1411 if (keyPair.value().type() !=
1412 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001413 {
James Feist8f2710a2018-05-09 17:18:55 -07001414 std::cerr << "bind_ value must be of "
1415 "type string "
1416 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001417 continue;
1418 }
James Feist8f2710a2018-05-09 17:18:55 -07001419 bool foundBind = false;
1420 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001421 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001422
James Feista465ccc2019-02-08 12:51:01 -08001423 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001424 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001425 {
James Feist1b2e2242018-01-30 13:45:19 -08001426
James Feist8f2710a2018-05-09 17:18:55 -07001427 auto configListFind =
1428 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001429 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001430
1431 if (configListFind ==
1432 configurationPair.value()
1433 .end() ||
1434 configListFind->type() !=
1435 nlohmann::json::value_t::array)
1436 {
1437 continue;
1438 }
James Feista465ccc2019-02-08 12:51:01 -08001439 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001440 *configListFind)
1441 {
1442 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001443 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001444 if (boost::iequals(
1445 foundObjectName,
1446 keyPair.value()
1447 .get<std::string>()))
1448 {
James Feist1e3e6982018-08-03 16:09:28 -07001449 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001450 "okay";
1451 expose[bind] = exposedObject;
1452
1453 foundBind = true;
1454 break;
1455 }
1456 }
1457 if (foundBind)
1458 {
James Feist3cb5fec2018-01-23 14:41:51 -08001459 break;
1460 }
1461 }
James Feist8f2710a2018-05-09 17:18:55 -07001462 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001463 {
James Feist8f2710a2018-05-09 17:18:55 -07001464 std::cerr << "configuration file "
1465 "dependency error, "
1466 "could not find bind "
1467 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001468 }
1469 }
1470 }
1471 }
James Feistf1b14142019-04-10 15:22:09 -07001472 // overwrite ourselves with cleaned up version
1473 _systemConfiguration[recordName] = record;
James Feist1df06a42019-04-11 14:23:04 -07001474 logDeviceAdded(record.at("Name").get<std::string>());
1475
James Feistf1b14142019-04-10 15:22:09 -07001476 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001477 }
James Feist8f2710a2018-05-09 17:18:55 -07001478 });
1479 p->run();
1480 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001481 }
1482 }
James Feist75fdeeb2018-02-20 14:26:16 -08001483
James Feist8f2710a2018-05-09 17:18:55 -07001484 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001485 {
James Feist8f2710a2018-05-09 17:18:55 -07001486 if (_passed)
1487 {
1488 auto nextScan = std::make_shared<PerformScan>(
1489 _systemConfiguration, _configurations, std::move(_callback));
1490 nextScan->run();
1491 }
1492 else
1493 {
1494 _callback();
1495 }
1496 }
James Feista465ccc2019-02-08 12:51:01 -08001497 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001498 std::list<nlohmann::json> _configurations;
1499 std::function<void(void)> _callback;
1500 std::vector<std::shared_ptr<PerformProbe>> _probes;
1501 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001502 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001503};
James Feistc95cb142018-02-26 10:41:42 -08001504
James Feist1df06a42019-04-11 14:23:04 -07001505void startRemovedTimer(boost::asio::deadline_timer& timer,
1506 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1507 nlohmann::json& systemConfiguration)
1508{
1509 static bool scannedPowerOff = false;
1510 static bool scannedPowerOn = false;
1511
1512 if (scannedPowerOn)
1513 {
1514 return;
1515 }
1516
1517 if (!isPowerOn() && scannedPowerOff)
1518 {
1519 return;
1520 }
1521
1522 timer.expires_from_now(boost::posix_time::seconds(10));
1523 timer.async_wait([&systemConfiguration](
1524 const boost::system::error_code& ec) {
1525 if (ec == boost::asio::error::operation_aborted)
1526 {
1527 // we were cancelled
1528 return;
1529 }
1530
1531 bool powerOff = !isPowerOn();
1532 for (const auto& item : lastJson.items())
1533 {
1534 if (systemConfiguration.find(item.key()) ==
1535 systemConfiguration.end())
1536 {
1537 bool isDetectedPowerOn = false;
1538 auto powerState = item.value().find("PowerState");
1539 if (powerState != item.value().end())
1540 {
1541 auto ptr = powerState->get_ptr<const std::string*>();
1542 if (ptr)
1543 {
1544 if (*ptr == "On" || *ptr == "BiosPost")
1545 {
1546 isDetectedPowerOn = true;
1547 }
1548 }
1549 }
1550 if (powerOff && isDetectedPowerOn)
1551 {
1552 // power not on yet, don't know if it's there or not
1553 continue;
1554 }
1555 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1556 {
1557 // already logged it when power was off
1558 continue;
1559 }
1560
1561 logDeviceRemoved(item.value().at("Name").get<std::string>());
1562 }
1563 }
1564 scannedPowerOff = true;
1565 if (!powerOff)
1566 {
1567 scannedPowerOn = true;
1568 }
1569 });
1570}
1571
James Feist8f2710a2018-05-09 17:18:55 -07001572// main properties changed entry
1573void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001574 boost::asio::io_service& io,
1575 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1576 nlohmann::json& systemConfiguration,
1577 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001578{
1579 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001580 static bool timerRunning;
1581
1582 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001583 timer.expires_from_now(boost::posix_time::seconds(1));
1584
1585 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001586 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001587 if (ec == boost::asio::error::operation_aborted)
1588 {
1589 // we were cancelled
1590 return;
1591 }
1592 else if (ec)
1593 {
1594 std::cerr << "async wait error " << ec << "\n";
1595 return;
1596 }
James Feist1df06a42019-04-11 14:23:04 -07001597 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001598
1599 nlohmann::json oldConfiguration = systemConfiguration;
1600 DBUS_PROBE_OBJECTS.clear();
1601
1602 std::list<nlohmann::json> configurations;
1603 if (!findJsonFiles(configurations))
1604 {
1605 std::cerr << "cannot find json files\n";
1606 return;
1607 }
1608
1609 auto perfScan = std::make_shared<PerformScan>(
1610 systemConfiguration, configurations, [&, oldConfiguration]() {
1611 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001612 for (auto it = newConfiguration.begin();
1613 it != newConfiguration.end();)
1614 {
1615 auto findKey = oldConfiguration.find(it.key());
1616 if (findKey != oldConfiguration.end())
1617 {
1618 it = newConfiguration.erase(it);
1619 }
1620 else
1621 {
1622 it++;
1623 }
1624 }
James Feist8f2710a2018-05-09 17:18:55 -07001625 registerCallbacks(io, dbusMatches, systemConfiguration,
1626 objServer);
1627 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001628 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001629
James Feistbb43d022018-06-12 15:44:33 -07001630 io.post([&]() {
1631 if (!writeJsonFiles(systemConfiguration))
1632 {
1633 std::cerr << "Error writing json files\n";
1634 }
1635 });
James Feist8f2710a2018-05-09 17:18:55 -07001636 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001637 postToDbus(newConfiguration, systemConfiguration,
1638 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001639 if (!timerRunning)
1640 {
1641 startRemovedTimer(timer, dbusMatches,
1642 systemConfiguration);
1643 }
James Feist8f2710a2018-05-09 17:18:55 -07001644 });
1645 });
1646 });
1647 perfScan->run();
1648 });
James Feist75fdeeb2018-02-20 14:26:16 -08001649}
1650
James Feista465ccc2019-02-08 12:51:01 -08001651void registerCallbacks(boost::asio::io_service& io,
1652 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1653 nlohmann::json& systemConfiguration,
1654 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001655{
1656 static boost::container::flat_set<std::string> watchedObjects;
1657
James Feista465ccc2019-02-08 12:51:01 -08001658 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001659 {
1660 auto findObject = watchedObjects.find(objectMap.first);
1661 if (findObject != watchedObjects.end())
1662 {
1663 continue;
1664 }
James Feist8f2710a2018-05-09 17:18:55 -07001665 std::function<void(sdbusplus::message::message & message)>
1666 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001667
James Feista465ccc2019-02-08 12:51:01 -08001668 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001669 propertiesChangedCallback(io, dbusMatches,
1670 systemConfiguration, objServer);
1671 };
1672
1673 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001674 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001675 "type='signal',member='PropertiesChanged',arg0='" +
1676 objectMap.first + "'",
1677 eventHandler);
1678 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001679 }
1680}
1681
James Feista465ccc2019-02-08 12:51:01 -08001682int main(int argc, char** argv)
James Feist75fdeeb2018-02-20 14:26:16 -08001683{
1684 // setup connection to dbus
1685 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001686 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001687 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001688
James Feist8f2710a2018-05-09 17:18:55 -07001689 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001690
James Feist8f2710a2018-05-09 17:18:55 -07001691 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1692 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1693 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001694
James Feist8f2710a2018-05-09 17:18:55 -07001695 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1696 objServer.add_interface("/xyz/openbmc_project/inventory",
1697 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001698
1699 // to keep reference to the match / filter objects so they don't get
1700 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001701 std::vector<sdbusplus::bus::match::match> dbusMatches;
1702
1703 nlohmann::json systemConfiguration = nlohmann::json::object();
1704
1705 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001706 "Notify",
1707 [](const boost::container::flat_map<
1708 std::string,
1709 boost::container::flat_map<std::string, BasicVariantType>>&
1710 object) { return; });
James Feist8f2710a2018-05-09 17:18:55 -07001711 inventoryIface->initialize();
1712
1713 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001714#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001715 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001716#endif
James Feist8f2710a2018-05-09 17:18:55 -07001717 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1718 objServer);
1719 });
James Feist4131aea2018-03-09 09:47:30 -08001720
James Feistfd1264a2018-05-03 12:10:00 -07001721 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001722 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1723 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001724 });
James Feist8f2710a2018-05-09 17:18:55 -07001725 entityIface->initialize();
1726
James Feist1df06a42019-04-11 14:23:04 -07001727 if (fwVersionIsSame())
1728 {
1729 if (std::filesystem::is_regular_file(currentConfiguration))
1730 {
1731 // this file could just be deleted, but it's nice for debug
1732 std::filesystem::create_directory(tempConfigDir);
1733 std::filesystem::remove(lastConfiguration);
1734 std::filesystem::copy(currentConfiguration, lastConfiguration);
1735 std::filesystem::remove(currentConfiguration);
1736
1737 std::ifstream jsonStream(lastConfiguration);
1738 if (jsonStream.good())
1739 {
1740 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1741 if (data.is_discarded())
1742 {
1743 std::cerr << "syntax error in " << lastConfiguration
1744 << "\n";
1745 }
1746 else
1747 {
1748 lastJson = std::move(data);
1749 }
1750 }
1751 else
1752 {
1753 std::cerr << "unable to open " << lastConfiguration << "\n";
1754 }
1755 }
1756 }
1757 else
1758 {
1759 // not an error, just logging at this level to make it in the journal
1760 std::cerr << "Clearing previous configuration\n";
1761 std::filesystem::remove(currentConfiguration);
1762 }
1763
1764 // some boards only show up after power is on, we want to not say they are
1765 // removed until the same state happens
1766 setupPowerMatch(SYSTEM_BUS);
1767
James Feist1b2e2242018-01-30 13:45:19 -08001768 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001769
1770 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001771}