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