blob: c6e22efc0decbced6d3daa8e0e16df12eea1f246 [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 Feist8f2710a2018-05-09 17:18:55 -0700354 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800355 }
James Feist9eb0b582018-04-27 12:15:46 -0700356 case probe_type_codes::TRUE_T:
357 {
James Feist8f2710a2018-05-09 17:18:55 -0700358 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700359 }
360 case probe_type_codes::MATCH_ONE:
361 {
362 // set current value to last, this probe type shouldn't
363 // affect the outcome
364 cur = ret;
365 matchOne = true;
366 break;
367 }
368 /*case probe_type_codes::AND:
369 break;
370 case probe_type_codes::OR:
371 break;
372 // these are no-ops until the last command switch
373 */
374 case probe_type_codes::FOUND:
375 {
376 if (!std::regex_search(probe, match, command))
377 {
James Feist0eb40352019-04-09 14:44:04 -0700378 std::cerr << "found probe syntax error " << probe
James Feist9eb0b582018-04-27 12:15:46 -0700379 << "\n";
380 return false;
381 }
382 std::string commandStr = *(match.begin() + 1);
383 boost::replace_all(commandStr, "'", "");
384 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
385 commandStr) != PASSED_PROBES.end());
386 break;
387 }
James Feist0eb40352019-04-09 14:44:04 -0700388 default:
389 {
390 break;
391 }
James Feist3cb5fec2018-01-23 14:41:51 -0800392 }
393 }
394 // look on dbus for object
395 else
396 {
397 if (!std::regex_search(probe, match, command))
398 {
James Feist0eb40352019-04-09 14:44:04 -0700399 std::cerr << "dbus probe syntax error " << probe << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800400 return false;
401 }
402 std::string commandStr = *(match.begin() + 1);
403 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700404 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800405 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800406 auto json = nlohmann::json::parse(commandStr, nullptr, false);
407 if (json.is_discarded())
408 {
James Feist0eb40352019-04-09 14:44:04 -0700409 std::cerr << "dbus command syntax error " << commandStr << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800410 return false;
411 }
412 // we can match any (string, variant) property. (string, string)
413 // does a regex
414 std::map<std::string, nlohmann::json> dbusProbeMap =
415 json.get<std::map<std::string, nlohmann::json>>();
416 auto findStart = probe.find("(");
417 if (findStart == std::string::npos)
418 {
419 return false;
420 }
421 std::string probeInterface = probe.substr(0, findStart);
422 cur =
423 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
424 }
425
426 // some functions like AND and OR only take affect after the
427 // fact
James Feist54a0dca2019-06-26 10:34:54 -0700428 if (lastCommand == probe_type_codes::AND)
James Feist3cb5fec2018-01-23 14:41:51 -0800429 {
James Feist54a0dca2019-06-26 10:34:54 -0700430 ret = cur && ret;
431 }
432 else if (lastCommand == probe_type_codes::OR)
433 {
434 ret = cur || ret;
435 }
436
437 if (first)
438 {
439 ret = cur;
440 first = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800441 }
442 lastCommand = probeType != PROBE_TYPES.end()
443 ? probeType->second
444 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800445 }
446
447 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800448 if (ret && foundDevs.size() == 0)
449 {
James Feistf1b14142019-04-10 15:22:09 -0700450 foundDevs.emplace_back(std::nullopt);
James Feist3cb5fec2018-01-23 14:41:51 -0800451 }
James Feist0eb40352019-04-09 14:44:04 -0700452 if (matchOne && ret)
James Feist6bd2a022018-03-13 12:30:58 -0700453 {
James Feist71f295f2019-06-20 13:35:12 -0700454 // match the last one
455 auto last = foundDevs.back();
James Feist0eb40352019-04-09 14:44:04 -0700456 foundDevs.clear();
James Feist71f295f2019-06-20 13:35:12 -0700457
458 foundDevs.emplace_back(std::move(last));
James Feist6bd2a022018-03-13 12:30:58 -0700459 }
James Feist3cb5fec2018-01-23 14:41:51 -0800460 return ret;
461}
James Feist8f2710a2018-05-09 17:18:55 -0700462// this class finds the needed dbus fields and on destruction runs the probe
463struct PerformProbe : std::enable_shared_from_this<PerformProbe>
464{
James Feist3cb5fec2018-01-23 14:41:51 -0800465
James Feist8f2710a2018-05-09 17:18:55 -0700466 PerformProbe(
James Feista465ccc2019-02-08 12:51:01 -0800467 const std::vector<std::string>& probeCommand,
James Feistf1b14142019-04-10 15:22:09 -0700468 std::function<void(std::vector<std::optional<boost::container::flat_map<
469 std::string, BasicVariantType>>>&)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -0700470 _probeCommand(probeCommand),
471 _callback(std::move(callback))
472 {
473 }
474 ~PerformProbe()
475 {
James Feistf1b14142019-04-10 15:22:09 -0700476 std::vector<std::optional<
477 boost::container::flat_map<std::string, BasicVariantType>>>
James Feist0eb40352019-04-09 14:44:04 -0700478 foundDevs;
479 if (probe(_probeCommand, foundDevs))
James Feist8f2710a2018-05-09 17:18:55 -0700480 {
James Feist0eb40352019-04-09 14:44:04 -0700481 _callback(foundDevs);
James Feist8f2710a2018-05-09 17:18:55 -0700482 }
483 }
484 void run()
485 {
486 // parse out dbus probes by discarding other probe types
James Feist8f2710a2018-05-09 17:18:55 -0700487
James Feista465ccc2019-02-08 12:51:01 -0800488 for (std::string& probe : _probeCommand)
James Feist8f2710a2018-05-09 17:18:55 -0700489 {
490 bool found = false;
James Feista465ccc2019-02-08 12:51:01 -0800491 boost::container::flat_map<const char*, probe_type_codes,
James Feist8f2710a2018-05-09 17:18:55 -0700492 cmp_str>::const_iterator probeType;
493 for (probeType = PROBE_TYPES.begin();
494 probeType != PROBE_TYPES.end(); probeType++)
495 {
496 if (probe.find(probeType->first) != std::string::npos)
497 {
498 found = true;
499 break;
500 }
501 }
502 if (found)
503 {
504 continue;
505 }
506 // syntax requires probe before first open brace
507 auto findStart = probe.find("(");
508 std::string interface = probe.substr(0, findStart);
509
510 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
511 }
512 }
513 std::vector<std::string> _probeCommand;
James Feistf1b14142019-04-10 15:22:09 -0700514 std::function<void(std::vector<std::optional<boost::container::flat_map<
515 std::string, BasicVariantType>>>&)>
James Feist8f2710a2018-05-09 17:18:55 -0700516 _callback;
James Feist8f2710a2018-05-09 17:18:55 -0700517};
518
519// writes output files to persist data
James Feista465ccc2019-02-08 12:51:01 -0800520bool writeJsonFiles(const nlohmann::json& systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800521{
James Feist1df06a42019-04-11 14:23:04 -0700522 std::filesystem::create_directory(configurationOutDir);
523 std::ofstream output(currentConfiguration);
James Feistbb43d022018-06-12 15:44:33 -0700524 if (!output.good())
525 {
526 return false;
527 }
James Feist1b2e2242018-01-30 13:45:19 -0800528 output << systemConfiguration.dump(4);
529 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700530 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700531}
James Feist1b2e2242018-01-30 13:45:19 -0800532
James Feist97a63f12018-05-17 13:50:57 -0700533template <typename JsonType>
James Feista465ccc2019-02-08 12:51:01 -0800534bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
535 nlohmann::json& systemConfiguration)
James Feist97a63f12018-05-17 13:50:57 -0700536{
537 try
538 {
539 nlohmann::json::json_pointer ptr(ptrStr);
James Feista465ccc2019-02-08 12:51:01 -0800540 nlohmann::json& ref = systemConfiguration[ptr];
James Feist97a63f12018-05-17 13:50:57 -0700541 ref = value;
542 return true;
543 }
James Feist98132792019-07-09 13:29:09 -0700544 catch (const std::out_of_range&)
James Feist97a63f12018-05-17 13:50:57 -0700545 {
546 return false;
547 }
548}
James Feistbb43d022018-06-12 15:44:33 -0700549
James Feistebcc26b2019-03-22 12:30:43 -0700550// template function to add array as dbus property
551template <typename PropertyType>
552void addArrayToDbus(const std::string& name, const nlohmann::json& array,
553 sdbusplus::asio::dbus_interface* iface,
554 sdbusplus::asio::PropertyPermission permission,
555 nlohmann::json& systemConfiguration,
556 const std::string& jsonPointerString)
557{
558 std::vector<PropertyType> values;
559 for (const auto& property : array)
560 {
561 auto ptr = property.get_ptr<const PropertyType*>();
562 if (ptr != nullptr)
563 {
564 values.emplace_back(*ptr);
565 }
566 }
567
568 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
569 {
570 iface->register_property(name, values);
571 }
572 else
573 {
574 iface->register_property(
575 name, values,
576 [&systemConfiguration,
577 jsonPointerString{std::string(jsonPointerString)}](
578 const std::vector<PropertyType>& newVal,
579 std::vector<PropertyType>& val) {
580 val = newVal;
581 if (!setJsonFromPointer(jsonPointerString, val,
582 systemConfiguration))
583 {
584 std::cerr << "error setting json field\n";
585 return -1;
586 }
587 if (!writeJsonFiles(systemConfiguration))
588 {
589 std::cerr << "error setting json file\n";
590 return -1;
591 }
592 return 1;
593 });
594 }
595}
596
James Feistbb43d022018-06-12 15:44:33 -0700597template <typename PropertyType>
James Feista465ccc2019-02-08 12:51:01 -0800598void addProperty(const std::string& propertyName, const PropertyType& value,
599 sdbusplus::asio::dbus_interface* iface,
600 nlohmann::json& systemConfiguration,
601 const std::string& jsonPointerString,
James Feistbb43d022018-06-12 15:44:33 -0700602 sdbusplus::asio::PropertyPermission permission)
603{
604 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
605 {
606 iface->register_property(propertyName, value);
607 return;
608 }
James Feist68500ff2018-08-08 15:40:42 -0700609 iface->register_property(
610 propertyName, value,
611 [&systemConfiguration,
612 jsonPointerString{std::string(jsonPointerString)}](
James Feista465ccc2019-02-08 12:51:01 -0800613 const PropertyType& newVal, PropertyType& val) {
James Feist68500ff2018-08-08 15:40:42 -0700614 val = newVal;
615 if (!setJsonFromPointer(jsonPointerString, val,
616 systemConfiguration))
617 {
618 std::cerr << "error setting json field\n";
619 return -1;
620 }
James Feistc6248a52018-08-14 10:09:45 -0700621 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700622 {
623 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700624 return -1;
625 }
626 return 1;
627 });
628}
629
630void createDeleteObjectMethod(
James Feista465ccc2019-02-08 12:51:01 -0800631 const std::string& jsonPointerPath,
632 const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
633 sdbusplus::asio::object_server& objServer,
634 nlohmann::json& systemConfiguration)
James Feistc6248a52018-08-14 10:09:45 -0700635{
636 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
637 iface->register_method(
638 "Delete", [&objServer, &systemConfiguration, interface,
639 jsonPointerPath{std::string(jsonPointerPath)}]() {
James Feist98132792019-07-09 13:29:09 -0700640 std::shared_ptr<sdbusplus::asio::dbus_interface> dbusInterface =
James Feistc6248a52018-08-14 10:09:45 -0700641 interface.lock();
James Feist98132792019-07-09 13:29:09 -0700642 if (!dbusInterface)
James Feistc6248a52018-08-14 10:09:45 -0700643 {
644 // this technically can't happen as the pointer is pointing to
645 // us
646 throw DBusInternalError();
647 }
648 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feist98132792019-07-09 13:29:09 -0700649 if (!objServer.remove_interface(dbusInterface))
James Feistc6248a52018-08-14 10:09:45 -0700650 {
651 std::cerr << "Can't delete interface " << jsonPointerPath
652 << "\n";
653 throw DBusInternalError();
654 }
655 systemConfiguration[ptr] = nullptr;
656
657 if (!writeJsonFiles(systemConfiguration))
658 {
659 std::cerr << "error setting json file\n";
660 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700661 }
James Feistbb43d022018-06-12 15:44:33 -0700662 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700663 });
James Feistbb43d022018-06-12 15:44:33 -0700664}
665
James Feist1b2e2242018-01-30 13:45:19 -0800666// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700667void populateInterfaceFromJson(
James Feista465ccc2019-02-08 12:51:01 -0800668 nlohmann::json& systemConfiguration, const std::string& jsonPointerPath,
669 std::shared_ptr<sdbusplus::asio::dbus_interface>& iface,
670 nlohmann::json& dict, sdbusplus::asio::object_server& objServer,
James Feistbb43d022018-06-12 15:44:33 -0700671 sdbusplus::asio::PropertyPermission permission =
672 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800673{
James Feista465ccc2019-02-08 12:51:01 -0800674 for (auto& dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800675 {
James Feist8f2710a2018-05-09 17:18:55 -0700676 auto type = dictPair.value().type();
677 bool array = false;
678 if (dictPair.value().type() == nlohmann::json::value_t::array)
679 {
680 array = true;
681 if (!dictPair.value().size())
682 {
683 continue;
684 }
685 type = dictPair.value()[0].type();
686 bool isLegal = true;
James Feista465ccc2019-02-08 12:51:01 -0800687 for (const auto& arrayItem : dictPair.value())
James Feist8f2710a2018-05-09 17:18:55 -0700688 {
689 if (arrayItem.type() != type)
690 {
691 isLegal = false;
692 break;
693 }
694 }
695 if (!isLegal)
696 {
697 std::cerr << "dbus format error" << dictPair.value() << "\n";
698 continue;
699 }
James Feista218ddb2019-04-11 14:01:31 -0700700 }
701 if (type == nlohmann::json::value_t::object)
702 {
703 continue; // handled elsewhere
James Feist8f2710a2018-05-09 17:18:55 -0700704 }
James Feist97a63f12018-05-17 13:50:57 -0700705 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700706 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
707 {
708 // all setable numbers are doubles as it is difficult to always
709 // create a configuration file with all whole numbers as decimals
710 // i.e. 1.0
James Feistebcc26b2019-03-22 12:30:43 -0700711 if (array)
712 {
713 if (dictPair.value()[0].is_number())
714 {
715 type = nlohmann::json::value_t::number_float;
716 }
717 }
718 else if (dictPair.value().is_number())
James Feistbb43d022018-06-12 15:44:33 -0700719 {
720 type = nlohmann::json::value_t::number_float;
721 }
722 }
723
James Feist8f2710a2018-05-09 17:18:55 -0700724 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800725 {
James Feist9eb0b582018-04-27 12:15:46 -0700726 case (nlohmann::json::value_t::boolean):
727 {
James Feist8f2710a2018-05-09 17:18:55 -0700728 if (array)
729 {
730 // todo: array of bool isn't detected correctly by
731 // sdbusplus, change it to numbers
732 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700733 iface.get(), permission,
734 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700735 }
James Feistbb43d022018-06-12 15:44:33 -0700736
James Feist97a63f12018-05-17 13:50:57 -0700737 else
738 {
James Feistbb43d022018-06-12 15:44:33 -0700739 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700740 iface.get(), systemConfiguration, key,
741 permission);
James Feist97a63f12018-05-17 13:50:57 -0700742 }
James Feist9eb0b582018-04-27 12:15:46 -0700743 break;
744 }
745 case (nlohmann::json::value_t::number_integer):
746 {
James Feist8f2710a2018-05-09 17:18:55 -0700747 if (array)
748 {
749 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700750 iface.get(), permission,
751 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700752 }
753 else
754 {
James Feistbb43d022018-06-12 15:44:33 -0700755 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700756 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700757 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700758 }
James Feist9eb0b582018-04-27 12:15:46 -0700759 break;
760 }
761 case (nlohmann::json::value_t::number_unsigned):
762 {
James Feist8f2710a2018-05-09 17:18:55 -0700763 if (array)
764 {
765 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700766 iface.get(), permission,
767 systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700768 }
769 else
770 {
James Feistbb43d022018-06-12 15:44:33 -0700771 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700772 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700773 systemConfiguration, key,
774 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700775 }
James Feist9eb0b582018-04-27 12:15:46 -0700776 break;
777 }
778 case (nlohmann::json::value_t::number_float):
779 {
James Feist8f2710a2018-05-09 17:18:55 -0700780 if (array)
781 {
782 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistebcc26b2019-03-22 12:30:43 -0700783 iface.get(), permission,
784 systemConfiguration, key);
James Feist8f2710a2018-05-09 17:18:55 -0700785 }
James Feistbb43d022018-06-12 15:44:33 -0700786
James Feist97a63f12018-05-17 13:50:57 -0700787 else
788 {
James Feistbb43d022018-06-12 15:44:33 -0700789 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700790 iface.get(), systemConfiguration, key,
791 permission);
James Feist97a63f12018-05-17 13:50:57 -0700792 }
James Feist9eb0b582018-04-27 12:15:46 -0700793 break;
794 }
795 case (nlohmann::json::value_t::string):
796 {
James Feist8f2710a2018-05-09 17:18:55 -0700797 if (array)
798 {
James Feistebcc26b2019-03-22 12:30:43 -0700799 addArrayToDbus<std::string>(
800 dictPair.key(), dictPair.value(), iface.get(),
801 permission, systemConfiguration, key);
James Feist97a63f12018-05-17 13:50:57 -0700802 }
803 else
804 {
James Feistc6248a52018-08-14 10:09:45 -0700805 addProperty(
806 dictPair.key(), dictPair.value().get<std::string>(),
807 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700808 }
James Feist9eb0b582018-04-27 12:15:46 -0700809 break;
810 }
James Feist0eb40352019-04-09 14:44:04 -0700811 default:
812 {
James Feista218ddb2019-04-11 14:01:31 -0700813 std::cerr << "Unexpected json type in system configuration "
814 << dictPair.key() << ": "
815 << dictPair.value().type_name() << "\n";
James Feist0eb40352019-04-09 14:44:04 -0700816 break;
817 }
James Feist1b2e2242018-01-30 13:45:19 -0800818 }
819 }
James Feistc6248a52018-08-14 10:09:45 -0700820 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
821 {
822 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
823 systemConfiguration);
824 }
James Feist8f2710a2018-05-09 17:18:55 -0700825 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800826}
827
James Feista465ccc2019-02-08 12:51:01 -0800828sdbusplus::asio::PropertyPermission getPermission(const std::string& interface)
James Feistc6248a52018-08-14 10:09:45 -0700829{
830 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
831 interface) != settableInterfaces.end()
832 ? sdbusplus::asio::PropertyPermission::readWrite
833 : sdbusplus::asio::PropertyPermission::readOnly;
834}
835
James Feista465ccc2019-02-08 12:51:01 -0800836void createAddObjectMethod(const std::string& jsonPointerPath,
837 const std::string& path,
838 nlohmann::json& systemConfiguration,
839 sdbusplus::asio::object_server& objServer)
James Feist68500ff2018-08-08 15:40:42 -0700840{
841 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
842
843 iface->register_method(
844 "AddObject",
845 [&systemConfiguration, &objServer,
846 jsonPointerPath{std::string(jsonPointerPath)},
847 path{std::string(path)}](
James Feista465ccc2019-02-08 12:51:01 -0800848 const boost::container::flat_map<std::string, JsonVariantType>&
849 data) {
James Feist68500ff2018-08-08 15:40:42 -0700850 nlohmann::json::json_pointer ptr(jsonPointerPath);
James Feista465ccc2019-02-08 12:51:01 -0800851 nlohmann::json& base = systemConfiguration[ptr];
James Feist68500ff2018-08-08 15:40:42 -0700852 auto findExposes = base.find("Exposes");
853
854 if (findExposes == base.end())
855 {
856 throw std::invalid_argument("Entity must have children.");
857 }
858
859 // this will throw invalid-argument to sdbusplus if invalid json
860 nlohmann::json newData{};
James Feista465ccc2019-02-08 12:51:01 -0800861 for (const auto& item : data)
James Feist68500ff2018-08-08 15:40:42 -0700862 {
James Feista465ccc2019-02-08 12:51:01 -0800863 nlohmann::json& newJson = newData[item.first];
864 std::visit([&newJson](auto&& val) { newJson = std::move(val); },
865 item.second);
James Feist68500ff2018-08-08 15:40:42 -0700866 }
867
868 auto findName = newData.find("Name");
869 auto findType = newData.find("Type");
870 if (findName == newData.end() || findType == newData.end())
871 {
872 throw std::invalid_argument("AddObject missing Name or Type");
873 }
James Feista465ccc2019-02-08 12:51:01 -0800874 const std::string* type = findType->get_ptr<const std::string*>();
875 const std::string* name = findName->get_ptr<const std::string*>();
James Feist68500ff2018-08-08 15:40:42 -0700876 if (type == nullptr || name == nullptr)
877 {
878 throw std::invalid_argument("Type and Name must be a string.");
879 }
880
881 size_t lastIndex = 0;
882 // we add in the "exposes"
883 for (; lastIndex < findExposes->size(); lastIndex++)
884 {
885 if (findExposes->at(lastIndex)["Name"] == *name &&
886 findExposes->at(lastIndex)["Type"] == *type)
887 {
888 throw std::invalid_argument(
889 "Field already in JSON, not adding");
890 }
891 lastIndex++;
892 }
893
894 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
895 *type + ".json");
896 // todo(james) we might want to also make a list of 'can add'
897 // interfaces but for now I think the assumption if there is a
898 // schema avaliable that it is allowed to update is fine
899 if (!schemaFile.good())
900 {
901 throw std::invalid_argument(
902 "No schema avaliable, cannot validate.");
903 }
904 nlohmann::json schema =
905 nlohmann::json::parse(schemaFile, nullptr, false);
906 if (schema.is_discarded())
907 {
908 std::cerr << "Schema not legal" << *type << ".json\n";
909 throw DBusInternalError();
910 }
911 if (!validateJson(schema, newData))
912 {
913 throw std::invalid_argument("Data does not match schema");
914 }
915
James Feist16a02f22019-05-13 15:21:37 -0700916 findExposes->push_back(newData);
James Feist68500ff2018-08-08 15:40:42 -0700917 if (!writeJsonFiles(systemConfiguration))
918 {
919 std::cerr << "Error writing json files\n";
920 throw DBusInternalError();
921 }
922 std::string dbusName = *name;
923
924 std::regex_replace(dbusName.begin(), dbusName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700925 dbusName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist98132792019-07-09 13:29:09 -0700926 auto interface = objServer.add_interface(
James Feist68500ff2018-08-08 15:40:42 -0700927 path + "/" + dbusName,
928 "xyz.openbmc_project.Configuration." + *type);
929 // permission is read-write, as since we just created it, must be
930 // runtime modifiable
931 populateInterfaceFromJson(
932 systemConfiguration,
James Feist16a02f22019-05-13 15:21:37 -0700933 jsonPointerPath + "/Exposes/" + std::to_string(lastIndex),
James Feist98132792019-07-09 13:29:09 -0700934 interface, newData, objServer,
James Feist68500ff2018-08-08 15:40:42 -0700935 sdbusplus::asio::PropertyPermission::readWrite);
James Feist68500ff2018-08-08 15:40:42 -0700936 });
937 iface->initialize();
938}
939
James Feista465ccc2019-02-08 12:51:01 -0800940void postToDbus(const nlohmann::json& newConfiguration,
941 nlohmann::json& systemConfiguration,
942 sdbusplus::asio::object_server& objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800943
James Feist1b2e2242018-01-30 13:45:19 -0800944{
James Feist97a63f12018-05-17 13:50:57 -0700945 // iterate through boards
James Feista465ccc2019-02-08 12:51:01 -0800946 for (auto& boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800947 {
James Feistf1b14142019-04-10 15:22:09 -0700948 std::string boardKey = boardPair.value()["Name"];
949 std::string jsonPointerPath = "/" + boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700950 // loop through newConfiguration, but use values from system
951 // configuration to be able to modify via dbus later
James Feistf1b14142019-04-10 15:22:09 -0700952 auto boardValues = systemConfiguration[boardPair.key()];
James Feistd63d18a2018-07-19 15:23:45 -0700953 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800954 std::string boardType;
955 if (findBoardType != boardValues.end() &&
956 findBoardType->type() == nlohmann::json::value_t::string)
957 {
958 boardType = findBoardType->get<std::string>();
959 std::regex_replace(boardType.begin(), boardType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700960 boardType.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist1b2e2242018-01-30 13:45:19 -0800961 }
962 else
963 {
964 std::cerr << "Unable to find type for " << boardKey
965 << " reverting to Chassis.\n";
966 boardType = "Chassis";
967 }
James Feist11be6672018-04-06 14:05:32 -0700968 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800969
970 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
Johnathan Mantey2015f752019-03-26 15:22:31 -0700971 ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700972 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
973 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800974
James Feist8f2710a2018-05-09 17:18:55 -0700975 auto inventoryIface = objServer.add_interface(
976 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700977
James Feist8f2710a2018-05-09 17:18:55 -0700978 auto boardIface = objServer.add_interface(
979 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700980
James Feist68500ff2018-08-08 15:40:42 -0700981 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
982 objServer);
983
James Feist97a63f12018-05-17 13:50:57 -0700984 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700985 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700986 jsonPointerPath += "/";
987 // iterate through board properties
James Feista465ccc2019-02-08 12:51:01 -0800988 for (auto& boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700989 {
990 if (boardField.value().type() == nlohmann::json::value_t::object)
991 {
James Feist8f2710a2018-05-09 17:18:55 -0700992 auto iface =
993 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700994 populateInterfaceFromJson(systemConfiguration,
995 jsonPointerPath + boardField.key(),
996 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700997 }
998 }
James Feist97a63f12018-05-17 13:50:57 -0700999
James Feist1e3e6982018-08-03 16:09:28 -07001000 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -08001001 if (exposes == boardValues.end())
1002 {
1003 continue;
1004 }
James Feist97a63f12018-05-17 13:50:57 -07001005 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -07001006 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -07001007
1008 // store the board level pointer so we can modify it on the way down
1009 std::string jsonPointerPathBoard = jsonPointerPath;
1010 size_t exposesIndex = -1;
James Feista465ccc2019-02-08 12:51:01 -08001011 for (auto& item : *exposes)
James Feist1b2e2242018-01-30 13:45:19 -08001012 {
James Feist97a63f12018-05-17 13:50:57 -07001013 exposesIndex++;
1014 jsonPointerPath = jsonPointerPathBoard;
1015 jsonPointerPath += std::to_string(exposesIndex);
1016
James Feistd63d18a2018-07-19 15:23:45 -07001017 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -08001018 if (findName == item.end())
1019 {
1020 std::cerr << "cannot find name in field " << item << "\n";
1021 continue;
1022 }
James Feist1e3e6982018-08-03 16:09:28 -07001023 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -08001024 // if status is not found it is assumed to be status = 'okay'
1025 if (findStatus != item.end())
1026 {
1027 if (*findStatus == "disabled")
1028 {
1029 continue;
1030 }
1031 }
James Feistd63d18a2018-07-19 15:23:45 -07001032 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -08001033 std::string itemType;
1034 if (findType != item.end())
1035 {
1036 itemType = findType->get<std::string>();
1037 std::regex_replace(itemType.begin(), itemType.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001038 itemType.end(), ILLEGAL_DBUS_PATH_REGEX,
1039 "_");
James Feist1b2e2242018-01-30 13:45:19 -08001040 }
1041 else
1042 {
1043 itemType = "unknown";
1044 }
1045 std::string itemName = findName->get<std::string>();
1046 std::regex_replace(itemName.begin(), itemName.begin(),
Johnathan Mantey2015f752019-03-26 15:22:31 -07001047 itemName.end(), ILLEGAL_DBUS_MEMBER_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -07001048
James Feist8f2710a2018-05-09 17:18:55 -07001049 auto itemIface = objServer.add_interface(
1050 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001051 "xyz.openbmc_project.Configuration." + itemType);
1052
James Feist97a63f12018-05-17 13:50:57 -07001053 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -07001054 itemIface, item, objServer,
1055 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001056
James Feista465ccc2019-02-08 12:51:01 -08001057 for (auto& objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -08001058 {
James Feist97a63f12018-05-17 13:50:57 -07001059 jsonPointerPath = jsonPointerPathBoard +
1060 std::to_string(exposesIndex) + "/" +
1061 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001062 if (objectPair.value().type() ==
1063 nlohmann::json::value_t::object)
1064 {
James Feist8f2710a2018-05-09 17:18:55 -07001065 auto objectIface = objServer.add_interface(
1066 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001067 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001068 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001069
1070 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001071 systemConfiguration, jsonPointerPath, objectIface,
1072 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001073 }
1074 else if (objectPair.value().type() ==
1075 nlohmann::json::value_t::array)
1076 {
1077 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001078 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001079 {
James Feist8f2710a2018-05-09 17:18:55 -07001080 continue;
1081 }
1082 bool isLegal = true;
1083 auto type = objectPair.value()[0].type();
1084 if (type != nlohmann::json::value_t::object)
1085 {
1086 continue;
1087 }
1088
1089 // verify legal json
James Feista465ccc2019-02-08 12:51:01 -08001090 for (const auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001091 {
1092 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001093 {
James Feist8f2710a2018-05-09 17:18:55 -07001094 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001095 break;
1096 }
James Feist8f2710a2018-05-09 17:18:55 -07001097 }
1098 if (!isLegal)
1099 {
1100 std::cerr << "dbus format error" << objectPair.value()
1101 << "\n";
1102 break;
1103 }
1104
James Feista465ccc2019-02-08 12:51:01 -08001105 for (auto& arrayItem : objectPair.value())
James Feist8f2710a2018-05-09 17:18:55 -07001106 {
James Feist97a63f12018-05-17 13:50:57 -07001107
James Feist8f2710a2018-05-09 17:18:55 -07001108 auto objectIface = objServer.add_interface(
1109 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001110 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001111 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001112 populateInterfaceFromJson(
1113 systemConfiguration,
1114 jsonPointerPath + "/" + std::to_string(index),
1115 objectIface, arrayItem, objServer,
1116 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001117 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001118 }
1119 }
1120 }
1121 }
1122 }
1123}
1124
1125// finds the template character (currently set to $) and replaces the value with
1126// the field found in a dbus object i.e. $ADDRESS would get populated with the
1127// ADDRESS field from a object on dbus
1128void templateCharReplace(
James Feista465ccc2019-02-08 12:51:01 -08001129 nlohmann::json::iterator& keyPair,
1130 const boost::container::flat_map<std::string, BasicVariantType>&
1131 foundDevice,
1132 size_t& foundDeviceIdx)
James Feist1b2e2242018-01-30 13:45:19 -08001133{
James Feist665267c2019-06-24 15:24:17 -07001134 if (keyPair.value().type() == nlohmann::json::value_t::object ||
1135 keyPair.value().type() == nlohmann::json::value_t::array)
James Feist11be6672018-04-06 14:05:32 -07001136 {
1137 for (auto nextLayer = keyPair.value().begin();
1138 nextLayer != keyPair.value().end(); nextLayer++)
1139 {
1140 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1141 }
1142 return;
1143 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001144
1145 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
1146 if (strPtr == nullptr)
James Feist1b2e2242018-01-30 13:45:19 -08001147 {
1148 return;
1149 }
1150
James Feistf5125b02019-06-06 11:27:43 -07001151 boost::replace_all(*strPtr, std::string(templateChar) + "index",
1152 std::to_string(foundDeviceIdx));
Ed Tanous12bc7932019-02-26 14:36:20 -08001153
Ed Tanous12bc7932019-02-26 14:36:20 -08001154 for (auto& foundDevicePair : foundDevice)
James Feist1b2e2242018-01-30 13:45:19 -08001155 {
James Feistf5125b02019-06-06 11:27:43 -07001156 std::string templateName = templateChar + foundDevicePair.first;
1157 boost::iterator_range<std::string::const_iterator> find =
1158 boost::ifind_first(*strPtr, templateName);
1159 if (find)
James Feist1b2e2242018-01-30 13:45:19 -08001160 {
James Feistf5125b02019-06-06 11:27:43 -07001161 size_t start = find.begin() - strPtr->begin();
1162 // check for additional operations
1163 if (find.end() != strPtr->end())
1164 {
1165 // save the prefix
1166 std::string prefix = strPtr->substr(0, start);
1167
1168 // operate on the rest (+1 for trailing space)
1169 std::string end =
1170 strPtr->substr(start + templateName.size() + 1);
1171
1172 std::vector<std::string> split;
1173 boost::split(split, end, boost::is_any_of(" "));
1174
1175 // need at least 1 operation and number
1176 if (split.size() < 2)
1177 {
1178 std::cerr << "Syntax error on template replacement of "
1179 << *strPtr << "\n";
1180 for (const std::string& data : split)
1181 {
1182 std::cerr << data << " ";
1183 }
1184 std::cerr << "\n";
1185 continue;
1186 }
1187
1188 // we assume that the replacement is a number, because we can
1189 // only do math on numbers.. we might concatenate strings in the
1190 // future, but thats later
1191 int number =
1192 std::visit(VariantToIntVisitor(), foundDevicePair.second);
1193
1194 bool isOperator = true;
1195 TemplateOperation next = TemplateOperation::addition;
1196
1197 auto it = split.begin();
1198
1199 for (; it != split.end(); it++)
1200 {
1201 if (isOperator)
1202 {
1203 if (*it == "+")
1204 {
1205 next = TemplateOperation::addition;
1206 }
1207 else if (*it == "-")
1208 {
1209 next = TemplateOperation::subtraction;
1210 }
1211 else if (*it == "*")
1212 {
1213 next = TemplateOperation::multiplication;
1214 }
1215 else if (*it == R"(%)")
1216 {
1217 next = TemplateOperation::modulo;
1218 }
1219 else if (*it == R"(/)")
1220 {
1221 next = TemplateOperation::division;
1222 }
1223 else
1224 {
1225 break;
1226 }
1227 }
1228 else
1229 {
1230 int constant = 0;
1231 try
1232 {
1233 constant = std::stoi(*it);
1234 }
1235 catch (std::invalid_argument&)
1236 {
1237 std::cerr
1238 << "Parameter not supported for templates "
1239 << *it << "\n";
1240 continue;
1241 }
1242 switch (next)
1243 {
1244 case TemplateOperation::addition:
1245 {
1246 number += constant;
1247 break;
1248 }
1249 case TemplateOperation::subtraction:
1250 {
1251 number -= constant;
1252 break;
1253 }
1254 case TemplateOperation::multiplication:
1255 {
1256 number *= constant;
1257 break;
1258 }
1259 case TemplateOperation::division:
1260 {
1261 number /= constant;
1262 break;
1263 }
1264 case TemplateOperation::modulo:
1265 {
1266 number = number % constant;
1267 break;
1268 }
1269
1270 default:
1271 break;
1272 }
1273 }
1274 isOperator = !isOperator;
1275 }
1276 std::string result = prefix + std::to_string(number);
1277
1278 if (it != split.end())
1279 {
1280 for (; it != split.end(); it++)
1281 {
1282 result += " " + *it;
1283 }
1284 }
1285 keyPair.value() = result;
1286 }
1287 else
1288 {
1289 std::visit([&](auto&& val) { keyPair.value() = val; },
1290 foundDevicePair.second);
James Feist27e75832019-07-08 11:52:15 -07001291 return;
James Feistf5125b02019-06-06 11:27:43 -07001292 }
1293
Ed Tanous12bc7932019-02-26 14:36:20 -08001294 // We probably just invalidated the pointer above, so set it to null
1295 strPtr = nullptr;
1296 break;
James Feist1b2e2242018-01-30 13:45:19 -08001297 }
Ed Tanous12bc7932019-02-26 14:36:20 -08001298 }
1299
1300 strPtr = keyPair.value().get_ptr<std::string*>();
1301 if (strPtr == nullptr)
1302 {
1303 return;
James Feist1b2e2242018-01-30 13:45:19 -08001304 }
James Feistc6090822019-01-04 16:02:48 -08001305
1306 // convert hex numbers to ints
Ed Tanous12bc7932019-02-26 14:36:20 -08001307 if (boost::starts_with(*strPtr, "0x"))
James Feist28dc2da2018-10-15 14:47:42 -07001308 {
1309 try
1310 {
James Feistc6090822019-01-04 16:02:48 -08001311 size_t pos = 0;
Ed Tanous12bc7932019-02-26 14:36:20 -08001312 int64_t temp = std::stoul(*strPtr, &pos, 0);
1313 if (pos == strPtr->size())
James Feistc6090822019-01-04 16:02:48 -08001314 {
1315 keyPair.value() = static_cast<uint64_t>(temp);
1316 }
James Feist28dc2da2018-10-15 14:47:42 -07001317 }
James Feist98132792019-07-09 13:29:09 -07001318 catch (std::invalid_argument&)
James Feist28dc2da2018-10-15 14:47:42 -07001319 {
1320 }
James Feist98132792019-07-09 13:29:09 -07001321 catch (std::out_of_range&)
James Feistc6090822019-01-04 16:02:48 -08001322 {
1323 }
James Feist28dc2da2018-10-15 14:47:42 -07001324 }
James Feistf5125b02019-06-06 11:27:43 -07001325 // non-hex numbers
1326 else
1327 {
1328 try
1329 {
1330 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
1331 keyPair.value() = temp;
1332 }
1333 catch (boost::bad_lexical_cast&)
1334 {
1335 }
1336 }
James Feist1b2e2242018-01-30 13:45:19 -08001337}
1338
James Feist8f2710a2018-05-09 17:18:55 -07001339// reads json files out of the filesystem
James Feista465ccc2019-02-08 12:51:01 -08001340bool findJsonFiles(std::list<nlohmann::json>& configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001341{
1342 // find configuration files
Ed Tanous072e25d2018-12-16 21:45:20 -08001343 std::vector<std::filesystem::path> jsonPaths;
Johnathan Mantey2015f752019-03-26 15:22:31 -07001344 if (!findFiles(std::filesystem::path(configurationDirectory), R"(.*\.json)",
1345 jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001346 {
1347 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001348 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001349 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001350 }
James Feistb4383f42018-08-06 16:54:10 -07001351
1352 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1353 globalSchema);
1354 if (!schemaStream.good())
1355 {
1356 std::cerr
1357 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1358 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001359 return false;
James Feistb4383f42018-08-06 16:54:10 -07001360 }
1361 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1362 if (schema.is_discarded())
1363 {
1364 std::cerr
1365 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1366 std::exit(EXIT_FAILURE);
Ed Tanous072e25d2018-12-16 21:45:20 -08001367 return false;
James Feistb4383f42018-08-06 16:54:10 -07001368 }
1369
James Feista465ccc2019-02-08 12:51:01 -08001370 for (auto& jsonPath : jsonPaths)
James Feist3cb5fec2018-01-23 14:41:51 -08001371 {
1372 std::ifstream jsonStream(jsonPath.c_str());
1373 if (!jsonStream.good())
1374 {
1375 std::cerr << "unable to open " << jsonPath.string() << "\n";
1376 continue;
1377 }
1378 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1379 if (data.is_discarded())
1380 {
1381 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1382 continue;
1383 }
James Feist8da99192019-01-24 08:20:16 -08001384 /*
1385 * todo(james): reenable this once less things are in flight
1386 *
James Feistb4383f42018-08-06 16:54:10 -07001387 if (!validateJson(schema, data))
1388 {
1389 std::cerr << "Error validating " << jsonPath.string() << "\n";
1390 continue;
1391 }
James Feist8da99192019-01-24 08:20:16 -08001392 */
James Feistb4383f42018-08-06 16:54:10 -07001393
James Feist3cb5fec2018-01-23 14:41:51 -08001394 if (data.type() == nlohmann::json::value_t::array)
1395 {
James Feista465ccc2019-02-08 12:51:01 -08001396 for (auto& d : data)
James Feist3cb5fec2018-01-23 14:41:51 -08001397 {
1398 configurations.emplace_back(d);
1399 }
1400 }
1401 else
1402 {
1403 configurations.emplace_back(data);
1404 }
1405 }
Ed Tanous072e25d2018-12-16 21:45:20 -08001406 return true;
James Feist75fdeeb2018-02-20 14:26:16 -08001407}
James Feist3cb5fec2018-01-23 14:41:51 -08001408
James Feist8f2710a2018-05-09 17:18:55 -07001409struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001410{
James Feist75fdeeb2018-02-20 14:26:16 -08001411
James Feista465ccc2019-02-08 12:51:01 -08001412 PerformScan(nlohmann::json& systemConfiguration,
1413 std::list<nlohmann::json>& configurations,
1414 std::function<void(void)>&& callback) :
James Feist8f2710a2018-05-09 17:18:55 -07001415 _systemConfiguration(systemConfiguration),
1416 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001417 {
James Feist8f2710a2018-05-09 17:18:55 -07001418 }
1419 void run()
1420 {
1421 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001422 {
James Feist1e3e6982018-08-03 16:09:28 -07001423 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001424 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001425
James Feist1b2e2242018-01-30 13:45:19 -08001426 nlohmann::json probeCommand;
1427 // check for poorly formatted fields, probe must be an array
1428 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001429 {
1430 std::cerr << "configuration file missing probe:\n " << *it
1431 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001432 it = _configurations.erase(it);
1433 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001434 }
James Feist1b2e2242018-01-30 13:45:19 -08001435 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001436 {
1437 probeCommand = nlohmann::json::array();
1438 probeCommand.push_back(*findProbe);
1439 }
1440 else
1441 {
1442 probeCommand = *findProbe;
1443 }
James Feist1b2e2242018-01-30 13:45:19 -08001444
1445 if (findName == it->end())
1446 {
1447 std::cerr << "configuration file missing name:\n " << *it
1448 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001449 it = _configurations.erase(it);
1450 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001451 }
James Feistf1b14142019-04-10 15:22:09 -07001452 std::string probeName = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001453
James Feistf1b14142019-04-10 15:22:09 -07001454 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
1455 probeName) != PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001456 {
James Feist8f2710a2018-05-09 17:18:55 -07001457 it = _configurations.erase(it);
1458 continue;
1459 }
James Feistf1b14142019-04-10 15:22:09 -07001460 nlohmann::json* recordPtr = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001461
James Feist8f2710a2018-05-09 17:18:55 -07001462 // store reference to this to children to makes sure we don't get
1463 // destroyed too early
1464 auto thisRef = shared_from_this();
1465 auto p = std::make_shared<PerformProbe>(
1466 probeCommand,
James Feistf1b14142019-04-10 15:22:09 -07001467 [&, recordPtr, probeName,
1468 thisRef](std::vector<std::optional<boost::container::flat_map<
1469 std::string, BasicVariantType>>>& foundDevices) {
James Feist8f2710a2018-05-09 17:18:55 -07001470 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001471
James Feistf1b14142019-04-10 15:22:09 -07001472 PASSED_PROBES.push_back(probeName);
James Feist8f2710a2018-05-09 17:18:55 -07001473 size_t foundDeviceIdx = 0;
1474
James Feista465ccc2019-02-08 12:51:01 -08001475 for (auto& foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001476 {
James Feistf5125b02019-06-06 11:27:43 -07001477 nlohmann::json record = *recordPtr;
James Feistf1b14142019-04-10 15:22:09 -07001478 std::string recordName;
1479 size_t hash = 0;
1480 if (foundDevice)
James Feist3cb5fec2018-01-23 14:41:51 -08001481 {
James Feistf1b14142019-04-10 15:22:09 -07001482 // use an array so alphabetical order from the
1483 // flat_map is maintained
1484 auto device = nlohmann::json::array();
1485 for (auto& devPair : *foundDevice)
1486 {
1487 device.push_back(devPair.first);
1488 std::visit(
1489 [&device](auto&& v) {
1490 device.push_back(v);
1491 },
1492 devPair.second);
1493 }
1494 hash = std::hash<std::string>{}(probeName +
1495 device.dump());
1496 // hashes are hard to distinguish, use the
1497 // non-hashed version if we want debug
1498 if constexpr (DEBUG)
1499 {
1500 recordName = probeName + device.dump();
1501 }
1502 else
1503 {
1504 recordName = std::to_string(hash);
1505 }
James Feist8f2710a2018-05-09 17:18:55 -07001506 }
James Feistf1b14142019-04-10 15:22:09 -07001507 else
1508 {
1509 recordName = probeName;
1510 }
1511
James Feist1df06a42019-04-11 14:23:04 -07001512 auto fromLastJson = lastJson.find(recordName);
1513 if (fromLastJson != lastJson.end())
1514 {
1515 // keep user changes
1516 _systemConfiguration[recordName] = *fromLastJson;
1517 continue;
1518 }
1519
James Feistf1b14142019-04-10 15:22:09 -07001520 // insert into configuration temporarily to be able to
1521 // reference ourselves
1522
1523 _systemConfiguration[recordName] = record;
1524
1525 if (foundDevice)
1526 {
1527 for (auto keyPair = record.begin();
1528 keyPair != record.end(); keyPair++)
1529 {
1530 templateCharReplace(keyPair, *foundDevice,
1531 foundDeviceIdx);
1532 }
1533 }
1534 auto findExpose = record.find("Exposes");
1535 if (findExpose == record.end())
James Feist8f2710a2018-05-09 17:18:55 -07001536 {
1537 continue;
1538 }
James Feista465ccc2019-02-08 12:51:01 -08001539 for (auto& expose : *findExpose)
James Feist8f2710a2018-05-09 17:18:55 -07001540 {
1541 for (auto keyPair = expose.begin();
1542 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001543 {
James Feist1b2e2242018-01-30 13:45:19 -08001544
James Feist8f2710a2018-05-09 17:18:55 -07001545 // fill in template characters with devices
1546 // found
James Feistf1b14142019-04-10 15:22:09 -07001547 if (foundDevice)
1548 {
1549 templateCharReplace(keyPair, *foundDevice,
1550 foundDeviceIdx);
1551 }
James Feist8f2710a2018-05-09 17:18:55 -07001552 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001553 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001554 {
1555 if (keyPair.value().type() !=
1556 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001557 {
James Feist8f2710a2018-05-09 17:18:55 -07001558 std::cerr << "bind_ value must be of "
1559 "type string "
1560 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001561 continue;
1562 }
James Feist8f2710a2018-05-09 17:18:55 -07001563 bool foundBind = false;
1564 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001565 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001566
James Feista465ccc2019-02-08 12:51:01 -08001567 for (auto& configurationPair :
James Feist8f2710a2018-05-09 17:18:55 -07001568 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001569 {
James Feist1b2e2242018-01-30 13:45:19 -08001570
James Feist8f2710a2018-05-09 17:18:55 -07001571 auto configListFind =
1572 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001573 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001574
1575 if (configListFind ==
1576 configurationPair.value()
1577 .end() ||
1578 configListFind->type() !=
1579 nlohmann::json::value_t::array)
1580 {
1581 continue;
1582 }
James Feista465ccc2019-02-08 12:51:01 -08001583 for (auto& exposedObject :
James Feist8f2710a2018-05-09 17:18:55 -07001584 *configListFind)
1585 {
1586 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001587 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001588 if (boost::iequals(
1589 foundObjectName,
1590 keyPair.value()
1591 .get<std::string>()))
1592 {
James Feist1e3e6982018-08-03 16:09:28 -07001593 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001594 "okay";
1595 expose[bind] = exposedObject;
1596
1597 foundBind = true;
1598 break;
1599 }
1600 }
1601 if (foundBind)
1602 {
James Feist3cb5fec2018-01-23 14:41:51 -08001603 break;
1604 }
1605 }
James Feist8f2710a2018-05-09 17:18:55 -07001606 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001607 {
James Feist8f2710a2018-05-09 17:18:55 -07001608 std::cerr << "configuration file "
1609 "dependency error, "
1610 "could not find bind "
1611 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001612 }
1613 }
1614 }
1615 }
James Feistf1b14142019-04-10 15:22:09 -07001616 // overwrite ourselves with cleaned up version
1617 _systemConfiguration[recordName] = record;
James Feist1a996582019-05-14 15:10:06 -07001618
1619 logDeviceAdded(record);
James Feist1df06a42019-04-11 14:23:04 -07001620
James Feistf1b14142019-04-10 15:22:09 -07001621 foundDeviceIdx++;
James Feist3cb5fec2018-01-23 14:41:51 -08001622 }
James Feist8f2710a2018-05-09 17:18:55 -07001623 });
1624 p->run();
1625 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001626 }
1627 }
James Feist75fdeeb2018-02-20 14:26:16 -08001628
James Feist8f2710a2018-05-09 17:18:55 -07001629 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001630 {
James Feist8f2710a2018-05-09 17:18:55 -07001631 if (_passed)
1632 {
1633 auto nextScan = std::make_shared<PerformScan>(
1634 _systemConfiguration, _configurations, std::move(_callback));
1635 nextScan->run();
1636 }
1637 else
1638 {
1639 _callback();
1640 }
1641 }
James Feista465ccc2019-02-08 12:51:01 -08001642 nlohmann::json& _systemConfiguration;
James Feist8f2710a2018-05-09 17:18:55 -07001643 std::list<nlohmann::json> _configurations;
1644 std::function<void(void)> _callback;
1645 std::vector<std::shared_ptr<PerformProbe>> _probes;
1646 bool _passed = false;
James Feist1df06a42019-04-11 14:23:04 -07001647 bool powerWasOn = isPowerOn();
James Feist8f2710a2018-05-09 17:18:55 -07001648};
James Feistc95cb142018-02-26 10:41:42 -08001649
James Feist1df06a42019-04-11 14:23:04 -07001650void startRemovedTimer(boost::asio::deadline_timer& timer,
James Feist1df06a42019-04-11 14:23:04 -07001651 nlohmann::json& systemConfiguration)
1652{
1653 static bool scannedPowerOff = false;
1654 static bool scannedPowerOn = false;
1655
James Feistfb00f392019-06-25 14:16:48 -07001656 if (systemConfiguration.empty() || lastJson.empty())
1657 {
1658 return; // not ready yet
1659 }
James Feist1df06a42019-04-11 14:23:04 -07001660 if (scannedPowerOn)
1661 {
1662 return;
1663 }
1664
1665 if (!isPowerOn() && scannedPowerOff)
1666 {
1667 return;
1668 }
1669
1670 timer.expires_from_now(boost::posix_time::seconds(10));
James Feist1a996582019-05-14 15:10:06 -07001671 timer.async_wait(
1672 [&systemConfiguration](const boost::system::error_code& ec) {
1673 if (ec == boost::asio::error::operation_aborted)
James Feist1df06a42019-04-11 14:23:04 -07001674 {
James Feist1a996582019-05-14 15:10:06 -07001675 // we were cancelled
1676 return;
1677 }
1678
1679 bool powerOff = !isPowerOn();
1680 for (const auto& item : lastJson.items())
1681 {
1682 if (systemConfiguration.find(item.key()) ==
1683 systemConfiguration.end())
James Feist1df06a42019-04-11 14:23:04 -07001684 {
James Feist1a996582019-05-14 15:10:06 -07001685 bool isDetectedPowerOn = false;
1686 auto powerState = item.value().find("PowerState");
1687 if (powerState != item.value().end())
James Feist1df06a42019-04-11 14:23:04 -07001688 {
James Feist1a996582019-05-14 15:10:06 -07001689 auto ptr = powerState->get_ptr<const std::string*>();
1690 if (ptr)
James Feist1df06a42019-04-11 14:23:04 -07001691 {
James Feist1a996582019-05-14 15:10:06 -07001692 if (*ptr == "On" || *ptr == "BiosPost")
1693 {
1694 isDetectedPowerOn = true;
1695 }
James Feist1df06a42019-04-11 14:23:04 -07001696 }
1697 }
James Feist1a996582019-05-14 15:10:06 -07001698 if (powerOff && isDetectedPowerOn)
1699 {
1700 // power not on yet, don't know if it's there or not
1701 continue;
1702 }
1703 if (!powerOff && scannedPowerOff && isDetectedPowerOn)
1704 {
1705 // already logged it when power was off
1706 continue;
1707 }
James Feist1df06a42019-04-11 14:23:04 -07001708
James Feist1a996582019-05-14 15:10:06 -07001709 logDeviceRemoved(item.value());
1710 }
James Feist1df06a42019-04-11 14:23:04 -07001711 }
James Feist1a996582019-05-14 15:10:06 -07001712 scannedPowerOff = true;
1713 if (!powerOff)
1714 {
1715 scannedPowerOn = true;
1716 }
1717 });
James Feist1df06a42019-04-11 14:23:04 -07001718}
1719
James Feist8f2710a2018-05-09 17:18:55 -07001720// main properties changed entry
1721void propertiesChangedCallback(
James Feista465ccc2019-02-08 12:51:01 -08001722 boost::asio::io_service& io,
1723 std::vector<sdbusplus::bus::match::match>& dbusMatches,
1724 nlohmann::json& systemConfiguration,
1725 sdbusplus::asio::object_server& objServer)
James Feist8f2710a2018-05-09 17:18:55 -07001726{
1727 static boost::asio::deadline_timer timer(io);
James Feist1df06a42019-04-11 14:23:04 -07001728 static bool timerRunning;
1729
1730 timerRunning = true;
James Feist8f2710a2018-05-09 17:18:55 -07001731 timer.expires_from_now(boost::posix_time::seconds(1));
1732
1733 // setup an async wait as we normally get flooded with new requests
James Feista465ccc2019-02-08 12:51:01 -08001734 timer.async_wait([&](const boost::system::error_code& ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001735 if (ec == boost::asio::error::operation_aborted)
1736 {
1737 // we were cancelled
1738 return;
1739 }
1740 else if (ec)
1741 {
1742 std::cerr << "async wait error " << ec << "\n";
1743 return;
1744 }
James Feist1df06a42019-04-11 14:23:04 -07001745 timerRunning = false;
James Feist8f2710a2018-05-09 17:18:55 -07001746
1747 nlohmann::json oldConfiguration = systemConfiguration;
1748 DBUS_PROBE_OBJECTS.clear();
1749
1750 std::list<nlohmann::json> configurations;
1751 if (!findJsonFiles(configurations))
1752 {
1753 std::cerr << "cannot find json files\n";
1754 return;
1755 }
1756
1757 auto perfScan = std::make_shared<PerformScan>(
1758 systemConfiguration, configurations, [&, oldConfiguration]() {
1759 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001760 for (auto it = newConfiguration.begin();
1761 it != newConfiguration.end();)
1762 {
1763 auto findKey = oldConfiguration.find(it.key());
1764 if (findKey != oldConfiguration.end())
1765 {
1766 it = newConfiguration.erase(it);
1767 }
1768 else
1769 {
1770 it++;
1771 }
1772 }
James Feist8f2710a2018-05-09 17:18:55 -07001773 registerCallbacks(io, dbusMatches, systemConfiguration,
1774 objServer);
1775 io.post([&, newConfiguration]() {
James Feist8f2710a2018-05-09 17:18:55 -07001776 loadOverlays(newConfiguration);
James Feistce4367c2018-10-16 09:19:57 -07001777
James Feistbb43d022018-06-12 15:44:33 -07001778 io.post([&]() {
1779 if (!writeJsonFiles(systemConfiguration))
1780 {
1781 std::cerr << "Error writing json files\n";
1782 }
1783 });
James Feist8f2710a2018-05-09 17:18:55 -07001784 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001785 postToDbus(newConfiguration, systemConfiguration,
1786 objServer);
James Feist1df06a42019-04-11 14:23:04 -07001787 if (!timerRunning)
1788 {
James Feist98132792019-07-09 13:29:09 -07001789 startRemovedTimer(timer, systemConfiguration);
James Feist1df06a42019-04-11 14:23:04 -07001790 }
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 Feist98132792019-07-09 13:29:09 -07001829int main()
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,
James Feist98132792019-07-09 13:29:09 -07001856 boost::container::flat_map<std::string, BasicVariantType>>&) {
1857 return;
1858 });
James Feist8f2710a2018-05-09 17:18:55 -07001859 inventoryIface->initialize();
1860
1861 io.post([&]() {
James Feistce4367c2018-10-16 09:19:57 -07001862#if OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001863 unloadAllOverlays();
James Feistce4367c2018-10-16 09:19:57 -07001864#endif
James Feist8f2710a2018-05-09 17:18:55 -07001865 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1866 objServer);
1867 });
James Feist4131aea2018-03-09 09:47:30 -08001868
James Feistfd1264a2018-05-03 12:10:00 -07001869 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001870 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1871 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001872 });
James Feist8f2710a2018-05-09 17:18:55 -07001873 entityIface->initialize();
1874
James Feist1df06a42019-04-11 14:23:04 -07001875 if (fwVersionIsSame())
1876 {
1877 if (std::filesystem::is_regular_file(currentConfiguration))
1878 {
1879 // this file could just be deleted, but it's nice for debug
1880 std::filesystem::create_directory(tempConfigDir);
1881 std::filesystem::remove(lastConfiguration);
1882 std::filesystem::copy(currentConfiguration, lastConfiguration);
1883 std::filesystem::remove(currentConfiguration);
1884
1885 std::ifstream jsonStream(lastConfiguration);
1886 if (jsonStream.good())
1887 {
1888 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1889 if (data.is_discarded())
1890 {
1891 std::cerr << "syntax error in " << lastConfiguration
1892 << "\n";
1893 }
1894 else
1895 {
1896 lastJson = std::move(data);
1897 }
1898 }
1899 else
1900 {
1901 std::cerr << "unable to open " << lastConfiguration << "\n";
1902 }
1903 }
1904 }
1905 else
1906 {
1907 // not an error, just logging at this level to make it in the journal
1908 std::cerr << "Clearing previous configuration\n";
1909 std::filesystem::remove(currentConfiguration);
1910 }
1911
1912 // some boards only show up after power is on, we want to not say they are
1913 // removed until the same state happens
1914 setupPowerMatch(SYSTEM_BUS);
1915
James Feist1b2e2242018-01-30 13:45:19 -08001916 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001917
1918 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001919}