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