blob: c92aabc121c57fc3159ac86a7a5b63c4946f177d [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 Feist637b3ef2019-04-15 16:35:30 -070028#include <filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080029#include <fstream>
30#include <regex>
James Feist1df06a42019-04-11 14:23:04 -070031#include <sdbusplus/bus/match.hpp>
James Feistb4383f42018-08-06 16:54:10 -070032#include <valijson/adapters/nlohmann_json_adapter.hpp>
33#include <valijson/schema.hpp>
34#include <valijson/schema_parser.hpp>
35#include <valijson/validator.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080036
James Feist481c5d52019-08-13 14:40:40 -070037constexpr const char* templateChar = "$";
38
Ed Tanous072e25d2018-12-16 21:45:20 -080039namespace fs = std::filesystem;
James Feist1df06a42019-04-11 14:23:04 -070040static bool powerStatusOn = false;
41static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
James Feist3cb5fec2018-01-23 14:41:51 -080042
James Feista465ccc2019-02-08 12:51:01 -080043bool findFiles(const fs::path& dirPath, const std::string& matchString,
44 std::vector<fs::path>& foundPaths)
James Feist3cb5fec2018-01-23 14:41:51 -080045{
James Feista3c180a2018-08-09 16:06:04 -070046 if (!fs::exists(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080047 return false;
48
James Feista3c180a2018-08-09 16:06:04 -070049 std::regex search(matchString);
James Feist3cb5fec2018-01-23 14:41:51 -080050 std::smatch match;
James Feista465ccc2019-02-08 12:51:01 -080051 for (const auto& p : fs::directory_iterator(dirPath))
James Feist3cb5fec2018-01-23 14:41:51 -080052 {
53 std::string path = p.path().string();
James Feistc95cb142018-02-26 10:41:42 -080054 if (std::regex_search(path, match, search))
James Feist3cb5fec2018-01-23 14:41:51 -080055 {
James Feista3c180a2018-08-09 16:06:04 -070056 foundPaths.emplace_back(p.path());
James Feist3cb5fec2018-01-23 14:41:51 -080057 }
58 }
59 return true;
James Feistb4383f42018-08-06 16:54:10 -070060}
61
Nikhil Potaded8884f12019-03-27 13:27:13 -070062bool getI2cDevicePaths(const fs::path& dirPath,
63 boost::container::flat_map<size_t, fs::path>& busPaths)
64{
65 if (!fs::exists(dirPath))
66 {
67 return false;
68 }
69
70 // Regex for matching the path
71 std::regex searchPath(std::string(R"(i2c-\d+$)"));
72 // Regex for matching the bus numbers
73 std::regex searchBus(std::string(R"(\w[^-]*$)"));
74 std::smatch matchPath;
75 std::smatch matchBus;
76 for (const auto& p : fs::directory_iterator(dirPath))
77 {
78 std::string path = p.path().string();
79 if (std::regex_search(path, matchPath, searchPath))
80 {
81 if (std::regex_search(path, matchBus, searchBus))
82 {
83 size_t bus = stoul(*matchBus.begin());
84 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
85 }
86 }
87 }
88
89 return true;
90}
91
James Feista465ccc2019-02-08 12:51:01 -080092bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
James Feistb4383f42018-08-06 16:54:10 -070093{
94 valijson::Schema schema;
95 valijson::SchemaParser parser;
96 valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
97 parser.populateSchema(schemaAdapter, schema);
98 valijson::Validator validator;
99 valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
100 if (!validator.validate(schema, targetAdapter, NULL))
101 {
102 return false;
103 }
104 return true;
Ed Tanous072e25d2018-12-16 21:45:20 -0800105}
James Feist1df06a42019-04-11 14:23:04 -0700106
107bool isPowerOn(void)
108{
109 if (!powerMatch)
110 {
111 throw std::runtime_error("Power Match Not Created");
112 }
113 return powerStatusOn;
114}
115
116void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
117{
James Feist1df06a42019-04-11 14:23:04 -0700118 powerMatch = std::make_unique<sdbusplus::bus::match::match>(
119 static_cast<sdbusplus::bus::bus&>(*conn),
James Feist61f5ac42020-03-11 14:37:25 -0700120 "type='signal',interface='" + std::string(properties::interface) +
121 "',path='" + std::string(power::path) + "',arg0='" +
122 std::string(power::interface) + "'",
123 [](sdbusplus::message::message& message) {
124 std::string objectName;
125 boost::container::flat_map<std::string, std::variant<std::string>>
126 values;
127 message.read(objectName, values);
128 auto findState = values.find(power::property);
129 if (findState != values.end())
130 {
131 powerStatusOn = boost::ends_with(
132 std::get<std::string>(findState->second), "Running");
133 }
134 });
135
136 conn->async_method_call(
137 [](boost::system::error_code ec,
138 const std::variant<std::string>& state) {
139 if (ec)
140 {
141 return;
142 }
143 powerStatusOn =
144 boost::ends_with(std::get<std::string>(state), "Running");
145 },
146 power::busname, power::path, properties::interface, properties::get,
147 power::interface, power::property);
James Feist481c5d52019-08-13 14:40:40 -0700148}
149
150// finds the template character (currently set to $) and replaces the value with
151// the field found in a dbus object i.e. $ADDRESS would get populated with the
152// ADDRESS field from a object on dbus
James Feist35f5e0e2020-03-16 14:02:27 -0700153std::optional<std::string> templateCharReplace(
James Feist481c5d52019-08-13 14:40:40 -0700154 nlohmann::json::iterator& keyPair,
155 const boost::container::flat_map<std::string, BasicVariantType>&
156 foundDevice,
James Feist35f5e0e2020-03-16 14:02:27 -0700157 const size_t foundDeviceIdx, const std::optional<std::string>& replaceStr)
James Feist481c5d52019-08-13 14:40:40 -0700158{
James Feist35f5e0e2020-03-16 14:02:27 -0700159 std::optional<std::string> ret = std::nullopt;
160
James Feist481c5d52019-08-13 14:40:40 -0700161 if (keyPair.value().type() == nlohmann::json::value_t::object ||
162 keyPair.value().type() == nlohmann::json::value_t::array)
163 {
164 for (auto nextLayer = keyPair.value().begin();
165 nextLayer != keyPair.value().end(); nextLayer++)
166 {
James Feist35f5e0e2020-03-16 14:02:27 -0700167 templateCharReplace(nextLayer, foundDevice, foundDeviceIdx,
168 replaceStr);
James Feist481c5d52019-08-13 14:40:40 -0700169 }
James Feist35f5e0e2020-03-16 14:02:27 -0700170 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700171 }
172
173 std::string* strPtr = keyPair.value().get_ptr<std::string*>();
174 if (strPtr == nullptr)
175 {
James Feist35f5e0e2020-03-16 14:02:27 -0700176 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700177 }
178
179 boost::replace_all(*strPtr, std::string(templateChar) + "index",
180 std::to_string(foundDeviceIdx));
James Feist35f5e0e2020-03-16 14:02:27 -0700181 if (replaceStr)
182 {
183 boost::replace_all(*strPtr, *replaceStr,
184 std::to_string(foundDeviceIdx));
185 }
James Feist481c5d52019-08-13 14:40:40 -0700186
187 for (auto& foundDevicePair : foundDevice)
188 {
189 std::string templateName = templateChar + foundDevicePair.first;
190 boost::iterator_range<std::string::const_iterator> find =
191 boost::ifind_first(*strPtr, templateName);
192 if (find)
193 {
James Feist8c20feb2019-08-14 15:10:11 -0700194 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*',
195 '/'};
James Feist481c5d52019-08-13 14:40:40 -0700196 size_t start = find.begin() - strPtr->begin();
James Feist8c20feb2019-08-14 15:10:11 -0700197 size_t nextItemIdx = start + templateName.size() + 1;
198
James Feist481c5d52019-08-13 14:40:40 -0700199 // check for additional operations
200 if (!start && find.end() == strPtr->end())
201 {
202 std::visit([&](auto&& val) { keyPair.value() = val; },
203 foundDevicePair.second);
James Feist35f5e0e2020-03-16 14:02:27 -0700204 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700205 }
James Feist8c20feb2019-08-14 15:10:11 -0700206 else if (nextItemIdx > strPtr->size() ||
207 std::find(mathChars.begin(), mathChars.end(),
208 strPtr->at(nextItemIdx)) == mathChars.end())
James Feist481c5d52019-08-13 14:40:40 -0700209 {
210 std::string val = std::visit(VariantToStringVisitor(),
211 foundDevicePair.second);
James Feistb0097d42019-08-15 09:24:13 -0700212 boost::ireplace_all(*strPtr,
213 templateChar + foundDevicePair.first, val);
James Feist8c20feb2019-08-14 15:10:11 -0700214 continue;
James Feist481c5d52019-08-13 14:40:40 -0700215 }
216
217 // save the prefix
218 std::string prefix = strPtr->substr(0, start);
219
James Feist8c20feb2019-08-14 15:10:11 -0700220 // operate on the rest
221 std::string end = strPtr->substr(nextItemIdx);
James Feist481c5d52019-08-13 14:40:40 -0700222
223 std::vector<std::string> split;
224 boost::split(split, end, boost::is_any_of(" "));
225
226 // need at least 1 operation and number
227 if (split.size() < 2)
228 {
229 std::cerr << "Syntax error on template replacement of "
230 << *strPtr << "\n";
231 for (const std::string& data : split)
232 {
233 std::cerr << data << " ";
234 }
235 std::cerr << "\n";
236 continue;
237 }
238
239 // we assume that the replacement is a number, because we can
240 // only do math on numbers.. we might concatenate strings in the
241 // future, but thats later
242 int number =
243 std::visit(VariantToIntVisitor(), foundDevicePair.second);
244
245 bool isOperator = true;
246 TemplateOperation next = TemplateOperation::addition;
247
248 auto it = split.begin();
249
250 for (; it != split.end(); it++)
251 {
252 if (isOperator)
253 {
254 if (*it == "+")
255 {
256 next = TemplateOperation::addition;
257 }
258 else if (*it == "-")
259 {
260 next = TemplateOperation::subtraction;
261 }
262 else if (*it == "*")
263 {
264 next = TemplateOperation::multiplication;
265 }
266 else if (*it == R"(%)")
267 {
268 next = TemplateOperation::modulo;
269 }
270 else if (*it == R"(/)")
271 {
272 next = TemplateOperation::division;
273 }
274 else
275 {
276 break;
277 }
278 }
279 else
280 {
281 int constant = 0;
282 try
283 {
284 constant = std::stoi(*it);
285 }
286 catch (std::invalid_argument&)
287 {
288 std::cerr << "Parameter not supported for templates "
289 << *it << "\n";
290 continue;
291 }
292 switch (next)
293 {
294 case TemplateOperation::addition:
295 {
296 number += constant;
297 break;
298 }
299 case TemplateOperation::subtraction:
300 {
301 number -= constant;
302 break;
303 }
304 case TemplateOperation::multiplication:
305 {
306 number *= constant;
307 break;
308 }
309 case TemplateOperation::division:
310 {
311 number /= constant;
312 break;
313 }
314 case TemplateOperation::modulo:
315 {
316 number = number % constant;
317 break;
318 }
319
320 default:
321 break;
322 }
323 }
324 isOperator = !isOperator;
325 }
James Feist35f5e0e2020-03-16 14:02:27 -0700326
James Feist481c5d52019-08-13 14:40:40 -0700327 std::string result = prefix + std::to_string(number);
328
James Feist35f5e0e2020-03-16 14:02:27 -0700329 std::string replaced(find.begin(), find.end());
330 for (auto it2 = split.begin(); it2 != split.end(); it2++)
331 {
332 replaced += " ";
333 replaced += *it2;
334 if (it2 == it)
335 {
336 break;
337 }
338 }
339 ret = replaced;
340
James Feist481c5d52019-08-13 14:40:40 -0700341 if (it != split.end())
342 {
343 for (; it != split.end(); it++)
344 {
345 result += " " + *it;
346 }
347 }
348 keyPair.value() = result;
349
350 // We probably just invalidated the pointer above, so set it to null
351 strPtr = nullptr;
352 break;
353 }
354 }
355
356 strPtr = keyPair.value().get_ptr<std::string*>();
357 if (strPtr == nullptr)
358 {
James Feist35f5e0e2020-03-16 14:02:27 -0700359 return ret;
James Feist481c5d52019-08-13 14:40:40 -0700360 }
361
362 // convert hex numbers to ints
363 if (boost::starts_with(*strPtr, "0x"))
364 {
365 try
366 {
367 size_t pos = 0;
368 int64_t temp = std::stoul(*strPtr, &pos, 0);
369 if (pos == strPtr->size())
370 {
371 keyPair.value() = static_cast<uint64_t>(temp);
372 }
373 }
374 catch (std::invalid_argument&)
375 {
376 }
377 catch (std::out_of_range&)
378 {
379 }
380 }
381 // non-hex numbers
382 else
383 {
384 try
385 {
386 uint64_t temp = boost::lexical_cast<uint64_t>(*strPtr);
387 keyPair.value() = temp;
388 }
389 catch (boost::bad_lexical_cast&)
390 {
391 }
392 }
James Feist35f5e0e2020-03-16 14:02:27 -0700393 return ret;
Xiang Liu2801a702020-01-20 14:29:34 -0800394}