blob: 612e495cb744b8c7935b6f6dabd1ff149f8947b3 [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
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -070036#include <charconv>
James Feist8c505da2020-05-28 10:06:33 -070037#include <filesystem>
38#include <fstream>
Andrew Jefferya9c58922021-06-01 09:28:59 +093039#include <map>
James Feist8c505da2020-05-28 10:06:33 -070040#include <regex>
41
James Feist481c5d52019-08-13 14:40:40 -070042constexpr const char* templateChar = "$";
43
Ed Tanous072e25d2018-12-16 21:45:20 -080044namespace fs = std::filesystem;
James Feist1df06a42019-04-11 14:23:04 -070045static bool powerStatusOn = false;
46static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist3cb5fec2018-01-23 14:41:51 -080047
James Feista465ccc2019-02-08 12:51:01 -080048bool findFiles(const fs::path& dirPath, const std::string& matchString,
49 std::vector<fs::path>& foundPaths)
James Feist3cb5fec2018-01-23 14:41:51 -080050{
James Feista3c180a2018-08-09 16:06:04 -070051 if (!fs::exists(dirPath))
Ed Tanous07d467b2021-02-23 14:48:37 -080052 {
James Feist3cb5fec2018-01-23 14:41:51 -080053 return false;
Ed Tanous07d467b2021-02-23 14:48:37 -080054 }
James Feist3cb5fec2018-01-23 14:41:51 -080055
James Feista3c180a2018-08-09 16:06:04 -070056 std::regex search(matchString);
James Feist3cb5fec2018-01-23 14:41:51 -080057 std::smatch match;
James Feista465ccc2019-02-08 12:51:01 -080058 for (const auto& p : fs::directory_iterator(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080059 {
60 std::string path = p.path().string();
James Feistc95cb142018-02-26 10:41:42 -080061 if (std::regex_search(path, match, search))
James Feist3cb5fec2018-01-23 14:41:51 -080062 {
James Feista3c180a2018-08-09 16:06:04 -070063 foundPaths.emplace_back(p.path());
James Feist3cb5fec2018-01-23 14:41:51 -080064 }
65 }
66 return true;
James Feistb4383f42018-08-06 16:54:10 -070067}
68
Andrew Jefferya9c58922021-06-01 09:28:59 +093069bool findFiles(const std::vector<fs::path>&& dirPaths,
70 const std::string& matchString,
71 std::vector<fs::path>& foundPaths)
72{
73 std::map<fs::path, fs::path> paths;
74 std::regex search(matchString);
75 std::smatch match;
76 for (const auto& dirPath : dirPaths)
77 {
78 if (!fs::exists(dirPath))
Bruce Mitchella6d47332021-12-13 10:18:03 -060079 {
Andrew Jefferya9c58922021-06-01 09:28:59 +093080 continue;
Bruce Mitchella6d47332021-12-13 10:18:03 -060081 }
Andrew Jefferya9c58922021-06-01 09:28:59 +093082
83 for (const auto& p : fs::directory_iterator(dirPath))
84 {
85 std::string path = p.path().string();
86 if (std::regex_search(path, match, search))
87 {
88 paths[p.path().filename()] = p.path();
89 }
90 }
91 }
92
93 for (const auto& [key, value] : paths)
94 {
95 foundPaths.emplace_back(value);
96 }
97
98 return !foundPaths.empty();
99}
100
Nikhil Potaded8884f12019-03-27 13:27:13 -0700101bool getI2cDevicePaths(const fs::path& dirPath,
102 boost::container::flat_map<size_t, fs::path>& busPaths)
103{
104 if (!fs::exists(dirPath))
105 {
106 return false;
107 }
108
109 // Regex for matching the path
110 std::regex searchPath(std::string(R"(i2c-\d+$)"));
111 // Regex for matching the bus numbers
112 std::regex searchBus(std::string(R"(\w[^-]*$)"));
113 std::smatch matchPath;
114 std::smatch matchBus;
115 for (const auto& p : fs::directory_iterator(dirPath))
116 {
117 std::string path = p.path().string();
118 if (std::regex_search(path, matchPath, searchPath))
119 {
120 if (std::regex_search(path, matchBus, searchBus))
121 {
122 size_t bus = stoul(*matchBus.begin());
123 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
124 }
125 }
126 }
127
128 return true;
129}
130
James Feista465ccc2019-02-08 12:51:01 -0800131bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
James Feistb4383f42018-08-06 16:54:10 -0700132{
133 valijson::Schema schema;
134 valijson::SchemaParser parser;
135 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
136 parser.populateSchema(schemaAdapter, schema);
137 valijson::Validator validator;
138 valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
Ed Tanous07d467b2021-02-23 14:48:37 -0800139 if (!validator.validate(schema, targetAdapter, nullptr))
James Feistb4383f42018-08-06 16:54:10 -0700140 {
141 return false;
142 }
143 return true;
Ed Tanous072e25d2018-12-16 21:45:20 -0800144}
James Feist1df06a42019-04-11 14:23:04 -0700145
146bool isPowerOn(void)
147{
148 if (!powerMatch)
149 {
150 throw std::runtime_error("Power Match Not Created");
151 }
152 return powerStatusOn;
153}
154
155void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
156{
James Feist1df06a42019-04-11 14:23:04 -0700157 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
158 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist61f5ac42020-03-11 14:37:25 -0700159 "type='signal',interface='" + std::string(properties::interface) +
160 "',path='" + std::string(power::path) + "',arg0='" +
161 std::string(power::interface) + "'",
162 [](sdbusplus::message::message& message) {
163 std::string objectName;
164 boost::container::flat_map<std::string, std::variant<std::string>>
165 values;
166 message.read(objectName, values);
167 auto findState = values.find(power::property);
168 if (findState != values.end())
169 {
170 powerStatusOn = boost::ends_with(
171 std::get<std::string>(findState->second), "Running");
172 }
173 });
174
175 conn->async_method_call(
176 [](boost::system::error_code ec,
177 const std::variant<std::string>& state) {
178 if (ec)
179 {
180 return;
181 }
182 powerStatusOn =
183 boost::ends_with(std::get<std::string>(state), "Running");
184 },
185 power::busname, power::path, properties::interface, properties::get,
186 power::interface, power::property);
James Feist481c5d52019-08-13 14:40:40 -0700187}
188
Matt Spinlere789bf12021-02-24 12:33:01 -0600189// Replaces the template character like the other version of this function,
190// but checks all properties on all interfaces provided to do the substitution
191// with.
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930192std::optional<std::string>
193 templateCharReplace(nlohmann::json::iterator& keyPair,
194 const DBusObject& object, const size_t index,
195 const std::optional<std::string>& replaceStr)
Matt Spinlere789bf12021-02-24 12:33:01 -0600196{
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930197 for (const auto& [_, interface] : object)
Matt Spinlere789bf12021-02-24 12:33:01 -0600198 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930199 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
Matt Spinlere789bf12021-02-24 12:33:01 -0600200 if (ret)
201 {
202 return ret;
203 }
204 }
205 return std::nullopt;
206}
207
James Feist481c5d52019-08-13 14:40:40 -0700208// finds the template character (currently set to $) and replaces the value with
209// the field found in a dbus object i.e. $ADDRESS would get populated with the
210// ADDRESS field from a object on dbus
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930211std::optional<std::string>
212 templateCharReplace(nlohmann::json::iterator& keyPair,
213 const DBusInterface& interface, const size_t index,
214 const std::optional<std::string>& replaceStr)
James Feist481c5d52019-08-13 14:40:40 -0700215{
James Feist35f5e0e2020-03-16 14:02:27 -0700216 std::optional<std::string> ret = std::nullopt;
217
James Feist481c5d52019-08-13 14:40:40 -0700218 if (keyPair.value().type() == nlohmann::json::value_t::object ||
219 keyPair.value().type() == nlohmann::json::value_t::array)
220 {
221 for (auto nextLayer = keyPair.value().begin();
222 nextLayer != keyPair.value().end(); nextLayer++)
223 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930224 templateCharReplace(nextLayer, interface, index, replaceStr);
James Feist481c5d52019-08-13 14:40:40 -0700225 }
James Feist35f5e0e2020-03-16 14:02:27 -0700226 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700227 }
228
229 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
230 if (strPtr == nullptr)
231 {
James Feist35f5e0e2020-03-16 14:02:27 -0700232 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700233 }
234
235 boost::replace_all(*strPtr, std::string(templateChar) + "index",
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930236 std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700237 if (replaceStr)
238 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930239 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700240 }
James Feist481c5d52019-08-13 14:40:40 -0700241
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930242 for (auto& [propName, propValue] : interface)
James Feist481c5d52019-08-13 14:40:40 -0700243 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930244 std::string templateName = templateChar + propName;
James Feist481c5d52019-08-13 14:40:40 -0700245 boost::iterator_range<std::string::const_iterator> find =
246 boost::ifind_first(*strPtr, templateName);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930247 if (!find)
James Feist481c5d52019-08-13 14:40:40 -0700248 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930249 continue;
250 }
James Feist8c20feb2019-08-14 15:10:11 -0700251
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930252 size_t start = find.begin() - strPtr->begin();
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930253
254 // check for additional operations
255 if (!start && find.end() == strPtr->end())
256 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930257 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930258 return ret;
259 }
Andrew Jeffery7d05afc2022-04-07 15:12:10 +0930260
261 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
262 '/'};
263 size_t nextItemIdx = start + templateName.size() + 1;
264
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930265 if (nextItemIdx > strPtr->size() ||
266 std::find(mathChars.begin(), mathChars.end(),
267 strPtr->at(nextItemIdx)) == mathChars.end())
268 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930269 std::string val = std::visit(VariantToStringVisitor(), propValue);
Andrew Jeffery8d8b62f2022-04-07 12:32:13 +0930270 boost::ireplace_all(*strPtr, templateName, val);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930271 continue;
272 }
273
274 // save the prefix
275 std::string prefix = strPtr->substr(0, start);
276
277 // operate on the rest
278 std::string end = strPtr->substr(nextItemIdx);
279
280 std::vector<std::string> split;
281 boost::split(split, end, boost::is_any_of(" "));
282
283 // need at least 1 operation and number
284 if (split.size() < 2)
285 {
286 std::cerr << "Syntax error on template replacement of " << *strPtr
287 << "\n";
288 for (const std::string& data : split)
James Feist481c5d52019-08-13 14:40:40 -0700289 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930290 std::cerr << data << " ";
James Feist481c5d52019-08-13 14:40:40 -0700291 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930292 std::cerr << "\n";
293 continue;
294 }
295
296 // we assume that the replacement is a number, because we can
297 // only do math on numbers.. we might concatenate strings in the
298 // future, but thats later
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930299 int number = std::visit(VariantToIntVisitor(), propValue);
Andrew Jeffery95559612022-04-07 17:22:47 +0930300 auto exprBegin = split.begin();
301 auto exprEnd = split.end();
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930302
Andrew Jeffery95559612022-04-07 17:22:47 +0930303 number = expression::evaluate(number, exprBegin, exprEnd);
James Feist481c5d52019-08-13 14:40:40 -0700304
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930305 std::string replaced(find.begin(), find.end());
Andrew Jeffery95559612022-04-07 17:22:47 +0930306 while (exprBegin != exprEnd)
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930307 {
Andrew Jefferyefcbf002022-04-07 17:29:22 +0930308 replaced.append(" ").append(*exprBegin++);
James Feist481c5d52019-08-13 14:40:40 -0700309 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930310 ret = replaced;
311
Andrew Jefferyf1102ff2022-04-07 17:30:46 +0930312 std::string result = prefix + std::to_string(number);
Andrew Jefferydeb97f12022-04-07 17:27:46 +0930313 while (exprEnd != split.end())
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930314 {
Andrew Jefferyefcbf002022-04-07 17:29:22 +0930315 result.append(" ").append(*exprEnd++);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930316 }
317 keyPair.value() = result;
318
319 // We probably just invalidated the pointer abovei,
320 // reset and continue to handle multiple templates
321 strPtr = keyPair.value().get_ptr<std::string*>();
322 if (strPtr == nullptr)
323 {
324 break;
325 }
James Feist481c5d52019-08-13 14:40:40 -0700326 }
327
328 strPtr = keyPair.value().get_ptr<std::string*>();
329 if (strPtr == nullptr)
330 {
James Feist35f5e0e2020-03-16 14:02:27 -0700331 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700332 }
333
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700334 std::string_view strView = *strPtr;
335 int base = 10;
336 if (boost::starts_with(strView, "0x"))
James Feist481c5d52019-08-13 14:40:40 -0700337 {
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700338 strView.remove_prefix(2);
339 base = 16;
James Feist481c5d52019-08-13 14:40:40 -0700340 }
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700341
342 uint64_t temp = 0;
343 const char* strDataEndPtr = strView.data() + strView.size();
344 const std::from_chars_result res =
345 std::from_chars(strView.data(), strDataEndPtr, temp, base);
346 if (res.ec == std::errc{} && res.ptr == strDataEndPtr)
347 {
348 keyPair.value() = temp;
349 }
Andrew Jeffery159588b2022-04-07 17:04:13 +0930350
James Feist35f5e0e2020-03-16 14:02:27 -0700351 return ret;
Xiang Liu2801a702020-01-20 14:29:34 -0800352}
Brad Bishop3cb8a602020-08-25 17:40:54 -0400353
Brad Bishop5d525412020-08-26 08:50:50 -0400354/// \brief JSON/DBus matching Callable for std::variant (visitor)
355///
356/// Default match JSON/DBus match implementation
Andrew Jefferyeab49292022-04-05 14:42:20 +0930357/// \tparam T The concrete DBus value type from DBusValueVariant
Brad Bishop5d525412020-08-26 08:50:50 -0400358template <typename T>
359struct MatchProbe
360{
361 /// \param probe the probe statement to match against
362 /// \param value the property value being matched to a probe
363 /// \return true if the dbusValue matched the probe otherwise false
364 static bool match(const nlohmann::json& probe, const T& value)
365 {
366 return probe == value;
367 }
368};
369
370/// \brief JSON/DBus matching Callable for std::variant (visitor)
371///
372/// std::string specialization of MatchProbe enabling regex matching
373template <>
374struct MatchProbe<std::string>
375{
376 /// \param probe the probe statement to match against
377 /// \param value the string value being matched to a probe
378 /// \return true if the dbusValue matched the probe otherwise false
379 static bool match(const nlohmann::json& probe, const std::string& value)
380 {
381 if (probe.is_string())
382 {
383 try
384 {
385 std::regex search(probe);
386 std::smatch regMatch;
387 return std::regex_search(value, regMatch, search);
388 }
389 catch (const std::regex_error&)
390 {
391 std::cerr << "Syntax error in regular expression: " << probe
392 << " will never match";
393 }
394 }
395
396 // Skip calling nlohmann here, since it will never match a non-string
397 // to a std::string
398 return false;
399 }
400};
401
402/// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor)
403///
404/// Forward calls to the correct template instantiation of MatchProbe
405struct MatchProbeForwarder
406{
407 explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe)
408 {}
409 const nlohmann::json& probeRef;
410
411 template <typename T>
412 bool operator()(const T& dbusValue) const
413 {
414 return MatchProbe<T>::match(probeRef, dbusValue);
415 }
416};
417
Andrew Jefferyeab49292022-04-05 14:42:20 +0930418bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue)
Brad Bishop3cb8a602020-08-25 17:40:54 -0400419{
Brad Bishop5d525412020-08-26 08:50:50 -0400420 return std::visit(MatchProbeForwarder(probe), dbusValue);
421}