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