blob: eb5a6ca6913c9c0994a2ac9c91d6a5f69e56846d [file] [log] [blame]
Matthew Bartha227a162020-08-05 10:51:45 -05001/**
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
Matthew Barthd9cb63b2021-03-24 14:36:49 -050018#include "action.hpp"
Matthew Barth44ab7692021-03-26 11:40:10 -050019#include "config_base.hpp"
20#include "event.hpp"
Matthew Barthd9cb63b2021-03-24 14:36:49 -050021#include "group.hpp"
Matthew Barthacd737c2021-03-04 11:04:01 -060022#include "json_config.hpp"
Matthew Barth48f44da2021-05-27 15:43:34 -050023#include "power_state.hpp"
Matthew Barth06764942021-03-04 09:28:40 -060024#include "profile.hpp"
Matthew Barth9403a212021-05-17 09:31:50 -050025#include "sdbusplus.hpp"
Matthew Barthacd737c2021-03-04 11:04:01 -060026#include "zone.hpp"
Matthew Barth06764942021-03-04 09:28:40 -060027
Matthew Barth68ac0042021-06-01 15:38:36 -050028#include <fmt/format.h>
29
Matthew Bartha227a162020-08-05 10:51:45 -050030#include <nlohmann/json.hpp>
Matthew Barth68ac0042021-06-01 15:38:36 -050031#include <phosphor-logging/log.hpp>
Matthew Bartha227a162020-08-05 10:51:45 -050032#include <sdbusplus/bus.hpp>
Matthew Barth1542fb52021-06-10 14:09:09 -050033#include <sdbusplus/server/manager.hpp>
Matthew Barth06764942021-03-04 09:28:40 -060034#include <sdeventplus/event.hpp>
Matt Spinler2fc0a352021-10-04 15:10:57 -050035#include <sdeventplus/source/event.hpp>
Matthew Barthd9cb63b2021-03-24 14:36:49 -050036#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>
Matthew Bartha227a162020-08-05 10:51:45 -050045
46namespace phosphor::fan::control::json
47{
48
49using json = nlohmann::json;
Matthew Barth68ac0042021-06-01 15:38:36 -050050using namespace phosphor::logging;
Matthew Bartha227a162020-08-05 10:51:45 -050051
Matthew Barthacd737c2021-03-04 11:04:01 -060052/* Application name to be appended to the path for loading a JSON config file */
53constexpr auto confAppName = "control";
54
Matthew Barthd9cb63b2021-03-24 14:36:49 -050055/* Type of timers supported */
56enum 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
Matthew Barthd9cb63b2021-03-24 14:36:49 -050065 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions
66 * that run when the timer expires
67 */
Matthew Barth00f6aa02021-04-09 10:49:47 -050068using TimerPkg =
69 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>;
Matthew Barthd9cb63b2021-03-24 14:36:49 -050070/**
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 */
76using TimerData = std::pair<TimerType, TimerPkg>;
77/* Dbus event timer */
78using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
79
Matthew Barthebabc042021-05-13 15:38:58 -050080/* Dbus signal object */
81constexpr auto Path = 0;
82constexpr auto Intf = 1;
83constexpr auto Prop = 2;
84using SignalObject = std::tuple<std::string, std::string, std::string>;
85/* Dbus signal actions */
86using 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 */
92using 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 */
101using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>;
102/**
103 * Data associated to a subscribed signal
104 * Tuple constructed of:
Matthew Barthfac8a2f2021-06-10 15:50:36 -0500105 * std::vector<SignalPkg> = List of the signal's packages
Matthew Barthebabc042021-05-13 15:38:58 -0500106 * std::unique_ptr<sdbusplus::server::match::match> =
107 * Pointer to match holding the subscription to a signal
108 */
Matthew Barthfac8a2f2021-06-10 15:50:36 -0500109using SignalData = std::tuple<std::vector<SignalPkg>,
Matthew Barthebabc042021-05-13 15:38:58 -0500110 std::unique_ptr<sdbusplus::server::match::match>>;
111
Matthew Bartha227a162020-08-05 10:51:45 -0500112/**
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 */
124class 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 *
Matthew Barth06764942021-03-04 09:28:40 -0600138 * @param[in] event - sdeventplus event loop
Matthew Bartha227a162020-08-05 10:51:45 -0500139 */
Matthew Barth9403a212021-05-17 09:31:50 -0500140 Manager(const sdeventplus::Event& event);
Matthew Bartha227a162020-08-05 10:51:45 -0500141
142 /**
Matthew Barthe91ac862021-05-25 16:22:17 -0500143 * @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 /**
Matt Spinler2fc0a352021-10-04 15:10:57 -0500150 * @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 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600157 * @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 *
Matthew Barthacd737c2021-03-04 11:04:01 -0600169 * @param[in] isOptional - JSON configuration file is optional or not
Matthew Barth603ef162021-03-24 15:34:53 -0500170 * @param[in] args - Arguments to be forwarded to each instance of `T`
Matthew Barthe6d1f782021-05-14 12:52:20 -0500171 * (*Note that a sdbusplus bus object is required as the first argument)
Matthew Barthacd737c2021-03-04 11:04:01 -0600172 *
173 * @return Map of configuration entries
174 * Map of configuration keys to their corresponding configuration object
175 */
Matthew Barth603ef162021-03-24 15:34:53 -0500176 template <typename T, typename... Args>
Matthew Barthe6d1f782021-05-14 12:52:20 -0500177 static std::map<configKey, std::unique_ptr<T>> getConfig(bool isOptional,
178 Args&&... args)
Matthew Barthacd737c2021-03-04 11:04:01 -0600179 {
180 std::map<configKey, std::unique_ptr<T>> config;
181
Matthew Barth9403a212021-05-17 09:31:50 -0500182 auto confFile =
183 fan::JsonConfig::getConfFile(util::SDBusPlus::getBus(), confAppName,
184 T::confFileName, isOptional);
Matthew Barthacd737c2021-03-04 11:04:01 -0600185 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
Matthew Barth42428052021-03-30 12:50:59 -0500199 if (!profiles.empty() &&
200 !std::any_of(profiles.begin(), profiles.end(),
Matthew Barthacd737c2021-03-04 11:04:01 -0600201 [](const auto& name) {
202 return std::find(
203 getActiveProfiles().begin(),
204 getActiveProfiles().end(),
205 name) !=
206 getActiveProfiles().end();
207 }))
208 {
209 continue;
210 }
211 }
Matthew Barth603ef162021-03-24 15:34:53 -0500212 auto obj =
213 std::make_unique<T>(entry, std::forward<Args>(args)...);
Matthew Barthacd737c2021-03-04 11:04:01 -0600214 config.emplace(
215 std::make_pair(obj->getName(), obj->getProfiles()),
216 std::move(obj));
217 }
Matthew Barth68ac0042021-06-01 15:38:36 -0500218 log<level::INFO>(
219 fmt::format("Configuration({}) loaded successfully",
220 T::confFileName)
221 .c_str());
Matthew Barthacd737c2021-03-04 11:04:01 -0600222 }
223 return config;
224 }
225
226 /**
Matthew Barth0206c722021-03-30 15:20:05 -0500227 * @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 /**
Matthew Barth12cb1252021-03-08 16:47:30 -0600238 * @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 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500249 * @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 *
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500263 * @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 */
Matthew Barth98f6fc12021-04-16 10:48:37 -0500269 static void addServices(const std::string& intf, int32_t depth);
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500270
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 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500288 * @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 /**
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500330 * @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 /**
Matthew Barthc2691402021-05-13 16:20:32 -0500344 * @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 /**
Matthew Barth07fecfc2021-01-29 09:04:43 -0600360 * @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 /**
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500376 * @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 /**
Matthew Barthebabc042021-05-13 15:38:58 -0500394 * @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,
Matthew Barthfac8a2f2021-06-10 15:50:36 -0500412 const std::vector<SignalPkg>& pkgs);
Matthew Barthebabc042021-05-13 15:38:58 -0500413
414 /**
Matthew Bartheebde062021-04-14 12:48:52 -0500415 * @brief Get the sdbusplus bus object
416 */
417 inline auto& getBus()
418 {
419 return _bus;
420 }
421
422 /**
Matthew Barth48f44da2021-05-27 15:43:34 -0500423 * @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
Matthew Barth3770a1d2021-06-10 15:09:37 -0500432 /**
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
Matt Spinlerd76351b2021-08-05 16:23:09 -0500442 /**
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
Matthew Bartha227a162020-08-05 10:51:45 -0500483 private:
Matthew Barth1542fb52021-06-10 14:09:09 -0500484 /* The sdbusplus bus object to use */
Matthew Barthacd737c2021-03-04 11:04:01 -0600485 sdbusplus::bus::bus& _bus;
486
Matthew Barth1542fb52021-06-10 14:09:09 -0500487 /* The sdeventplus even loop to use */
Matthew Barthacd737c2021-03-04 11:04:01 -0600488 sdeventplus::Event _event;
489
Matthew Barth1542fb52021-06-10 14:09:09 -0500490 /* The sdbusplus manager object to set the ObjectManager interface */
491 sdbusplus::server::manager::manager _mgr;
492
Matthew Barth3770a1d2021-06-10 15:09:37 -0500493 /* Whether loading the config files is allowed or not */
494 bool _loadAllowed;
495
Matthew Barth48f44da2021-05-27 15:43:34 -0500496 /* The system's power state determination object */
497 std::unique_ptr<PowerState> _powerState;
498
Matthew Barth06764942021-03-04 09:28:40 -0600499 /* List of profiles configured */
Matthew Barthacd737c2021-03-04 11:04:01 -0600500 std::map<configKey, std::unique_ptr<Profile>> _profiles;
Matthew Barth06764942021-03-04 09:28:40 -0600501
502 /* List of active profiles */
Matthew Barthacd737c2021-03-04 11:04:01 -0600503 static std::vector<std::string> _activeProfiles;
504
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500505 /* 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>>>>
Matthew Barth12cb1252021-03-08 16:47:30 -0600509 _servTree;
510
Matthew Barth07fecfc2021-01-29 09:04:43 -0600511 /* 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
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500517 /* List of timers and their data to be processed when expired */
518 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers;
519
Matthew Barthebabc042021-05-13 15:38:58 -0500520 /* Map of signal match strings to a list of signal handler data */
521 std::unordered_map<std::string, std::vector<SignalData>> _signals;
522
Matthew Barthacd737c2021-03-04 11:04:01 -0600523 /* List of zones configured */
524 std::map<configKey, std::unique_ptr<Zone>> _zones;
525
Matthew Barth44ab7692021-03-26 11:40:10 -0500526 /* List of events configured */
527 std::map<configKey, std::unique_ptr<Event>> _events;
528
Matt Spinler2fc0a352021-10-04 15:10:57 -0500529 /* 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
Matthew Barthacd737c2021-03-04 11:04:01 -0600533 /**
Matt Spinlerd76351b2021-08-05 16:23:09 -0500534 * @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 /**
Matthew Barthb2cd93f2021-06-16 16:37:40 -0500541 * @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 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500551 * @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 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500563 * @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 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600575 * @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();
Matt Spinler2fc0a352021-10-04 15:10:57 -0500583
584 /**
585 * @brief Callback from _flightRecEventSource to dump the flight recorder
586 */
587 void dumpFlightRecorder(sdeventplus::source::EventBase&);
Matthew Bartha227a162020-08-05 10:51:45 -0500588};
589
590} // namespace phosphor::fan::control::json