blob: f135c2076191a7bedd92566e14ac6c42170e11b8 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
James Feist1df06a42019-04-11 14:23:04 -070017#include "EntityManager.hpp"
18
James Feistc95cb142018-02-26 10:41:42 -080019#include <Overlay.hpp>
James Feista465ccc2019-02-08 12:51:01 -080020#include <Utils.hpp>
21#include <VariantVisitors.hpp>
James Feist11be6672018-04-06 14:05:32 -070022#include <boost/algorithm/string/case_conv.hpp>
James Feistf5125b02019-06-06 11:27:43 -070023#include <boost/algorithm/string/classification.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080024#include <boost/algorithm/string/predicate.hpp>
25#include <boost/algorithm/string/replace.hpp>
James Feistf5125b02019-06-06 11:27:43 -070026#include <boost/algorithm/string/split.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <boost/container/flat_map.hpp>
28#include <boost/container/flat_set.hpp>
James Feista465ccc2019-02-08 12:51:01 -080029#include <boost/lexical_cast.hpp>
James Feistf5125b02019-06-06 11:27:43 -070030#include <boost/range/iterator_range.hpp>
James Feist637b3ef2019-04-15 16:35:30 -070031#include <filesystem>
James Feista465ccc2019-02-08 12:51:01 -080032#include <fstream>
33#include <iostream>
34#include <nlohmann/json.hpp>
35#include <regex>
36#include <sdbusplus/asio/connection.hpp>
37#include <sdbusplus/asio/object_server.hpp>
38#include <variant>
James Feist3cb5fec2018-01-23 14:41:51 -080039
James Feista465ccc2019-02-08 12:51:01 -080040constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
41constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
James Feist1df06a42019-04-11 14:23:04 -070042constexpr const char* tempConfigDir = "/tmp/configuration/";
43constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
44constexpr const char* currentConfiguration = "/var/configuration/system.json";
James Feista465ccc2019-02-08 12:51:01 -080045constexpr const char* globalSchema = "global.json";
James Feistf5125b02019-06-06 11:27:43 -070046constexpr const char* templateChar = "$";
James Feist8f2710a2018-05-09 17:18:55 -070047constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist3cb5fec2018-01-23 14:41:51 -080048
James Feistf1b14142019-04-10 15:22:09 -070049constexpr const bool DEBUG = false;
50
James Feist3cb5fec2018-01-23 14:41:51 -080051struct cmp_str
52{
James Feista465ccc2019-02-08 12:51:01 -080053 bool operator()(const char* a, const char* b) const
James Feist3cb5fec2018-01-23 14:41:51 -080054 {
55 return std::strcmp(a, b) < 0;
56 }
57};
58
James Feist8f2710a2018-05-09 17:18:55 -070059struct PerformProbe;
60
James Feist3cb5fec2018-01-23 14:41:51 -080061// underscore T for collison with dbus c api
62enum class probe_type_codes
63{
64 FALSE_T,
65 TRUE_T,
66 AND,
67 OR,
James Feist6bd2a022018-03-13 12:30:58 -070068 FOUND,
69 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080070};
James Feista465ccc2019-02-08 12:51:01 -080071const static boost::container::flat_map<const char*, probe_type_codes, cmp_str>
James Feist3cb5fec2018-01-23 14:41:51 -080072 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
73 {"TRUE", probe_type_codes::TRUE_T},
74 {"AND", probe_type_codes::AND},
75 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070076 {"FOUND", probe_type_codes::FOUND},
77 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080078
James Feist41334262019-03-25 13:30:20 -070079static constexpr std::array<const char*, 5> settableInterfaces = {
80 "FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds"};
James Feist68500ff2018-08-08 15:40:42 -070081using JsonVariantType =
James Feist338b8a72019-03-01 10:16:45 -080082 std::variant<std::vector<std::string>, std::vector<double>, std::string,
83 int64_t, uint64_t, double, int32_t, uint32_t, int16_t,
84 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070085using BasicVariantType =
James Feista465ccc2019-02-08 12:51:01 -080086 std::variant<std::string, int64_t, uint64_t, double, int32_t, uint32_t,
87 int16_t, uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070088
James Feist3cb5fec2018-01-23 14:41:51 -080089using GetSubTreeType = std::vector<
90 std::pair<std::string,
91 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
92
93using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070094 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080095 boost::container::flat_map<
96 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070097 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080098
99boost::container::flat_map<
100 std::string,
James Feist8f2710a2018-05-09 17:18:55 -0700101 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -0800102 DBUS_PROBE_OBJECTS;
103std::vector<std::string> PASSED_PROBES;
104
105// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700106std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist1df06a42019-04-11 14:23:04 -0700107static nlohmann::json lastJson;
James Feist3cb5fec2018-01-23 14:41:51 -0800108
Johnathan Mantey2015f752019-03-26 15:22:31 -0700109const std::regex ILLEGAL_DBUS_PATH_REGEX("[^A-Za-z0-9_.]");
110const std::regex ILLEGAL_DBUS_MEMBER_REGEX("[^A-Za-z0-9_]");
James Feist1b2e2242018-01-30 13:45:19 -0800111
James Feista465ccc2019-02-08 12:51:01 -0800112void registerCallbacks(boost::asio::io_service& io,
113 std::vector<sdbusplus::bus::match::match>& dbusMatches,
114 nlohmann::json& systemConfiguration,
115 sdbusplus::asio::object_server& objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800116
James Feist3cb5fec2018-01-23 14:41:51 -0800117// calls the mapper to find all exposed objects of an interface type
118// and creates a vector<flat_map> that contains all the key value pairs
119// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700120void findDbusObjects(std::shared_ptr<PerformProbe> probe,
121 std::shared_ptr<sdbusplus::asio::connection> connection,
James Feista465ccc2019-02-08 12:51:01 -0800122 std::string& interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800123{
James Feist8f2710a2018-05-09 17:18:55 -0700124
125 // store reference to pending callbacks so we don't overwhelm services
126 static boost::container::flat_map<
127 std::string, std::vector<std::shared_ptr<PerformProbe>>>
128 pendingProbes;
129
130 if (DBUS_PROBE_OBJECTS[interface].size())
131 {
132 return;
133 }
134
135 // add shared_ptr to vector of Probes waiting for callback from a specific
136 // interface to keep alive while waiting for response
James Feista465ccc2019-02-08 12:51:01 -0800137 std::array<const char*, 1> objects = {interface.c_str()};
138 std::vector<std::shared_ptr<PerformProbe>>& pending =
James Feist8f2710a2018-05-09 17:18:55 -0700139 pendingProbes[interface];
140 auto iter = pending.emplace(pending.end(), probe);
141 // only allow first call to run to not overwhelm processes
142 if (iter != pending.begin())
143 {
144 return;
145 }
146
James Feist3cb5fec2018-01-23 14:41:51 -0800147 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700148 connection->async_method_call(
James Feista465ccc2019-02-08 12:51:01 -0800149 [connection, interface, probe](boost::system::error_code& ec,
150 const GetSubTreeType& interfaceSubtree) {
James Feist0de40152018-07-25 11:56:12 -0700151 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700152 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700153 {
James Feist0de40152018-07-25 11:56:12 -0700154 pendingProbes[interface].clear();
155 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700156 {
James Feist0de40152018-07-25 11:56:12 -0700157 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700158 }
James Feist0de40152018-07-25 11:56:12 -0700159 std::cerr << "Error communicating to mapper.\n";
160
161 // if we can't communicate to the mapper something is very wrong
162 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700163 }
James Feist8f2710a2018-05-09 17:18:55 -0700164 else
James Feist3cb5fec2018-01-23 14:41:51 -0800165 {
James Feista465ccc2019-02-08 12:51:01 -0800166 for (auto& object : interfaceSubtree)
James Feist8f2710a2018-05-09 17:18:55 -0700167 {
James Feista465ccc2019-02-08 12:51:01 -0800168 for (auto& connPair : object.second)
James Feist8f2710a2018-05-09 17:18:55 -0700169 {
James Feist0eb40352019-04-09 14:44:04 -0700170 interfaceConnections.insert(connPair.first);
James Feist8f2710a2018-05-09 17:18:55 -0700171 }
172 }
James Feist3cb5fec2018-01-23 14:41:51 -0800173 }
James Feist63845bf2019-01-24 12:19:51 -0800174 if (interfaceConnections.empty())
175 {
176 pendingProbes[interface].clear();
177 return;
178 }
James Feist8f2710a2018-05-09 17:18:55 -0700179 // get managed objects for all interfaces
James Feista465ccc2019-02-08 12:51:01 -0800180 for (const auto& conn : interfaceConnections)
James Feist8f2710a2018-05-09 17:18:55 -0700181 {
182 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700183 [conn,
James Feist98132792019-07-09 13:29:09 -0700184 interface](boost::system::error_code& errc,
James Feista465ccc2019-02-08 12:51:01 -0800185 const ManagedObjectType& managedInterface) {
James Feist98132792019-07-09 13:29:09 -0700186 if (errc)
James Feist8f2710a2018-05-09 17:18:55 -0700187 {
188 std::cerr
189 << "error getting managed object for device "
190 << conn << "\n";
191 pendingProbes[interface].clear();
192 return;
193 }
James Feista465ccc2019-02-08 12:51:01 -0800194 for (auto& interfaceManagedObj : managedInterface)
James Feist8f2710a2018-05-09 17:18:55 -0700195 {
196 auto ifaceObjFind =
197 interfaceManagedObj.second.find(interface);
198 if (ifaceObjFind !=
199 interfaceManagedObj.second.end())
200 {
201 std::vector<boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800202 std::string, BasicVariantType>>&
203 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist8f2710a2018-05-09 17:18:55 -0700204 dbusObject.emplace_back(ifaceObjFind->second);
205 }
206 }
207 pendingProbes[interface].clear();
208 },
209 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
210 "GetManagedObjects");
211 }
212 },
213 "xyz.openbmc_project.ObjectMapper",
214 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700215 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700216 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800217}
James Feist8f2710a2018-05-09 17:18:55 -0700218// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800219bool probeDbus(
James Feista465ccc2019-02-08 12:51:01 -0800220 const std::string& interface,
221 const std::map<std::string, nlohmann::json>& matches,
James Feistf1b14142019-04-10 15:22:09 -0700222 std::vector<std::optional<
223 boost::container::flat_map<std::string, BasicVariantType>>>& devices,
James Feista465ccc2019-02-08 12:51:01 -0800224 bool& foundProbe)
James Feist3cb5fec2018-01-23 14:41:51 -0800225{
James Feista465ccc2019-02-08 12:51:01 -0800226 std::vector<boost::container::flat_map<std::string, BasicVariantType>>&
227 dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800228 if (dbusObject.empty())
229 {
James Feist8f2710a2018-05-09 17:18:55 -0700230 foundProbe = false;
231 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800232 }
233 foundProbe = true;
234
235 bool foundMatch = false;
James Feista465ccc2019-02-08 12:51:01 -0800236 for (auto& device : dbusObject)
James Feist3cb5fec2018-01-23 14:41:51 -0800237 {
238 bool deviceMatches = true;
James Feista465ccc2019-02-08 12:51:01 -0800239 for (auto& match : matches)
James Feist3cb5fec2018-01-23 14:41:51 -0800240 {
241 auto deviceValue = device.find(match.first);
242 if (deviceValue != device.end())
243 {
244 switch (match.second.type())
245 {
James Feist9eb0b582018-04-27 12:15:46 -0700246 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800247 {
James Feist9eb0b582018-04-27 12:15:46 -0700248 std::regex search(match.second.get<std::string>());
James Feist98132792019-07-09 13:29:09 -0700249 std::smatch regMatch;
James Feist9eb0b582018-04-27 12:15:46 -0700250
251 // convert value to string respresentation
James Feista465ccc2019-02-08 12:51:01 -0800252 std::string probeValue = std::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700253 VariantToStringVisitor(), deviceValue->second);
James Feist98132792019-07-09 13:29:09 -0700254 if (!std::regex_search(probeValue, regMatch, search))
James Feist9eb0b582018-04-27 12:15:46 -0700255 {
256 deviceMatches = false;
257 break;
258 }
James Feist3cb5fec2018-01-23 14:41:51 -0800259 break;
260 }
James Feist9eb0b582018-04-27 12:15:46 -0700261 case nlohmann::json::value_t::boolean:
262 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800263 {
James Feista465ccc2019-02-08 12:51:01 -0800264 unsigned int probeValue = std::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700265 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800266
James Feist9eb0b582018-04-27 12:15:46 -0700267 if (probeValue != match.second.get<unsigned int>())
268 {
269 deviceMatches = false;
270 }
271 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800272 }
James Feist9eb0b582018-04-27 12:15:46 -0700273 case nlohmann::json::value_t::number_integer:
274 {
James Feista465ccc2019-02-08 12:51:01 -0800275 int probeValue = std::visit(VariantToIntVisitor(),
276 deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800277
James Feist9eb0b582018-04-27 12:15:46 -0700278 if (probeValue != match.second.get<int>())
279 {
280 deviceMatches = false;
281 }
282 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800283 }
James Feist9eb0b582018-04-27 12:15:46 -0700284 case nlohmann::json::value_t::number_float:
285 {
James Feista465ccc2019-02-08 12:51:01 -0800286 float probeValue = std::visit(VariantToFloatVisitor(),
287 deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700288
289 if (probeValue != match.second.get<float>())
290 {
291 deviceMatches = false;
292 }
293 break;
294 }
James Feist0eb40352019-04-09 14:44:04 -0700295 default:
296 {
297 std::cerr << "unexpected dbus probe type "
298 << match.second.type_name() << "\n";
299 }
James Feist3cb5fec2018-01-23 14:41:51 -0800300 }
301 }
302 else
303 {
304 deviceMatches = false;
305 break;
306 }
307 }
308 if (deviceMatches)
309 {
James Feistf5125b02019-06-06 11:27:43 -0700310 devices.emplace_back(device);
James Feist3cb5fec2018-01-23 14:41:51 -0800311 foundMatch = true;
312 deviceMatches = false; // for next iteration
313 }
314 }
315 return foundMatch;
316}
317
318// default probe entry point, iterates a list looking for specific types to
319// call specific probe functions
320bool probe(
James Feista465ccc2019-02-08 12:51:01 -0800321 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700322 std::vector<std::optional<
323 boost::container::flat_map<std::string, BasicVariantType>>>& foundDevs)
James Feist3cb5fec2018-01-23 14:41:51 -0800324{
325 const static std::regex command(R"(\((.*)\))");
326 std::smatch match;
327 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700328 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800329 bool cur = true;
330 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
James Feist54a0dca2019-06-26 10:34:54 -0700331 bool first = true;
James Feist3cb5fec2018-01-23 14:41:51 -0800332
James Feista465ccc2019-02-08 12:51:01 -0800333 for (auto& probe : probeCommand)
James Feist3cb5fec2018-01-23 14:41:51 -0800334 {
335 bool foundProbe = false;
James Feista465ccc2019-02-08 12:51:01 -0800336 boost::container::flat_map<const char*, probe_type_codes,
James Feist3cb5fec2018-01-23 14:41:51 -0800337 cmp_str>::const_iterator probeType;
338
339 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
340 probeType++)
341 {
342 if (probe.find(probeType->first) != std::string::npos)
343 {
344 foundProbe = true;
345 break;
346 }
347 }
348 if (foundProbe)
349 {
350 switch (probeType->second)
351 {
James Feist9eb0b582018-04-27 12:15:46 -0700352 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800353 {
James Feiste31e00a2019-07-24 10:45:43 -0700354 cur = false;
355 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800356 }
James Feist9eb0b582018-04-27 12:15:46 -0700357 case probe_type_codes::TRUE_T:
358 {
James Feiste31e00a2019-07-24 10:45:43 -0700359 cur = true;
360 break;
James Feist9eb0b582018-04-27 12:15:46 -0700361 }
362 case probe_type_codes::MATCH_ONE:
363 {
364 // set current value to last, this probe type shouldn't
365 // affect the outcome
366 cur = ret;
367 matchOne = true;
368 break;
369 }
370 /*case probe_type_codes::AND:
371 break;
372 case probe_type_codes::OR:
373 break;
374 // these are no-ops until the last command switch
375 */
376 case probe_type_codes::FOUND:
377 {
378 if (!std::regex_search(probe, match, command))
379 {
James Feist0eb40352019-04-09 14:44:04 -0700380 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700381 << "\n";
382 return false;
383 }
384 std::string commandStr = *(match.begin() + 1);
385 boost::replace_all(commandStr, "'", "");
386 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
387 commandStr) != PASSED_PROBES.end());
388 break;
389 }
James Feist0eb40352019-04-09 14:44:04 -0700390 default:
391 {
392 break;
393 }
James Feist3cb5fec2018-01-23 14:41:51 -0800394 }
395 }
396 // look on dbus for object
397 else
398 {
399 if (!std::regex_search(probe, match, command))
400 {
James Feist0eb40352019-04-09 14:44:04 -0700401 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800402 return false;
403 }
404 std::string commandStr = *(match.begin() + 1);
405 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700406 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800407 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800408 auto json = nlohmann::json::parse(commandStr, nullptr, false);
409 if (json.is_discarded())
410 {
James Feist0eb40352019-04-09 14:44:04 -0700411 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800412 return false;
413 }
414 // we can match any (string, variant) property. (string, string)
415 // does a regex
416 std::map<std::string, nlohmann::json> dbusProbeMap =
417 json.get<std::map<std::string, nlohmann::json>>();
418 auto findStart = probe.find("(");
419 if (findStart == std::string::npos)
420 {
421 return false;
422 }
423 std::string probeInterface = probe.substr(0, findStart);
424 cur =
425 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
426 }
427
428 // some functions like AND and OR only take affect after the
429 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700430 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800431 {
James Feist54a0dca2019-06-26 10:34:54 -0700432 ret = cur && ret;
433 }
434 else if (lastCommand == probe_type_codes::OR)
435 {
436 ret = cur || ret;
437 }
438
439 if (first)
440 {
441 ret = cur;
442 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
444 lastCommand = probeType != PROBE_TYPES.end()
445 ? probeType->second
446 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800447 }
448
449 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800450 if (ret && foundDevs.size() == 0)
451 {
James Feistf1b14142019-04-10 15:22:09 -0700452 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800453 }
James Feist0eb40352019-04-09 14:44:04 -0700454 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700455 {
James Feist71f295f2019-06-20 13:35:12 -0700456 // match the last one
457 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700458 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700459
460 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700461 }
James Feist3cb5fec2018-01-23 14:41:51 -0800462 return ret;
463}
James Feist8f2710a2018-05-09 17:18:55 -0700464// this class finds the needed dbus fields and on destruction runs the probe
465struct PerformProbe : std::enable_shared_from_this<PerformProbe>
466{
James Feist3cb5fec2018-01-23 14:41:51 -0800467
James Feist8f2710a2018-05-09 17:18:55 -0700468 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800469 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700470 std::function<void(std::vector<std::optional<boost::container::flat_map<
471 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700472 _probeCommand(probeCommand),
473 _callback(std::move(callback))
474 {
475 }
476 ~PerformProbe()
477 {
James Feistf1b14142019-04-10 15:22:09 -0700478 std::vector<std::optional<
479 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700480 foundDevs;
481 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700482 {
James Feist0eb40352019-04-09 14:44:04 -0700483 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700484 }
485 }
486 void run()
487 {
488 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700489
James Feista465ccc2019-02-08 12:51:01 -0800490 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700491 {
492 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800493 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700494 cmp_str>::const_iterator probeType;
495 for (probeType = PROBE_TYPES.begin();
496 probeType != PROBE_TYPES.end(); probeType++)
497 {
498 if (probe.find(probeType->first) != std::string::npos)
499 {
500 found = true;
501 break;
502 }
503 }
504 if (found)
505 {
506 continue;
507 }
508 // syntax requires probe before first open brace
509 auto findStart = probe.find("(");
510 std::string interface = probe.substr(0, findStart);
511
512 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
513 }
514 }
515 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700516 std::function<void(std::vector<std::optional<boost::container::flat_map<
517 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700518 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700519};
520
521// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800522bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800523{
James Feist1df06a42019-04-11 14:23:04 -0700524 std::filesystem::create_directory(configurationOutDir);
525 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700526 if (!output.good())
527 {
528 return false;
529 }
James Feist1b2e2242018-01-30 13:45:19 -0800530 output << systemConfiguration.dump(4);
531 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700532 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700533}
James Feist1b2e2242018-01-30 13:45:19 -0800534
James Feist97a63f12018-05-17 13:50:57 -0700535template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800536bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
537 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700538{
539 try
540 {
541 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800542 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700543 ref = value;
544 return true;
545 }
James Feist98132792019-07-09 13:29:09 -0700546 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700547 {
548 return false;
549 }
550}
James Feistbb43d022018-06-12 15:44:33 -0700551
James Feistebcc26b2019-03-22 12:30:43 -0700552// template function to add array as dbus property
553template <typename PropertyType>
554void addArrayToDbus(const std::string& name, const nlohmann::json& array,
555 sdbusplus::asio::dbus_interface* iface,
556 sdbusplus::asio::PropertyPermission permission,
557 nlohmann::json& systemConfiguration,
558 const std::string& jsonPointerString)
559{
560 std::vector<PropertyType> values;
561 for (const auto& property : array)
562 {
563 auto ptr = property.get_ptr<const PropertyType*>();
564 if (ptr != nullptr)
565 {
566 values.emplace_back(*ptr);
567 }
568 }
569
570 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
571 {
572 iface->register_property(name, values);
573 }
574 else
575 {
576 iface->register_property(
577 name, values,
578 [&systemConfiguration,
579 jsonPointerString{std::string(jsonPointerString)}](
580 const std::vector<PropertyType>& newVal,
581 std::vector<PropertyType>& val) {
582 val = newVal;
583 if (!setJsonFromPointer(jsonPointerString, val,
584 systemConfiguration))
585 {
586 std::cerr << "error setting json field\n";
587 return -1;
588 }
589 if (!writeJsonFiles(systemConfiguration))
590 {
591 std::cerr << "error setting json file\n";
592 return -1;
593 }
594 return 1;
595 });
596 }
597}
598
James Feistbb43d022018-06-12 15:44:33 -0700599template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800600void addProperty(const std::string& propertyName, const PropertyType& value,
601 sdbusplus::asio::dbus_interface* iface,
602 nlohmann::json& systemConfiguration,
603 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700604 sdbusplus::asio::PropertyPermission permission)
605{
606 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
607 {
608 iface->register_property(propertyName, value);
609 return;
610 }
James Feist68500ff2018-08-08 15:40:42 -0700611 iface->register_property(
612 propertyName, value,
613 [&systemConfiguration,
614 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800615 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700616 val = newVal;
617 if (!setJsonFromPointer(jsonPointerString, val,
618 systemConfiguration))
619 {
620 std::cerr << "error setting json field\n";
621 return -1;
622 }
James Feistc6248a52018-08-14 10:09:45 -0700623 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700624 {
625 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700626 return -1;
627 }
628 return 1;
629 });
630}
631
632void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800633 const std::string& jsonPointerPath,
634 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
635 sdbusplus::asio::object_server& objServer,
636 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700637{
638 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
639 iface->register_method(
640 "Delete", [&objServer, &systemConfiguration, interface,
641 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700642 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700643 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700644 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700645 {
646 // this technically can't happen as the pointer is pointing to
647 // us
648 throw DBusInternalError();
649 }
650 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700651 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700652 {
653 std::cerr << "Can't delete interface " << jsonPointerPath
654 << "\n";
655 throw DBusInternalError();
656 }
657 systemConfiguration[ptr] = nullptr;
658
659 if (!writeJsonFiles(systemConfiguration))
660 {
661 std::cerr << "error setting json file\n";
662 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700663 }
James Feistbb43d022018-06-12 15:44:33 -0700664 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700665 });
James Feistbb43d022018-06-12 15:44:33 -0700666}
667
James Feist1b2e2242018-01-30 13:45:19 -0800668// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700669void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800670 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
671 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
672 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700673 sdbusplus::asio::PropertyPermission permission =
674 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800675{
James Feista465ccc2019-02-08 12:51:01 -0800676 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800677 {
James Feist8f2710a2018-05-09 17:18:55 -0700678 auto type = dictPair.value().type();
679 bool array = false;
680 if (dictPair.value().type() == nlohmann::json::value_t::array)
681 {
682 array = true;
683 if (!dictPair.value().size())
684 {
685 continue;
686 }
687 type = dictPair.value()[0].type();
688 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800689 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700690 {
691 if (arrayItem.type() != type)
692 {
693 isLegal = false;
694 break;
695 }
696 }
697 if (!isLegal)
698 {
699 std::cerr << "dbus format error" << dictPair.value() << "\n";
700 continue;
701 }
James Feista218ddb2019-04-11 14:01:31 -0700702 }
703 if (type == nlohmann::json::value_t::object)
704 {
705 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700706 }
James Feist97a63f12018-05-17 13:50:57 -0700707 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700708 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
709 {
710 // all setable numbers are doubles as it is difficult to always
711 // create a configuration file with all whole numbers as decimals
712 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700713 if (array)
714 {
715 if (dictPair.value()[0].is_number())
716 {
717 type = nlohmann::json::value_t::number_float;
718 }
719 }
720 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700721 {
722 type = nlohmann::json::value_t::number_float;
723 }
724 }
725
James Feist8f2710a2018-05-09 17:18:55 -0700726 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800727 {
James Feist9eb0b582018-04-27 12:15:46 -0700728 case (nlohmann::json::value_t::boolean):
729 {
James Feist8f2710a2018-05-09 17:18:55 -0700730 if (array)
731 {
732 // todo: array of bool isn't detected correctly by
733 // sdbusplus, change it to numbers
734 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700735 iface.get(), permission,
736 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700737 }
James Feistbb43d022018-06-12 15:44:33 -0700738
James Feist97a63f12018-05-17 13:50:57 -0700739 else
740 {
James Feistbb43d022018-06-12 15:44:33 -0700741 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700742 iface.get(), systemConfiguration, key,
743 permission);
James Feist97a63f12018-05-17 13:50:57 -0700744 }
James Feist9eb0b582018-04-27 12:15:46 -0700745 break;
746 }
747 case (nlohmann::json::value_t::number_integer):
748 {
James Feist8f2710a2018-05-09 17:18:55 -0700749 if (array)
750 {
751 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700752 iface.get(), permission,
753 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700754 }
755 else
756 {
James Feistbb43d022018-06-12 15:44:33 -0700757 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700758 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700759 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700760 }
James Feist9eb0b582018-04-27 12:15:46 -0700761 break;
762 }
763 case (nlohmann::json::value_t::number_unsigned):
764 {
James Feist8f2710a2018-05-09 17:18:55 -0700765 if (array)
766 {
767 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700768 iface.get(), permission,
769 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700770 }
771 else
772 {
James Feistbb43d022018-06-12 15:44:33 -0700773 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700774 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700775 systemConfiguration, key,
776 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700777 }
James Feist9eb0b582018-04-27 12:15:46 -0700778 break;
779 }
780 case (nlohmann::json::value_t::number_float):
781 {
James Feist8f2710a2018-05-09 17:18:55 -0700782 if (array)
783 {
784 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700785 iface.get(), permission,
786 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700787 }
James Feistbb43d022018-06-12 15:44:33 -0700788
James Feist97a63f12018-05-17 13:50:57 -0700789 else
790 {
James Feistbb43d022018-06-12 15:44:33 -0700791 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700792 iface.get(), systemConfiguration, key,
793 permission);
James Feist97a63f12018-05-17 13:50:57 -0700794 }
James Feist9eb0b582018-04-27 12:15:46 -0700795 break;
796 }
797 case (nlohmann::json::value_t::string):
798 {
James Feist8f2710a2018-05-09 17:18:55 -0700799 if (array)
800 {
James Feistebcc26b2019-03-22 12:30:43 -0700801 addArrayToDbus<std::string>(
802 dictPair.key(), dictPair.value(), iface.get(),
803 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700804 }
805 else
806 {
James Feistc6248a52018-08-14 10:09:45 -0700807 addProperty(
808 dictPair.key(), dictPair.value().get<std::string>(),
809 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700810 }
James Feist9eb0b582018-04-27 12:15:46 -0700811 break;
812 }
James Feist0eb40352019-04-09 14:44:04 -0700813 default:
814 {
James Feista218ddb2019-04-11 14:01:31 -0700815 std::cerr << "Unexpected json type in system configuration "
816 << dictPair.key() << ": "
817 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700818 break;
819 }
James Feist1b2e2242018-01-30 13:45:19 -0800820 }
821 }
James Feistc6248a52018-08-14 10:09:45 -0700822 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
823 {
824 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
825 systemConfiguration);
826 }
James Feist8f2710a2018-05-09 17:18:55 -0700827 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800828}
829
James Feista465ccc2019-02-08 12:51:01 -0800830sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700831{
832 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
833 interface) != settableInterfaces.end()
834 ? sdbusplus::asio::PropertyPermission::readWrite
835 : sdbusplus::asio::PropertyPermission::readOnly;
836}
837
James Feista465ccc2019-02-08 12:51:01 -0800838void createAddObjectMethod(const std::string& jsonPointerPath,
839 const std::string& path,
840 nlohmann::json& systemConfiguration,
841 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700842{
843 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
844
845 iface->register_method(
846 "AddObject",
847 [&systemConfiguration, &objServer,
848 jsonPointerPath{std::string(jsonPointerPath)},
849 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800850 const boost::container::flat_map<std::string, JsonVariantType>&
851 data) {
James Feist68500ff2018-08-08 15:40:42 -0700852 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800853 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700854 auto findExposes = base.find("Exposes");
855
856 if (findExposes == base.end())
857 {
858 throw std::invalid_argument("Entity must have children.");
859 }
860
861 // this will throw invalid-argument to sdbusplus if invalid json
862 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800863 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700864 {
James Feista465ccc2019-02-08 12:51:01 -0800865 nlohmann::json& newJson = newData[item.first];
866 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
867 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700868 }
869
870 auto findName = newData.find("Name");
871 auto findType = newData.find("Type");
872 if (findName == newData.end() || findType == newData.end())
873 {
874 throw std::invalid_argument("AddObject missing Name or Type");
875 }
James Feista465ccc2019-02-08 12:51:01 -0800876 const std::string* type = findType->get_ptr<const std::string*>();
877 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700878 if (type == nullptr || name == nullptr)
879 {
880 throw std::invalid_argument("Type and Name must be a string.");
881 }
882
883 size_t lastIndex = 0;
884 // we add in the "exposes"
885 for (; lastIndex < findExposes->size(); lastIndex++)
886 {
887 if (findExposes->at(lastIndex)["Name"] == *name &&
888 findExposes->at(lastIndex)["Type"] == *type)
889 {
890 throw std::invalid_argument(
891 "Field already in JSON, not adding");
892 }
893 lastIndex++;
894 }
895
896 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
897 *type + ".json");
898 // todo(james) we might want to also make a list of 'can add'
899 // interfaces but for now I think the assumption if there is a
900 // schema avaliable that it is allowed to update is fine
901 if (!schemaFile.good())
902 {
903 throw std::invalid_argument(
904 "No schema avaliable, cannot validate.");
905 }
906 nlohmann::json schema =
907 nlohmann::json::parse(schemaFile, nullptr, false);
908 if (schema.is_discarded())
909 {
910 std::cerr << "Schema not legal" << *type << ".json\n";
911 throw DBusInternalError();
912 }
913 if (!validateJson(schema, newData))
914 {
915 throw std::invalid_argument("Data does not match schema");
916 }
917
James Feist16a02f22019-05-13 15:21:37 -0700918 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700919 if (!writeJsonFiles(systemConfiguration))
920 {
921 std::cerr << "Error writing json files\n";
922 throw DBusInternalError();
923 }
924 std::string dbusName = *name;
925
926 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700927 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist98132792019-07-09 13:29:09 -0700928 auto interface = objServer.add_interface(
James Feist68500ff2018-08-08 15:40:42 -0700929 path + "/" + dbusName,
930 "xyz.openbmc_project.Configuration." + *type);
931 // permission is read-write, as since we just created it, must be
932 // runtime modifiable
933 populateInterfaceFromJson(
934 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700935 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700936 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700937 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700938 });
939 iface->initialize();
940}
941
James Feista465ccc2019-02-08 12:51:01 -0800942void postToDbus(const nlohmann::json& newConfiguration,
943 nlohmann::json& systemConfiguration,
944 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800945
James Feist1b2e2242018-01-30 13:45:19 -0800946{
James Feist97a63f12018-05-17 13:50:57 -0700947 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800948 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800949 {
James Feistf1b14142019-04-10 15:22:09 -0700950 std::string boardKey = boardPair.value()["Name"];
951 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700952 // loop through newConfiguration, but use values from system
953 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700954 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700955 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800956 std::string boardType;
957 if (findBoardType != boardValues.end() &&
958 findBoardType->type() == nlohmann::json::value_t::string)
959 {
960 boardType = findBoardType->get<std::string>();
961 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700962 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800963 }
964 else
965 {
966 std::cerr << "Unable to find type for " << boardKey
967 << " reverting to Chassis.\n";
968 boardType = "Chassis";
969 }
James Feist11be6672018-04-06 14:05:32 -0700970 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800971
972 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700973 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700974 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
975 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800976
James Feist8f2710a2018-05-09 17:18:55 -0700977 auto inventoryIface = objServer.add_interface(
978 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700979
James Feist8f2710a2018-05-09 17:18:55 -0700980 auto boardIface = objServer.add_interface(
981 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700982
James Feist68500ff2018-08-08 15:40:42 -0700983 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
984 objServer);
985
James Feist97a63f12018-05-17 13:50:57 -0700986 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700987 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700988 jsonPointerPath += "/";
989 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800990 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700991 {
992 if (boardField.value().type() == nlohmann::json::value_t::object)
993 {
James Feist8f2710a2018-05-09 17:18:55 -0700994 auto iface =
995 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700996 populateInterfaceFromJson(systemConfiguration,
997 jsonPointerPath + boardField.key(),
998 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700999 }
1000 }
James Feist97a63f12018-05-17 13:50:57 -07001001
James Feist1e3e6982018-08-03 16:09:28 -07001002 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001003 if (exposes == boardValues.end())
1004 {
1005 continue;
1006 }
James Feist97a63f12018-05-17 13:50:57 -07001007 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001008 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001009
1010 // store the board level pointer so we can modify it on the way down
1011 std::string jsonPointerPathBoard = jsonPointerPath;
1012 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001013 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001014 {
James Feist97a63f12018-05-17 13:50:57 -07001015 exposesIndex++;
1016 jsonPointerPath = jsonPointerPathBoard;
1017 jsonPointerPath += std::to_string(exposesIndex);
1018
James Feistd63d18a2018-07-19 15:23:45 -07001019 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001020 if (findName == item.end())
1021 {
1022 std::cerr << "cannot find name in field " << item << "\n";
1023 continue;
1024 }
James Feist1e3e6982018-08-03 16:09:28 -07001025 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001026 // if status is not found it is assumed to be status = 'okay'
1027 if (findStatus != item.end())
1028 {
1029 if (*findStatus == "disabled")
1030 {
1031 continue;
1032 }
1033 }
James Feistd63d18a2018-07-19 15:23:45 -07001034 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001035 std::string itemType;
1036 if (findType != item.end())
1037 {
1038 itemType = findType->get<std::string>();
1039 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001040 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1041 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001042 }
1043 else
1044 {
1045 itemType = "unknown";
1046 }
1047 std::string itemName = findName->get<std::string>();
1048 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001049 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001050
James Feist8f2710a2018-05-09 17:18:55 -07001051 auto itemIface = objServer.add_interface(
1052 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001053 "xyz.openbmc_project.Configuration." + itemType);
1054
James Feist97a63f12018-05-17 13:50:57 -07001055 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001056 itemIface, item, objServer,
1057 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001058
James Feista465ccc2019-02-08 12:51:01 -08001059 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001060 {
James Feist97a63f12018-05-17 13:50:57 -07001061 jsonPointerPath = jsonPointerPathBoard +
1062 std::to_string(exposesIndex) + "/" +
1063 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001064 if (objectPair.value().type() ==
1065 nlohmann::json::value_t::object)
1066 {
James Feist8f2710a2018-05-09 17:18:55 -07001067 auto objectIface = objServer.add_interface(
1068 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001069 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001070 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001071
1072 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001073 systemConfiguration, jsonPointerPath, objectIface,
1074 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001075 }
1076 else if (objectPair.value().type() ==
1077 nlohmann::json::value_t::array)
1078 {
1079 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001080 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001081 {
James Feist8f2710a2018-05-09 17:18:55 -07001082 continue;
1083 }
1084 bool isLegal = true;
1085 auto type = objectPair.value()[0].type();
1086 if (type != nlohmann::json::value_t::object)
1087 {
1088 continue;
1089 }
1090
1091 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001092 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001093 {
1094 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001095 {
James Feist8f2710a2018-05-09 17:18:55 -07001096 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001097 break;
1098 }
James Feist8f2710a2018-05-09 17:18:55 -07001099 }
1100 if (!isLegal)
1101 {
1102 std::cerr << "dbus format error" << objectPair.value()
1103 << "\n";
1104 break;
1105 }
1106
James Feista465ccc2019-02-08 12:51:01 -08001107 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001108 {
James Feist97a63f12018-05-17 13:50:57 -07001109
James Feist8f2710a2018-05-09 17:18:55 -07001110 auto objectIface = objServer.add_interface(
1111 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001112 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001113 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001114 populateInterfaceFromJson(
1115 systemConfiguration,
1116 jsonPointerPath + "/" + std::to_string(index),
1117 objectIface, arrayItem, objServer,
1118 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001119 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001120 }
1121 }
1122 }
1123 }
1124 }
1125}
1126
1127// finds the template character (currently set to $) and replaces the value with
1128// the field found in a dbus object i.e. $ADDRESS would get populated with the
1129// ADDRESS field from a object on dbus
1130void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001131 nlohmann::json::iterator& keyPair,
1132 const boost::container::flat_map<std::string, BasicVariantType>&
1133 foundDevice,
1134 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001135{
James Feist665267c2019-06-24 15:24:17 -07001136 if (keyPair.value().type() == nlohmann::json::value_t::object ||
1137 keyPair.value().type() == nlohmann::json::value_t::array)
James Feist11be6672018-04-06 14:05:32 -07001138 {
1139 for (auto nextLayer = keyPair.value().begin();
1140 nextLayer != keyPair.value().end(); nextLayer++)
1141 {
1142 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1143 }
1144 return;
1145 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001146
1147 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1148 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001149 {
1150 return;
1151 }
1152
James Feistf5125b02019-06-06 11:27:43 -07001153 boost::replace_all(*strPtr, std::string(templateChar) + "index",
1154 std::to_string(foundDeviceIdx));
Ed Tanous12bc7932019-02-26 14:36:20 -08001155
Ed Tanous12bc7932019-02-26 14:36:20 -08001156 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001157 {
James Feistf5125b02019-06-06 11:27:43 -07001158 std::string templateName = templateChar + foundDevicePair.first;
1159 boost::iterator_range<std::string::const_iterator> find =
1160 boost::ifind_first(*strPtr, templateName);
1161 if (find)
James Feist1b2e2242018-01-30 13:45:19 -08001162 {
James Feistf5125b02019-06-06 11:27:43 -07001163 size_t start = find.begin() - strPtr->begin();
1164 // check for additional operations
Patrick Venture4d988822019-08-09 11:25:19 -07001165 if (find.end() == strPtr->end())
James Feistf5125b02019-06-06 11:27:43 -07001166 {
1167 std::visit([&](auto&& val) { keyPair.value() = val; },
1168 foundDevicePair.second);
James Feist27e75832019-07-08 11:52:15 -07001169 return;
James Feistf5125b02019-06-06 11:27:43 -07001170 }
1171
Patrick Venture4d988822019-08-09 11:25:19 -07001172 // save the prefix
1173 std::string prefix = strPtr->substr(0, start);
1174
1175 // operate on the rest (+1 for trailing space)
1176 std::string end = strPtr->substr(start + templateName.size() + 1);
1177
1178 std::vector<std::string> split;
1179 boost::split(split, end, boost::is_any_of(" "));
1180
1181 // need at least 1 operation and number
1182 if (split.size() < 2)
1183 {
1184 std::cerr << "Syntax error on template replacement of "
1185 << *strPtr << "\n";
1186 for (const std::string& data : split)
1187 {
1188 std::cerr << data << " ";
1189 }
1190 std::cerr << "\n";
1191 continue;
1192 }
1193
1194 // we assume that the replacement is a number, because we can
1195 // only do math on numbers.. we might concatenate strings in the
1196 // future, but thats later
1197 int number =
1198 std::visit(VariantToIntVisitor(), foundDevicePair.second);
1199
1200 bool isOperator = true;
1201 TemplateOperation next = TemplateOperation::addition;
1202
1203 auto it = split.begin();
1204
1205 for (; it != split.end(); it++)
1206 {
1207 if (isOperator)
1208 {
1209 if (*it == "+")
1210 {
1211 next = TemplateOperation::addition;
1212 }
1213 else if (*it == "-")
1214 {
1215 next = TemplateOperation::subtraction;
1216 }
1217 else if (*it == "*")
1218 {
1219 next = TemplateOperation::multiplication;
1220 }
1221 else if (*it == R"(%)")
1222 {
1223 next = TemplateOperation::modulo;
1224 }
1225 else if (*it == R"(/)")
1226 {
1227 next = TemplateOperation::division;
1228 }
1229 else
1230 {
1231 break;
1232 }
1233 }
1234 else
1235 {
1236 int constant = 0;
1237 try
1238 {
1239 constant = std::stoi(*it);
1240 }
1241 catch (std::invalid_argument&)
1242 {
1243 std::cerr << "Parameter not supported for templates "
1244 << *it << "\n";
1245 continue;
1246 }
1247 switch (next)
1248 {
1249 case TemplateOperation::addition:
1250 {
1251 number += constant;
1252 break;
1253 }
1254 case TemplateOperation::subtraction:
1255 {
1256 number -= constant;
1257 break;
1258 }
1259 case TemplateOperation::multiplication:
1260 {
1261 number *= constant;
1262 break;
1263 }
1264 case TemplateOperation::division:
1265 {
1266 number /= constant;
1267 break;
1268 }
1269 case TemplateOperation::modulo:
1270 {
1271 number = number % constant;
1272 break;
1273 }
1274
1275 default:
1276 break;
1277 }
1278 }
1279 isOperator = !isOperator;
1280 }
1281 std::string result = prefix + std::to_string(number);
1282
1283 if (it != split.end())
1284 {
1285 for (; it != split.end(); it++)
1286 {
1287 result += " " + *it;
1288 }
1289 }
1290 keyPair.value() = result;
1291
Ed Tanous12bc7932019-02-26 14:36:20 -08001292 // We probably just invalidated the pointer above, so set it to null
1293 strPtr = nullptr;
1294 break;
James Feist1b2e2242018-01-30 13:45:19 -08001295 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001296 }
1297
1298 strPtr = keyPair.value().get_ptr<std::string*>();
1299 if (strPtr == nullptr)
1300 {
1301 return;
James Feist1b2e2242018-01-30 13:45:19 -08001302 }
James Feistc6090822019-01-04 16:02:48 -08001303
1304 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001305 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001306 {
1307 try
1308 {
James Feistc6090822019-01-04 16:02:48 -08001309 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001310 int64_t temp = std::stoul(*strPtr, &pos, 0);
1311 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001312 {
1313 keyPair.value() = static_cast<uint64_t>(temp);
1314 }
James Feist28dc2da2018-10-15 14:47:42 -07001315 }
James Feist98132792019-07-09 13:29:09 -07001316 catch (std::invalid_argument&)
James Feist28dc2da2018-10-15 14:47:42 -07001317 {
1318 }
James Feist98132792019-07-09 13:29:09 -07001319 catch (std::out_of_range&)
James Feistc6090822019-01-04 16:02:48 -08001320 {
1321 }
James Feist28dc2da2018-10-15 14:47:42 -07001322 }
James Feistf5125b02019-06-06 11:27:43 -07001323 // non-hex numbers
1324 else
1325 {
1326 try
1327 {
1328 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
1329 keyPair.value() = temp;
1330 }
1331 catch (boost::bad_lexical_cast&)
1332 {
1333 }
1334 }
James Feist1b2e2242018-01-30 13:45:19 -08001335}
1336
James Feist8f2710a2018-05-09 17:18:55 -07001337// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001338bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001339{
1340 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001341 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001342 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1343 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001344 {
1345 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001346 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001347 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001348 }
James Feistb4383f42018-08-06 16:54:10 -07001349
1350 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1351 globalSchema);
1352 if (!schemaStream.good())
1353 {
1354 std::cerr
1355 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1356 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001357 return false;
James Feistb4383f42018-08-06 16:54:10 -07001358 }
1359 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1360 if (schema.is_discarded())
1361 {
1362 std::cerr
1363 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1364 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001365 return false;
James Feistb4383f42018-08-06 16:54:10 -07001366 }
1367
James Feista465ccc2019-02-08 12:51:01 -08001368 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001369 {
1370 std::ifstream jsonStream(jsonPath.c_str());
1371 if (!jsonStream.good())
1372 {
1373 std::cerr << "unable to open " << jsonPath.string() << "\n";
1374 continue;
1375 }
1376 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1377 if (data.is_discarded())
1378 {
1379 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1380 continue;
1381 }
James Feist8da99192019-01-24 08:20:16 -08001382 /*
1383 * todo(james): reenable this once less things are in flight
1384 *
James Feistb4383f42018-08-06 16:54:10 -07001385 if (!validateJson(schema, data))
1386 {
1387 std::cerr << "Error validating " << jsonPath.string() << "\n";
1388 continue;
1389 }
James Feist8da99192019-01-24 08:20:16 -08001390 */
James Feistb4383f42018-08-06 16:54:10 -07001391
James Feist3cb5fec2018-01-23 14:41:51 -08001392 if (data.type() == nlohmann::json::value_t::array)
1393 {
James Feista465ccc2019-02-08 12:51:01 -08001394 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001395 {
1396 configurations.emplace_back(d);
1397 }
1398 }
1399 else
1400 {
1401 configurations.emplace_back(data);
1402 }
1403 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001404 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001405}
James Feist3cb5fec2018-01-23 14:41:51 -08001406
James Feist8f2710a2018-05-09 17:18:55 -07001407struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001408{
James Feist75fdeeb2018-02-20 14:26:16 -08001409
James Feista465ccc2019-02-08 12:51:01 -08001410 PerformScan(nlohmann::json& systemConfiguration,
1411 std::list<nlohmann::json>& configurations,
1412 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001413 _systemConfiguration(systemConfiguration),
1414 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001415 {
James Feist8f2710a2018-05-09 17:18:55 -07001416 }
1417 void run()
1418 {
1419 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001420 {
James Feist1e3e6982018-08-03 16:09:28 -07001421 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001422 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001423
James Feist1b2e2242018-01-30 13:45:19 -08001424 nlohmann::json probeCommand;
1425 // check for poorly formatted fields, probe must be an array
1426 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001427 {
1428 std::cerr << "configuration file missing probe:\n " << *it
1429 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001430 it = _configurations.erase(it);
1431 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001432 }
James Feist1b2e2242018-01-30 13:45:19 -08001433 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001434 {
1435 probeCommand = nlohmann::json::array();
1436 probeCommand.push_back(*findProbe);
1437 }
1438 else
1439 {
1440 probeCommand = *findProbe;
1441 }
James Feist1b2e2242018-01-30 13:45:19 -08001442
1443 if (findName == it->end())
1444 {
1445 std::cerr << "configuration file missing name:\n " << *it
1446 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001447 it = _configurations.erase(it);
1448 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001449 }
James Feistf1b14142019-04-10 15:22:09 -07001450 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001451
James Feistf1b14142019-04-10 15:22:09 -07001452 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1453 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001454 {
James Feist8f2710a2018-05-09 17:18:55 -07001455 it = _configurations.erase(it);
1456 continue;
1457 }
James Feistf1b14142019-04-10 15:22:09 -07001458 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001459
James Feist8f2710a2018-05-09 17:18:55 -07001460 // store reference to this to children to makes sure we don't get
1461 // destroyed too early
1462 auto thisRef = shared_from_this();
1463 auto p = std::make_shared<PerformProbe>(
1464 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001465 [&, recordPtr, probeName,
1466 thisRef](std::vector<std::optional<boost::container::flat_map<
1467 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001468 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001469
James Feistf1b14142019-04-10 15:22:09 -07001470 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001471 size_t foundDeviceIdx = 0;
1472
James Feista465ccc2019-02-08 12:51:01 -08001473 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001474 {
James Feistf5125b02019-06-06 11:27:43 -07001475 nlohmann::json record = *recordPtr;
James Feistf1b14142019-04-10 15:22:09 -07001476 std::string recordName;
1477 size_t hash = 0;
1478 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001479 {
James Feistf1b14142019-04-10 15:22:09 -07001480 // use an array so alphabetical order from the
1481 // flat_map is maintained
1482 auto device = nlohmann::json::array();
1483 for (auto& devPair : *foundDevice)
1484 {
1485 device.push_back(devPair.first);
1486 std::visit(
1487 [&device](auto&& v) {
1488 device.push_back(v);
1489 },
1490 devPair.second);
1491 }
1492 hash = std::hash<std::string>{}(probeName +
1493 device.dump());
1494 // hashes are hard to distinguish, use the
1495 // non-hashed version if we want debug
1496 if constexpr (DEBUG)
1497 {
1498 recordName = probeName + device.dump();
1499 }
1500 else
1501 {
1502 recordName = std::to_string(hash);
1503 }
James Feist8f2710a2018-05-09 17:18:55 -07001504 }
James Feistf1b14142019-04-10 15:22:09 -07001505 else
1506 {
1507 recordName = probeName;
1508 }
1509
James Feist1df06a42019-04-11 14:23:04 -07001510 auto fromLastJson = lastJson.find(recordName);
1511 if (fromLastJson != lastJson.end())
1512 {
1513 // keep user changes
1514 _systemConfiguration[recordName] = *fromLastJson;
1515 continue;
1516 }
1517
James Feistf1b14142019-04-10 15:22:09 -07001518 // insert into configuration temporarily to be able to
1519 // reference ourselves
1520
1521 _systemConfiguration[recordName] = record;
1522
1523 if (foundDevice)
1524 {
1525 for (auto keyPair = record.begin();
1526 keyPair != record.end(); keyPair++)
1527 {
1528 templateCharReplace(keyPair, *foundDevice,
1529 foundDeviceIdx);
1530 }
1531 }
Patrick Ventureb8293c02019-08-09 14:12:47 -07001532
James Feistf1b14142019-04-10 15:22:09 -07001533 auto findExpose = record.find("Exposes");
1534 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001535 {
Patrick Ventureb8293c02019-08-09 14:12:47 -07001536 _systemConfiguration[recordName] = record;
1537 logDeviceAdded(record);
1538 foundDeviceIdx++;
James Feist8f2710a2018-05-09 17:18:55 -07001539 continue;
1540 }
Patrick Ventureb8293c02019-08-09 14:12:47 -07001541
James Feista465ccc2019-02-08 12:51:01 -08001542 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001543 {
1544 for (auto keyPair = expose.begin();
1545 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001546 {
James Feist1b2e2242018-01-30 13:45:19 -08001547
James Feist8f2710a2018-05-09 17:18:55 -07001548 // fill in template characters with devices
1549 // found
James Feistf1b14142019-04-10 15:22:09 -07001550 if (foundDevice)
1551 {
1552 templateCharReplace(keyPair, *foundDevice,
1553 foundDeviceIdx);
1554 }
James Feist8f2710a2018-05-09 17:18:55 -07001555 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001556 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001557 {
1558 if (keyPair.value().type() !=
1559 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001560 {
James Feist8f2710a2018-05-09 17:18:55 -07001561 std::cerr << "bind_ value must be of "
1562 "type string "
1563 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001564 continue;
1565 }
James Feist8f2710a2018-05-09 17:18:55 -07001566 bool foundBind = false;
1567 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001568 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001569
James Feista465ccc2019-02-08 12:51:01 -08001570 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001571 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001572 {
James Feist1b2e2242018-01-30 13:45:19 -08001573
James Feist8f2710a2018-05-09 17:18:55 -07001574 auto configListFind =
1575 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001576 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001577
1578 if (configListFind ==
1579 configurationPair.value()
1580 .end() ||
1581 configListFind->type() !=
1582 nlohmann::json::value_t::array)
1583 {
1584 continue;
1585 }
James Feista465ccc2019-02-08 12:51:01 -08001586 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001587 *configListFind)
1588 {
1589 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001590 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001591 if (boost::iequals(
1592 foundObjectName,
1593 keyPair.value()
1594 .get<std::string>()))
1595 {
James Feist1e3e6982018-08-03 16:09:28 -07001596 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001597 "okay";
1598 expose[bind] = exposedObject;
1599
1600 foundBind = true;
1601 break;
1602 }
1603 }
1604 if (foundBind)
1605 {
James Feist3cb5fec2018-01-23 14:41:51 -08001606 break;
1607 }
1608 }
James Feist8f2710a2018-05-09 17:18:55 -07001609 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001610 {
James Feist8f2710a2018-05-09 17:18:55 -07001611 std::cerr << "configuration file "
1612 "dependency error, "
1613 "could not find bind "
1614 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001615 }
1616 }
1617 }
1618 }
James Feistf1b14142019-04-10 15:22:09 -07001619 // overwrite ourselves with cleaned up version
1620 _systemConfiguration[recordName] = record;
James Feist1a996582019-05-14 15:10:06 -07001621
1622 logDeviceAdded(record);
James Feist1df06a42019-04-11 14:23:04 -07001623
James Feistf1b14142019-04-10 15:22:09 -07001624 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001625 }
James Feist8f2710a2018-05-09 17:18:55 -07001626 });
1627 p->run();
1628 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001629 }
1630 }
James Feist75fdeeb2018-02-20 14:26:16 -08001631
James Feist8f2710a2018-05-09 17:18:55 -07001632 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001633 {
James Feist8f2710a2018-05-09 17:18:55 -07001634 if (_passed)
1635 {
1636 auto nextScan = std::make_shared<PerformScan>(
1637 _systemConfiguration, _configurations, std::move(_callback));
1638 nextScan->run();
1639 }
1640 else
1641 {
1642 _callback();
1643 }
1644 }
James Feista465ccc2019-02-08 12:51:01 -08001645 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001646 std::list<nlohmann::json> _configurations;
1647 std::function<void(void)> _callback;
1648 std::vector<std::shared_ptr<PerformProbe>> _probes;
1649 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001650 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001651};
James Feistc95cb142018-02-26 10:41:42 -08001652
James Feist1df06a42019-04-11 14:23:04 -07001653void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001654 nlohmann::json& systemConfiguration)
1655{
1656 static bool scannedPowerOff = false;
1657 static bool scannedPowerOn = false;
1658
James Feistfb00f392019-06-25 14:16:48 -07001659 if (systemConfiguration.empty() || lastJson.empty())
1660 {
1661 return; // not ready yet
1662 }
James Feist1df06a42019-04-11 14:23:04 -07001663 if (scannedPowerOn)
1664 {
1665 return;
1666 }
1667
1668 if (!isPowerOn() && scannedPowerOff)
1669 {
1670 return;
1671 }
1672
1673 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001674 timer.async_wait(
1675 [&systemConfiguration](const boost::system::error_code& ec) {
1676 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001677 {
James Feist1a996582019-05-14 15:10:06 -07001678 // we were cancelled
1679 return;
1680 }
1681
1682 bool powerOff = !isPowerOn();
1683 for (const auto& item : lastJson.items())
1684 {
1685 if (systemConfiguration.find(item.key()) ==
1686 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001687 {
James Feist1a996582019-05-14 15:10:06 -07001688 bool isDetectedPowerOn = false;
1689 auto powerState = item.value().find("PowerState");
1690 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001691 {
James Feist1a996582019-05-14 15:10:06 -07001692 auto ptr = powerState->get_ptr<const std::string*>();
1693 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001694 {
James Feist1a996582019-05-14 15:10:06 -07001695 if (*ptr == "On" || *ptr == "BiosPost")
1696 {
1697 isDetectedPowerOn = true;
1698 }
James Feist1df06a42019-04-11 14:23:04 -07001699 }
1700 }
James Feist1a996582019-05-14 15:10:06 -07001701 if (powerOff && isDetectedPowerOn)
1702 {
1703 // power not on yet, don't know if it's there or not
1704 continue;
1705 }
1706 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1707 {
1708 // already logged it when power was off
1709 continue;
1710 }
James Feist1df06a42019-04-11 14:23:04 -07001711
James Feist1a996582019-05-14 15:10:06 -07001712 logDeviceRemoved(item.value());
1713 }
James Feist1df06a42019-04-11 14:23:04 -07001714 }
James Feist1a996582019-05-14 15:10:06 -07001715 scannedPowerOff = true;
1716 if (!powerOff)
1717 {
1718 scannedPowerOn = true;
1719 }
1720 });
James Feist1df06a42019-04-11 14:23:04 -07001721}
1722
James Feist8f2710a2018-05-09 17:18:55 -07001723// main properties changed entry
1724void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001725 boost::asio::io_service& io,
1726 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1727 nlohmann::json& systemConfiguration,
1728 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001729{
1730 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001731 static bool timerRunning;
1732
1733 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001734 timer.expires_from_now(boost::posix_time::seconds(1));
1735
1736 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001737 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001738 if (ec == boost::asio::error::operation_aborted)
1739 {
1740 // we were cancelled
1741 return;
1742 }
1743 else if (ec)
1744 {
1745 std::cerr << "async wait error " << ec << "\n";
1746 return;
1747 }
James Feist1df06a42019-04-11 14:23:04 -07001748 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001749
1750 nlohmann::json oldConfiguration = systemConfiguration;
1751 DBUS_PROBE_OBJECTS.clear();
1752
1753 std::list<nlohmann::json> configurations;
1754 if (!findJsonFiles(configurations))
1755 {
1756 std::cerr << "cannot find json files\n";
1757 return;
1758 }
1759
1760 auto perfScan = std::make_shared<PerformScan>(
1761 systemConfiguration, configurations, [&, oldConfiguration]() {
1762 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001763 for (auto it = newConfiguration.begin();
1764 it != newConfiguration.end();)
1765 {
1766 auto findKey = oldConfiguration.find(it.key());
1767 if (findKey != oldConfiguration.end())
1768 {
1769 it = newConfiguration.erase(it);
1770 }
1771 else
1772 {
1773 it++;
1774 }
1775 }
James Feist8f2710a2018-05-09 17:18:55 -07001776 registerCallbacks(io, dbusMatches, systemConfiguration,
1777 objServer);
1778 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001779 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001780
James Feistbb43d022018-06-12 15:44:33 -07001781 io.post([&]() {
1782 if (!writeJsonFiles(systemConfiguration))
1783 {
1784 std::cerr << "Error writing json files\n";
1785 }
1786 });
James Feist8f2710a2018-05-09 17:18:55 -07001787 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001788 postToDbus(newConfiguration, systemConfiguration,
1789 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001790 if (!timerRunning)
1791 {
James Feist98132792019-07-09 13:29:09 -07001792 startRemovedTimer(timer, systemConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001793 }
James Feist8f2710a2018-05-09 17:18:55 -07001794 });
1795 });
1796 });
1797 perfScan->run();
1798 });
James Feist75fdeeb2018-02-20 14:26:16 -08001799}
1800
James Feista465ccc2019-02-08 12:51:01 -08001801void registerCallbacks(boost::asio::io_service& io,
1802 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1803 nlohmann::json& systemConfiguration,
1804 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001805{
1806 static boost::container::flat_set<std::string> watchedObjects;
1807
James Feista465ccc2019-02-08 12:51:01 -08001808 for (const auto& objectMap : DBUS_PROBE_OBJECTS)
James Feist75fdeeb2018-02-20 14:26:16 -08001809 {
1810 auto findObject = watchedObjects.find(objectMap.first);
1811 if (findObject != watchedObjects.end())
1812 {
1813 continue;
1814 }
James Feist8f2710a2018-05-09 17:18:55 -07001815 std::function<void(sdbusplus::message::message & message)>
1816 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001817
James Feista465ccc2019-02-08 12:51:01 -08001818 [&](sdbusplus::message::message&) {
James Feist8f2710a2018-05-09 17:18:55 -07001819 propertiesChangedCallback(io, dbusMatches,
1820 systemConfiguration, objServer);
1821 };
1822
1823 sdbusplus::bus::match::match match(
James Feista465ccc2019-02-08 12:51:01 -08001824 static_cast<sdbusplus::bus::bus&>(*SYSTEM_BUS),
James Feist8f2710a2018-05-09 17:18:55 -07001825 "type='signal',member='PropertiesChanged',arg0='" +
1826 objectMap.first + "'",
1827 eventHandler);
1828 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001829 }
1830}
1831
James Feist98132792019-07-09 13:29:09 -07001832int main()
James Feist75fdeeb2018-02-20 14:26:16 -08001833{
1834 // setup connection to dbus
1835 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001836 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001837 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001838
James Feist8f2710a2018-05-09 17:18:55 -07001839 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001840
James Feist8f2710a2018-05-09 17:18:55 -07001841 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1842 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1843 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001844
James Feist8f2710a2018-05-09 17:18:55 -07001845 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1846 objServer.add_interface("/xyz/openbmc_project/inventory",
1847 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001848
1849 // to keep reference to the match / filter objects so they don't get
1850 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001851 std::vector<sdbusplus::bus::match::match> dbusMatches;
1852
1853 nlohmann::json systemConfiguration = nlohmann::json::object();
1854
1855 inventoryIface->register_method(
James Feista465ccc2019-02-08 12:51:01 -08001856 "Notify",
1857 [](const boost::container::flat_map<
1858 std::string,
James Feist98132792019-07-09 13:29:09 -07001859 boost::container::flat_map<std::string, BasicVariantType>>&) {
1860 return;
1861 });
James Feist8f2710a2018-05-09 17:18:55 -07001862 inventoryIface->initialize();
1863
1864 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001865#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001866 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001867#endif
James Feist8f2710a2018-05-09 17:18:55 -07001868 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1869 objServer);
1870 });
James Feist4131aea2018-03-09 09:47:30 -08001871
James Feistfd1264a2018-05-03 12:10:00 -07001872 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001873 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1874 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001875 });
James Feist8f2710a2018-05-09 17:18:55 -07001876 entityIface->initialize();
1877
James Feist1df06a42019-04-11 14:23:04 -07001878 if (fwVersionIsSame())
1879 {
1880 if (std::filesystem::is_regular_file(currentConfiguration))
1881 {
1882 // this file could just be deleted, but it's nice for debug
1883 std::filesystem::create_directory(tempConfigDir);
1884 std::filesystem::remove(lastConfiguration);
1885 std::filesystem::copy(currentConfiguration, lastConfiguration);
1886 std::filesystem::remove(currentConfiguration);
1887
1888 std::ifstream jsonStream(lastConfiguration);
1889 if (jsonStream.good())
1890 {
1891 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1892 if (data.is_discarded())
1893 {
1894 std::cerr << "syntax error in " << lastConfiguration
1895 << "\n";
1896 }
1897 else
1898 {
1899 lastJson = std::move(data);
1900 }
1901 }
1902 else
1903 {
1904 std::cerr << "unable to open " << lastConfiguration << "\n";
1905 }
1906 }
1907 }
1908 else
1909 {
1910 // not an error, just logging at this level to make it in the journal
1911 std::cerr << "Clearing previous configuration\n";
1912 std::filesystem::remove(currentConfiguration);
1913 }
1914
1915 // some boards only show up after power is on, we want to not say they are
1916 // removed until the same state happens
1917 setupPowerMatch(SYSTEM_BUS);
1918
James Feist1b2e2242018-01-30 13:45:19 -08001919 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001920
1921 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001922}