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