1 /** 2 * Copyright © 2020 IBM 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 #include "config.h" 17 18 #include "manager.hpp" 19 20 #include "action.hpp" 21 #include "event.hpp" 22 #include "fan.hpp" 23 #include "group.hpp" 24 #include "json_config.hpp" 25 #include "power_state.hpp" 26 #include "profile.hpp" 27 #include "sdbusplus.hpp" 28 #include "zone.hpp" 29 30 #include <nlohmann/json.hpp> 31 #include <sdbusplus/bus.hpp> 32 #include <sdbusplus/server/manager.hpp> 33 #include <sdeventplus/event.hpp> 34 #include <sdeventplus/utility/timer.hpp> 35 36 #include <algorithm> 37 #include <chrono> 38 #include <filesystem> 39 #include <functional> 40 #include <map> 41 #include <memory> 42 #include <tuple> 43 #include <utility> 44 #include <vector> 45 46 namespace phosphor::fan::control::json 47 { 48 49 using json = nlohmann::json; 50 51 std::vector<std::string> Manager::_activeProfiles; 52 std::map<std::string, 53 std::map<std::string, std::pair<bool, std::vector<std::string>>>> 54 Manager::_servTree; 55 std::map<std::string, 56 std::map<std::string, std::map<std::string, PropertyVariantType>>> 57 Manager::_objects; 58 59 Manager::Manager(const sdeventplus::Event& event) : 60 _bus(util::SDBusPlus::getBus()), _event(event), 61 _mgr(util::SDBusPlus::getBus(), CONTROL_OBJPATH), _loadAllowed(true), 62 _powerState(std::make_unique<PGoodState>( 63 util::SDBusPlus::getBus(), 64 std::bind(std::mem_fn(&Manager::powerStateChanged), this, 65 std::placeholders::_1))) 66 {} 67 68 void Manager::sighupHandler(sdeventplus::source::Signal&, 69 const struct signalfd_siginfo*) 70 { 71 // Save current set of available and active profiles 72 std::map<configKey, std::unique_ptr<Profile>> profiles; 73 profiles.swap(_profiles); 74 std::vector<std::string> activeProfiles; 75 activeProfiles.swap(_activeProfiles); 76 77 try 78 { 79 _loadAllowed = true; 80 load(); 81 } 82 catch (std::runtime_error& re) 83 { 84 // Restore saved available and active profiles 85 _loadAllowed = false; 86 _profiles.swap(profiles); 87 _activeProfiles.swap(activeProfiles); 88 log<level::ERR>("Error reloading configs, no changes made", 89 entry("LOAD_ERROR=%s", re.what())); 90 } 91 } 92 93 void Manager::load() 94 { 95 if (_loadAllowed) 96 { 97 // Load the available profiles and which are active 98 setProfiles(); 99 100 // Load the zone configurations 101 auto zones = getConfig<Zone>(false, _event, this); 102 // Load the fan configurations and move each fan into its zone 103 auto fans = getConfig<Fan>(false); 104 for (auto& fan : fans) 105 { 106 configKey fanProfile = 107 std::make_pair(fan.second->getZone(), fan.first.second); 108 auto itZone = std::find_if( 109 zones.begin(), zones.end(), [&fanProfile](const auto& zone) { 110 return Manager::inConfig(fanProfile, zone.first); 111 }); 112 if (itZone != zones.end()) 113 { 114 if (itZone->second->getTarget() != fan.second->getTarget() && 115 fan.second->getTarget() != 0) 116 { 117 // Update zone target to current target of the fan in the 118 // zone 119 itZone->second->setTarget(fan.second->getTarget()); 120 } 121 itZone->second->addFan(std::move(fan.second)); 122 } 123 } 124 125 // Load any events configured 126 auto events = getConfig<Event>(true, this, zones); 127 128 // Enable zones 129 _zones = std::move(zones); 130 std::for_each(_zones.begin(), _zones.end(), 131 [](const auto& entry) { entry.second->enable(); }); 132 133 // Clear current timers and signal subscriptions before enabling events 134 // To save reloading services and/or objects into cache, do not clear 135 // cache 136 _timers.clear(); 137 _signals.clear(); 138 139 // Enable events 140 _events = std::move(events); 141 std::for_each(_events.begin(), _events.end(), 142 [](const auto& entry) { entry.second->enable(); }); 143 144 _loadAllowed = false; 145 } 146 } 147 148 void Manager::powerStateChanged(bool powerStateOn) 149 { 150 if (powerStateOn) 151 { 152 std::for_each(_zones.begin(), _zones.end(), [](const auto& entry) { 153 entry.second->setTarget(entry.second->getPoweronTarget()); 154 }); 155 } 156 } 157 158 const std::vector<std::string>& Manager::getActiveProfiles() 159 { 160 return _activeProfiles; 161 } 162 163 bool Manager::inConfig(const configKey& input, const configKey& comp) 164 { 165 // Config names dont match, do not include in config 166 if (input.first != comp.first) 167 { 168 return false; 169 } 170 // No profiles specified by input config, can be used in any config 171 if (input.second.empty()) 172 { 173 return true; 174 } 175 else 176 { 177 // Profiles must have one match in the other's profiles(and they must be 178 // an active profile) to be used in the config 179 return std::any_of( 180 input.second.begin(), input.second.end(), 181 [&comp](const auto& lProfile) { 182 return std::any_of( 183 comp.second.begin(), comp.second.end(), 184 [&lProfile](const auto& rProfile) { 185 if (lProfile != rProfile) 186 { 187 return false; 188 } 189 auto activeProfs = getActiveProfiles(); 190 return std::find(activeProfs.begin(), activeProfs.end(), 191 lProfile) != activeProfs.end(); 192 }); 193 }); 194 } 195 } 196 197 bool Manager::hasOwner(const std::string& path, const std::string& intf) 198 { 199 auto itServ = _servTree.find(path); 200 if (itServ == _servTree.end()) 201 { 202 // Path not found in cache, therefore owner missing 203 return false; 204 } 205 for (const auto& service : itServ->second) 206 { 207 auto itIntf = std::find_if( 208 service.second.second.begin(), service.second.second.end(), 209 [&intf](const auto& interface) { return intf == interface; }); 210 if (itIntf != std::end(service.second.second)) 211 { 212 // Service found, return owner state 213 return service.second.first; 214 } 215 } 216 // Interface not found in cache, therefore owner missing 217 return false; 218 } 219 220 void Manager::setOwner(const std::string& path, const std::string& serv, 221 const std::string& intf, bool isOwned) 222 { 223 // Set owner state for specific object given 224 auto& ownIntf = _servTree[path][serv]; 225 ownIntf.first = isOwned; 226 auto itIntf = std::find_if( 227 ownIntf.second.begin(), ownIntf.second.end(), 228 [&intf](const auto& interface) { return intf == interface; }); 229 if (itIntf == std::end(ownIntf.second)) 230 { 231 ownIntf.second.emplace_back(intf); 232 } 233 234 // Update owner state on all entries of the same `serv` & `intf` 235 for (auto& itPath : _servTree) 236 { 237 if (itPath.first == path) 238 { 239 // Already set/updated owner on this path for `serv` & `intf` 240 continue; 241 } 242 for (auto& itServ : itPath.second) 243 { 244 if (itServ.first != serv) 245 { 246 continue; 247 } 248 auto itIntf = std::find_if( 249 itServ.second.second.begin(), itServ.second.second.end(), 250 [&intf](const auto& interface) { return intf == interface; }); 251 if (itIntf != std::end(itServ.second.second)) 252 { 253 itServ.second.first = isOwned; 254 } 255 } 256 } 257 } 258 259 const std::string& Manager::findService(const std::string& path, 260 const std::string& intf) 261 { 262 static const std::string empty = ""; 263 264 auto itServ = _servTree.find(path); 265 if (itServ != _servTree.end()) 266 { 267 for (const auto& service : itServ->second) 268 { 269 auto itIntf = std::find_if( 270 service.second.second.begin(), service.second.second.end(), 271 [&intf](const auto& interface) { return intf == interface; }); 272 if (itIntf != std::end(service.second.second)) 273 { 274 // Service found, return service name 275 return service.first; 276 } 277 } 278 } 279 280 return empty; 281 } 282 283 void Manager::addServices(const std::string& intf, int32_t depth) 284 { 285 // Get all subtree objects for the given interface 286 auto objects = util::SDBusPlus::getSubTree(util::SDBusPlus::getBus(), "/", 287 intf, depth); 288 // Add what's returned to the cache of path->services 289 for (auto& itPath : objects) 290 { 291 auto pathIter = _servTree.find(itPath.first); 292 if (pathIter != _servTree.end()) 293 { 294 // Path found in cache 295 for (auto& itServ : itPath.second) 296 { 297 auto servIter = pathIter->second.find(itServ.first); 298 if (servIter != pathIter->second.end()) 299 { 300 // Service found in cache 301 for (auto& itIntf : itServ.second) 302 { 303 if (std::find(servIter->second.second.begin(), 304 servIter->second.second.end(), 305 itIntf) == servIter->second.second.end()) 306 { 307 // Add interface to cache 308 servIter->second.second.emplace_back(itIntf); 309 } 310 } 311 } 312 else 313 { 314 // Service not found in cache 315 auto intfs = {intf}; 316 pathIter->second[itServ.first] = 317 std::make_pair(true, intfs); 318 } 319 } 320 } 321 else 322 { 323 // Path not found in cache 324 auto intfs = {intf}; 325 _servTree[itPath.first] = { 326 {itPath.second.begin()->first, std::make_pair(true, intfs)}}; 327 } 328 } 329 } 330 331 const std::string& Manager::getService(const std::string& path, 332 const std::string& intf) 333 { 334 // Retrieve service from cache 335 const auto& serviceName = findService(path, intf); 336 if (serviceName.empty()) 337 { 338 addServices(intf, 0); 339 return findService(path, intf); 340 } 341 342 return serviceName; 343 } 344 345 std::vector<std::string> Manager::findPaths(const std::string& serv, 346 const std::string& intf) 347 { 348 std::vector<std::string> paths; 349 350 for (const auto& path : _servTree) 351 { 352 auto itServ = path.second.find(serv); 353 if (itServ != path.second.end()) 354 { 355 if (std::find(itServ->second.second.begin(), 356 itServ->second.second.end(), 357 intf) != itServ->second.second.end()) 358 { 359 if (std::find(paths.begin(), paths.end(), path.first) == 360 paths.end()) 361 { 362 paths.push_back(path.first); 363 } 364 } 365 } 366 } 367 368 return paths; 369 } 370 371 std::vector<std::string> Manager::getPaths(const std::string& serv, 372 const std::string& intf) 373 { 374 auto paths = findPaths(serv, intf); 375 if (paths.empty()) 376 { 377 addServices(intf, 0); 378 return findPaths(serv, intf); 379 } 380 381 return paths; 382 } 383 384 void Manager::addObjects(const std::string& path, const std::string& intf, 385 const std::string& prop) 386 { 387 auto service = getService(path, intf); 388 if (service.empty()) 389 { 390 // Log service not found for object 391 log<level::ERR>( 392 fmt::format("Unable to get service name for path {}, interface {}", 393 path, intf) 394 .c_str()); 395 return; 396 } 397 398 auto objMgrPaths = getPaths(service, "org.freedesktop.DBus.ObjectManager"); 399 if (objMgrPaths.empty()) 400 { 401 // No object manager interface provided by service? 402 // Attempt to retrieve property directly 403 auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 404 _bus, service, path, intf, prop); 405 _objects[path][intf][prop] = variant; 406 return; 407 } 408 409 for (const auto& objMgrPath : objMgrPaths) 410 { 411 // Get all managed objects of service 412 auto objects = util::SDBusPlus::getManagedObjects<PropertyVariantType>( 413 _bus, service, objMgrPath); 414 415 // Add what's returned to the cache of objects 416 for (auto& object : objects) 417 { 418 auto itPath = _objects.find(object.first); 419 if (itPath != _objects.end()) 420 { 421 // Path found in cache 422 for (auto& interface : itPath->second) 423 { 424 auto itIntf = itPath->second.find(interface.first); 425 if (itIntf != itPath->second.end()) 426 { 427 // Interface found in cache 428 for (auto& property : itIntf->second) 429 { 430 auto itProp = itIntf->second.find(property.first); 431 if (itProp != itIntf->second.end()) 432 { 433 // Property found, update value 434 itProp->second = property.second; 435 } 436 else 437 { 438 itIntf->second.insert(property); 439 } 440 } 441 } 442 else 443 { 444 // Interface not found in cache 445 itPath->second.insert(interface); 446 } 447 } 448 } 449 else 450 { 451 // Path not found in cache 452 _objects.insert(object); 453 } 454 } 455 } 456 } 457 458 const std::optional<PropertyVariantType> 459 Manager::getProperty(const std::string& path, const std::string& intf, 460 const std::string& prop) 461 { 462 // TODO Objects hosted by fan control (i.e. ThermalMode) are required to 463 // update the cache upon being set/updated 464 auto itPath = _objects.find(path); 465 if (itPath != _objects.end()) 466 { 467 auto itIntf = itPath->second.find(intf); 468 if (itIntf != itPath->second.end()) 469 { 470 auto itProp = itIntf->second.find(prop); 471 if (itProp != itIntf->second.end()) 472 { 473 return itProp->second; 474 } 475 } 476 } 477 478 return std::nullopt; 479 } 480 481 void Manager::addTimer(const TimerType type, 482 const std::chrono::microseconds interval, 483 std::unique_ptr<TimerPkg> pkg) 484 { 485 auto dataPtr = 486 std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg))); 487 Timer timer(_event, 488 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr))); 489 if (type == TimerType::repeating) 490 { 491 timer.restart(interval); 492 } 493 else if (type == TimerType::oneshot) 494 { 495 timer.restartOnce(interval); 496 } 497 else 498 { 499 throw std::invalid_argument("Invalid Timer Type"); 500 } 501 _timers.emplace_back(std::move(dataPtr), std::move(timer)); 502 } 503 504 void Manager::timerExpired(TimerData& data) 505 { 506 auto& actions = 507 std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second); 508 // Perform the actions in the timer data 509 std::for_each(actions.begin(), actions.end(), 510 [](auto& action) { action->run(); }); 511 512 // Remove oneshot timers after they expired 513 if (data.first == TimerType::oneshot) 514 { 515 auto itTimer = std::find_if( 516 _timers.begin(), _timers.end(), [&data](const auto& timer) { 517 return (data.first == timer.first->first && 518 (std::get<std::string>(data.second) == 519 std::get<std::string>(timer.first->second))); 520 }); 521 if (itTimer != std::end(_timers)) 522 { 523 _timers.erase(itTimer); 524 } 525 } 526 } 527 528 void Manager::handleSignal(sdbusplus::message::message& msg, 529 const std::vector<SignalPkg>& pkgs) 530 { 531 for (auto& pkg : pkgs) 532 { 533 // Handle the signal callback and only run the actions if the handler 534 // updated the cache for the given SignalObject 535 if (std::get<SignalHandler>(pkg)(msg, std::get<SignalObject>(pkg), 536 *this)) 537 { 538 // Perform the actions in the handler package 539 auto& actions = std::get<SignalActions>(pkg); 540 std::for_each(actions.begin(), actions.end(), 541 [](auto& action) { action.get()->run(); }); 542 } 543 } 544 } 545 546 void Manager::setProfiles() 547 { 548 // Profiles JSON config file is optional 549 auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName, 550 Profile::confFileName, true); 551 552 _profiles.clear(); 553 if (!confFile.empty()) 554 { 555 for (const auto& entry : fan::JsonConfig::load(confFile)) 556 { 557 auto obj = std::make_unique<Profile>(entry); 558 _profiles.emplace( 559 std::make_pair(obj->getName(), obj->getProfiles()), 560 std::move(obj)); 561 } 562 } 563 564 // Ensure all configurations use the same set of active profiles 565 // (In case a profile's active state changes during configuration) 566 _activeProfiles.clear(); 567 for (const auto& profile : _profiles) 568 { 569 if (profile.second->isActive()) 570 { 571 _activeProfiles.emplace_back(profile.first.first); 572 } 573 } 574 } 575 576 } // namespace phosphor::fan::control::json 577