blob: 04f8f256e4559b6f3c6553068fb92e1193218cd9 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2017 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*/
Brad Bishop1fb9f3f2020-08-28 08:15:13 -040016/// \file Utils.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
James Feist481c5d52019-08-13 14:40:40 -070018#include "Utils.hpp"
19
Andrew Jefferya05437e2022-04-07 16:17:21 +093020#include "Expression.hpp"
James Feist481c5d52019-08-13 14:40:40 -070021#include "VariantVisitors.hpp"
22
23#include <boost/algorithm/string/classification.hpp>
24#include <boost/algorithm/string/find.hpp>
25#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
27#include <boost/algorithm/string/split.hpp>
28#include <boost/container/flat_map.hpp>
29#include <boost/lexical_cast.hpp>
James Feist1df06a42019-04-11 14:23:04 -070030#include <sdbusplus/bus/match.hpp>
James Feistb4383f42018-08-06 16:54:10 -070031#include <valijson/adapters/nlohmann_json_adapter.hpp>
32#include <valijson/schema.hpp>
33#include <valijson/schema_parser.hpp>
34#include <valijson/validator.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080035
James Feist8c505da2020-05-28 10:06:33 -070036#include <filesystem>
37#include <fstream>
Andrew Jefferya9c58922021-06-01 09:28:59 +093038#include <map>
James Feist8c505da2020-05-28 10:06:33 -070039#include <regex>
40
James Feist481c5d52019-08-13 14:40:40 -070041constexpr const char* templateChar = "$";
42
Ed Tanous072e25d2018-12-16 21:45:20 -080043namespace fs = std::filesystem;
James Feist1df06a42019-04-11 14:23:04 -070044static bool powerStatusOn = false;
45static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist3cb5fec2018-01-23 14:41:51 -080046
James Feista465ccc2019-02-08 12:51:01 -080047bool findFiles(const fs::path& dirPath, const std::string& matchString,
48 std::vector<fs::path>& foundPaths)
James Feist3cb5fec2018-01-23 14:41:51 -080049{
James Feista3c180a2018-08-09 16:06:04 -070050 if (!fs::exists(dirPath))
Ed Tanous07d467b2021-02-23 14:48:37 -080051 {
James Feist3cb5fec2018-01-23 14:41:51 -080052 return false;
Ed Tanous07d467b2021-02-23 14:48:37 -080053 }
James Feist3cb5fec2018-01-23 14:41:51 -080054
James Feista3c180a2018-08-09 16:06:04 -070055 std::regex search(matchString);
James Feist3cb5fec2018-01-23 14:41:51 -080056 std::smatch match;
James Feista465ccc2019-02-08 12:51:01 -080057 for (const auto& p : fs::directory_iterator(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080058 {
59 std::string path = p.path().string();
James Feistc95cb142018-02-26 10:41:42 -080060 if (std::regex_search(path, match, search))
James Feist3cb5fec2018-01-23 14:41:51 -080061 {
James Feista3c180a2018-08-09 16:06:04 -070062 foundPaths.emplace_back(p.path());
James Feist3cb5fec2018-01-23 14:41:51 -080063 }
64 }
65 return true;
James Feistb4383f42018-08-06 16:54:10 -070066}
67
Andrew Jefferya9c58922021-06-01 09:28:59 +093068bool findFiles(const std::vector<fs::path>&& dirPaths,
69 const std::string& matchString,
70 std::vector<fs::path>& foundPaths)
71{
72 std::map<fs::path, fs::path> paths;
73 std::regex search(matchString);
74 std::smatch match;
75 for (const auto& dirPath : dirPaths)
76 {
77 if (!fs::exists(dirPath))
Bruce Mitchella6d47332021-12-13 10:18:03 -060078 {
Andrew Jefferya9c58922021-06-01 09:28:59 +093079 continue;
Bruce Mitchella6d47332021-12-13 10:18:03 -060080 }
Andrew Jefferya9c58922021-06-01 09:28:59 +093081
82 for (const auto& p : fs::directory_iterator(dirPath))
83 {
84 std::string path = p.path().string();
85 if (std::regex_search(path, match, search))
86 {
87 paths[p.path().filename()] = p.path();
88 }
89 }
90 }
91
92 for (const auto& [key, value] : paths)
93 {
94 foundPaths.emplace_back(value);
95 }
96
97 return !foundPaths.empty();
98}
99
Nikhil Potaded8884f12019-03-27 13:27:13 -0700100bool getI2cDevicePaths(const fs::path& dirPath,
101 boost::container::flat_map<size_t, fs::path>& busPaths)
102{
103 if (!fs::exists(dirPath))
104 {
105 return false;
106 }
107
108 // Regex for matching the path
109 std::regex searchPath(std::string(R"(i2c-\d+$)"));
110 // Regex for matching the bus numbers
111 std::regex searchBus(std::string(R"(\w[^-]*$)"));
112 std::smatch matchPath;
113 std::smatch matchBus;
114 for (const auto& p : fs::directory_iterator(dirPath))
115 {
116 std::string path = p.path().string();
117 if (std::regex_search(path, matchPath, searchPath))
118 {
119 if (std::regex_search(path, matchBus, searchBus))
120 {
121 size_t bus = stoul(*matchBus.begin());
122 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
123 }
124 }
125 }
126
127 return true;
128}
129
James Feista465ccc2019-02-08 12:51:01 -0800130bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
James Feistb4383f42018-08-06 16:54:10 -0700131{
132 valijson::Schema schema;
133 valijson::SchemaParser parser;
134 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
135 parser.populateSchema(schemaAdapter, schema);
136 valijson::Validator validator;
137 valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
Ed Tanous07d467b2021-02-23 14:48:37 -0800138 if (!validator.validate(schema, targetAdapter, nullptr))
James Feistb4383f42018-08-06 16:54:10 -0700139 {
140 return false;
141 }
142 return true;
Ed Tanous072e25d2018-12-16 21:45:20 -0800143}
James Feist1df06a42019-04-11 14:23:04 -0700144
145bool isPowerOn(void)
146{
147 if (!powerMatch)
148 {
149 throw std::runtime_error("Power Match Not Created");
150 }
151 return powerStatusOn;
152}
153
154void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
155{
James Feist1df06a42019-04-11 14:23:04 -0700156 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
157 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist61f5ac42020-03-11 14:37:25 -0700158 "type='signal',interface='" + std::string(properties::interface) +
159 "',path='" + std::string(power::path) + "',arg0='" +
160 std::string(power::interface) + "'",
161 [](sdbusplus::message::message& message) {
162 std::string objectName;
163 boost::container::flat_map<std::string, std::variant<std::string>>
164 values;
165 message.read(objectName, values);
166 auto findState = values.find(power::property);
167 if (findState != values.end())
168 {
169 powerStatusOn = boost::ends_with(
170 std::get<std::string>(findState->second), "Running");
171 }
172 });
173
174 conn->async_method_call(
175 [](boost::system::error_code ec,
176 const std::variant<std::string>& state) {
177 if (ec)
178 {
179 return;
180 }
181 powerStatusOn =
182 boost::ends_with(std::get<std::string>(state), "Running");
183 },
184 power::busname, power::path, properties::interface, properties::get,
185 power::interface, power::property);
James Feist481c5d52019-08-13 14:40:40 -0700186}
187
Matt Spinlere789bf12021-02-24 12:33:01 -0600188// Replaces the template character like the other version of this function,
189// but checks all properties on all interfaces provided to do the substitution
190// with.
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930191std::optional<std::string>
192 templateCharReplace(nlohmann::json::iterator& keyPair,
193 const DBusObject& object, const size_t index,
194 const std::optional<std::string>& replaceStr)
Matt Spinlere789bf12021-02-24 12:33:01 -0600195{
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930196 for (const auto& [_, interface] : object)
Matt Spinlere789bf12021-02-24 12:33:01 -0600197 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930198 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
Matt Spinlere789bf12021-02-24 12:33:01 -0600199 if (ret)
200 {
201 return ret;
202 }
203 }
204 return std::nullopt;
205}
206
James Feist481c5d52019-08-13 14:40:40 -0700207// finds the template character (currently set to $) and replaces the value with
208// the field found in a dbus object i.e. $ADDRESS would get populated with the
209// ADDRESS field from a object on dbus
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930210std::optional<std::string>
211 templateCharReplace(nlohmann::json::iterator& keyPair,
212 const DBusInterface& interface, const size_t index,
213 const std::optional<std::string>& replaceStr)
James Feist481c5d52019-08-13 14:40:40 -0700214{
James Feist35f5e0e2020-03-16 14:02:27 -0700215 std::optional<std::string> ret = std::nullopt;
216
James Feist481c5d52019-08-13 14:40:40 -0700217 if (keyPair.value().type() == nlohmann::json::value_t::object ||
218 keyPair.value().type() == nlohmann::json::value_t::array)
219 {
220 for (auto nextLayer = keyPair.value().begin();
221 nextLayer != keyPair.value().end(); nextLayer++)
222 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930223 templateCharReplace(nextLayer, interface, index, replaceStr);
James Feist481c5d52019-08-13 14:40:40 -0700224 }
James Feist35f5e0e2020-03-16 14:02:27 -0700225 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700226 }
227
228 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
229 if (strPtr == nullptr)
230 {
James Feist35f5e0e2020-03-16 14:02:27 -0700231 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700232 }
233
234 boost::replace_all(*strPtr, std::string(templateChar) + "index",
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930235 std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700236 if (replaceStr)
237 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930238 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700239 }
James Feist481c5d52019-08-13 14:40:40 -0700240
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930241 for (auto& [propName, propValue] : interface)
James Feist481c5d52019-08-13 14:40:40 -0700242 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930243 std::string templateName = templateChar + propName;
James Feist481c5d52019-08-13 14:40:40 -0700244 boost::iterator_range<std::string::const_iterator> find =
245 boost::ifind_first(*strPtr, templateName);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930246 if (!find)
James Feist481c5d52019-08-13 14:40:40 -0700247 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930248 continue;
249 }
James Feist8c20feb2019-08-14 15:10:11 -0700250
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930251 size_t start = find.begin() - strPtr->begin();
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930252
253 // check for additional operations
254 if (!start && find.end() == strPtr->end())
255 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930256 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930257 return ret;
258 }
Andrew Jeffery7d05afc2022-04-07 15:12:10 +0930259
260 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
261 '/'};
262 size_t nextItemIdx = start + templateName.size() + 1;
263
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930264 if (nextItemIdx > strPtr->size() ||
265 std::find(mathChars.begin(), mathChars.end(),
266 strPtr->at(nextItemIdx)) == mathChars.end())
267 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930268 std::string val = std::visit(VariantToStringVisitor(), propValue);
Andrew Jeffery8d8b62f2022-04-07 12:32:13 +0930269 boost::ireplace_all(*strPtr, templateName, val);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930270 continue;
271 }
272
273 // save the prefix
274 std::string prefix = strPtr->substr(0, start);
275
276 // operate on the rest
277 std::string end = strPtr->substr(nextItemIdx);
278
279 std::vector<std::string> split;
280 boost::split(split, end, boost::is_any_of(" "));
281
282 // need at least 1 operation and number
283 if (split.size() < 2)
284 {
285 std::cerr << "Syntax error on template replacement of " << *strPtr
286 << "\n";
287 for (const std::string& data : split)
James Feist481c5d52019-08-13 14:40:40 -0700288 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930289 std::cerr << data << " ";
James Feist481c5d52019-08-13 14:40:40 -0700290 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930291 std::cerr << "\n";
292 continue;
293 }
294
Andrew Jeffery5ace4e42022-04-07 16:58:30 +0930295 auto it = split.begin();
296
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930297 // we assume that the replacement is a number, because we can
298 // only do math on numbers.. we might concatenate strings in the
299 // future, but thats later
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930300 int number = std::visit(VariantToIntVisitor(), propValue);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930301
Andrew Jeffery5ace4e42022-04-07 16:58:30 +0930302 number = expression::evaluate(number, it, split.end());
James Feist481c5d52019-08-13 14:40:40 -0700303
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930304 std::string result = prefix + std::to_string(number);
305
306 std::string replaced(find.begin(), find.end());
307 for (auto it2 = split.begin(); it2 != split.end(); it2++)
308 {
309 replaced += " ";
310 replaced += *it2;
311 if (it2 == it)
Zhikui Rena0d1b3f2021-10-05 16:21:56 -0700312 {
313 break;
314 }
James Feist481c5d52019-08-13 14:40:40 -0700315 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930316 ret = replaced;
317
318 if (it != split.end())
319 {
320 for (; it != split.end(); it++)
321 {
322 result += " " + *it;
323 }
324 }
325 keyPair.value() = result;
326
327 // We probably just invalidated the pointer abovei,
328 // reset and continue to handle multiple templates
329 strPtr = keyPair.value().get_ptr<std::string*>();
330 if (strPtr == nullptr)
331 {
332 break;
333 }
James Feist481c5d52019-08-13 14:40:40 -0700334 }
335
336 strPtr = keyPair.value().get_ptr<std::string*>();
337 if (strPtr == nullptr)
338 {
James Feist35f5e0e2020-03-16 14:02:27 -0700339 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700340 }
341
Andrew Jeffery159588b2022-04-07 17:04:13 +0930342 try
James Feist481c5d52019-08-13 14:40:40 -0700343 {
Andrew Jeffery159588b2022-04-07 17:04:13 +0930344 size_t pos = 0;
345 int64_t temp = std::stoul(*strPtr, &pos, 0);
346 if (pos == strPtr->size())
James Feist481c5d52019-08-13 14:40:40 -0700347 {
Andrew Jeffery159588b2022-04-07 17:04:13 +0930348 keyPair.value() = static_cast<uint64_t>(temp);
James Feist481c5d52019-08-13 14:40:40 -0700349 }
James Feist481c5d52019-08-13 14:40:40 -0700350 }
Andrew Jeffery159588b2022-04-07 17:04:13 +0930351 catch (const std::invalid_argument&)
352 {}
353 catch (const std::out_of_range&)
354 {}
355
James Feist35f5e0e2020-03-16 14:02:27 -0700356 return ret;
Xiang Liu2801a702020-01-20 14:29:34 -0800357}
Brad Bishop3cb8a602020-08-25 17:40:54 -0400358
Brad Bishop5d525412020-08-26 08:50:50 -0400359/// \brief JSON/DBus matching Callable for std::variant (visitor)
360///
361/// Default match JSON/DBus match implementation
Andrew Jefferyeab49292022-04-05 14:42:20 +0930362/// \tparam T The concrete DBus value type from DBusValueVariant
Brad Bishop5d525412020-08-26 08:50:50 -0400363template <typename T>
364struct MatchProbe
365{
366 /// \param probe the probe statement to match against
367 /// \param value the property value being matched to a probe
368 /// \return true if the dbusValue matched the probe otherwise false
369 static bool match(const nlohmann::json& probe, const T& value)
370 {
371 return probe == value;
372 }
373};
374
375/// \brief JSON/DBus matching Callable for std::variant (visitor)
376///
377/// std::string specialization of MatchProbe enabling regex matching
378template <>
379struct MatchProbe<std::string>
380{
381 /// \param probe the probe statement to match against
382 /// \param value the string value being matched to a probe
383 /// \return true if the dbusValue matched the probe otherwise false
384 static bool match(const nlohmann::json& probe, const std::string& value)
385 {
386 if (probe.is_string())
387 {
388 try
389 {
390 std::regex search(probe);
391 std::smatch regMatch;
392 return std::regex_search(value, regMatch, search);
393 }
394 catch (const std::regex_error&)
395 {
396 std::cerr << "Syntax error in regular expression: " << probe
397 << " will never match";
398 }
399 }
400
401 // Skip calling nlohmann here, since it will never match a non-string
402 // to a std::string
403 return false;
404 }
405};
406
407/// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor)
408///
409/// Forward calls to the correct template instantiation of MatchProbe
410struct MatchProbeForwarder
411{
412 explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe)
413 {}
414 const nlohmann::json& probeRef;
415
416 template <typename T>
417 bool operator()(const T& dbusValue) const
418 {
419 return MatchProbe<T>::match(probeRef, dbusValue);
420 }
421};
422
Andrew Jefferyeab49292022-04-05 14:42:20 +0930423bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue)
Brad Bishop3cb8a602020-08-25 17:40:54 -0400424{
Brad Bishop5d525412020-08-26 08:50:50 -0400425 return std::visit(MatchProbeForwarder(probe), dbusValue);
426}