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