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 _servTree[itPath.first] = { 417 {itPath.second.begin()->first, std::make_pair(true, intfs)}}; 418 } 419 } 420 } 421 422 const std::string& Manager::getService(const std::string& path, 423 const std::string& intf) 424 { 425 // Retrieve service from cache 426 const auto& serviceName = findService(path, intf); 427 if (serviceName.empty()) 428 { 429 addServices(intf, 0); 430 return findService(path, intf); 431 } 432 433 return serviceName; 434 } 435 436 std::vector<std::string> Manager::findPaths(const std::string& serv, 437 const std::string& intf) 438 { 439 std::vector<std::string> paths; 440 441 for (const auto& path : _servTree) 442 { 443 auto itServ = path.second.find(serv); 444 if (itServ != path.second.end()) 445 { 446 if (std::find(itServ->second.second.begin(), 447 itServ->second.second.end(), 448 intf) != itServ->second.second.end()) 449 { 450 if (std::find(paths.begin(), paths.end(), path.first) == 451 paths.end()) 452 { 453 paths.push_back(path.first); 454 } 455 } 456 } 457 } 458 459 return paths; 460 } 461 462 std::vector<std::string> Manager::getPaths(const std::string& serv, 463 const std::string& intf) 464 { 465 auto paths = findPaths(serv, intf); 466 if (paths.empty()) 467 { 468 addServices(intf, 0); 469 return findPaths(serv, intf); 470 } 471 472 return paths; 473 } 474 475 void Manager::addObjects(const std::string& path, const std::string& intf, 476 const std::string& prop) 477 { 478 auto service = getService(path, intf); 479 if (service.empty()) 480 { 481 // Log service not found for object 482 log<level::DEBUG>( 483 fmt::format("Unable to get service name for path {}, interface {}", 484 path, intf) 485 .c_str()); 486 return; 487 } 488 489 auto objMgrPaths = getPaths(service, "org.freedesktop.DBus.ObjectManager"); 490 if (objMgrPaths.empty()) 491 { 492 // No object manager interface provided by service? 493 // Attempt to retrieve property directly 494 auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 495 _bus, service, path, intf, prop); 496 _objects[path][intf][prop] = variant; 497 return; 498 } 499 500 for (const auto& objMgrPath : objMgrPaths) 501 { 502 // Get all managed objects of service 503 auto objects = util::SDBusPlus::getManagedObjects<PropertyVariantType>( 504 _bus, service, objMgrPath); 505 506 // Add what's returned to the cache of objects 507 for (auto& object : objects) 508 { 509 auto itPath = _objects.find(object.first); 510 if (itPath != _objects.end()) 511 { 512 // Path found in cache 513 for (auto& interface : itPath->second) 514 { 515 auto itIntf = itPath->second.find(interface.first); 516 if (itIntf != itPath->second.end()) 517 { 518 // Interface found in cache 519 for (auto& property : itIntf->second) 520 { 521 auto itProp = itIntf->second.find(property.first); 522 if (itProp != itIntf->second.end()) 523 { 524 // Property found, update value 525 itProp->second = property.second; 526 } 527 else 528 { 529 itIntf->second.insert(property); 530 } 531 } 532 } 533 else 534 { 535 // Interface not found in cache 536 itPath->second.insert(interface); 537 } 538 } 539 } 540 else 541 { 542 // Path not found in cache 543 _objects.insert(object); 544 } 545 } 546 } 547 } 548 549 const std::optional<PropertyVariantType> 550 Manager::getProperty(const std::string& path, const std::string& intf, 551 const std::string& prop) 552 { 553 // TODO Objects hosted by fan control (i.e. ThermalMode) are required to 554 // update the cache upon being set/updated 555 auto itPath = _objects.find(path); 556 if (itPath != _objects.end()) 557 { 558 auto itIntf = itPath->second.find(intf); 559 if (itIntf != itPath->second.end()) 560 { 561 auto itProp = itIntf->second.find(prop); 562 if (itProp != itIntf->second.end()) 563 { 564 return itProp->second; 565 } 566 } 567 } 568 569 return std::nullopt; 570 } 571 572 void Manager::addTimer(const TimerType type, 573 const std::chrono::microseconds interval, 574 std::unique_ptr<TimerPkg> pkg) 575 { 576 auto dataPtr = 577 std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg))); 578 Timer timer(_event, 579 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr))); 580 if (type == TimerType::repeating) 581 { 582 timer.restart(interval); 583 } 584 else if (type == TimerType::oneshot) 585 { 586 timer.restartOnce(interval); 587 } 588 else 589 { 590 throw std::invalid_argument("Invalid Timer Type"); 591 } 592 _timers.emplace_back(std::move(dataPtr), std::move(timer)); 593 } 594 595 void Manager::addGroup(const Group& group) 596 { 597 const auto& members = group.getMembers(); 598 for (const auto& member : members) 599 { 600 try 601 { 602 auto service = getService(member, group.getInterface()); 603 604 auto variant = 605 util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 606 service, member, group.getInterface(), group.getProperty()); 607 608 setProperty(member, group.getInterface(), group.getProperty(), 609 variant); 610 } 611 catch (const std::exception& e) 612 { 613 try 614 { 615 _objects.at(member) 616 .at(group.getInterface()) 617 .erase(group.getProperty()); 618 } 619 catch (const std::out_of_range&) 620 {} 621 } 622 } 623 } 624 625 void Manager::timerExpired(TimerData& data) 626 { 627 if (std::get<bool>(data.second)) 628 { 629 const auto& groups = std::get<const std::vector<Group>&>(data.second); 630 std::for_each(groups.begin(), groups.end(), 631 [this](const auto& group) { addGroup(group); }); 632 } 633 634 auto& actions = 635 std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second); 636 // Perform the actions in the timer data 637 std::for_each(actions.begin(), actions.end(), 638 [](auto& action) { action->run(); }); 639 640 // Remove oneshot timers after they expired 641 if (data.first == TimerType::oneshot) 642 { 643 auto itTimer = std::find_if( 644 _timers.begin(), _timers.end(), [&data](const auto& timer) { 645 return (data.first == timer.first->first && 646 (std::get<std::string>(data.second) == 647 std::get<std::string>(timer.first->second))); 648 }); 649 if (itTimer != std::end(_timers)) 650 { 651 _timers.erase(itTimer); 652 } 653 } 654 } 655 656 void Manager::handleSignal(sdbusplus::message::message& msg, 657 const std::vector<SignalPkg>& pkgs) 658 { 659 for (auto& pkg : pkgs) 660 { 661 // Handle the signal callback and only run the actions if the handler 662 // updated the cache for the given SignalObject 663 if (std::get<SignalHandler>(pkg)(msg, std::get<SignalObject>(pkg), 664 *this)) 665 { 666 // Perform the actions in the handler package 667 auto& actions = std::get<SignalActions>(pkg); 668 std::for_each(actions.begin(), actions.end(), 669 [](auto& action) { action.get()->run(); }); 670 } 671 } 672 } 673 674 void Manager::setProfiles() 675 { 676 // Profiles JSON config file is optional 677 auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName, 678 Profile::confFileName, true); 679 680 _profiles.clear(); 681 if (!confFile.empty()) 682 { 683 for (const auto& entry : fan::JsonConfig::load(confFile)) 684 { 685 auto obj = std::make_unique<Profile>(entry); 686 _profiles.emplace( 687 std::make_pair(obj->getName(), obj->getProfiles()), 688 std::move(obj)); 689 } 690 } 691 692 // Ensure all configurations use the same set of active profiles 693 // (In case a profile's active state changes during configuration) 694 _activeProfiles.clear(); 695 for (const auto& profile : _profiles) 696 { 697 if (profile.second->isActive()) 698 { 699 _activeProfiles.emplace_back(profile.first.first); 700 } 701 } 702 } 703 704 } // namespace phosphor::fan::control::json 705