xref: /openbmc/entity-manager/src/utils.cpp (revision fd977ec6188e8837a4bcf0528cf2b456d70d22d9)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2017 Intel Corporation
3 
4 #include "utils.hpp"
5 
6 #include <boost/container/flat_map.hpp>
7 #include <boost/lexical_cast.hpp>
8 #include <phosphor-logging/lg2.hpp>
9 #include <sdbusplus/bus/match.hpp>
10 
11 #include <algorithm>
12 #include <cctype>
13 #include <filesystem>
14 #include <map>
15 #include <ranges>
16 #include <regex>
17 #include <string_view>
18 #include <utility>
19 
20 namespace fs = std::filesystem;
21 
findFiles(const fs::path & dirPath,const std::string & matchString,std::vector<fs::path> & foundPaths)22 bool findFiles(const fs::path& dirPath, const std::string& matchString,
23                std::vector<fs::path>& foundPaths)
24 {
25     if (!fs::exists(dirPath))
26     {
27         return false;
28     }
29 
30     std::regex search(matchString);
31     std::smatch match;
32     for (const auto& p : fs::directory_iterator(dirPath))
33     {
34         std::string path = p.path().string();
35         if (std::regex_search(path, match, search))
36         {
37             foundPaths.emplace_back(p.path());
38         }
39     }
40     return true;
41 }
42 
findFiles(const std::vector<fs::path> && dirPaths,const std::string & matchString,std::vector<fs::path> & foundPaths)43 bool findFiles(const std::vector<fs::path>&& dirPaths,
44                const std::string& matchString,
45                std::vector<fs::path>& foundPaths)
46 {
47     std::map<fs::path, fs::path> paths;
48     std::regex search(matchString);
49     std::smatch match;
50     for (const auto& dirPath : dirPaths)
51     {
52         if (!fs::exists(dirPath))
53         {
54             continue;
55         }
56 
57         for (const auto& p : fs::recursive_directory_iterator(dirPath))
58         {
59             std::error_code ec;
60             if (p.is_directory(ec))
61             {
62                 continue;
63             }
64 
65             std::string path = p.path().string();
66             if (std::regex_search(path, match, search))
67             {
68                 paths[p.path().filename()] = p.path();
69             }
70         }
71     }
72 
73     for (const auto& [key, value] : paths)
74     {
75         foundPaths.emplace_back(value);
76     }
77 
78     return !foundPaths.empty();
79 }
80 
getI2cDevicePaths(const fs::path & dirPath,boost::container::flat_map<size_t,fs::path> & busPaths)81 bool getI2cDevicePaths(const fs::path& dirPath,
82                        boost::container::flat_map<size_t, fs::path>& busPaths)
83 {
84     if (!fs::exists(dirPath))
85     {
86         return false;
87     }
88 
89     // Regex for matching the path
90     std::regex searchPath(std::string(R"(i2c-\d+$)"));
91     // Regex for matching the bus numbers
92     std::regex searchBus(std::string(R"(\w[^-]*$)"));
93     std::smatch matchPath;
94     std::smatch matchBus;
95     for (const auto& p : fs::directory_iterator(dirPath))
96     {
97         std::string path = p.path().string();
98         if (std::regex_search(path, matchPath, searchPath))
99         {
100             if (std::regex_search(path, matchBus, searchBus))
101             {
102                 size_t bus = stoul(*matchBus.begin());
103                 busPaths.insert(std::pair<size_t, fs::path>(bus, p.path()));
104             }
105         }
106     }
107 
108     return true;
109 }
110 
111 /// \brief JSON/DBus matching Callable for std::variant (visitor)
112 ///
113 /// Default match JSON/DBus match implementation
114 /// \tparam T The concrete DBus value type from DBusValueVariant
115 template <typename T>
116 struct MatchProbe
117 {
118     /// \param probe the probe statement to match against
119     /// \param value the property value being matched to a probe
120     /// \return true if the dbusValue matched the probe otherwise false
matchMatchProbe121     static bool match(const nlohmann::json& probe, const T& value)
122     {
123         return probe == value;
124     }
125 };
126 
127 /// \brief JSON/DBus matching Callable for std::variant (visitor)
128 ///
129 /// std::string specialization of MatchProbe enabling regex matching
130 template <>
131 struct MatchProbe<std::string>
132 {
133     /// \param probe the probe statement to match against
134     /// \param value the string value being matched to a probe
135     /// \return true if the dbusValue matched the probe otherwise false
matchMatchProbe136     static bool match(const nlohmann::json& probe, const std::string& value)
137     {
138         if (probe.is_string())
139         {
140             try
141             {
142                 std::regex search(probe);
143                 std::smatch regMatch;
144                 return std::regex_search(value, regMatch, search);
145             }
146             catch (const std::regex_error&)
147             {
148                 lg2::error(
149                     "Syntax error in regular expression: {PROBE} will never match",
150                     "PROBE", probe);
151             }
152         }
153 
154         // Skip calling nlohmann here, since it will never match a non-string
155         // to a std::string
156         return false;
157     }
158 };
159 
160 /// \brief Forwarding JSON/DBus matching Callable for std::variant (visitor)
161 ///
162 /// Forward calls to the correct template instantiation of MatchProbe
163 struct MatchProbeForwarder
164 {
MatchProbeForwarderMatchProbeForwarder165     explicit MatchProbeForwarder(const nlohmann::json& probe) : probeRef(probe)
166     {}
167     const nlohmann::json& probeRef;
168 
169     template <typename T>
operator ()MatchProbeForwarder170     bool operator()(const T& dbusValue) const
171     {
172         return MatchProbe<T>::match(probeRef, dbusValue);
173     }
174 };
175 
matchProbe(const nlohmann::json & probe,const DBusValueVariant & dbusValue)176 bool matchProbe(const nlohmann::json& probe, const DBusValueVariant& dbusValue)
177 {
178     return std::visit(MatchProbeForwarder(probe), dbusValue);
179 }
180 
split(std::string_view str,char delim)181 std::vector<std::string> split(std::string_view str, char delim)
182 {
183     std::vector<std::string> out;
184 
185     size_t start = 0;
186     while (start <= str.size())
187     {
188         size_t end = str.find(delim, start);
189         if (end == std::string_view::npos)
190         {
191             out.emplace_back(str.substr(start));
192             break;
193         }
194 
195         out.emplace_back(str.substr(start, end - start));
196         start = end + 1;
197     }
198 
199     return out;
200 }
201 
iReplaceAll(std::string & str,std::string_view search,std::string_view replace)202 void iReplaceAll(std::string& str, std::string_view search,
203                  std::string_view replace)
204 {
205     if (search.empty() || search == replace)
206     {
207         return;
208     }
209 
210     while (true)
211     {
212         std::ranges::subrange<std::string::iterator> match =
213             iFindFirst(str, search);
214         if (!match)
215         {
216             break;
217         }
218 
219         str.replace(match.begin(), match.end(), replace.begin(), replace.end());
220     }
221 }
222 
replaceAll(std::string & str,std::string_view search,std::string_view replace)223 void replaceAll(std::string& str, std::string_view search,
224                 std::string_view replace)
225 {
226     if (search.empty())
227     {
228         return;
229     }
230 
231     size_t pos = 0;
232     while ((pos = str.find(search, pos)) != std::string::npos)
233     {
234         str.replace(pos, search.size(), replace);
235         pos += replace.size();
236     }
237 }
238 
toLowerCopy(std::string_view str)239 std::string toLowerCopy(std::string_view str)
240 {
241     std::string result(str);
242     std::transform(result.begin(), result.end(), result.begin(),
243                    [](unsigned char c) { return asciiToLower(c); });
244     return result;
245 }
246