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