blob: a0fa9044e257ae6d9cf73207f7f6bd32022bc44d [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
17#include <Utils.hpp>
James Feistc95cb142018-02-26 10:41:42 -080018#include <Overlay.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080019#include <nlohmann/json.hpp>
20#include <fstream>
21#include <regex>
James Feist8f2710a2018-05-09 17:18:55 -070022#include <iostream>
23#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist11be6672018-04-06 14:05:32 -070025#include <boost/algorithm/string/case_conv.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080026#include <boost/algorithm/string/predicate.hpp>
27#include <boost/algorithm/string/replace.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080028#include <boost/lexical_cast.hpp>
29#include <boost/container/flat_map.hpp>
30#include <boost/container/flat_set.hpp>
31#include <VariantVisitors.hpp>
James Feist7b7e4e82018-01-24 14:56:00 -080032#include <experimental/filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080033
34constexpr const char *OUTPUT_DIR = "/var/configuration/";
James Feistb4383f42018-08-06 16:54:10 -070035constexpr const char *configurationDirectory = "/usr/share/configurations";
36constexpr const char *schemaDirectory = "/usr/share/configurations/schemas";
37constexpr const char *globalSchema = "global.json";
James Feist3cb5fec2018-01-23 14:41:51 -080038constexpr const char *TEMPLATE_CHAR = "$";
James Feistc95cb142018-02-26 10:41:42 -080039constexpr const size_t PROPERTIES_CHANGED_UNTIL_FLUSH_COUNT = 20;
James Feist8f2710a2018-05-09 17:18:55 -070040constexpr const int32_t MAX_MAPPER_DEPTH = 0;
James Feist4131aea2018-03-09 09:47:30 -080041constexpr const size_t SLEEP_AFTER_PROPERTIES_CHANGE_SECONDS = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080042
43namespace fs = std::experimental::filesystem;
James Feistee0de612018-10-12 11:15:46 -070044namespace variant_ns = sdbusplus::message::variant_ns;
James Feist3cb5fec2018-01-23 14:41:51 -080045struct cmp_str
46{
47 bool operator()(const char *a, const char *b) const
48 {
49 return std::strcmp(a, b) < 0;
50 }
51};
52
James Feist8f2710a2018-05-09 17:18:55 -070053struct PerformProbe;
54
James Feist3cb5fec2018-01-23 14:41:51 -080055// underscore T for collison with dbus c api
56enum class probe_type_codes
57{
58 FALSE_T,
59 TRUE_T,
60 AND,
61 OR,
James Feist6bd2a022018-03-13 12:30:58 -070062 FOUND,
63 MATCH_ONE
James Feist3cb5fec2018-01-23 14:41:51 -080064};
65const static boost::container::flat_map<const char *, probe_type_codes, cmp_str>
66 PROBE_TYPES{{{"FALSE", probe_type_codes::FALSE_T},
67 {"TRUE", probe_type_codes::TRUE_T},
68 {"AND", probe_type_codes::AND},
69 {"OR", probe_type_codes::OR},
James Feist6bd2a022018-03-13 12:30:58 -070070 {"FOUND", probe_type_codes::FOUND},
71 {"MATCH_ONE", probe_type_codes::MATCH_ONE}}};
James Feist3cb5fec2018-01-23 14:41:51 -080072
James Feistc6248a52018-08-14 10:09:45 -070073static constexpr std::array<const char *, 3> settableInterfaces = {
74 "Thresholds", "Pid", "Pid.Zone"};
James Feist68500ff2018-08-08 15:40:42 -070075using JsonVariantType =
76 sdbusplus::message::variant<std::vector<std::string>, std::string, int64_t,
77 uint64_t, double, int32_t, uint32_t, int16_t,
78 uint16_t, uint8_t, bool>;
James Feist8f2710a2018-05-09 17:18:55 -070079using BasicVariantType =
80 sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
81 uint32_t, int16_t, uint16_t, uint8_t, bool>;
82
James Feist3cb5fec2018-01-23 14:41:51 -080083using GetSubTreeType = std::vector<
84 std::pair<std::string,
85 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
86
87using ManagedObjectType = boost::container::flat_map<
James Feist8f2710a2018-05-09 17:18:55 -070088 sdbusplus::message::object_path,
James Feist3cb5fec2018-01-23 14:41:51 -080089 boost::container::flat_map<
90 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070091 boost::container::flat_map<std::string, BasicVariantType>>>;
James Feist3cb5fec2018-01-23 14:41:51 -080092
93boost::container::flat_map<
94 std::string,
James Feist8f2710a2018-05-09 17:18:55 -070095 std::vector<boost::container::flat_map<std::string, BasicVariantType>>>
James Feist3cb5fec2018-01-23 14:41:51 -080096 DBUS_PROBE_OBJECTS;
97std::vector<std::string> PASSED_PROBES;
98
99// todo: pass this through nicer
James Feist8f2710a2018-05-09 17:18:55 -0700100std::shared_ptr<sdbusplus::asio::connection> SYSTEM_BUS;
James Feist3cb5fec2018-01-23 14:41:51 -0800101
James Feista6750242018-07-16 14:12:27 -0700102const std::regex ILLEGAL_DBUS_REGEX("[^A-Za-z0-9_.]");
James Feist1b2e2242018-01-30 13:45:19 -0800103
James Feist8f2710a2018-05-09 17:18:55 -0700104void registerCallbacks(boost::asio::io_service &io,
105 std::vector<sdbusplus::bus::match::match> &dbusMatches,
106 nlohmann::json &systemConfiguration,
107 sdbusplus::asio::object_server &objServer);
James Feist75fdeeb2018-02-20 14:26:16 -0800108
James Feist3cb5fec2018-01-23 14:41:51 -0800109// calls the mapper to find all exposed objects of an interface type
110// and creates a vector<flat_map> that contains all the key value pairs
111// getManagedObjects
James Feist8f2710a2018-05-09 17:18:55 -0700112void findDbusObjects(std::shared_ptr<PerformProbe> probe,
113 std::shared_ptr<sdbusplus::asio::connection> connection,
114 std::string &interface)
James Feist3cb5fec2018-01-23 14:41:51 -0800115{
James Feist8f2710a2018-05-09 17:18:55 -0700116
117 // store reference to pending callbacks so we don't overwhelm services
118 static boost::container::flat_map<
119 std::string, std::vector<std::shared_ptr<PerformProbe>>>
120 pendingProbes;
121
122 if (DBUS_PROBE_OBJECTS[interface].size())
123 {
124 return;
125 }
126
127 // add shared_ptr to vector of Probes waiting for callback from a specific
128 // interface to keep alive while waiting for response
129 std::array<const char *, 1> objects = {interface.c_str()};
130 std::vector<std::shared_ptr<PerformProbe>> &pending =
131 pendingProbes[interface];
132 auto iter = pending.emplace(pending.end(), probe);
133 // only allow first call to run to not overwhelm processes
134 if (iter != pending.begin())
135 {
136 return;
137 }
138
James Feist3cb5fec2018-01-23 14:41:51 -0800139 // find all connections in the mapper that expose a specific type
James Feist8f2710a2018-05-09 17:18:55 -0700140 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700141 [connection, interface, probe](boost::system::error_code &ec,
142 const GetSubTreeType &interfaceSubtree) {
143 boost::container::flat_set<std::string> interfaceConnections;
James Feist8f2710a2018-05-09 17:18:55 -0700144 if (ec)
James Feist494155a2018-03-14 16:23:24 -0700145 {
James Feist0de40152018-07-25 11:56:12 -0700146 pendingProbes[interface].clear();
147 if (ec.value() == ENOENT)
James Feist8f2710a2018-05-09 17:18:55 -0700148 {
James Feist0de40152018-07-25 11:56:12 -0700149 return; // wasn't found by mapper
James Feist8f2710a2018-05-09 17:18:55 -0700150 }
James Feist0de40152018-07-25 11:56:12 -0700151 std::cerr << "Error communicating to mapper.\n";
152
153 // if we can't communicate to the mapper something is very wrong
154 std::exit(EXIT_FAILURE);
James Feist494155a2018-03-14 16:23:24 -0700155 }
James Feist8f2710a2018-05-09 17:18:55 -0700156 else
James Feist3cb5fec2018-01-23 14:41:51 -0800157 {
James Feist8f2710a2018-05-09 17:18:55 -0700158 for (auto &object : interfaceSubtree)
159 {
160 for (auto &connPair : object.second)
161 {
162 auto insertPair =
163 interfaceConnections.insert(connPair.first);
164 }
165 }
James Feist3cb5fec2018-01-23 14:41:51 -0800166 }
James Feist8f2710a2018-05-09 17:18:55 -0700167 // get managed objects for all interfaces
168 for (const auto &conn : interfaceConnections)
169 {
170 connection->async_method_call(
James Feist0de40152018-07-25 11:56:12 -0700171 [conn,
James Feist8f2710a2018-05-09 17:18:55 -0700172 interface](boost::system::error_code &ec,
173 const ManagedObjectType &managedInterface) {
174 if (ec)
175 {
176 std::cerr
177 << "error getting managed object for device "
178 << conn << "\n";
179 pendingProbes[interface].clear();
180 return;
181 }
182 for (auto &interfaceManagedObj : managedInterface)
183 {
184 auto ifaceObjFind =
185 interfaceManagedObj.second.find(interface);
186 if (ifaceObjFind !=
187 interfaceManagedObj.second.end())
188 {
189 std::vector<boost::container::flat_map<
190 std::string, BasicVariantType>>
191 &dbusObject = DBUS_PROBE_OBJECTS[interface];
192 dbusObject.emplace_back(ifaceObjFind->second);
193 }
194 }
195 pendingProbes[interface].clear();
196 },
197 conn.c_str(), "/", "org.freedesktop.DBus.ObjectManager",
198 "GetManagedObjects");
199 }
200 },
201 "xyz.openbmc_project.ObjectMapper",
202 "/xyz/openbmc_project/object_mapper",
James Feist5131ac22018-10-25 12:22:23 -0700203 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", MAX_MAPPER_DEPTH,
James Feist8f2710a2018-05-09 17:18:55 -0700204 objects);
James Feist3cb5fec2018-01-23 14:41:51 -0800205}
James Feist8f2710a2018-05-09 17:18:55 -0700206// probes dbus interface dictionary for a key with a value that matches a regex
James Feist3cb5fec2018-01-23 14:41:51 -0800207bool probeDbus(
208 const std::string &interface,
209 const std::map<std::string, nlohmann::json> &matches,
James Feist8f2710a2018-05-09 17:18:55 -0700210 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800211 &devices,
212 bool &foundProbe)
213{
James Feist8f2710a2018-05-09 17:18:55 -0700214 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
215 &dbusObject = DBUS_PROBE_OBJECTS[interface];
James Feist3cb5fec2018-01-23 14:41:51 -0800216 if (dbusObject.empty())
217 {
James Feist8f2710a2018-05-09 17:18:55 -0700218 foundProbe = false;
219 return false;
James Feist3cb5fec2018-01-23 14:41:51 -0800220 }
221 foundProbe = true;
222
223 bool foundMatch = false;
224 for (auto &device : dbusObject)
225 {
226 bool deviceMatches = true;
227 for (auto &match : matches)
228 {
229 auto deviceValue = device.find(match.first);
230 if (deviceValue != device.end())
231 {
232 switch (match.second.type())
233 {
James Feist9eb0b582018-04-27 12:15:46 -0700234 case nlohmann::json::value_t::string:
James Feist3cb5fec2018-01-23 14:41:51 -0800235 {
James Feist9eb0b582018-04-27 12:15:46 -0700236 std::regex search(match.second.get<std::string>());
237 std::smatch match;
238
239 // convert value to string respresentation
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700240 std::string probeValue = variant_ns::visit(
James Feist8f2710a2018-05-09 17:18:55 -0700241 VariantToStringVisitor(), deviceValue->second);
James Feist9eb0b582018-04-27 12:15:46 -0700242 if (!std::regex_search(probeValue, match, search))
243 {
244 deviceMatches = false;
245 break;
246 }
James Feist3cb5fec2018-01-23 14:41:51 -0800247 break;
248 }
James Feist9eb0b582018-04-27 12:15:46 -0700249 case nlohmann::json::value_t::boolean:
250 case nlohmann::json::value_t::number_unsigned:
James Feist3cb5fec2018-01-23 14:41:51 -0800251 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700252 unsigned int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700253 VariantToUnsignedIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800254
James Feist9eb0b582018-04-27 12:15:46 -0700255 if (probeValue != match.second.get<unsigned int>())
256 {
257 deviceMatches = false;
258 }
259 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800260 }
James Feist9eb0b582018-04-27 12:15:46 -0700261 case nlohmann::json::value_t::number_integer:
262 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700263 int probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700264 VariantToIntVisitor(), deviceValue->second);
James Feist3cb5fec2018-01-23 14:41:51 -0800265
James Feist9eb0b582018-04-27 12:15:46 -0700266 if (probeValue != match.second.get<int>())
267 {
268 deviceMatches = false;
269 }
270 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800271 }
James Feist9eb0b582018-04-27 12:15:46 -0700272 case nlohmann::json::value_t::number_float:
273 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700274 float probeValue = variant_ns::visit(
James Feist9eb0b582018-04-27 12:15:46 -0700275 VariantToFloatVisitor(), deviceValue->second);
276
277 if (probeValue != match.second.get<float>())
278 {
279 deviceMatches = false;
280 }
281 break;
282 }
James Feist3cb5fec2018-01-23 14:41:51 -0800283 }
284 }
285 else
286 {
287 deviceMatches = false;
288 break;
289 }
290 }
291 if (deviceMatches)
292 {
293 devices.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700294 boost::container::flat_map<std::string, BasicVariantType>(
James Feist3cb5fec2018-01-23 14:41:51 -0800295 device));
296 foundMatch = true;
297 deviceMatches = false; // for next iteration
298 }
299 }
300 return foundMatch;
301}
302
303// default probe entry point, iterates a list looking for specific types to
304// call specific probe functions
305bool probe(
James Feist8f2710a2018-05-09 17:18:55 -0700306 const std::vector<std::string> &probeCommand,
307 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
James Feist3cb5fec2018-01-23 14:41:51 -0800308 &foundDevs)
309{
310 const static std::regex command(R"(\((.*)\))");
311 std::smatch match;
312 bool ret = false;
James Feist6bd2a022018-03-13 12:30:58 -0700313 bool matchOne = false;
James Feist3cb5fec2018-01-23 14:41:51 -0800314 bool cur = true;
315 probe_type_codes lastCommand = probe_type_codes::FALSE_T;
316
317 for (auto &probe : probeCommand)
318 {
319 bool foundProbe = false;
320 boost::container::flat_map<const char *, probe_type_codes,
321 cmp_str>::const_iterator probeType;
322
323 for (probeType = PROBE_TYPES.begin(); probeType != PROBE_TYPES.end();
324 probeType++)
325 {
326 if (probe.find(probeType->first) != std::string::npos)
327 {
328 foundProbe = true;
329 break;
330 }
331 }
332 if (foundProbe)
333 {
334 switch (probeType->second)
335 {
James Feist9eb0b582018-04-27 12:15:46 -0700336 case probe_type_codes::FALSE_T:
James Feist3cb5fec2018-01-23 14:41:51 -0800337 {
James Feist8f2710a2018-05-09 17:18:55 -0700338 return false;
James Feist9eb0b582018-04-27 12:15:46 -0700339 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800340 }
James Feist9eb0b582018-04-27 12:15:46 -0700341 case probe_type_codes::TRUE_T:
342 {
James Feist8f2710a2018-05-09 17:18:55 -0700343 return true;
James Feist9eb0b582018-04-27 12:15:46 -0700344 break;
345 }
346 case probe_type_codes::MATCH_ONE:
347 {
348 // set current value to last, this probe type shouldn't
349 // affect the outcome
350 cur = ret;
351 matchOne = true;
352 break;
353 }
354 /*case probe_type_codes::AND:
355 break;
356 case probe_type_codes::OR:
357 break;
358 // these are no-ops until the last command switch
359 */
360 case probe_type_codes::FOUND:
361 {
362 if (!std::regex_search(probe, match, command))
363 {
364 std::cerr << "found probe sytax error " << probe
365 << "\n";
366 return false;
367 }
368 std::string commandStr = *(match.begin() + 1);
369 boost::replace_all(commandStr, "'", "");
370 cur = (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(),
371 commandStr) != PASSED_PROBES.end());
372 break;
373 }
James Feist3cb5fec2018-01-23 14:41:51 -0800374 }
375 }
376 // look on dbus for object
377 else
378 {
379 if (!std::regex_search(probe, match, command))
380 {
381 std::cerr << "dbus probe sytax error " << probe << "\n";
382 return false;
383 }
384 std::string commandStr = *(match.begin() + 1);
385 // convert single ticks and single slashes into legal json
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700386 boost::replace_all(commandStr, "'", "\"");
James Feist3f8a2782018-02-12 09:24:42 -0800387 boost::replace_all(commandStr, R"(\)", R"(\\)");
James Feist3cb5fec2018-01-23 14:41:51 -0800388 auto json = nlohmann::json::parse(commandStr, nullptr, false);
389 if (json.is_discarded())
390 {
391 std::cerr << "dbus command sytax error " << commandStr << "\n";
392 return false;
393 }
394 // we can match any (string, variant) property. (string, string)
395 // does a regex
396 std::map<std::string, nlohmann::json> dbusProbeMap =
397 json.get<std::map<std::string, nlohmann::json>>();
398 auto findStart = probe.find("(");
399 if (findStart == std::string::npos)
400 {
401 return false;
402 }
403 std::string probeInterface = probe.substr(0, findStart);
404 cur =
405 probeDbus(probeInterface, dbusProbeMap, foundDevs, foundProbe);
406 }
407
408 // some functions like AND and OR only take affect after the
409 // fact
410 switch (lastCommand)
411 {
James Feist9eb0b582018-04-27 12:15:46 -0700412 case probe_type_codes::AND:
413 ret = cur && ret;
414 break;
415 case probe_type_codes::OR:
416 ret = cur || ret;
417 break;
418 default:
419 ret = cur;
420 break;
James Feist3cb5fec2018-01-23 14:41:51 -0800421 }
422 lastCommand = probeType != PROBE_TYPES.end()
423 ? probeType->second
424 : probe_type_codes::FALSE_T;
James Feist3cb5fec2018-01-23 14:41:51 -0800425 }
426
427 // probe passed, but empty device
James Feist3cb5fec2018-01-23 14:41:51 -0800428 if (ret && foundDevs.size() == 0)
429 {
430 foundDevs.emplace_back(
James Feist8f2710a2018-05-09 17:18:55 -0700431 boost::container::flat_map<std::string, BasicVariantType>());
James Feist3cb5fec2018-01-23 14:41:51 -0800432 }
James Feist6bd2a022018-03-13 12:30:58 -0700433 if (matchOne && foundDevs.size() > 1)
434 {
435 foundDevs.erase(foundDevs.begin() + 1, foundDevs.end());
436 }
James Feist3cb5fec2018-01-23 14:41:51 -0800437 return ret;
438}
James Feist8f2710a2018-05-09 17:18:55 -0700439// this class finds the needed dbus fields and on destruction runs the probe
440struct PerformProbe : std::enable_shared_from_this<PerformProbe>
441{
James Feist3cb5fec2018-01-23 14:41:51 -0800442
James Feist8f2710a2018-05-09 17:18:55 -0700443 PerformProbe(
444 const std::vector<std::string> &probeCommand,
445 std::function<void(std::vector<boost::container::flat_map<
446 std::string, BasicVariantType>> &)> &&callback) :
447 _probeCommand(probeCommand),
448 _callback(std::move(callback))
449 {
450 }
451 ~PerformProbe()
452 {
453 if (probe(_probeCommand, _foundDevs))
454 {
455 _callback(_foundDevs);
456 }
457 }
458 void run()
459 {
460 // parse out dbus probes by discarding other probe types
461 boost::container::flat_map<const char *, probe_type_codes,
462 cmp_str>::const_iterator probeType;
463
464 std::vector<std::string> dbusProbes;
465 for (std::string &probe : _probeCommand)
466 {
467 bool found = false;
468 boost::container::flat_map<const char *, probe_type_codes,
469 cmp_str>::const_iterator probeType;
470 for (probeType = PROBE_TYPES.begin();
471 probeType != PROBE_TYPES.end(); probeType++)
472 {
473 if (probe.find(probeType->first) != std::string::npos)
474 {
475 found = true;
476 break;
477 }
478 }
479 if (found)
480 {
481 continue;
482 }
483 // syntax requires probe before first open brace
484 auto findStart = probe.find("(");
485 std::string interface = probe.substr(0, findStart);
486
487 findDbusObjects(shared_from_this(), SYSTEM_BUS, interface);
488 }
489 }
490 std::vector<std::string> _probeCommand;
491 std::function<void(
492 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
493 &)>
494 _callback;
495 std::vector<boost::container::flat_map<std::string, BasicVariantType>>
496 _foundDevs;
497};
498
499// writes output files to persist data
James Feistbb43d022018-06-12 15:44:33 -0700500bool writeJsonFiles(const nlohmann::json &systemConfiguration)
James Feist1b2e2242018-01-30 13:45:19 -0800501{
502 std::experimental::filesystem::create_directory(OUTPUT_DIR);
503 std::ofstream output(std::string(OUTPUT_DIR) + "system.json");
James Feistbb43d022018-06-12 15:44:33 -0700504 if (!output.good())
505 {
506 return false;
507 }
James Feist1b2e2242018-01-30 13:45:19 -0800508 output << systemConfiguration.dump(4);
509 output.close();
James Feistbb43d022018-06-12 15:44:33 -0700510 return true;
James Feist8f2710a2018-05-09 17:18:55 -0700511}
James Feist1b2e2242018-01-30 13:45:19 -0800512
James Feist8f2710a2018-05-09 17:18:55 -0700513// template function to add array as dbus property
514template <typename PropertyType>
515void addArrayToDbus(const std::string &name, const nlohmann::json &array,
James Feistbb43d022018-06-12 15:44:33 -0700516 sdbusplus::asio::dbus_interface *iface,
517 sdbusplus::asio::PropertyPermission permission)
James Feist8f2710a2018-05-09 17:18:55 -0700518{
519 std::vector<PropertyType> values;
520 for (const auto &property : array)
James Feist1b2e2242018-01-30 13:45:19 -0800521 {
James Feist8f2710a2018-05-09 17:18:55 -0700522 auto ptr = property.get_ptr<const PropertyType *>();
523 if (ptr != nullptr)
James Feist1b2e2242018-01-30 13:45:19 -0800524 {
James Feist8f2710a2018-05-09 17:18:55 -0700525 values.emplace_back(*ptr);
James Feist1b2e2242018-01-30 13:45:19 -0800526 }
527 }
James Feistbb43d022018-06-12 15:44:33 -0700528 // todo(james), currently there are no reason to persist arrays, get around
529 // to it if needed
530
531 iface->register_property(name, values, permission);
James Feist1b2e2242018-01-30 13:45:19 -0800532}
James Feist97a63f12018-05-17 13:50:57 -0700533
534template <typename JsonType>
James Feistbb43d022018-06-12 15:44:33 -0700535bool setJsonFromPointer(const std::string &ptrStr, const JsonType &value,
James Feist97a63f12018-05-17 13:50:57 -0700536 nlohmann::json &systemConfiguration)
537{
538 try
539 {
540 nlohmann::json::json_pointer ptr(ptrStr);
541 nlohmann::json &ref = systemConfiguration[ptr];
542 ref = value;
543 return true;
544 }
545 catch (const std::out_of_range)
546 {
547 return false;
548 }
549}
James Feistbb43d022018-06-12 15:44:33 -0700550
551template <typename PropertyType>
552void addProperty(const std::string &propertyName, const PropertyType &value,
553 sdbusplus::asio::dbus_interface *iface,
554 nlohmann::json &systemConfiguration,
555 const std::string &jsonPointerString,
556 sdbusplus::asio::PropertyPermission permission)
557{
558 if (permission == sdbusplus::asio::PropertyPermission::readOnly)
559 {
560 iface->register_property(propertyName, value);
561 return;
562 }
James Feist68500ff2018-08-08 15:40:42 -0700563 iface->register_property(
564 propertyName, value,
565 [&systemConfiguration,
566 jsonPointerString{std::string(jsonPointerString)}](
567 const PropertyType &newVal, PropertyType &val) {
568 val = newVal;
569 if (!setJsonFromPointer(jsonPointerString, val,
570 systemConfiguration))
571 {
572 std::cerr << "error setting json field\n";
573 return -1;
574 }
James Feistc6248a52018-08-14 10:09:45 -0700575 if (!writeJsonFiles(systemConfiguration))
James Feist68500ff2018-08-08 15:40:42 -0700576 {
577 std::cerr << "error setting json file\n";
James Feistc6248a52018-08-14 10:09:45 -0700578 return -1;
579 }
580 return 1;
581 });
582}
583
584void createDeleteObjectMethod(
585 const std::string &jsonPointerPath,
586 const std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
587 sdbusplus::asio::object_server &objServer,
588 nlohmann::json &systemConfiguration)
589{
590 std::weak_ptr<sdbusplus::asio::dbus_interface> interface = iface;
591 iface->register_method(
592 "Delete", [&objServer, &systemConfiguration, interface,
593 jsonPointerPath{std::string(jsonPointerPath)}]() {
594 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
595 interface.lock();
596 if (!iface)
597 {
598 // this technically can't happen as the pointer is pointing to
599 // us
600 throw DBusInternalError();
601 }
602 nlohmann::json::json_pointer ptr(jsonPointerPath);
603 if (!objServer.remove_interface(iface))
604 {
605 std::cerr << "Can't delete interface " << jsonPointerPath
606 << "\n";
607 throw DBusInternalError();
608 }
609 systemConfiguration[ptr] = nullptr;
610
611 if (!writeJsonFiles(systemConfiguration))
612 {
613 std::cerr << "error setting json file\n";
614 throw DBusInternalError();
James Feist68500ff2018-08-08 15:40:42 -0700615 }
James Feistbb43d022018-06-12 15:44:33 -0700616 return -1;
James Feist68500ff2018-08-08 15:40:42 -0700617 });
James Feistbb43d022018-06-12 15:44:33 -0700618}
619
James Feist1b2e2242018-01-30 13:45:19 -0800620// adds simple json types to interface's properties
James Feistbb43d022018-06-12 15:44:33 -0700621void populateInterfaceFromJson(
622 nlohmann::json &systemConfiguration, const std::string &jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700623 std::shared_ptr<sdbusplus::asio::dbus_interface> &iface,
624 nlohmann::json &dict, sdbusplus::asio::object_server &objServer,
James Feistbb43d022018-06-12 15:44:33 -0700625 sdbusplus::asio::PropertyPermission permission =
626 sdbusplus::asio::PropertyPermission::readOnly)
James Feist1b2e2242018-01-30 13:45:19 -0800627{
James Feist9eb0b582018-04-27 12:15:46 -0700628 for (auto &dictPair : dict.items())
James Feist1b2e2242018-01-30 13:45:19 -0800629 {
James Feist8f2710a2018-05-09 17:18:55 -0700630 auto type = dictPair.value().type();
631 bool array = false;
632 if (dictPair.value().type() == nlohmann::json::value_t::array)
633 {
634 array = true;
635 if (!dictPair.value().size())
636 {
637 continue;
638 }
639 type = dictPair.value()[0].type();
640 bool isLegal = true;
641 for (const auto &arrayItem : dictPair.value())
642 {
643 if (arrayItem.type() != type)
644 {
645 isLegal = false;
646 break;
647 }
648 }
649 if (!isLegal)
650 {
651 std::cerr << "dbus format error" << dictPair.value() << "\n";
652 continue;
653 }
654 if (type == nlohmann::json::value_t::object)
655 {
656 continue; // handled elsewhere
657 }
658 }
James Feist97a63f12018-05-17 13:50:57 -0700659 std::string key = jsonPointerPath + "/" + dictPair.key();
James Feistbb43d022018-06-12 15:44:33 -0700660 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
661 {
662 // all setable numbers are doubles as it is difficult to always
663 // create a configuration file with all whole numbers as decimals
664 // i.e. 1.0
665 if (dictPair.value().is_number())
666 {
667 type = nlohmann::json::value_t::number_float;
668 }
669 }
670
James Feist8f2710a2018-05-09 17:18:55 -0700671 switch (type)
James Feist1b2e2242018-01-30 13:45:19 -0800672 {
James Feist9eb0b582018-04-27 12:15:46 -0700673 case (nlohmann::json::value_t::boolean):
674 {
James Feist8f2710a2018-05-09 17:18:55 -0700675 if (array)
676 {
677 // todo: array of bool isn't detected correctly by
678 // sdbusplus, change it to numbers
679 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700680 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700681 }
James Feistbb43d022018-06-12 15:44:33 -0700682
James Feist97a63f12018-05-17 13:50:57 -0700683 else
684 {
James Feistbb43d022018-06-12 15:44:33 -0700685 addProperty(dictPair.key(), dictPair.value().get<bool>(),
James Feistc6248a52018-08-14 10:09:45 -0700686 iface.get(), systemConfiguration, key,
687 permission);
James Feist97a63f12018-05-17 13:50:57 -0700688 }
James Feist9eb0b582018-04-27 12:15:46 -0700689 break;
690 }
691 case (nlohmann::json::value_t::number_integer):
692 {
James Feist8f2710a2018-05-09 17:18:55 -0700693 if (array)
694 {
695 addArrayToDbus<int64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700696 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700697 }
698 else
699 {
James Feistbb43d022018-06-12 15:44:33 -0700700 addProperty(dictPair.key(), dictPair.value().get<int64_t>(),
James Feistc6248a52018-08-14 10:09:45 -0700701 iface.get(), systemConfiguration, key,
James Feistbb43d022018-06-12 15:44:33 -0700702 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700703 }
James Feist9eb0b582018-04-27 12:15:46 -0700704 break;
705 }
706 case (nlohmann::json::value_t::number_unsigned):
707 {
James Feist8f2710a2018-05-09 17:18:55 -0700708 if (array)
709 {
710 addArrayToDbus<uint64_t>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700711 iface.get(), permission);
James Feist97a63f12018-05-17 13:50:57 -0700712 }
713 else
714 {
James Feistbb43d022018-06-12 15:44:33 -0700715 addProperty(dictPair.key(),
James Feistc6248a52018-08-14 10:09:45 -0700716 dictPair.value().get<uint64_t>(), iface.get(),
James Feistbb43d022018-06-12 15:44:33 -0700717 systemConfiguration, key,
718 sdbusplus::asio::PropertyPermission::readOnly);
James Feist97a63f12018-05-17 13:50:57 -0700719 }
James Feist9eb0b582018-04-27 12:15:46 -0700720 break;
721 }
722 case (nlohmann::json::value_t::number_float):
723 {
James Feist8f2710a2018-05-09 17:18:55 -0700724 if (array)
725 {
726 addArrayToDbus<double>(dictPair.key(), dictPair.value(),
James Feistc6248a52018-08-14 10:09:45 -0700727 iface.get(), permission);
James Feist8f2710a2018-05-09 17:18:55 -0700728 }
James Feistbb43d022018-06-12 15:44:33 -0700729
James Feist97a63f12018-05-17 13:50:57 -0700730 else
731 {
James Feistbb43d022018-06-12 15:44:33 -0700732 addProperty(dictPair.key(), dictPair.value().get<double>(),
James Feistc6248a52018-08-14 10:09:45 -0700733 iface.get(), systemConfiguration, key,
734 permission);
James Feist97a63f12018-05-17 13:50:57 -0700735 }
James Feist9eb0b582018-04-27 12:15:46 -0700736 break;
737 }
738 case (nlohmann::json::value_t::string):
739 {
James Feist8f2710a2018-05-09 17:18:55 -0700740 if (array)
741 {
James Feistc6248a52018-08-14 10:09:45 -0700742 addArrayToDbus<std::string>(dictPair.key(),
743 dictPair.value(), iface.get(),
744 permission);
James Feist97a63f12018-05-17 13:50:57 -0700745 }
746 else
747 {
James Feistc6248a52018-08-14 10:09:45 -0700748 addProperty(
749 dictPair.key(), dictPair.value().get<std::string>(),
750 iface.get(), systemConfiguration, key, permission);
James Feist97a63f12018-05-17 13:50:57 -0700751 }
James Feist9eb0b582018-04-27 12:15:46 -0700752 break;
753 }
James Feist1b2e2242018-01-30 13:45:19 -0800754 }
755 }
James Feistc6248a52018-08-14 10:09:45 -0700756 if (permission == sdbusplus::asio::PropertyPermission::readWrite)
757 {
758 createDeleteObjectMethod(jsonPointerPath, iface, objServer,
759 systemConfiguration);
760 }
James Feist8f2710a2018-05-09 17:18:55 -0700761 iface->initialize();
James Feist1b2e2242018-01-30 13:45:19 -0800762}
763
James Feistc6248a52018-08-14 10:09:45 -0700764sdbusplus::asio::PropertyPermission getPermission(const std::string &interface)
765{
766 return std::find(settableInterfaces.begin(), settableInterfaces.end(),
767 interface) != settableInterfaces.end()
768 ? sdbusplus::asio::PropertyPermission::readWrite
769 : sdbusplus::asio::PropertyPermission::readOnly;
770}
771
James Feist68500ff2018-08-08 15:40:42 -0700772void createAddObjectMethod(const std::string &jsonPointerPath,
773 const std::string &path,
774 nlohmann::json &systemConfiguration,
775 sdbusplus::asio::object_server &objServer)
776{
777 auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
778
779 iface->register_method(
780 "AddObject",
781 [&systemConfiguration, &objServer,
782 jsonPointerPath{std::string(jsonPointerPath)},
783 path{std::string(path)}](
784 const boost::container::flat_map<std::string, JsonVariantType>
785 &data) {
786 nlohmann::json::json_pointer ptr(jsonPointerPath);
787 nlohmann::json &base = systemConfiguration[ptr];
788 auto findExposes = base.find("Exposes");
789
790 if (findExposes == base.end())
791 {
792 throw std::invalid_argument("Entity must have children.");
793 }
794
795 // this will throw invalid-argument to sdbusplus if invalid json
796 nlohmann::json newData{};
797 for (const auto &item : data)
798 {
799 nlohmann::json &newJson = newData[item.first];
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -0700800 variant_ns::visit(
James Feist68500ff2018-08-08 15:40:42 -0700801 [&newJson](auto &&val) { newJson = std::move(val); },
802 item.second);
803 }
804
805 auto findName = newData.find("Name");
806 auto findType = newData.find("Type");
807 if (findName == newData.end() || findType == newData.end())
808 {
809 throw std::invalid_argument("AddObject missing Name or Type");
810 }
811 const std::string *type = findType->get_ptr<const std::string *>();
812 const std::string *name = findName->get_ptr<const std::string *>();
813 if (type == nullptr || name == nullptr)
814 {
815 throw std::invalid_argument("Type and Name must be a string.");
816 }
817
818 size_t lastIndex = 0;
819 // we add in the "exposes"
820 for (; lastIndex < findExposes->size(); lastIndex++)
821 {
822 if (findExposes->at(lastIndex)["Name"] == *name &&
823 findExposes->at(lastIndex)["Type"] == *type)
824 {
825 throw std::invalid_argument(
826 "Field already in JSON, not adding");
827 }
828 lastIndex++;
829 }
830
831 std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
832 *type + ".json");
833 // todo(james) we might want to also make a list of 'can add'
834 // interfaces but for now I think the assumption if there is a
835 // schema avaliable that it is allowed to update is fine
836 if (!schemaFile.good())
837 {
838 throw std::invalid_argument(
839 "No schema avaliable, cannot validate.");
840 }
841 nlohmann::json schema =
842 nlohmann::json::parse(schemaFile, nullptr, false);
843 if (schema.is_discarded())
844 {
845 std::cerr << "Schema not legal" << *type << ".json\n";
846 throw DBusInternalError();
847 }
848 if (!validateJson(schema, newData))
849 {
850 throw std::invalid_argument("Data does not match schema");
851 }
852
853 if (!writeJsonFiles(systemConfiguration))
854 {
855 std::cerr << "Error writing json files\n";
856 throw DBusInternalError();
857 }
858 std::string dbusName = *name;
859
860 std::regex_replace(dbusName.begin(), dbusName.begin(),
861 dbusName.end(), ILLEGAL_DBUS_REGEX, "_");
862 auto iface = objServer.add_interface(
863 path + "/" + dbusName,
864 "xyz.openbmc_project.Configuration." + *type);
865 // permission is read-write, as since we just created it, must be
866 // runtime modifiable
867 populateInterfaceFromJson(
868 systemConfiguration,
James Feistc6248a52018-08-14 10:09:45 -0700869 jsonPointerPath + "/" + std::to_string(lastIndex), iface,
James Feist68500ff2018-08-08 15:40:42 -0700870 newData, objServer,
871 sdbusplus::asio::PropertyPermission::readWrite);
872 // todo(james) generate patch
873 findExposes->push_back(newData);
874 });
875 iface->initialize();
876}
877
James Feist97a63f12018-05-17 13:50:57 -0700878void postToDbus(const nlohmann::json &newConfiguration,
879 nlohmann::json &systemConfiguration,
James Feist8f2710a2018-05-09 17:18:55 -0700880 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -0800881
James Feist1b2e2242018-01-30 13:45:19 -0800882{
James Feist97a63f12018-05-17 13:50:57 -0700883 // iterate through boards
884 for (auto &boardPair : newConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -0800885 {
886 std::string boardKey = boardPair.key();
James Feist97a63f12018-05-17 13:50:57 -0700887 std::vector<std::string> path;
888 std::string jsonPointerPath = "/" + boardKey;
889 // loop through newConfiguration, but use values from system
890 // configuration to be able to modify via dbus later
891 auto boardValues = systemConfiguration[boardKey];
James Feistd63d18a2018-07-19 15:23:45 -0700892 auto findBoardType = boardValues.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800893 std::string boardType;
894 if (findBoardType != boardValues.end() &&
895 findBoardType->type() == nlohmann::json::value_t::string)
896 {
897 boardType = findBoardType->get<std::string>();
898 std::regex_replace(boardType.begin(), boardType.begin(),
899 boardType.end(), ILLEGAL_DBUS_REGEX, "_");
900 }
901 else
902 {
903 std::cerr << "Unable to find type for " << boardKey
904 << " reverting to Chassis.\n";
905 boardType = "Chassis";
906 }
James Feist11be6672018-04-06 14:05:32 -0700907 std::string boardtypeLower = boost::algorithm::to_lower_copy(boardType);
James Feist1b2e2242018-01-30 13:45:19 -0800908
909 std::regex_replace(boardKey.begin(), boardKey.begin(), boardKey.end(),
910 ILLEGAL_DBUS_REGEX, "_");
James Feist11be6672018-04-06 14:05:32 -0700911 std::string boardName = "/xyz/openbmc_project/inventory/system/" +
912 boardtypeLower + "/" + boardKey;
James Feist1b2e2242018-01-30 13:45:19 -0800913
James Feist8f2710a2018-05-09 17:18:55 -0700914 auto inventoryIface = objServer.add_interface(
915 boardName, "xyz.openbmc_project.Inventory.Item");
James Feist68500ff2018-08-08 15:40:42 -0700916
James Feist8f2710a2018-05-09 17:18:55 -0700917 auto boardIface = objServer.add_interface(
918 boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
James Feist11be6672018-04-06 14:05:32 -0700919
James Feist68500ff2018-08-08 15:40:42 -0700920 createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
921 objServer);
922
James Feist97a63f12018-05-17 13:50:57 -0700923 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700924 boardIface, boardValues, objServer);
James Feist97a63f12018-05-17 13:50:57 -0700925 jsonPointerPath += "/";
926 // iterate through board properties
James Feist9eb0b582018-04-27 12:15:46 -0700927 for (auto &boardField : boardValues.items())
James Feist11be6672018-04-06 14:05:32 -0700928 {
929 if (boardField.value().type() == nlohmann::json::value_t::object)
930 {
James Feist8f2710a2018-05-09 17:18:55 -0700931 auto iface =
932 objServer.add_interface(boardName, boardField.key());
James Feistc6248a52018-08-14 10:09:45 -0700933 populateInterfaceFromJson(systemConfiguration,
934 jsonPointerPath + boardField.key(),
935 iface, boardField.value(), objServer);
James Feist11be6672018-04-06 14:05:32 -0700936 }
937 }
James Feist97a63f12018-05-17 13:50:57 -0700938
James Feist1e3e6982018-08-03 16:09:28 -0700939 auto exposes = boardValues.find("Exposes");
James Feist1b2e2242018-01-30 13:45:19 -0800940 if (exposes == boardValues.end())
941 {
942 continue;
943 }
James Feist97a63f12018-05-17 13:50:57 -0700944 // iterate through exposes
James Feist1e3e6982018-08-03 16:09:28 -0700945 jsonPointerPath += "Exposes/";
James Feist97a63f12018-05-17 13:50:57 -0700946
947 // store the board level pointer so we can modify it on the way down
948 std::string jsonPointerPathBoard = jsonPointerPath;
949 size_t exposesIndex = -1;
James Feist1b2e2242018-01-30 13:45:19 -0800950 for (auto &item : *exposes)
951 {
James Feist97a63f12018-05-17 13:50:57 -0700952 exposesIndex++;
953 jsonPointerPath = jsonPointerPathBoard;
954 jsonPointerPath += std::to_string(exposesIndex);
955
James Feistd63d18a2018-07-19 15:23:45 -0700956 auto findName = item.find("Name");
James Feist1b2e2242018-01-30 13:45:19 -0800957 if (findName == item.end())
958 {
959 std::cerr << "cannot find name in field " << item << "\n";
960 continue;
961 }
James Feist1e3e6982018-08-03 16:09:28 -0700962 auto findStatus = item.find("Status");
James Feist1b2e2242018-01-30 13:45:19 -0800963 // if status is not found it is assumed to be status = 'okay'
964 if (findStatus != item.end())
965 {
966 if (*findStatus == "disabled")
967 {
968 continue;
969 }
970 }
James Feistd63d18a2018-07-19 15:23:45 -0700971 auto findType = item.find("Type");
James Feist1b2e2242018-01-30 13:45:19 -0800972 std::string itemType;
973 if (findType != item.end())
974 {
975 itemType = findType->get<std::string>();
976 std::regex_replace(itemType.begin(), itemType.begin(),
977 itemType.end(), ILLEGAL_DBUS_REGEX, "_");
978 }
979 else
980 {
981 itemType = "unknown";
982 }
983 std::string itemName = findName->get<std::string>();
984 std::regex_replace(itemName.begin(), itemName.begin(),
985 itemName.end(), ILLEGAL_DBUS_REGEX, "_");
James Feistc6248a52018-08-14 10:09:45 -0700986
James Feist8f2710a2018-05-09 17:18:55 -0700987 auto itemIface = objServer.add_interface(
988 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -0800989 "xyz.openbmc_project.Configuration." + itemType);
990
James Feist97a63f12018-05-17 13:50:57 -0700991 populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
James Feistc6248a52018-08-14 10:09:45 -0700992 itemIface, item, objServer,
993 getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -0800994
James Feist9eb0b582018-04-27 12:15:46 -0700995 for (auto &objectPair : item.items())
James Feist1b2e2242018-01-30 13:45:19 -0800996 {
James Feist97a63f12018-05-17 13:50:57 -0700997 jsonPointerPath = jsonPointerPathBoard +
998 std::to_string(exposesIndex) + "/" +
999 objectPair.key();
James Feist1b2e2242018-01-30 13:45:19 -08001000 if (objectPair.value().type() ==
1001 nlohmann::json::value_t::object)
1002 {
James Feist8f2710a2018-05-09 17:18:55 -07001003 auto objectIface = objServer.add_interface(
1004 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001005 "xyz.openbmc_project.Configuration." + itemType + "." +
James Feist8f2710a2018-05-09 17:18:55 -07001006 objectPair.key());
James Feist97a63f12018-05-17 13:50:57 -07001007
1008 populateInterfaceFromJson(
James Feistc6248a52018-08-14 10:09:45 -07001009 systemConfiguration, jsonPointerPath, objectIface,
1010 objectPair.value(), objServer, getPermission(itemType));
James Feist1b2e2242018-01-30 13:45:19 -08001011 }
1012 else if (objectPair.value().type() ==
1013 nlohmann::json::value_t::array)
1014 {
1015 size_t index = 0;
James Feist8f2710a2018-05-09 17:18:55 -07001016 if (!objectPair.value().size())
James Feist1b2e2242018-01-30 13:45:19 -08001017 {
James Feist8f2710a2018-05-09 17:18:55 -07001018 continue;
1019 }
1020 bool isLegal = true;
1021 auto type = objectPair.value()[0].type();
1022 if (type != nlohmann::json::value_t::object)
1023 {
1024 continue;
1025 }
1026
1027 // verify legal json
1028 for (const auto &arrayItem : objectPair.value())
1029 {
1030 if (arrayItem.type() != type)
James Feist1b2e2242018-01-30 13:45:19 -08001031 {
James Feist8f2710a2018-05-09 17:18:55 -07001032 isLegal = false;
James Feist1b2e2242018-01-30 13:45:19 -08001033 break;
1034 }
James Feist8f2710a2018-05-09 17:18:55 -07001035 }
1036 if (!isLegal)
1037 {
1038 std::cerr << "dbus format error" << objectPair.value()
1039 << "\n";
1040 break;
1041 }
1042
1043 for (auto &arrayItem : objectPair.value())
1044 {
James Feist97a63f12018-05-17 13:50:57 -07001045
James Feist8f2710a2018-05-09 17:18:55 -07001046 auto objectIface = objServer.add_interface(
1047 boardName + "/" + itemName,
James Feist1b2e2242018-01-30 13:45:19 -08001048 "xyz.openbmc_project.Configuration." + itemType +
James Feistbb43d022018-06-12 15:44:33 -07001049 "." + objectPair.key() + std::to_string(index));
James Feistc6248a52018-08-14 10:09:45 -07001050 populateInterfaceFromJson(
1051 systemConfiguration,
1052 jsonPointerPath + "/" + std::to_string(index),
1053 objectIface, arrayItem, objServer,
1054 getPermission(objectPair.key()));
James Feistbb43d022018-06-12 15:44:33 -07001055 index++;
James Feist1b2e2242018-01-30 13:45:19 -08001056 }
1057 }
1058 }
1059 }
1060 }
1061}
1062
1063// finds the template character (currently set to $) and replaces the value with
1064// the field found in a dbus object i.e. $ADDRESS would get populated with the
1065// ADDRESS field from a object on dbus
1066void templateCharReplace(
1067 nlohmann::json::iterator &keyPair,
James Feist8f2710a2018-05-09 17:18:55 -07001068 const boost::container::flat_map<std::string, BasicVariantType>
James Feist1b2e2242018-01-30 13:45:19 -08001069 &foundDevice,
1070 size_t &foundDeviceIdx)
1071{
James Feist11be6672018-04-06 14:05:32 -07001072 if (keyPair.value().type() == nlohmann::json::value_t::object)
1073 {
1074 for (auto nextLayer = keyPair.value().begin();
1075 nextLayer != keyPair.value().end(); nextLayer++)
1076 {
1077 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx);
1078 }
1079 return;
1080 }
1081 else if (keyPair.value().type() != nlohmann::json::value_t::string)
James Feist1b2e2242018-01-30 13:45:19 -08001082 {
1083 return;
1084 }
1085
1086 std::string value = keyPair.value();
1087 if (value.find(TEMPLATE_CHAR) != std::string::npos)
1088 {
1089 std::string templateValue = value;
1090
1091 templateValue.erase(0, 1); // remove template character
1092
1093 // special case index
1094 if ("index" == templateValue)
1095 {
1096 keyPair.value() = foundDeviceIdx;
1097 }
1098 else
1099 {
James Feist13b86d62018-05-29 11:24:35 -07001100 bool found = false;
James Feist1b2e2242018-01-30 13:45:19 -08001101 for (auto &foundDevicePair : foundDevice)
1102 {
1103 if (boost::iequals(foundDevicePair.first, templateValue))
1104 {
Jae Hyun Yoo1ff19272018-10-18 10:58:26 -07001105 variant_ns::visit(
James Feist13b86d62018-05-29 11:24:35 -07001106 [&](auto &&val) { keyPair.value() = val; },
1107 foundDevicePair.second);
1108 found = true;
James Feist1b2e2242018-01-30 13:45:19 -08001109 break;
1110 }
1111 }
James Feist13b86d62018-05-29 11:24:35 -07001112 if (!found)
James Feist1b2e2242018-01-30 13:45:19 -08001113 {
1114 std::cerr << "could not find symbol " << templateValue << "\n";
1115 }
James Feist1b2e2242018-01-30 13:45:19 -08001116 }
1117 }
James Feist28dc2da2018-10-15 14:47:42 -07001118 else if (boost::starts_with(value, "0x"))
1119 {
1120 try
1121 {
1122 keyPair.value() = static_cast<uint64_t>(std::stoul(value, 0, 0));
1123 }
1124 catch (std::invalid_argument)
1125 {
1126 }
1127 }
James Feist1b2e2242018-01-30 13:45:19 -08001128}
1129
James Feist8f2710a2018-05-09 17:18:55 -07001130// reads json files out of the filesystem
1131bool findJsonFiles(std::list<nlohmann::json> &configurations)
James Feist3cb5fec2018-01-23 14:41:51 -08001132{
1133 // find configuration files
1134 std::vector<fs::path> jsonPaths;
James Feista3c180a2018-08-09 16:06:04 -07001135 if (!findFiles(fs::path(configurationDirectory),
1136 R"(.*\.json)", jsonPaths))
James Feist3cb5fec2018-01-23 14:41:51 -08001137 {
1138 std::cerr << "Unable to find any configuration files in "
James Feistb4383f42018-08-06 16:54:10 -07001139 << configurationDirectory << "\n";
James Feist75fdeeb2018-02-20 14:26:16 -08001140 return false;
James Feist3cb5fec2018-01-23 14:41:51 -08001141 }
James Feistb4383f42018-08-06 16:54:10 -07001142
1143 std::ifstream schemaStream(std::string(schemaDirectory) + "/" +
1144 globalSchema);
1145 if (!schemaStream.good())
1146 {
1147 std::cerr
1148 << "Cannot open schema file, cannot validate JSON, exiting\n\n";
1149 std::exit(EXIT_FAILURE);
1150 }
1151 nlohmann::json schema = nlohmann::json::parse(schemaStream, nullptr, false);
1152 if (schema.is_discarded())
1153 {
1154 std::cerr
1155 << "Illegal schema file detected, cannot validate JSON, exiting\n";
1156 std::exit(EXIT_FAILURE);
1157 }
1158
James Feist3cb5fec2018-01-23 14:41:51 -08001159 for (auto &jsonPath : jsonPaths)
1160 {
1161 std::ifstream jsonStream(jsonPath.c_str());
1162 if (!jsonStream.good())
1163 {
1164 std::cerr << "unable to open " << jsonPath.string() << "\n";
1165 continue;
1166 }
1167 auto data = nlohmann::json::parse(jsonStream, nullptr, false);
1168 if (data.is_discarded())
1169 {
1170 std::cerr << "syntax error in " << jsonPath.string() << "\n";
1171 continue;
1172 }
James Feistb4383f42018-08-06 16:54:10 -07001173 if (!validateJson(schema, data))
1174 {
1175 std::cerr << "Error validating " << jsonPath.string() << "\n";
1176 continue;
1177 }
1178
James Feist3cb5fec2018-01-23 14:41:51 -08001179 if (data.type() == nlohmann::json::value_t::array)
1180 {
1181 for (auto &d : data)
1182 {
1183 configurations.emplace_back(d);
1184 }
1185 }
1186 else
1187 {
1188 configurations.emplace_back(data);
1189 }
1190 }
James Feist75fdeeb2018-02-20 14:26:16 -08001191}
James Feist3cb5fec2018-01-23 14:41:51 -08001192
James Feist8f2710a2018-05-09 17:18:55 -07001193struct PerformScan : std::enable_shared_from_this<PerformScan>
James Feist75fdeeb2018-02-20 14:26:16 -08001194{
James Feist75fdeeb2018-02-20 14:26:16 -08001195
James Feist8f2710a2018-05-09 17:18:55 -07001196 PerformScan(nlohmann::json &systemConfiguration,
1197 std::list<nlohmann::json> &configurations,
1198 std::function<void(void)> &&callback) :
1199 _systemConfiguration(systemConfiguration),
1200 _configurations(configurations), _callback(std::move(callback))
James Feist3cb5fec2018-01-23 14:41:51 -08001201 {
James Feist8f2710a2018-05-09 17:18:55 -07001202 }
1203 void run()
1204 {
1205 for (auto it = _configurations.begin(); it != _configurations.end();)
James Feist3cb5fec2018-01-23 14:41:51 -08001206 {
James Feist1e3e6982018-08-03 16:09:28 -07001207 auto findProbe = it->find("Probe");
James Feistd63d18a2018-07-19 15:23:45 -07001208 auto findName = it->find("Name");
James Feist3cb5fec2018-01-23 14:41:51 -08001209
James Feist1b2e2242018-01-30 13:45:19 -08001210 nlohmann::json probeCommand;
1211 // check for poorly formatted fields, probe must be an array
1212 if (findProbe == it->end())
James Feist3cb5fec2018-01-23 14:41:51 -08001213 {
1214 std::cerr << "configuration file missing probe:\n " << *it
1215 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001216 it = _configurations.erase(it);
1217 continue;
James Feist3cb5fec2018-01-23 14:41:51 -08001218 }
James Feist1b2e2242018-01-30 13:45:19 -08001219 else if ((*findProbe).type() != nlohmann::json::value_t::array)
James Feist3cb5fec2018-01-23 14:41:51 -08001220 {
1221 probeCommand = nlohmann::json::array();
1222 probeCommand.push_back(*findProbe);
1223 }
1224 else
1225 {
1226 probeCommand = *findProbe;
1227 }
James Feist1b2e2242018-01-30 13:45:19 -08001228
1229 if (findName == it->end())
1230 {
1231 std::cerr << "configuration file missing name:\n " << *it
1232 << "\n";
James Feist8f2710a2018-05-09 17:18:55 -07001233 it = _configurations.erase(it);
1234 continue;
James Feist1b2e2242018-01-30 13:45:19 -08001235 }
James Feist8f2710a2018-05-09 17:18:55 -07001236 std::string name = *findName;
James Feist1b2e2242018-01-30 13:45:19 -08001237
James Feist8f2710a2018-05-09 17:18:55 -07001238 if (std::find(PASSED_PROBES.begin(), PASSED_PROBES.end(), name) !=
1239 PASSED_PROBES.end())
James Feist3cb5fec2018-01-23 14:41:51 -08001240 {
James Feist8f2710a2018-05-09 17:18:55 -07001241 it = _configurations.erase(it);
1242 continue;
1243 }
1244 nlohmann::json *record = &(*it);
James Feist3cb5fec2018-01-23 14:41:51 -08001245
James Feist8f2710a2018-05-09 17:18:55 -07001246 // store reference to this to children to makes sure we don't get
1247 // destroyed too early
1248 auto thisRef = shared_from_this();
1249 auto p = std::make_shared<PerformProbe>(
1250 probeCommand,
1251 [&, record, name,
1252 thisRef](std::vector<boost::container::flat_map<
1253 std::string, BasicVariantType>> &foundDevices) {
1254 _passed = true;
James Feist3cb5fec2018-01-23 14:41:51 -08001255
James Feist8f2710a2018-05-09 17:18:55 -07001256 PASSED_PROBES.push_back(name);
1257 size_t foundDeviceIdx = 0;
1258
James Feistbe5425f2018-06-08 10:30:55 -07001259 // insert into configuration temporarly to be able to
1260 // reference ourselves
1261 _systemConfiguration[name] = *record;
1262
James Feist8f2710a2018-05-09 17:18:55 -07001263 for (auto &foundDevice : foundDevices)
James Feist3cb5fec2018-01-23 14:41:51 -08001264 {
James Feist8f2710a2018-05-09 17:18:55 -07001265 for (auto keyPair = record->begin();
1266 keyPair != record->end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001267 {
James Feist1b2e2242018-01-30 13:45:19 -08001268 templateCharReplace(keyPair, foundDevice,
1269 foundDeviceIdx);
James Feist8f2710a2018-05-09 17:18:55 -07001270 }
James Feist1e3e6982018-08-03 16:09:28 -07001271 auto findExpose = record->find("Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001272 if (findExpose == record->end())
1273 {
1274 continue;
1275 }
1276 for (auto &expose : *findExpose)
1277 {
1278 for (auto keyPair = expose.begin();
1279 keyPair != expose.end(); keyPair++)
James Feist3cb5fec2018-01-23 14:41:51 -08001280 {
James Feist1b2e2242018-01-30 13:45:19 -08001281
James Feist8f2710a2018-05-09 17:18:55 -07001282 // fill in template characters with devices
1283 // found
1284 templateCharReplace(keyPair, foundDevice,
1285 foundDeviceIdx);
1286 // special case bind
James Feist1e3e6982018-08-03 16:09:28 -07001287 if (boost::starts_with(keyPair.key(), "Bind"))
James Feist8f2710a2018-05-09 17:18:55 -07001288 {
1289 if (keyPair.value().type() !=
1290 nlohmann::json::value_t::string)
James Feist3cb5fec2018-01-23 14:41:51 -08001291 {
James Feist8f2710a2018-05-09 17:18:55 -07001292 std::cerr << "bind_ value must be of "
1293 "type string "
1294 << keyPair.key() << "\n";
James Feist1b2e2242018-01-30 13:45:19 -08001295 continue;
1296 }
James Feist8f2710a2018-05-09 17:18:55 -07001297 bool foundBind = false;
1298 std::string bind = keyPair.key().substr(
James Feist1e3e6982018-08-03 16:09:28 -07001299 sizeof("Bind") - 1);
James Feistbe5425f2018-06-08 10:30:55 -07001300
James Feist8f2710a2018-05-09 17:18:55 -07001301 for (auto &configurationPair :
1302 _systemConfiguration.items())
James Feist1b2e2242018-01-30 13:45:19 -08001303 {
James Feist1b2e2242018-01-30 13:45:19 -08001304
James Feist8f2710a2018-05-09 17:18:55 -07001305 auto configListFind =
1306 configurationPair.value().find(
James Feist1e3e6982018-08-03 16:09:28 -07001307 "Exposes");
James Feist8f2710a2018-05-09 17:18:55 -07001308
1309 if (configListFind ==
1310 configurationPair.value()
1311 .end() ||
1312 configListFind->type() !=
1313 nlohmann::json::value_t::array)
1314 {
1315 continue;
1316 }
1317 for (auto &exposedObject :
1318 *configListFind)
1319 {
1320 std::string foundObjectName =
James Feistd63d18a2018-07-19 15:23:45 -07001321 (exposedObject)["Name"];
James Feist8f2710a2018-05-09 17:18:55 -07001322 if (boost::iequals(
1323 foundObjectName,
1324 keyPair.value()
1325 .get<std::string>()))
1326 {
James Feist1e3e6982018-08-03 16:09:28 -07001327 exposedObject["Status"] =
James Feist8f2710a2018-05-09 17:18:55 -07001328 "okay";
1329 expose[bind] = exposedObject;
1330
1331 foundBind = true;
1332 break;
1333 }
1334 }
1335 if (foundBind)
1336 {
James Feist3cb5fec2018-01-23 14:41:51 -08001337 break;
1338 }
1339 }
James Feist8f2710a2018-05-09 17:18:55 -07001340 if (!foundBind)
James Feist3cb5fec2018-01-23 14:41:51 -08001341 {
James Feist8f2710a2018-05-09 17:18:55 -07001342 std::cerr << "configuration file "
1343 "dependency error, "
1344 "could not find bind "
1345 << keyPair.value() << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -08001346 }
1347 }
1348 }
1349 }
1350 }
James Feistbe5425f2018-06-08 10:30:55 -07001351 // overwrite ourselves with cleaned up version
James Feist8f2710a2018-05-09 17:18:55 -07001352 _systemConfiguration[name] = *record;
1353 });
1354 p->run();
1355 it++;
James Feist3cb5fec2018-01-23 14:41:51 -08001356 }
1357 }
James Feist75fdeeb2018-02-20 14:26:16 -08001358
James Feist8f2710a2018-05-09 17:18:55 -07001359 ~PerformScan()
James Feist75fdeeb2018-02-20 14:26:16 -08001360 {
James Feist8f2710a2018-05-09 17:18:55 -07001361 if (_passed)
1362 {
1363 auto nextScan = std::make_shared<PerformScan>(
1364 _systemConfiguration, _configurations, std::move(_callback));
1365 nextScan->run();
1366 }
1367 else
1368 {
1369 _callback();
1370 }
1371 }
1372 nlohmann::json &_systemConfiguration;
1373 std::list<nlohmann::json> _configurations;
1374 std::function<void(void)> _callback;
1375 std::vector<std::shared_ptr<PerformProbe>> _probes;
1376 bool _passed = false;
1377};
James Feistc95cb142018-02-26 10:41:42 -08001378
James Feist8f2710a2018-05-09 17:18:55 -07001379// main properties changed entry
1380void propertiesChangedCallback(
1381 boost::asio::io_service &io,
1382 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1383 nlohmann::json &systemConfiguration,
1384 sdbusplus::asio::object_server &objServer)
1385{
1386 static boost::asio::deadline_timer timer(io);
1387 timer.expires_from_now(boost::posix_time::seconds(1));
1388
1389 // setup an async wait as we normally get flooded with new requests
1390 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist8f2710a2018-05-09 17:18:55 -07001391 if (ec == boost::asio::error::operation_aborted)
1392 {
1393 // we were cancelled
1394 return;
1395 }
1396 else if (ec)
1397 {
1398 std::cerr << "async wait error " << ec << "\n";
1399 return;
1400 }
1401
1402 nlohmann::json oldConfiguration = systemConfiguration;
1403 DBUS_PROBE_OBJECTS.clear();
1404
1405 std::list<nlohmann::json> configurations;
1406 if (!findJsonFiles(configurations))
1407 {
1408 std::cerr << "cannot find json files\n";
1409 return;
1410 }
1411
1412 auto perfScan = std::make_shared<PerformScan>(
1413 systemConfiguration, configurations, [&, oldConfiguration]() {
1414 nlohmann::json newConfiguration = systemConfiguration;
James Feist4131aea2018-03-09 09:47:30 -08001415 for (auto it = newConfiguration.begin();
1416 it != newConfiguration.end();)
1417 {
1418 auto findKey = oldConfiguration.find(it.key());
1419 if (findKey != oldConfiguration.end())
1420 {
1421 it = newConfiguration.erase(it);
1422 }
1423 else
1424 {
1425 it++;
1426 }
1427 }
James Feist8f2710a2018-05-09 17:18:55 -07001428 registerCallbacks(io, dbusMatches, systemConfiguration,
1429 objServer);
1430 io.post([&, newConfiguration]() {
James Feista2a98112018-08-07 11:15:37 -07001431
1432#ifdef OVERLAYS
James Feist8f2710a2018-05-09 17:18:55 -07001433 // todo: for now, only add new configurations,
1434 // unload to come later unloadOverlays();
1435 loadOverlays(newConfiguration);
James Feista2a98112018-08-07 11:15:37 -07001436#endif
James Feistbb43d022018-06-12 15:44:33 -07001437 io.post([&]() {
1438 if (!writeJsonFiles(systemConfiguration))
1439 {
1440 std::cerr << "Error writing json files\n";
1441 }
1442 });
James Feist8f2710a2018-05-09 17:18:55 -07001443 io.post([&, newConfiguration]() {
James Feist97a63f12018-05-17 13:50:57 -07001444 postToDbus(newConfiguration, systemConfiguration,
1445 objServer);
James Feist8f2710a2018-05-09 17:18:55 -07001446 });
1447 });
1448 });
1449 perfScan->run();
1450 });
James Feist75fdeeb2018-02-20 14:26:16 -08001451}
1452
James Feist8f2710a2018-05-09 17:18:55 -07001453void registerCallbacks(boost::asio::io_service &io,
1454 std::vector<sdbusplus::bus::match::match> &dbusMatches,
1455 nlohmann::json &systemConfiguration,
1456 sdbusplus::asio::object_server &objServer)
James Feist75fdeeb2018-02-20 14:26:16 -08001457{
1458 static boost::container::flat_set<std::string> watchedObjects;
1459
1460 for (const auto &objectMap : DBUS_PROBE_OBJECTS)
1461 {
1462 auto findObject = watchedObjects.find(objectMap.first);
1463 if (findObject != watchedObjects.end())
1464 {
1465 continue;
1466 }
James Feist8f2710a2018-05-09 17:18:55 -07001467 std::function<void(sdbusplus::message::message & message)>
1468 eventHandler =
James Feist75fdeeb2018-02-20 14:26:16 -08001469
James Feist8f2710a2018-05-09 17:18:55 -07001470 [&](sdbusplus::message::message &) {
1471 propertiesChangedCallback(io, dbusMatches,
1472 systemConfiguration, objServer);
1473 };
1474
1475 sdbusplus::bus::match::match match(
1476 static_cast<sdbusplus::bus::bus &>(*SYSTEM_BUS),
1477 "type='signal',member='PropertiesChanged',arg0='" +
1478 objectMap.first + "'",
1479 eventHandler);
1480 dbusMatches.emplace_back(std::move(match));
James Feist75fdeeb2018-02-20 14:26:16 -08001481 }
1482}
1483
1484int main(int argc, char **argv)
1485{
1486 // setup connection to dbus
1487 boost::asio::io_service io;
James Feist8f2710a2018-05-09 17:18:55 -07001488 SYSTEM_BUS = std::make_shared<sdbusplus::asio::connection>(io);
James Feist75fdeeb2018-02-20 14:26:16 -08001489 SYSTEM_BUS->request_name("xyz.openbmc_project.EntityManager");
James Feist4131aea2018-03-09 09:47:30 -08001490
James Feist8f2710a2018-05-09 17:18:55 -07001491 sdbusplus::asio::object_server objServer(SYSTEM_BUS);
James Feistfd1264a2018-05-03 12:10:00 -07001492
James Feist8f2710a2018-05-09 17:18:55 -07001493 std::shared_ptr<sdbusplus::asio::dbus_interface> entityIface =
1494 objServer.add_interface("/xyz/openbmc_project/EntityManager",
1495 "xyz.openbmc_project.EntityManager");
James Feistfd1264a2018-05-03 12:10:00 -07001496
James Feist8f2710a2018-05-09 17:18:55 -07001497 std::shared_ptr<sdbusplus::asio::dbus_interface> inventoryIface =
1498 objServer.add_interface("/xyz/openbmc_project/inventory",
1499 "xyz.openbmc_project.Inventory.Manager");
James Feist4131aea2018-03-09 09:47:30 -08001500
1501 // to keep reference to the match / filter objects so they don't get
1502 // destroyed
James Feist8f2710a2018-05-09 17:18:55 -07001503 std::vector<sdbusplus::bus::match::match> dbusMatches;
1504
1505 nlohmann::json systemConfiguration = nlohmann::json::object();
1506
1507 inventoryIface->register_method(
1508 "Notify", [](const boost::container::flat_map<
1509 std::string,
1510 boost::container::flat_map<std::string, BasicVariantType>>
1511 &object) { return; });
1512 inventoryIface->initialize();
1513
1514 io.post([&]() {
1515 unloadAllOverlays();
1516 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1517 objServer);
1518 });
James Feist4131aea2018-03-09 09:47:30 -08001519
James Feistfd1264a2018-05-03 12:10:00 -07001520 entityIface->register_method("ReScan", [&]() {
James Feist8f2710a2018-05-09 17:18:55 -07001521 propertiesChangedCallback(io, dbusMatches, systemConfiguration,
1522 objServer);
James Feist75fdeeb2018-02-20 14:26:16 -08001523 });
James Feist8f2710a2018-05-09 17:18:55 -07001524 entityIface->initialize();
1525
James Feist1b2e2242018-01-30 13:45:19 -08001526 io.run();
James Feist3cb5fec2018-01-23 14:41:51 -08001527
1528 return 0;
James Feist75fdeeb2018-02-20 14:26:16 -08001529}