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