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 } 238 239 const std::vector<std::string>& Manager::getActiveProfiles() 240 { 241 return _activeProfiles; 242 } 243 244 bool Manager::inConfig(const configKey& input, const configKey& comp) 245 { 246 // Config names dont match, do not include in config 247 if (input.first != comp.first) 248 { 249 return false; 250 } 251 // No profiles specified by input config, can be used in any config 252 if (input.second.empty()) 253 { 254 return true; 255 } 256 else 257 { 258 // Profiles must have one match in the other's profiles(and they must be 259 // an active profile) to be used in the config 260 return std::any_of( 261 input.second.begin(), input.second.end(), 262 [&comp](const auto& lProfile) { 263 return std::any_of( 264 comp.second.begin(), comp.second.end(), 265 [&lProfile](const auto& rProfile) { 266 if (lProfile != rProfile) 267 { 268 return false; 269 } 270 auto activeProfs = getActiveProfiles(); 271 return std::find(activeProfs.begin(), activeProfs.end(), 272 lProfile) != activeProfs.end(); 273 }); 274 }); 275 } 276 } 277 278 bool Manager::hasOwner(const std::string& path, const std::string& intf) 279 { 280 auto itServ = _servTree.find(path); 281 if (itServ == _servTree.end()) 282 { 283 // Path not found in cache, therefore owner missing 284 return false; 285 } 286 for (const auto& service : itServ->second) 287 { 288 auto itIntf = std::find_if( 289 service.second.second.begin(), service.second.second.end(), 290 [&intf](const auto& interface) { return intf == interface; }); 291 if (itIntf != std::end(service.second.second)) 292 { 293 // Service found, return owner state 294 return service.second.first; 295 } 296 } 297 // Interface not found in cache, therefore owner missing 298 return false; 299 } 300 301 void Manager::setOwner(const std::string& path, const std::string& serv, 302 const std::string& intf, bool isOwned) 303 { 304 // Set owner state for specific object given 305 auto& ownIntf = _servTree[path][serv]; 306 ownIntf.first = isOwned; 307 auto itIntf = std::find_if( 308 ownIntf.second.begin(), ownIntf.second.end(), 309 [&intf](const auto& interface) { return intf == interface; }); 310 if (itIntf == std::end(ownIntf.second)) 311 { 312 ownIntf.second.emplace_back(intf); 313 } 314 315 // Update owner state on all entries of the same `serv` & `intf` 316 for (auto& itPath : _servTree) 317 { 318 if (itPath.first == path) 319 { 320 // Already set/updated owner on this path for `serv` & `intf` 321 continue; 322 } 323 for (auto& itServ : itPath.second) 324 { 325 if (itServ.first != serv) 326 { 327 continue; 328 } 329 auto itIntf = std::find_if( 330 itServ.second.second.begin(), itServ.second.second.end(), 331 [&intf](const auto& interface) { return intf == interface; }); 332 if (itIntf != std::end(itServ.second.second)) 333 { 334 itServ.second.first = isOwned; 335 } 336 } 337 } 338 } 339 340 const std::string& Manager::findService(const std::string& path, 341 const std::string& intf) 342 { 343 static const std::string empty = ""; 344 345 auto itServ = _servTree.find(path); 346 if (itServ != _servTree.end()) 347 { 348 for (const auto& service : itServ->second) 349 { 350 auto itIntf = std::find_if( 351 service.second.second.begin(), service.second.second.end(), 352 [&intf](const auto& interface) { return intf == interface; }); 353 if (itIntf != std::end(service.second.second)) 354 { 355 // Service found, return service name 356 return service.first; 357 } 358 } 359 } 360 361 return empty; 362 } 363 364 void Manager::addServices(const std::string& intf, int32_t depth) 365 { 366 // Get all subtree objects for the given interface 367 auto objects = util::SDBusPlus::getSubTreeRaw(util::SDBusPlus::getBus(), 368 "/", intf, depth); 369 // Add what's returned to the cache of path->services 370 for (auto& itPath : objects) 371 { 372 auto pathIter = _servTree.find(itPath.first); 373 if (pathIter != _servTree.end()) 374 { 375 // Path found in cache 376 for (auto& itServ : itPath.second) 377 { 378 auto servIter = pathIter->second.find(itServ.first); 379 if (servIter != pathIter->second.end()) 380 { 381 // Service found in cache 382 for (auto& itIntf : itServ.second) 383 { 384 if (std::find(servIter->second.second.begin(), 385 servIter->second.second.end(), 386 itIntf) == servIter->second.second.end()) 387 { 388 // Add interface to cache 389 servIter->second.second.emplace_back(itIntf); 390 } 391 } 392 } 393 else 394 { 395 // Service not found in cache 396 auto intfs = {intf}; 397 pathIter->second[itServ.first] = 398 std::make_pair(true, intfs); 399 } 400 } 401 } 402 else 403 { 404 // Path not found in cache 405 auto intfs = {intf}; 406 _servTree[itPath.first] = { 407 {itPath.second.begin()->first, std::make_pair(true, intfs)}}; 408 } 409 } 410 } 411 412 const std::string& Manager::getService(const std::string& path, 413 const std::string& intf) 414 { 415 // Retrieve service from cache 416 const auto& serviceName = findService(path, intf); 417 if (serviceName.empty()) 418 { 419 addServices(intf, 0); 420 return findService(path, intf); 421 } 422 423 return serviceName; 424 } 425 426 std::vector<std::string> Manager::findPaths(const std::string& serv, 427 const std::string& intf) 428 { 429 std::vector<std::string> paths; 430 431 for (const auto& path : _servTree) 432 { 433 auto itServ = path.second.find(serv); 434 if (itServ != path.second.end()) 435 { 436 if (std::find(itServ->second.second.begin(), 437 itServ->second.second.end(), 438 intf) != itServ->second.second.end()) 439 { 440 if (std::find(paths.begin(), paths.end(), path.first) == 441 paths.end()) 442 { 443 paths.push_back(path.first); 444 } 445 } 446 } 447 } 448 449 return paths; 450 } 451 452 std::vector<std::string> Manager::getPaths(const std::string& serv, 453 const std::string& intf) 454 { 455 auto paths = findPaths(serv, intf); 456 if (paths.empty()) 457 { 458 addServices(intf, 0); 459 return findPaths(serv, intf); 460 } 461 462 return paths; 463 } 464 465 void Manager::addObjects(const std::string& path, const std::string& intf, 466 const std::string& prop) 467 { 468 auto service = getService(path, intf); 469 if (service.empty()) 470 { 471 // Log service not found for object 472 log<level::DEBUG>( 473 fmt::format("Unable to get service name for path {}, interface {}", 474 path, intf) 475 .c_str()); 476 return; 477 } 478 479 auto objMgrPaths = getPaths(service, "org.freedesktop.DBus.ObjectManager"); 480 if (objMgrPaths.empty()) 481 { 482 // No object manager interface provided by service? 483 // Attempt to retrieve property directly 484 auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 485 _bus, service, path, intf, prop); 486 _objects[path][intf][prop] = variant; 487 return; 488 } 489 490 for (const auto& objMgrPath : objMgrPaths) 491 { 492 // Get all managed objects of service 493 auto objects = util::SDBusPlus::getManagedObjects<PropertyVariantType>( 494 _bus, service, objMgrPath); 495 496 // Add what's returned to the cache of objects 497 for (auto& object : objects) 498 { 499 auto itPath = _objects.find(object.first); 500 if (itPath != _objects.end()) 501 { 502 // Path found in cache 503 for (auto& interface : itPath->second) 504 { 505 auto itIntf = itPath->second.find(interface.first); 506 if (itIntf != itPath->second.end()) 507 { 508 // Interface found in cache 509 for (auto& property : itIntf->second) 510 { 511 auto itProp = itIntf->second.find(property.first); 512 if (itProp != itIntf->second.end()) 513 { 514 // Property found, update value 515 itProp->second = property.second; 516 } 517 else 518 { 519 itIntf->second.insert(property); 520 } 521 } 522 } 523 else 524 { 525 // Interface not found in cache 526 itPath->second.insert(interface); 527 } 528 } 529 } 530 else 531 { 532 // Path not found in cache 533 _objects.insert(object); 534 } 535 } 536 } 537 } 538 539 const std::optional<PropertyVariantType> 540 Manager::getProperty(const std::string& path, const std::string& intf, 541 const std::string& prop) 542 { 543 // TODO Objects hosted by fan control (i.e. ThermalMode) are required to 544 // update the cache upon being set/updated 545 auto itPath = _objects.find(path); 546 if (itPath != _objects.end()) 547 { 548 auto itIntf = itPath->second.find(intf); 549 if (itIntf != itPath->second.end()) 550 { 551 auto itProp = itIntf->second.find(prop); 552 if (itProp != itIntf->second.end()) 553 { 554 return itProp->second; 555 } 556 } 557 } 558 559 return std::nullopt; 560 } 561 562 void Manager::addTimer(const TimerType type, 563 const std::chrono::microseconds interval, 564 std::unique_ptr<TimerPkg> pkg) 565 { 566 auto dataPtr = 567 std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg))); 568 Timer timer(_event, 569 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr))); 570 if (type == TimerType::repeating) 571 { 572 timer.restart(interval); 573 } 574 else if (type == TimerType::oneshot) 575 { 576 timer.restartOnce(interval); 577 } 578 else 579 { 580 throw std::invalid_argument("Invalid Timer Type"); 581 } 582 _timers.emplace_back(std::move(dataPtr), std::move(timer)); 583 } 584 585 void Manager::addGroup(const Group& group) 586 { 587 const auto& members = group.getMembers(); 588 for (const auto& member : members) 589 { 590 try 591 { 592 auto service = getService(member, group.getInterface()); 593 594 auto variant = 595 util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 596 service, member, group.getInterface(), group.getProperty()); 597 598 setProperty(member, group.getInterface(), group.getProperty(), 599 variant); 600 } 601 catch (const std::exception& e) 602 { 603 try 604 { 605 _objects.at(member) 606 .at(group.getInterface()) 607 .erase(group.getProperty()); 608 } 609 catch (const std::out_of_range&) 610 {} 611 } 612 } 613 } 614 615 void Manager::timerExpired(TimerData& data) 616 { 617 if (std::get<bool>(data.second)) 618 { 619 const auto& groups = std::get<const std::vector<Group>&>(data.second); 620 std::for_each(groups.begin(), groups.end(), 621 [this](const auto& group) { addGroup(group); }); 622 } 623 624 auto& actions = 625 std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second); 626 // Perform the actions in the timer data 627 std::for_each(actions.begin(), actions.end(), 628 [](auto& action) { action->run(); }); 629 630 // Remove oneshot timers after they expired 631 if (data.first == TimerType::oneshot) 632 { 633 auto itTimer = std::find_if( 634 _timers.begin(), _timers.end(), [&data](const auto& timer) { 635 return (data.first == timer.first->first && 636 (std::get<std::string>(data.second) == 637 std::get<std::string>(timer.first->second))); 638 }); 639 if (itTimer != std::end(_timers)) 640 { 641 _timers.erase(itTimer); 642 } 643 } 644 } 645 646 void Manager::handleSignal(sdbusplus::message::message& msg, 647 const std::vector<SignalPkg>& pkgs) 648 { 649 for (auto& pkg : pkgs) 650 { 651 // Handle the signal callback and only run the actions if the handler 652 // updated the cache for the given SignalObject 653 if (std::get<SignalHandler>(pkg)(msg, std::get<SignalObject>(pkg), 654 *this)) 655 { 656 // Perform the actions in the handler package 657 auto& actions = std::get<SignalActions>(pkg); 658 std::for_each(actions.begin(), actions.end(), 659 [](auto& action) { action.get()->run(); }); 660 } 661 } 662 } 663 664 void Manager::setProfiles() 665 { 666 // Profiles JSON config file is optional 667 auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName, 668 Profile::confFileName, true); 669 670 _profiles.clear(); 671 if (!confFile.empty()) 672 { 673 for (const auto& entry : fan::JsonConfig::load(confFile)) 674 { 675 auto obj = std::make_unique<Profile>(entry); 676 _profiles.emplace( 677 std::make_pair(obj->getName(), obj->getProfiles()), 678 std::move(obj)); 679 } 680 } 681 682 // Ensure all configurations use the same set of active profiles 683 // (In case a profile's active state changes during configuration) 684 _activeProfiles.clear(); 685 for (const auto& profile : _profiles) 686 { 687 if (profile.second->isActive()) 688 { 689 _activeProfiles.emplace_back(profile.first.first); 690 } 691 } 692 } 693 694 } // namespace phosphor::fan::control::json 695