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