xref: /openbmc/dbus-sensors/src/Utils.cpp (revision 93dc2c8e)
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 "filesystem.hpp"
18 
19 #include <Utils.hpp>
20 #include <boost/algorithm/string/predicate.hpp>
21 #include <fstream>
22 #include <regex>
23 #include <sdbusplus/asio/connection.hpp>
24 #include <sdbusplus/asio/object_server.hpp>
25 #include <sdbusplus/bus/match.hpp>
26 
27 namespace fs = std::filesystem;
28 const static constexpr char* powerInterfaceName =
29     "xyz.openbmc_project.Chassis.Control.Power";
30 const static constexpr char* powerObjectName =
31     "/xyz/openbmc_project/Chassis/Control/Power0";
32 
33 static bool powerStatusOn = false;
34 static bool biosHasPost = false;
35 static std::unique_ptr<sdbusplus::bus::match::match> powerMatch = nullptr;
36 
37 bool getSensorConfiguration(
38     const std::string& type,
39     const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
40     ManagedObjectType& resp, bool useCache)
41 {
42     static ManagedObjectType managedObj;
43 
44     if (!useCache)
45     {
46         managedObj.clear();
47         sdbusplus::message::message getManagedObjects =
48             dbusConnection->new_method_call(
49                 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
50                 "GetManagedObjects");
51         bool err = false;
52         try
53         {
54             sdbusplus::message::message reply =
55                 dbusConnection->call(getManagedObjects);
56             reply.read(managedObj);
57         }
58         catch (const sdbusplus::exception::exception&)
59         {
60             err = true;
61         }
62 
63         if (err)
64         {
65             std::cerr << "Error communicating to entity manager\n";
66             return false;
67         }
68     }
69     for (const auto& pathPair : managedObj)
70     {
71         std::vector<boost::container::flat_map<std::string, BasicVariantType>>
72             sensorData;
73         bool correctType = false;
74         for (const auto& entry : pathPair.second)
75         {
76             if (boost::starts_with(entry.first, type))
77             {
78                 correctType = true;
79                 break;
80             }
81         }
82         if (correctType)
83         {
84             resp.emplace(pathPair);
85         }
86     }
87     return true;
88 }
89 
90 bool findFiles(const fs::path dirPath, const std::string& matchString,
91                std::vector<fs::path>& foundPaths, unsigned int symlinkDepth)
92 {
93     if (!fs::exists(dirPath))
94         return false;
95 
96     std::regex search(matchString);
97     std::smatch match;
98     for (auto& p : fs::recursive_directory_iterator(dirPath))
99     {
100         std::string path = p.path().string();
101         if (!is_directory(p))
102         {
103             if (std::regex_search(path, match, search))
104                 foundPaths.emplace_back(p.path());
105         }
106         else if (is_symlink(p) && symlinkDepth)
107         {
108             findFiles(p.path(), matchString, foundPaths, symlinkDepth - 1);
109         }
110     }
111     return true;
112 }
113 
114 bool isPowerOn(void)
115 {
116     if (!powerMatch)
117     {
118         throw std::runtime_error("Power Match Not Created");
119     }
120     return powerStatusOn;
121 }
122 
123 bool hasBiosPost(void)
124 {
125     if (!powerMatch)
126     {
127         throw std::runtime_error("Power Match Not Created");
128     }
129     return biosHasPost;
130 }
131 
132 void setupPowerMatch(const std::shared_ptr<sdbusplus::asio::connection>& conn)
133 {
134     // create a match for powergood changes, first time do a method call to
135     // cache the correct value
136     std::function<void(sdbusplus::message::message & message)> eventHandler =
137         [](sdbusplus::message::message& message) {
138             std::string objectName;
139             boost::container::flat_map<std::string, std::variant<int32_t, bool>>
140                 values;
141             message.read(objectName, values);
142             auto findPgood = values.find("pgood");
143             if (findPgood != values.end())
144             {
145                 powerStatusOn = std::get<int32_t>(findPgood->second);
146             }
147             auto findPostComplete = values.find("post_complete");
148             if (findPostComplete != values.end())
149             {
150                 biosHasPost = std::get<bool>(findPostComplete->second);
151             }
152         };
153 
154     powerMatch = std::make_unique<sdbusplus::bus::match::match>(
155         static_cast<sdbusplus::bus::bus&>(*conn),
156         "type='signal',interface='org.freedesktop.DBus.Properties',path_"
157         "namespace='/xyz/openbmc_project/Chassis/Control/"
158         "Power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
159         eventHandler);
160 
161     conn->async_method_call(
162         [](boost::system::error_code ec, const std::variant<int32_t>& pgood) {
163             if (ec)
164             {
165                 // we commonly come up before power control, we'll capture the
166                 // property change later
167                 return;
168             }
169             powerStatusOn = std::get<int32_t>(pgood);
170         },
171         powerInterfaceName, powerObjectName, "org.freedesktop.DBus.Properties",
172         "Get", powerInterfaceName, "pgood");
173 
174     conn->async_method_call(
175         [](boost::system::error_code ec,
176            const std::variant<int32_t>& postComplete) {
177             if (ec)
178             {
179                 // we commonly come up before power control, we'll capture the
180                 // property change later
181                 return;
182             }
183             biosHasPost = std::get<int32_t>(postComplete);
184         },
185         powerInterfaceName, powerObjectName, "org.freedesktop.DBus.Properties",
186         "Get", powerInterfaceName, "post_complete");
187 }
188 
189 // replaces limits if MinReading and MaxReading are found.
190 void findLimits(std::pair<double, double>& limits,
191                 const SensorBaseConfiguration* data)
192 {
193     if (!data)
194     {
195         return;
196     }
197     auto maxFind = data->second.find("MaxReading");
198     auto minFind = data->second.find("MinReading");
199 
200     if (minFind != data->second.end())
201     {
202         limits.first = std::visit(VariantToDoubleVisitor(), minFind->second);
203     }
204     if (maxFind != data->second.end())
205     {
206         limits.second = std::visit(VariantToDoubleVisitor(), maxFind->second);
207     }
208 }
209 
210 void createAssociation(
211     std::shared_ptr<sdbusplus::asio::dbus_interface>& association,
212     const std::string& path)
213 {
214     if (association)
215     {
216         std::filesystem::path p(path);
217 
218         using Association = std::tuple<std::string, std::string, std::string>;
219         std::vector<Association> associations;
220         associations.push_back(
221             Association("inventory", "sensors", p.parent_path().string()));
222         association->register_property("associations", associations);
223         association->initialize();
224     }
225 }