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