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