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 #pragma once 17 18 #include "action.hpp" 19 #include "config_base.hpp" 20 #include "event.hpp" 21 #include "group.hpp" 22 #include "json_config.hpp" 23 #include "power_state.hpp" 24 #include "profile.hpp" 25 #include "sdbusplus.hpp" 26 #include "zone.hpp" 27 28 #include <fmt/format.h> 29 30 #include <nlohmann/json.hpp> 31 #include <phosphor-logging/log.hpp> 32 #include <sdbusplus/bus.hpp> 33 #include <sdbusplus/server/manager.hpp> 34 #include <sdeventplus/event.hpp> 35 #include <sdeventplus/source/event.hpp> 36 #include <sdeventplus/utility/timer.hpp> 37 38 #include <chrono> 39 #include <map> 40 #include <memory> 41 #include <optional> 42 #include <tuple> 43 #include <utility> 44 #include <vector> 45 46 namespace phosphor::fan::control::json 47 { 48 49 using json = nlohmann::json; 50 using namespace phosphor::logging; 51 52 /* Application name to be appended to the path for loading a JSON config file */ 53 constexpr auto confAppName = "control"; 54 55 /* Type of timers supported */ 56 enum class TimerType 57 { 58 oneshot, 59 repeating, 60 }; 61 /** 62 * Package of data required when a timer expires 63 * Tuple constructed of: 64 * std::string = Timer package unique identifier 65 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions 66 * that run when the timer expires 67 */ 68 using TimerPkg = 69 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>; 70 /** 71 * Data associated with a running timer that's used when it expires 72 * Pair constructed of: 73 * TimerType = Type of timer to manage expired timer instances 74 * TimerPkg = Package of data required when the timer expires 75 */ 76 using TimerData = std::pair<TimerType, TimerPkg>; 77 /* Dbus event timer */ 78 using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>; 79 80 /* Dbus signal object */ 81 constexpr auto Path = 0; 82 constexpr auto Intf = 1; 83 constexpr auto Prop = 2; 84 using SignalObject = std::tuple<std::string, std::string, std::string>; 85 /* Dbus signal actions */ 86 using SignalActions = 87 std::vector<std::reference_wrapper<std::unique_ptr<ActionBase>>>; 88 /** 89 * Signal handler function that handles parsing a signal's message for a 90 * particular signal object and stores the results in the manager 91 */ 92 using SignalHandler = std::function<bool(sdbusplus::message::message&, 93 const SignalObject&, Manager&)>; 94 /** 95 * Package of data required when a signal is received 96 * Tuple constructed of: 97 * SignalHandler = Signal handler function 98 * SignalObject = Dbus signal object 99 * SignalActions = List of actions that are run when the signal is received 100 */ 101 using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>; 102 /** 103 * Data associated to a subscribed signal 104 * Tuple constructed of: 105 * std::vector<SignalPkg> = List of the signal's packages 106 * std::unique_ptr<sdbusplus::server::match::match> = 107 * Pointer to match holding the subscription to a signal 108 */ 109 using SignalData = std::tuple<std::vector<SignalPkg>, 110 std::unique_ptr<sdbusplus::server::match::match>>; 111 112 /** 113 * @class Manager - Represents the fan control manager's configuration 114 * 115 * A fan control manager configuration is optional, therefore the "manager.json" 116 * file is also optional. The manager configuration is used to populate 117 * fan control's manager parameters which are used in how the application 118 * operates, not in how the fans are controlled. 119 * 120 * When no manager configuration exists, the fan control application starts, 121 * processes any configured events and then begins controlling fans according 122 * to those events. 123 */ 124 class Manager 125 { 126 public: 127 Manager() = delete; 128 Manager(const Manager&) = delete; 129 Manager(Manager&&) = delete; 130 Manager& operator=(const Manager&) = delete; 131 Manager& operator=(Manager&&) = delete; 132 ~Manager() = default; 133 134 /** 135 * Constructor 136 * Parses and populates the fan control manager attributes from a json file 137 * 138 * @param[in] event - sdeventplus event loop 139 */ 140 Manager(const sdeventplus::Event& event); 141 142 /** 143 * @brief Callback function to handle receiving a HUP signal to reload the 144 * JSON configurations. 145 */ 146 void sighupHandler(sdeventplus::source::Signal&, 147 const struct signalfd_siginfo*); 148 149 /** 150 * @brief Callback function to handle receiving a USR1 signal to dump 151 * the flight recorder. 152 */ 153 void sigUsr1Handler(sdeventplus::source::Signal&, 154 const struct signalfd_siginfo*); 155 156 /** 157 * @brief Get the active profiles of the system where an empty list 158 * represents that only configuration entries without a profile defined will 159 * be loaded. 160 * 161 * @return - The list of active profiles 162 */ 163 static const std::vector<std::string>& getActiveProfiles(); 164 165 /** 166 * @brief Load the configuration of a given JSON class object based on the 167 * active profiles 168 * 169 * @param[in] isOptional - JSON configuration file is optional or not 170 * @param[in] args - Arguments to be forwarded to each instance of `T` 171 * (*Note that a sdbusplus bus object is required as the first argument) 172 * 173 * @return Map of configuration entries 174 * Map of configuration keys to their corresponding configuration object 175 */ 176 template <typename T, typename... Args> 177 static std::map<configKey, std::unique_ptr<T>> getConfig(bool isOptional, 178 Args&&... args) 179 { 180 std::map<configKey, std::unique_ptr<T>> config; 181 182 auto confFile = 183 fan::JsonConfig::getConfFile(util::SDBusPlus::getBus(), confAppName, 184 T::confFileName, isOptional); 185 if (!confFile.empty()) 186 { 187 for (const auto& entry : fan::JsonConfig::load(confFile)) 188 { 189 if (entry.contains("profiles")) 190 { 191 std::vector<std::string> profiles; 192 for (const auto& profile : entry["profiles"]) 193 { 194 profiles.emplace_back( 195 profile.template get<std::string>()); 196 } 197 // Do not create the object if its profiles are not in the 198 // list of active profiles 199 if (!profiles.empty() && 200 !std::any_of(profiles.begin(), profiles.end(), 201 [](const auto& name) { 202 return std::find( 203 getActiveProfiles().begin(), 204 getActiveProfiles().end(), 205 name) != 206 getActiveProfiles().end(); 207 })) 208 { 209 continue; 210 } 211 } 212 auto obj = 213 std::make_unique<T>(entry, std::forward<Args>(args)...); 214 config.emplace( 215 std::make_pair(obj->getName(), obj->getProfiles()), 216 std::move(obj)); 217 } 218 log<level::INFO>( 219 fmt::format("Configuration({}) loaded successfully", 220 T::confFileName) 221 .c_str()); 222 } 223 return config; 224 } 225 226 /** 227 * @brief Check if the given input configuration key matches with another 228 * configuration key that it's to be included in 229 * 230 * @param[in] input - Config key to be included in another config object 231 * @param[in] comp - Config key of the config object to compare with 232 * 233 * @return Whether the configuration object should be included 234 */ 235 static bool inConfig(const configKey& input, const configKey& comp); 236 237 /** 238 * @brief Check if the given path and inteface is owned by a dbus service 239 * 240 * @param[in] path - Dbus object path 241 * @param[in] intf - Dbus object interface 242 * 243 * @return - Whether the service has an owner for the given object path and 244 * interface 245 */ 246 static bool hasOwner(const std::string& path, const std::string& intf); 247 248 /** 249 * @brief Sets the dbus service owner state of a given object 250 * 251 * @param[in] path - Dbus object path 252 * @param[in] serv - Dbus service name 253 * @param[in] intf - Dbus object interface 254 * @param[in] isOwned - Dbus service owner state 255 */ 256 void setOwner(const std::string& path, const std::string& serv, 257 const std::string& intf, bool isOwned); 258 259 /** 260 * @brief Add a set of services for a path and interface by retrieving all 261 * the path subtrees to the given depth from root for the interface 262 * 263 * @param[in] intf - Interface to add services for 264 * @param[in] depth - Depth of tree traversal from root path 265 * 266 * @throws - DBusMethodError 267 * Throws a DBusMethodError when the `getSubTree` method call fails 268 */ 269 static void addServices(const std::string& intf, int32_t depth); 270 271 /** 272 * @brief Get the service for a given path and interface from cached 273 * dataset and attempt to add all the services for the given path/interface 274 * when it's not found 275 * 276 * @param[in] path - Path to get service for 277 * @param[in] intf - Interface to get service for 278 * 279 * @return - The now cached service name 280 * 281 * @throws - DBusMethodError 282 * Ripples up a DBusMethodError exception from calling addServices 283 */ 284 static const std::string& getService(const std::string& path, 285 const std::string& intf); 286 287 /** 288 * @brief Get all the object paths for a given service and interface from 289 * the cached dataset and try to add all the services for the given 290 * interface when no paths are found and then attempt to get all the object 291 * paths again 292 * 293 * @param[in] serv - Service name to get paths for 294 * @param[in] intf - Interface to get paths for 295 * 296 * @return The cached object paths 297 */ 298 std::vector<std::string> getPaths(const std::string& serv, 299 const std::string& intf); 300 301 /** 302 * @brief Add objects to the cached dataset by first using 303 * `getManagedObjects` for the same service providing the given path and 304 * interface or just add the single object of the given path, interface, and 305 * property if that fails. 306 * 307 * @param[in] path - Dbus object's path 308 * @param[in] intf - Dbus object's interface 309 * @param[in] prop - Dbus object's property 310 * 311 * @throws - DBusMethodError 312 * Throws a DBusMethodError when the the service is failed to be found or 313 * when the `getManagedObjects` method call fails 314 */ 315 void addObjects(const std::string& path, const std::string& intf, 316 const std::string& prop); 317 318 /** 319 * @brief Get an object's property value 320 * 321 * @param[in] path - Dbus object's path 322 * @param[in] intf - Dbus object's interface 323 * @param[in] prop - Dbus object's property 324 */ 325 const std::optional<PropertyVariantType> 326 getProperty(const std::string& path, const std::string& intf, 327 const std::string& prop); 328 329 /** 330 * @brief Set/update an object's property value 331 * 332 * @param[in] path - Dbus object's path 333 * @param[in] intf - Dbus object's interface 334 * @param[in] prop - Dbus object's property 335 * @param[in] value - Dbus object's property value 336 */ 337 void setProperty(const std::string& path, const std::string& intf, 338 const std::string& prop, PropertyVariantType value) 339 { 340 _objects[path][intf][prop] = std::move(value); 341 } 342 343 /** 344 * @brief Remove an object's interface 345 * 346 * @param[in] path - Dbus object's path 347 * @param[in] intf - Dbus object's interface 348 */ 349 inline void removeInterface(const std::string& path, 350 const std::string& intf) 351 { 352 auto itPath = _objects.find(path); 353 if (itPath != std::end(_objects)) 354 { 355 _objects[path].erase(intf); 356 } 357 } 358 359 /** 360 * @brief Get the object's property value as a variant 361 * 362 * @param[in] path - Path of the object containing the property 363 * @param[in] intf - Interface name containing the property 364 * @param[in] prop - Name of property 365 * 366 * @return - The object's property value as a variant 367 */ 368 static inline auto getObjValueVariant(const std::string& path, 369 const std::string& intf, 370 const std::string& prop) 371 { 372 return _objects.at(path).at(intf).at(prop); 373 }; 374 375 /** 376 * @brief Add a dbus timer 377 * 378 * @param[in] type - Type of timer 379 * @param[in] interval - Timer interval in microseconds 380 * @param[in] pkg - Packaged data for when timer expires 381 */ 382 void addTimer(const TimerType type, 383 const std::chrono::microseconds interval, 384 std::unique_ptr<TimerPkg> pkg); 385 386 /** 387 * @brief Callback when a timer expires 388 * 389 * @param[in] data - Data to be used when the timer expired 390 */ 391 void timerExpired(TimerData& data); 392 393 /** 394 * @brief Get the signal data for a given match string 395 * 396 * @param[in] sigMatch - Signal match string 397 * 398 * @return - Reference to the signal data for the given match string 399 */ 400 std::vector<SignalData>& getSignal(const std::string& sigMatch) 401 { 402 return _signals[sigMatch]; 403 } 404 405 /** 406 * @brief Handle receiving signals 407 * 408 * @param[in] msg - Signal message containing the signal's data 409 * @param[in] pkgs - Signal packages associated to the signal being handled 410 */ 411 void handleSignal(sdbusplus::message::message& msg, 412 const std::vector<SignalPkg>& pkgs); 413 414 /** 415 * @brief Get the sdbusplus bus object 416 */ 417 inline auto& getBus() 418 { 419 return _bus; 420 } 421 422 /** 423 * @brief Is the power state on 424 * 425 * @return Current power state of the system 426 */ 427 inline bool isPowerOn() const 428 { 429 return _powerState->isPowerOn(); 430 } 431 432 /** 433 * @brief Load all the fan control JSON configuration files 434 * 435 * This is where all the fan control JSON configuration files are parsed and 436 * loaded into their associated objects. Anything that needs to be done when 437 * the Manager object is constructed or handling a SIGHUP to reload the 438 * configurations needs to be done here. 439 */ 440 void load(); 441 442 /** 443 * @brief Sets a value in the parameter map. 444 * 445 * @param[in] name - The parameter name 446 * @param[in] value - The parameter value 447 */ 448 static void setParameter(const std::string& name, 449 const PropertyVariantType& value) 450 { 451 _parameters[name] = value; 452 } 453 454 /** 455 * @brief Returns a value from the parameter map 456 * 457 * @param[in] name - The parameter name 458 * 459 * @return The parameter value, or std::nullopt if not found 460 */ 461 static std::optional<PropertyVariantType> 462 getParameter(const std::string& name) 463 { 464 auto it = _parameters.find(name); 465 if (it != _parameters.end()) 466 { 467 return it->second; 468 } 469 470 return std::nullopt; 471 } 472 473 /** 474 * @brief Deletes a parameter from the parameter map 475 * 476 * @param[in] name - The parameter name 477 */ 478 static void deleteParameter(const std::string& name) 479 { 480 _parameters.erase(name); 481 } 482 483 private: 484 /* The sdbusplus bus object to use */ 485 sdbusplus::bus::bus& _bus; 486 487 /* The sdeventplus even loop to use */ 488 sdeventplus::Event _event; 489 490 /* The sdbusplus manager object to set the ObjectManager interface */ 491 sdbusplus::server::manager::manager _mgr; 492 493 /* Whether loading the config files is allowed or not */ 494 bool _loadAllowed; 495 496 /* The system's power state determination object */ 497 std::unique_ptr<PowerState> _powerState; 498 499 /* List of profiles configured */ 500 std::map<configKey, std::unique_ptr<Profile>> _profiles; 501 502 /* List of active profiles */ 503 static std::vector<std::string> _activeProfiles; 504 505 /* Subtree map of paths to services of interfaces(with ownership state) */ 506 static std::map< 507 std::string, 508 std::map<std::string, std::pair<bool, std::vector<std::string>>>> 509 _servTree; 510 511 /* Object map of paths to interfaces of properties and their values */ 512 static std::map< 513 std::string, 514 std::map<std::string, std::map<std::string, PropertyVariantType>>> 515 _objects; 516 517 /* List of timers and their data to be processed when expired */ 518 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers; 519 520 /* Map of signal match strings to a list of signal handler data */ 521 std::unordered_map<std::string, std::vector<SignalData>> _signals; 522 523 /* List of zones configured */ 524 std::map<configKey, std::unique_ptr<Zone>> _zones; 525 526 /* List of events configured */ 527 std::map<configKey, std::unique_ptr<Event>> _events; 528 529 /* The sdeventplus wrapper around sd_event_add_defer to dump the 530 * flight recorder from the event loop after the USR1 signal. */ 531 std::unique_ptr<sdeventplus::source::Defer> _flightRecEventSource; 532 533 /** 534 * @brief A map of parameter names and values that are something 535 * other than just D-Bus property values that other actions 536 * can set and use. 537 */ 538 static std::unordered_map<std::string, PropertyVariantType> _parameters; 539 540 /** 541 * @brief Callback for power state changes 542 * 543 * @param[in] powerStateOn - Whether the power state is on or not 544 * 545 * Callback function bound to the PowerState object instance to handle each 546 * time the power state changes. 547 */ 548 void powerStateChanged(bool powerStateOn); 549 550 /** 551 * @brief Find the service name for a given path and interface from the 552 * cached dataset 553 * 554 * @param[in] path - Path to get service for 555 * @param[in] intf - Interface to get service for 556 * 557 * @return - The cached service name 558 */ 559 static const std::string& findService(const std::string& path, 560 const std::string& intf); 561 562 /** 563 * @brief Find all the paths for a given service and interface from the 564 * cached dataset 565 * 566 * @param[in] serv - Service name to get paths for 567 * @param[in] intf - Interface to get paths for 568 * 569 * @return - The cached object paths 570 */ 571 std::vector<std::string> findPaths(const std::string& serv, 572 const std::string& intf); 573 574 /** 575 * @brief Parse and set the configured profiles from the profiles JSON file 576 * 577 * Retrieves the optional profiles JSON configuration file, parses it, and 578 * creates a list of configured profiles available to the other 579 * configuration files. These profiles can be used to remove or include 580 * entries within the other configuration files. 581 */ 582 void setProfiles(); 583 584 /** 585 * @brief Callback from _flightRecEventSource to dump the flight recorder 586 */ 587 void dumpFlightRecorder(sdeventplus::source::EventBase&); 588 }; 589 590 } // namespace phosphor::fan::control::json 591