blob: fd0101649698857d08bd6efe93858647e677b896 [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 Bishope45d8c72022-05-25 15:12:53 -040016/// \file utils.cpp
James Feist3cb5fec2018-01-23 14:41:51 -080017
Brad Bishope45d8c72022-05-25 15:12:53 -040018#include "utils.hpp"
James Feist481c5d52019-08-13 14:40:40 -070019
Brad Bishope45d8c72022-05-25 15:12:53 -040020#include "expression.hpp"
21#include "variant_visitors.hpp"
James Feist481c5d52019-08-13 14:40:40 -070022
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 Feist3cb5fec2018-01-23 14:41:51 -080031
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -070032#include <charconv>
James Feist8c505da2020-05-28 10:06:33 -070033#include <filesystem>
34#include <fstream>
Andrew Jefferya9c58922021-06-01 09:28:59 +093035#include <map>
James Feist8c505da2020-05-28 10:06:33 -070036#include <regex>
37
James Feist481c5d52019-08-13 14:40:40 -070038constexpr const char* templateChar = "$";
39
Ed Tanous072e25d2018-12-16 21:45:20 -080040namespace fs = std::filesystem;
Ed Tanousfc171422024-04-04 17:18:16 -070041// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
James Feist1df06a42019-04-11 14:23:04 -070042static bool powerStatusOn = false;
Ed Tanousfc171422024-04-04 17:18:16 -070043// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Patrick Williams2af39222022-07-22 19:26:56 -050044static std::unique_ptr<sdbusplus::bus::match_t> powerMatch = nullptr;
James Feist3cb5fec2018-01-23 14:41:51 -080045
James Feista465ccc2019-02-08 12:51:01 -080046bool findFiles(const fs::path& dirPath, const std::string& matchString,
47 std::vector<fs::path>& foundPaths)
James Feist3cb5fec2018-01-23 14:41:51 -080048{
James Feista3c180a2018-08-09 16:06:04 -070049 if (!fs::exists(dirPath))
Ed Tanous07d467b2021-02-23 14:48:37 -080050 {
James Feist3cb5fec2018-01-23 14:41:51 -080051 return false;
Ed Tanous07d467b2021-02-23 14:48:37 -080052 }
James Feist3cb5fec2018-01-23 14:41:51 -080053
James Feista3c180a2018-08-09 16:06:04 -070054 std::regex search(matchString);
James Feist3cb5fec2018-01-23 14:41:51 -080055 std::smatch match;
James Feista465ccc2019-02-08 12:51:01 -080056 for (const auto& p : fs::directory_iterator(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080057 {
58 std::string path = p.path().string();
James Feistc95cb142018-02-26 10:41:42 -080059 if (std::regex_search(path, match, search))
James Feist3cb5fec2018-01-23 14:41:51 -080060 {
James Feista3c180a2018-08-09 16:06:04 -070061 foundPaths.emplace_back(p.path());
James Feist3cb5fec2018-01-23 14:41:51 -080062 }
63 }
64 return true;
James Feistb4383f42018-08-06 16:54:10 -070065}
66
Andrew Jefferya9c58922021-06-01 09:28:59 +093067bool findFiles(const std::vector<fs::path>&& dirPaths,
68 const std::string& matchString,
69 std::vector<fs::path>& foundPaths)
70{
71 std::map<fs::path, fs::path> paths;
72 std::regex search(matchString);
73 std::smatch match;
74 for (const auto& dirPath : dirPaths)
75 {
76 if (!fs::exists(dirPath))
Bruce Mitchella6d47332021-12-13 10:18:03 -060077 {
Andrew Jefferya9c58922021-06-01 09:28:59 +093078 continue;
Bruce Mitchella6d47332021-12-13 10:18:03 -060079 }
Andrew Jefferya9c58922021-06-01 09:28:59 +093080
81 for (const auto& p : fs::directory_iterator(dirPath))
82 {
83 std::string path = p.path().string();
84 if (std::regex_search(path, match, search))
85 {
86 paths[p.path().filename()] = p.path();
87 }
88 }
89 }
90
91 for (const auto& [key, value] : paths)
92 {
93 foundPaths.emplace_back(value);
94 }
95
96 return !foundPaths.empty();
97}
98
Nikhil Potaded8884f12019-03-27 13:27:13 -070099bool getI2cDevicePaths(const fs::path& dirPath,
100 boost::container::flat_map<size_t, fs::path>& busPaths)
101{
102 if (!fs::exists(dirPath))
103 {
104 return false;
105 }
106
107 // Regex for matching the path
108 std::regex searchPath(std::string(R"(i2c-\d+$)"));
109 // Regex for matching the bus numbers
110 std::regex searchBus(std::string(R"(\w[^-]*$)"));
111 std::smatch matchPath;
112 std::smatch matchBus;
113 for (const auto& p : fs::directory_iterator(dirPath))
114 {
115 std::string path = p.path().string();
116 if (std::regex_search(path, matchPath, searchPath))
117 {
118 if (std::regex_search(path, matchBus, searchBus))
119 {
120 size_t bus = stoul(*matchBus.begin());
121 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
122 }
123 }
124 }
125
126 return true;
127}
128
Delphine CC Chiua3ca14a2024-03-27 17:02:24 +0800129bool isPowerOn()
James Feist1df06a42019-04-11 14:23:04 -0700130{
131 if (!powerMatch)
132 {
133 throw std::runtime_error("Power Match Not Created");
134 }
135 return powerStatusOn;
136}
137
138void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
139{
Patrick Williams2af39222022-07-22 19:26:56 -0500140 powerMatch = std::make_unique<sdbusplus::bus::match_t>(
141 static_cast<sdbusplus::bus_t&>(*conn),
James Feist61f5ac42020-03-11 14:37:25 -0700142 "type='signal',interface='" + std::string(properties::interface) +
143 "',path='" + std::string(power::path) + "',arg0='" +
144 std::string(power::interface) + "'",
Patrick Williams2af39222022-07-22 19:26:56 -0500145 [](sdbusplus::message_t& message) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400146 std::string objectName;
147 boost::container::flat_map<std::string, std::variant<std::string>>
148 values;
149 message.read(objectName, values);
150 auto findState = values.find(power::property);
151 if (findState != values.end())
152 {
153 powerStatusOn = boost::ends_with(
154 std::get<std::string>(findState->second), "Running");
155 }
156 });
James Feist61f5ac42020-03-11 14:37:25 -0700157
158 conn->async_method_call(
159 [](boost::system::error_code ec,
160 const std::variant<std::string>& state) {
Patrick Williamsb7077432024-08-16 15:22:21 -0400161 if (ec)
162 {
163 return;
164 }
165 powerStatusOn =
166 boost::ends_with(std::get<std::string>(state), "Running");
167 },
James Feist61f5ac42020-03-11 14:37:25 -0700168 power::busname, power::path, properties::interface, properties::get,
169 power::interface, power::property);
James Feist481c5d52019-08-13 14:40:40 -0700170}
171
Matt Spinlere789bf12021-02-24 12:33:01 -0600172// Replaces the template character like the other version of this function,
173// but checks all properties on all interfaces provided to do the substitution
174// with.
Patrick Williamsb7077432024-08-16 15:22:21 -0400175std::optional<std::string> templateCharReplace(
176 nlohmann::json::iterator& keyPair, const DBusObject& object,
177 const size_t index, const std::optional<std::string>& replaceStr)
Matt Spinlere789bf12021-02-24 12:33:01 -0600178{
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930179 for (const auto& [_, interface] : object)
Matt Spinlere789bf12021-02-24 12:33:01 -0600180 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930181 auto ret = templateCharReplace(keyPair, interface, index, replaceStr);
Matt Spinlere789bf12021-02-24 12:33:01 -0600182 if (ret)
183 {
184 return ret;
185 }
186 }
187 return std::nullopt;
188}
189
James Feist481c5d52019-08-13 14:40:40 -0700190// finds the template character (currently set to $) and replaces the value with
191// the field found in a dbus object i.e. $ADDRESS would get populated with the
192// ADDRESS field from a object on dbus
Patrick Williamsb7077432024-08-16 15:22:21 -0400193std::optional<std::string> templateCharReplace(
194 nlohmann::json::iterator& keyPair, const DBusInterface& interface,
195 const size_t index, const std::optional<std::string>& replaceStr)
James Feist481c5d52019-08-13 14:40:40 -0700196{
James Feist35f5e0e2020-03-16 14:02:27 -0700197 std::optional<std::string> ret = std::nullopt;
198
James Feist481c5d52019-08-13 14:40:40 -0700199 if (keyPair.value().type() == nlohmann::json::value_t::object ||
200 keyPair.value().type() == nlohmann::json::value_t::array)
201 {
202 for (auto nextLayer = keyPair.value().begin();
203 nextLayer != keyPair.value().end(); nextLayer++)
204 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930205 templateCharReplace(nextLayer, interface, index, replaceStr);
James Feist481c5d52019-08-13 14:40:40 -0700206 }
James Feist35f5e0e2020-03-16 14:02:27 -0700207 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700208 }
209
210 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
211 if (strPtr == nullptr)
212 {
James Feist35f5e0e2020-03-16 14:02:27 -0700213 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700214 }
215
216 boost::replace_all(*strPtr, std::string(templateChar) + "index",
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930217 std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700218 if (replaceStr)
219 {
Andrew Jeffery1983d2f2022-04-05 14:55:13 +0930220 boost::replace_all(*strPtr, *replaceStr, std::to_string(index));
James Feist35f5e0e2020-03-16 14:02:27 -0700221 }
James Feist481c5d52019-08-13 14:40:40 -0700222
Ed Tanous3013fb42022-07-09 08:27:06 -0700223 for (const auto& [propName, propValue] : interface)
James Feist481c5d52019-08-13 14:40:40 -0700224 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930225 std::string templateName = templateChar + propName;
James Feist481c5d52019-08-13 14:40:40 -0700226 boost::iterator_range<std::string::const_iterator> find =
227 boost::ifind_first(*strPtr, templateName);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930228 if (!find)
James Feist481c5d52019-08-13 14:40:40 -0700229 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930230 continue;
231 }
James Feist8c20feb2019-08-14 15:10:11 -0700232
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930233 size_t start = find.begin() - strPtr->begin();
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930234
235 // check for additional operations
Ed Tanous3013fb42022-07-09 08:27:06 -0700236 if ((start == 0U) && find.end() == strPtr->end())
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930237 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930238 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930239 return ret;
240 }
Andrew Jeffery7d05afc2022-04-07 15:12:10 +0930241
242 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
243 '/'};
244 size_t nextItemIdx = start + templateName.size() + 1;
245
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930246 if (nextItemIdx > strPtr->size() ||
247 std::find(mathChars.begin(), mathChars.end(),
248 strPtr->at(nextItemIdx)) == mathChars.end())
249 {
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930250 std::string val = std::visit(VariantToStringVisitor(), propValue);
Andrew Jeffery8d8b62f2022-04-07 12:32:13 +0930251 boost::ireplace_all(*strPtr, templateName, val);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930252 continue;
253 }
254
255 // save the prefix
256 std::string prefix = strPtr->substr(0, start);
257
258 // operate on the rest
259 std::string end = strPtr->substr(nextItemIdx);
260
261 std::vector<std::string> split;
262 boost::split(split, end, boost::is_any_of(" "));
263
264 // need at least 1 operation and number
265 if (split.size() < 2)
266 {
267 std::cerr << "Syntax error on template replacement of " << *strPtr
268 << "\n";
269 for (const std::string& data : split)
James Feist481c5d52019-08-13 14:40:40 -0700270 {
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930271 std::cerr << data << " ";
James Feist481c5d52019-08-13 14:40:40 -0700272 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930273 std::cerr << "\n";
274 continue;
275 }
276
277 // we assume that the replacement is a number, because we can
278 // only do math on numbers.. we might concatenate strings in the
279 // future, but thats later
Andrew Jefferyd9e3e192022-04-07 12:30:45 +0930280 int number = std::visit(VariantToIntVisitor(), propValue);
Andrew Jeffery95559612022-04-07 17:22:47 +0930281 auto exprBegin = split.begin();
282 auto exprEnd = split.end();
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930283
Andrew Jeffery95559612022-04-07 17:22:47 +0930284 number = expression::evaluate(number, exprBegin, exprEnd);
James Feist481c5d52019-08-13 14:40:40 -0700285
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930286 std::string replaced(find.begin(), find.end());
Andrew Jeffery95559612022-04-07 17:22:47 +0930287 while (exprBegin != exprEnd)
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930288 {
Andrew Jefferyefcbf002022-04-07 17:29:22 +0930289 replaced.append(" ").append(*exprBegin++);
James Feist481c5d52019-08-13 14:40:40 -0700290 }
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930291 ret = replaced;
292
Andrew Jefferyf1102ff2022-04-07 17:30:46 +0930293 std::string result = prefix + std::to_string(number);
Andrew Jefferydeb97f12022-04-07 17:27:46 +0930294 while (exprEnd != split.end())
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930295 {
Andrew Jefferyefcbf002022-04-07 17:29:22 +0930296 result.append(" ").append(*exprEnd++);
Andrew Jefferye0ed5872022-04-07 12:16:33 +0930297 }
298 keyPair.value() = result;
299
300 // We probably just invalidated the pointer abovei,
301 // reset and continue to handle multiple templates
302 strPtr = keyPair.value().get_ptr<std::string*>();
303 if (strPtr == nullptr)
304 {
305 break;
306 }
James Feist481c5d52019-08-13 14:40:40 -0700307 }
308
309 strPtr = keyPair.value().get_ptr<std::string*>();
310 if (strPtr == nullptr)
311 {
James Feist35f5e0e2020-03-16 14:02:27 -0700312 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700313 }
314
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700315 std::string_view strView = *strPtr;
316 int base = 10;
317 if (boost::starts_with(strView, "0x"))
James Feist481c5d52019-08-13 14:40:40 -0700318 {
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700319 strView.remove_prefix(2);
320 base = 16;
James Feist481c5d52019-08-13 14:40:40 -0700321 }
Jae Hyun Yoo1c9d3782022-03-30 15:10:14 -0700322
323 uint64_t temp = 0;
324 const char* strDataEndPtr = strView.data() + strView.size();
325 const std::from_chars_result res =
326 std::from_chars(strView.data(), strDataEndPtr, temp, base);
327 if (res.ec == std::errc{} && res.ptr == strDataEndPtr)
328 {
329 keyPair.value() = temp;
330 }
Andrew Jeffery159588b2022-04-07 17:04:13 +0930331
James Feist35f5e0e2020-03-16 14:02:27 -0700332 return ret;
Xiang Liu2801a702020-01-20 14:29:34 -0800333}
Brad Bishop3cb8a602020-08-25 17:40:54 -0400334
Brad Bishop5d525412020-08-26 08:50:50 -0400335/// \brief JSON/DBus matching Callable for std::variant (visitor)
336///
337/// Default match JSON/DBus match implementation
Andrew Jefferyeab49292022-04-05 14:42:20 +0930338/// \tparam T The concrete DBus value type from DBusValueVariant
Brad Bishop5d525412020-08-26 08:50:50 -0400339template <typename T>
340struct MatchProbe
341{
342 /// \param probe the probe statement to match against
343 /// \param value the property value being matched to a probe
344 /// \return true if the dbusValue matched the probe otherwise false
345 static bool match(const nlohmann::json& probe, const T& value)
346 {
347 return probe == value;
348 }
349};
350
351/// \brief JSON/DBus matching Callable for std::variant (visitor)
352///
353/// std::string specialization of MatchProbe enabling regex matching
354template <>
355struct MatchProbe<std::string>
356{
357 /// \param probe the probe statement to match against
358 /// \param value the string value being matched to a probe
359 /// \return true if the dbusValue matched the probe otherwise false
360 static bool match(const nlohmann::json& probe, const std::string& value)
361 {
362 if (probe.is_string())
363 {
364 try
365 {
366 std::regex search(probe);
367 std::smatch regMatch;
368 return std::regex_search(value, regMatch, search);
369 }
370 catch (const std::regex_error&)
371 {
372 std::cerr << "Syntax error in regular expression: " << probe
373 << " will never match";
374 }
375 }
376
377 // Skip calling nlohmann here, since it will never match a non-string
378 // to a std::string
379 return false;
380 }
381};
382
383/// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor)
384///
385/// Forward calls to the correct template instantiation of MatchProbe
386struct MatchProbeForwarder
387{
388 explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe)
389 {}
390 const nlohmann::json& probeRef;
391
392 template <typename T>
393 bool operator()(const T& dbusValue) const
394 {
395 return MatchProbe<T>::match(probeRef, dbusValue);
396 }
397};
398
Andrew Jefferyeab49292022-04-05 14:42:20 +0930399bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue)
Brad Bishop3cb8a602020-08-25 17:40:54 -0400400{
Brad Bishop5d525412020-08-26 08:50:50 -0400401 return std::visit(MatchProbeForwarder(probe), dbusValue);
402}