xref: /openbmc/dbus-sensors/src/Utils.cpp (revision 49c17cbc)
1 /*
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 
17 #include <Utils.hpp>
18 #include <boost/algorithm/string/predicate.hpp>
19 #include <filesystem>
20 #include <fstream>
21 #include <regex>
22 #include <sdbusplus/asio/connection.hpp>
23 #include <sdbusplus/asio/object_server.hpp>
24 #include <sdbusplus/bus/match.hpp>
25 
26 namespace fs = std::filesystem;
27 
28 static bool powerStatusOn = false;
29 static bool biosHasPost = false;
30 
31 static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
32 static std::unique_ptr<sdbusplus::bus::match::match> postMatch = nullptr;
33 
34 bool getSensorConfiguration(
35     const std::string& type,
36     const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
37     ManagedObjectType& resp, bool useCache)
38 {
39     static ManagedObjectType managedObj;
40 
41     if (!useCache)
42     {
43         managedObj.clear();
44         sdbusplus::message::message getManagedObjects =
45             dbusConnection->new_method_call(
46                 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
47                 "GetManagedObjects");
48         bool err = false;
49         try
50         {
51             sdbusplus::message::message reply =
52                 dbusConnection->call(getManagedObjects);
53             reply.read(managedObj);
54         }
55         catch (const sdbusplus::exception::exception&)
56         {
57             err = true;
58         }
59 
60         if (err)
61         {
62             std::cerr << "Error communicating to entity manager\n";
63             return false;
64         }
65     }
66     for (const auto& pathPair : managedObj)
67     {
68         std::vector<boost::container::flat_map<std::string, BasicVariantType>>
69             sensorData;
70         bool correctType = false;
71         for (const auto& entry : pathPair.second)
72         {
73             if (boost::starts_with(entry.first, type))
74             {
75                 correctType = true;
76                 break;
77             }
78         }
79         if (correctType)
80         {
81             resp.emplace(pathPair);
82         }
83     }
84     return true;
85 }
86 
87 bool findFiles(const fs::path dirPath, const std::string& matchString,
88                std::vector<fs::path>& foundPaths, unsigned int symlinkDepth)
89 {
90     if (!fs::exists(dirPath))
91         return false;
92 
93     std::regex search(matchString);
94     std::smatch match;
95     for (auto& p : fs::recursive_directory_iterator(dirPath))
96     {
97         std::string path = p.path().string();
98         if (!is_directory(p))
99         {
100             if (std::regex_search(path, match, search))
101                 foundPaths.emplace_back(p.path());
102         }
103         else if (is_symlink(p) && symlinkDepth)
104         {
105             findFiles(p.path(), matchString, foundPaths, symlinkDepth - 1);
106         }
107     }
108     return true;
109 }
110 
111 bool isPowerOn(void)
112 {
113     if (!powerMatch)
114     {
115         throw std::runtime_error("Power Match Not Created");
116     }
117     return powerStatusOn;
118 }
119 
120 bool hasBiosPost(void)
121 {
122     if (!postMatch)
123     {
124         throw std::runtime_error("Post Match Not Created");
125     }
126     return biosHasPost;
127 }
128 
129 void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
130 {
131     static boost::asio::steady_timer timer(conn->get_io_context());
132     // create a match for powergood changes, first time do a method call to
133     // cache the correct value
134     if (powerMatch)
135     {
136         return;
137     }
138 
139     powerMatch = std::make_unique<sdbusplus::bus::match::match>(
140         static_cast<sdbusplus::bus::bus&>(*conn),
141         "type='signal',interface='" + std::string(properties::interface) +
142             "',path='" + std::string(power::path) + "',arg0='" +
143             std::string(power::interface) + "'",
144         [](sdbusplus::message::message& message) {
145             std::string objectName;
146             boost::container::flat_map<std::string, std::variant<std::string>>
147                 values;
148             message.read(objectName, values);
149             auto findState = values.find(power::property);
150             if (findState != values.end())
151             {
152                 bool on = boost::ends_with(
153                     std::get<std::string>(findState->second), "Running");
154                 if (!on)
155                 {
156                     timer.cancel();
157                     powerStatusOn = false;
158                     return;
159                 }
160                 // on comes too quickly
161                 timer.expires_after(std::chrono::seconds(10));
162                 timer.async_wait([](boost::system::error_code ec) {
163                     if (ec == boost::asio::error::operation_aborted)
164                     {
165                         return;
166                     }
167                     else if (ec)
168                     {
169                         std::cerr << "Timer error " << ec.message() << "\n";
170                         return;
171                     }
172                     powerStatusOn = true;
173                 });
174             }
175         });
176 
177     postMatch = std::make_unique<sdbusplus::bus::match::match>(
178         static_cast<sdbusplus::bus::bus&>(*conn),
179         "type='signal',interface='" + std::string(properties::interface) +
180             "',path='" + std::string(post::path) + "',arg0='" +
181             std::string(post::interface) + "'",
182         [](sdbusplus::message::message& message) {
183             std::string objectName;
184             boost::container::flat_map<std::string, std::variant<std::string>>
185                 values;
186             message.read(objectName, values);
187             auto findState = values.find(post::property);
188             if (findState != values.end())
189             {
190                 biosHasPost =
191                     std::get<std::string>(findState->second) != "Inactive";
192             }
193         });
194 
195     conn->async_method_call(
196         [](boost::system::error_code ec,
197            const std::variant<std::string>& state) {
198             if (ec)
199             {
200                 // we commonly come up before power control, we'll capture the
201                 // property change later
202                 return;
203             }
204             powerStatusOn =
205                 boost::ends_with(std::get<std::string>(state), "Running");
206         },
207         power::busname, power::path, properties::interface, properties::get,
208         power::interface, power::property);
209 
210     conn->async_method_call(
211         [](boost::system::error_code ec,
212            const std::variant<std::string>& state) {
213             if (ec)
214             {
215                 // we commonly come up before power control, we'll capture the
216                 // property change later
217                 return;
218             }
219             biosHasPost = std::get<std::string>(state) != "Inactive";
220         },
221         post::busname, post::path, properties::interface, properties::get,
222         post::interface, post::property);
223 }
224 
225 // replaces limits if MinReading and MaxReading are found.
226 void findLimits(std::pair<double, double>& limits,
227                 const SensorBaseConfiguration* data)
228 {
229     if (!data)
230     {
231         return;
232     }
233     auto maxFind = data->second.find("MaxReading");
234     auto minFind = data->second.find("MinReading");
235 
236     if (minFind != data->second.end())
237     {
238         limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
239     }
240     if (maxFind != data->second.end())
241     {
242         limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
243     }
244 }
245 
246 void createAssociation(
247     std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
248     const std::string& path)
249 {
250     if (association)
251     {
252         std::filesystem::path p(path);
253 
254         std::vector<Association> associations;
255         associations.push_back(
256             Association("chassis", "all_sensors", p.parent_path().string()));
257         association->register_property("Associations", associations);
258         association->initialize();
259     }
260 }
261 
262 void setInventoryAssociation(
263     std::shared_ptr<sdbusplus::asio::dbus_interface> association,
264     const std::string& path, const std::string& chassisPath)
265 {
266     if (association)
267     {
268         std::filesystem::path p(path);
269 
270         std::vector<Association> associations;
271         associations.push_back(
272             Association("inventory", "sensors", p.parent_path().string()));
273         associations.push_back(
274             Association("chassis", "all_sensors", chassisPath));
275         association->register_property("Associations", associations);
276         association->initialize();
277     }
278 }
279 
280 void createInventoryAssoc(
281     std::shared_ptr<sdbusplus::asio::connection> conn,
282     std::shared_ptr<sdbusplus::asio::dbus_interface> association,
283     const std::string& path)
284 {
285     if (!association)
286     {
287         return;
288     }
289     conn->async_method_call(
290         [association, path](const boost::system::error_code ec,
291                             const std::vector<std::string>& ret) {
292             if (ec)
293             {
294                 std::cerr << "Error calling mapper\n";
295                 return;
296             }
297             for (const auto& object : ret)
298             {
299                 setInventoryAssociation(association, path, object);
300             }
301         },
302         mapper::busName, mapper::path, mapper::interface, "GetSubTreePaths",
303         "/xyz/openbmc_project/inventory/system", 2,
304         std::array<std::string, 1>{
305             "xyz.openbmc_project.Inventory.Item.System"});
306 }
307