blob: 4e9d4a106d1a3d8ab878e8c9026427d60dbe6557 [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*/
16
James Feist481c5d52019-08-13 14:40:40 -070017#include "Utils.hpp"
18
19#include "VariantVisitors.hpp"
20
21#include <boost/algorithm/string/classification.hpp>
22#include <boost/algorithm/string/find.hpp>
23#include <boost/algorithm/string/predicate.hpp>
24#include <boost/algorithm/string/replace.hpp>
25#include <boost/algorithm/string/split.hpp>
26#include <boost/container/flat_map.hpp>
27#include <boost/lexical_cast.hpp>
James Feist1df06a42019-04-11 14:23:04 -070028#include <sdbusplus/bus/match.hpp>
James Feistb4383f42018-08-06 16:54:10 -070029#include <valijson/adapters/nlohmann_json_adapter.hpp>
30#include <valijson/schema.hpp>
31#include <valijson/schema_parser.hpp>
32#include <valijson/validator.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080033
James Feist8c505da2020-05-28 10:06:33 -070034#include <filesystem>
35#include <fstream>
36#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;
James Feist1df06a42019-04-11 14:23:04 -070041static bool powerStatusOn = false;
42static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist3cb5fec2018-01-23 14:41:51 -080043
James Feista465ccc2019-02-08 12:51:01 -080044bool findFiles(const fs::path& dirPath, const std::string& matchString,
45 std::vector<fs::path>& foundPaths)
James Feist3cb5fec2018-01-23 14:41:51 -080046{
James Feista3c180a2018-08-09 16:06:04 -070047 if (!fs::exists(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080048 return false;
49
James Feista3c180a2018-08-09 16:06:04 -070050 std::regex search(matchString);
James Feist3cb5fec2018-01-23 14:41:51 -080051 std::smatch match;
James Feista465ccc2019-02-08 12:51:01 -080052 for (const auto& p : fs::directory_iterator(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080053 {
54 std::string path = p.path().string();
James Feistc95cb142018-02-26 10:41:42 -080055 if (std::regex_search(path, match, search))
James Feist3cb5fec2018-01-23 14:41:51 -080056 {
James Feista3c180a2018-08-09 16:06:04 -070057 foundPaths.emplace_back(p.path());
James Feist3cb5fec2018-01-23 14:41:51 -080058 }
59 }
60 return true;
James Feistb4383f42018-08-06 16:54:10 -070061}
62
Nikhil Potaded8884f12019-03-27 13:27:13 -070063bool getI2cDevicePaths(const fs::path& dirPath,
64 boost::container::flat_map<size_t, fs::path>& busPaths)
65{
66 if (!fs::exists(dirPath))
67 {
68 return false;
69 }
70
71 // Regex for matching the path
72 std::regex searchPath(std::string(R"(i2c-\d+$)"));
73 // Regex for matching the bus numbers
74 std::regex searchBus(std::string(R"(\w[^-]*$)"));
75 std::smatch matchPath;
76 std::smatch matchBus;
77 for (const auto& p : fs::directory_iterator(dirPath))
78 {
79 std::string path = p.path().string();
80 if (std::regex_search(path, matchPath, searchPath))
81 {
82 if (std::regex_search(path, matchBus, searchBus))
83 {
84 size_t bus = stoul(*matchBus.begin());
85 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
86 }
87 }
88 }
89
90 return true;
91}
92
James Feista465ccc2019-02-08 12:51:01 -080093bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
James Feistb4383f42018-08-06 16:54:10 -070094{
95 valijson::Schema schema;
96 valijson::SchemaParser parser;
97 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
98 parser.populateSchema(schemaAdapter, schema);
99 valijson::Validator validator;
100 valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
101 if (!validator.validate(schema, targetAdapter, NULL))
102 {
103 return false;
104 }
105 return true;
Ed Tanous072e25d2018-12-16 21:45:20 -0800106}
James Feist1df06a42019-04-11 14:23:04 -0700107
108bool isPowerOn(void)
109{
110 if (!powerMatch)
111 {
112 throw std::runtime_error("Power Match Not Created");
113 }
114 return powerStatusOn;
115}
116
117void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
118{
James Feist1df06a42019-04-11 14:23:04 -0700119 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
120 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist61f5ac42020-03-11 14:37:25 -0700121 "type='signal',interface='" + std::string(properties::interface) +
122 "',path='" + std::string(power::path) + "',arg0='" +
123 std::string(power::interface) + "'",
124 [](sdbusplus::message::message& message) {
125 std::string objectName;
126 boost::container::flat_map<std::string, std::variant<std::string>>
127 values;
128 message.read(objectName, values);
129 auto findState = values.find(power::property);
130 if (findState != values.end())
131 {
132 powerStatusOn = boost::ends_with(
133 std::get<std::string>(findState->second), "Running");
134 }
135 });
136
137 conn->async_method_call(
138 [](boost::system::error_code ec,
139 const std::variant<std::string>& state) {
140 if (ec)
141 {
142 return;
143 }
144 powerStatusOn =
145 boost::ends_with(std::get<std::string>(state), "Running");
146 },
147 power::busname, power::path, properties::interface, properties::get,
148 power::interface, power::property);
James Feist481c5d52019-08-13 14:40:40 -0700149}
150
151// finds the template character (currently set to $) and replaces the value with
152// the field found in a dbus object i.e. $ADDRESS would get populated with the
153// ADDRESS field from a object on dbus
James Feist35f5e0e2020-03-16 14:02:27 -0700154std::optional<std::string> templateCharReplace(
James Feist481c5d52019-08-13 14:40:40 -0700155 nlohmann::json::iterator& keyPair,
156 const boost::container::flat_map<std::string, BasicVariantType>&
157 foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -0700158 const size_t foundDeviceIdx, const std::optional<std::string>& replaceStr)
James Feist481c5d52019-08-13 14:40:40 -0700159{
James Feist35f5e0e2020-03-16 14:02:27 -0700160 std::optional<std::string> ret = std::nullopt;
161
James Feist481c5d52019-08-13 14:40:40 -0700162 if (keyPair.value().type() == nlohmann::json::value_t::object ||
163 keyPair.value().type() == nlohmann::json::value_t::array)
164 {
165 for (auto nextLayer = keyPair.value().begin();
166 nextLayer != keyPair.value().end(); nextLayer++)
167 {
James Feist35f5e0e2020-03-16 14:02:27 -0700168 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx,
169 replaceStr);
James Feist481c5d52019-08-13 14:40:40 -0700170 }
James Feist35f5e0e2020-03-16 14:02:27 -0700171 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700172 }
173
174 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
175 if (strPtr == nullptr)
176 {
James Feist35f5e0e2020-03-16 14:02:27 -0700177 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700178 }
179
180 boost::replace_all(*strPtr, std::string(templateChar) + "index",
181 std::to_string(foundDeviceIdx));
James Feist35f5e0e2020-03-16 14:02:27 -0700182 if (replaceStr)
183 {
184 boost::replace_all(*strPtr, *replaceStr,
185 std::to_string(foundDeviceIdx));
186 }
James Feist481c5d52019-08-13 14:40:40 -0700187
188 for (auto& foundDevicePair : foundDevice)
189 {
190 std::string templateName = templateChar + foundDevicePair.first;
191 boost::iterator_range<std::string::const_iterator> find =
192 boost::ifind_first(*strPtr, templateName);
193 if (find)
194 {
James Feist8c20feb2019-08-14 15:10:11 -0700195 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
196 '/'};
James Feist481c5d52019-08-13 14:40:40 -0700197 size_t start = find.begin() - strPtr->begin();
James Feist8c20feb2019-08-14 15:10:11 -0700198 size_t nextItemIdx = start + templateName.size() + 1;
199
James Feist481c5d52019-08-13 14:40:40 -0700200 // check for additional operations
201 if (!start && find.end() == strPtr->end())
202 {
203 std::visit([&](auto&& val) { keyPair.value() = val; },
204 foundDevicePair.second);
James Feist35f5e0e2020-03-16 14:02:27 -0700205 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700206 }
James Feist8c20feb2019-08-14 15:10:11 -0700207 else if (nextItemIdx > strPtr->size() ||
208 std::find(mathChars.begin(), mathChars.end(),
209 strPtr->at(nextItemIdx)) == mathChars.end())
James Feist481c5d52019-08-13 14:40:40 -0700210 {
211 std::string val = std::visit(VariantToStringVisitor(),
212 foundDevicePair.second);
James Feistb0097d42019-08-15 09:24:13 -0700213 boost::ireplace_all(*strPtr,
214 templateChar + foundDevicePair.first, val);
James Feist8c20feb2019-08-14 15:10:11 -0700215 continue;
James Feist481c5d52019-08-13 14:40:40 -0700216 }
217
218 // save the prefix
219 std::string prefix = strPtr->substr(0, start);
220
James Feist8c20feb2019-08-14 15:10:11 -0700221 // operate on the rest
222 std::string end = strPtr->substr(nextItemIdx);
James Feist481c5d52019-08-13 14:40:40 -0700223
224 std::vector<std::string> split;
225 boost::split(split, end, boost::is_any_of(" "));
226
227 // need at least 1 operation and number
228 if (split.size() < 2)
229 {
230 std::cerr << "Syntax error on template replacement of "
231 << *strPtr << "\n";
232 for (const std::string& data : split)
233 {
234 std::cerr << data << " ";
235 }
236 std::cerr << "\n";
237 continue;
238 }
239
240 // we assume that the replacement is a number, because we can
241 // only do math on numbers.. we might concatenate strings in the
242 // future, but thats later
243 int number =
244 std::visit(VariantToIntVisitor(), foundDevicePair.second);
245
246 bool isOperator = true;
247 TemplateOperation next = TemplateOperation::addition;
248
249 auto it = split.begin();
250
251 for (; it != split.end(); it++)
252 {
253 if (isOperator)
254 {
255 if (*it == "+")
256 {
257 next = TemplateOperation::addition;
258 }
259 else if (*it == "-")
260 {
261 next = TemplateOperation::subtraction;
262 }
263 else if (*it == "*")
264 {
265 next = TemplateOperation::multiplication;
266 }
267 else if (*it == R"(%)")
268 {
269 next = TemplateOperation::modulo;
270 }
271 else if (*it == R"(/)")
272 {
273 next = TemplateOperation::division;
274 }
275 else
276 {
277 break;
278 }
279 }
280 else
281 {
282 int constant = 0;
283 try
284 {
285 constant = std::stoi(*it);
286 }
287 catch (std::invalid_argument&)
288 {
289 std::cerr << "Parameter not supported for templates "
290 << *it << "\n";
291 continue;
292 }
293 switch (next)
294 {
295 case TemplateOperation::addition:
296 {
297 number += constant;
298 break;
299 }
300 case TemplateOperation::subtraction:
301 {
302 number -= constant;
303 break;
304 }
305 case TemplateOperation::multiplication:
306 {
307 number *= constant;
308 break;
309 }
310 case TemplateOperation::division:
311 {
312 number /= constant;
313 break;
314 }
315 case TemplateOperation::modulo:
316 {
317 number = number % constant;
318 break;
319 }
320
321 default:
322 break;
323 }
324 }
325 isOperator = !isOperator;
326 }
James Feist35f5e0e2020-03-16 14:02:27 -0700327
James Feist481c5d52019-08-13 14:40:40 -0700328 std::string result = prefix + std::to_string(number);
329
James Feist35f5e0e2020-03-16 14:02:27 -0700330 std::string replaced(find.begin(), find.end());
331 for (auto it2 = split.begin(); it2 != split.end(); it2++)
332 {
333 replaced += " ";
334 replaced += *it2;
335 if (it2 == it)
336 {
337 break;
338 }
339 }
340 ret = replaced;
341
James Feist481c5d52019-08-13 14:40:40 -0700342 if (it != split.end())
343 {
344 for (; it != split.end(); it++)
345 {
346 result += " " + *it;
347 }
348 }
349 keyPair.value() = result;
350
351 // We probably just invalidated the pointer above, so set it to null
352 strPtr = nullptr;
353 break;
354 }
355 }
356
357 strPtr = keyPair.value().get_ptr<std::string*>();
358 if (strPtr == nullptr)
359 {
James Feist35f5e0e2020-03-16 14:02:27 -0700360 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700361 }
362
363 // convert hex numbers to ints
364 if (boost::starts_with(*strPtr, "0x"))
365 {
366 try
367 {
368 size_t pos = 0;
369 int64_t temp = std::stoul(*strPtr, &pos, 0);
370 if (pos == strPtr->size())
371 {
372 keyPair.value() = static_cast<uint64_t>(temp);
373 }
374 }
375 catch (std::invalid_argument&)
James Feist8c505da2020-05-28 10:06:33 -0700376 {}
James Feist481c5d52019-08-13 14:40:40 -0700377 catch (std::out_of_range&)
James Feist8c505da2020-05-28 10:06:33 -0700378 {}
James Feist481c5d52019-08-13 14:40:40 -0700379 }
380 // non-hex numbers
381 else
382 {
383 try
384 {
385 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
386 keyPair.value() = temp;
387 }
388 catch (boost::bad_lexical_cast&)
James Feist8c505da2020-05-28 10:06:33 -0700389 {}
James Feist481c5d52019-08-13 14:40:40 -0700390 }
James Feist35f5e0e2020-03-16 14:02:27 -0700391 return ret;
Xiang Liu2801a702020-01-20 14:29:34 -0800392}