blob: 56681382c019748aa89f5638e7233bfcf9ca0e61 [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 Bartha227a162020-08-05 10:51:45 -050028#include <nlohmann/json.hpp>
29#include <sdbusplus/bus.hpp>
Matthew Barth06764942021-03-04 09:28:40 -060030#include <sdeventplus/event.hpp>
Matthew Barthd9cb63b2021-03-24 14:36:49 -050031#include <sdeventplus/utility/timer.hpp>
32
33#include <chrono>
34#include <map>
35#include <memory>
36#include <optional>
37#include <tuple>
38#include <utility>
39#include <vector>
Matthew Bartha227a162020-08-05 10:51:45 -050040
41namespace phosphor::fan::control::json
42{
43
44using json = nlohmann::json;
45
Matthew Barthacd737c2021-03-04 11:04:01 -060046/* Application name to be appended to the path for loading a JSON config file */
47constexpr auto confAppName = "control";
48
Matthew Barthd9cb63b2021-03-24 14:36:49 -050049/* Type of timers supported */
50enum class TimerType
51{
52 oneshot,
53 repeating,
54};
55/**
56 * Package of data required when a timer expires
57 * Tuple constructed of:
58 * std::string = Timer package unique identifier
Matthew Barthd9cb63b2021-03-24 14:36:49 -050059 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions
60 * that run when the timer expires
61 */
Matthew Barth00f6aa02021-04-09 10:49:47 -050062using TimerPkg =
63 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>;
Matthew Barthd9cb63b2021-03-24 14:36:49 -050064/**
65 * Data associated with a running timer that's used when it expires
66 * Pair constructed of:
67 * TimerType = Type of timer to manage expired timer instances
68 * TimerPkg = Package of data required when the timer expires
69 */
70using TimerData = std::pair<TimerType, TimerPkg>;
71/* Dbus event timer */
72using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
73
Matthew Barthebabc042021-05-13 15:38:58 -050074/* Dbus signal object */
75constexpr auto Path = 0;
76constexpr auto Intf = 1;
77constexpr auto Prop = 2;
78using SignalObject = std::tuple<std::string, std::string, std::string>;
79/* Dbus signal actions */
80using SignalActions =
81 std::vector<std::reference_wrapper<std::unique_ptr<ActionBase>>>;
82/**
83 * Signal handler function that handles parsing a signal's message for a
84 * particular signal object and stores the results in the manager
85 */
86using SignalHandler = std::function<bool(sdbusplus::message::message&,
87 const SignalObject&, Manager&)>;
88/**
89 * Package of data required when a signal is received
90 * Tuple constructed of:
91 * SignalHandler = Signal handler function
92 * SignalObject = Dbus signal object
93 * SignalActions = List of actions that are run when the signal is received
94 */
95using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>;
96/**
97 * Data associated to a subscribed signal
98 * Tuple constructed of:
99 * std::unique_ptr<std::vector<SignalPkg>> =
100 * Pointer to the signal's packages
101 * std::unique_ptr<sdbusplus::server::match::match> =
102 * Pointer to match holding the subscription to a signal
103 */
104using SignalData = std::tuple<std::unique_ptr<std::vector<SignalPkg>>,
105 std::unique_ptr<sdbusplus::server::match::match>>;
106
Matthew Bartha227a162020-08-05 10:51:45 -0500107/**
108 * @class Manager - Represents the fan control manager's configuration
109 *
110 * A fan control manager configuration is optional, therefore the "manager.json"
111 * file is also optional. The manager configuration is used to populate
112 * fan control's manager parameters which are used in how the application
113 * operates, not in how the fans are controlled.
114 *
115 * When no manager configuration exists, the fan control application starts,
116 * processes any configured events and then begins controlling fans according
117 * to those events.
118 */
119class Manager
120{
121 public:
122 Manager() = delete;
123 Manager(const Manager&) = delete;
124 Manager(Manager&&) = delete;
125 Manager& operator=(const Manager&) = delete;
126 Manager& operator=(Manager&&) = delete;
127 ~Manager() = default;
128
129 /**
130 * Constructor
131 * Parses and populates the fan control manager attributes from a json file
132 *
Matthew Barth06764942021-03-04 09:28:40 -0600133 * @param[in] event - sdeventplus event loop
Matthew Bartha227a162020-08-05 10:51:45 -0500134 */
Matthew Barth9403a212021-05-17 09:31:50 -0500135 Manager(const sdeventplus::Event& event);
Matthew Bartha227a162020-08-05 10:51:45 -0500136
137 /**
Matthew Barthe91ac862021-05-25 16:22:17 -0500138 * @brief Callback function to handle receiving a HUP signal to reload the
139 * JSON configurations.
140 */
141 void sighupHandler(sdeventplus::source::Signal&,
142 const struct signalfd_siginfo*);
143
144 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600145 * @brief Get the active profiles of the system where an empty list
146 * represents that only configuration entries without a profile defined will
147 * be loaded.
148 *
149 * @return - The list of active profiles
150 */
151 static const std::vector<std::string>& getActiveProfiles();
152
153 /**
154 * @brief Load the configuration of a given JSON class object based on the
155 * active profiles
156 *
Matthew Barthacd737c2021-03-04 11:04:01 -0600157 * @param[in] isOptional - JSON configuration file is optional or not
Matthew Barth603ef162021-03-24 15:34:53 -0500158 * @param[in] args - Arguments to be forwarded to each instance of `T`
Matthew Barthe6d1f782021-05-14 12:52:20 -0500159 * (*Note that a sdbusplus bus object is required as the first argument)
Matthew Barthacd737c2021-03-04 11:04:01 -0600160 *
161 * @return Map of configuration entries
162 * Map of configuration keys to their corresponding configuration object
163 */
Matthew Barth603ef162021-03-24 15:34:53 -0500164 template <typename T, typename... Args>
Matthew Barthe6d1f782021-05-14 12:52:20 -0500165 static std::map<configKey, std::unique_ptr<T>> getConfig(bool isOptional,
166 Args&&... args)
Matthew Barthacd737c2021-03-04 11:04:01 -0600167 {
168 std::map<configKey, std::unique_ptr<T>> config;
169
Matthew Barth9403a212021-05-17 09:31:50 -0500170 auto confFile =
171 fan::JsonConfig::getConfFile(util::SDBusPlus::getBus(), confAppName,
172 T::confFileName, isOptional);
Matthew Barthacd737c2021-03-04 11:04:01 -0600173 if (!confFile.empty())
174 {
175 for (const auto& entry : fan::JsonConfig::load(confFile))
176 {
177 if (entry.contains("profiles"))
178 {
179 std::vector<std::string> profiles;
180 for (const auto& profile : entry["profiles"])
181 {
182 profiles.emplace_back(
183 profile.template get<std::string>());
184 }
185 // Do not create the object if its profiles are not in the
186 // list of active profiles
Matthew Barth42428052021-03-30 12:50:59 -0500187 if (!profiles.empty() &&
188 !std::any_of(profiles.begin(), profiles.end(),
Matthew Barthacd737c2021-03-04 11:04:01 -0600189 [](const auto& name) {
190 return std::find(
191 getActiveProfiles().begin(),
192 getActiveProfiles().end(),
193 name) !=
194 getActiveProfiles().end();
195 }))
196 {
197 continue;
198 }
199 }
Matthew Barth603ef162021-03-24 15:34:53 -0500200 auto obj =
201 std::make_unique<T>(entry, std::forward<Args>(args)...);
Matthew Barthacd737c2021-03-04 11:04:01 -0600202 config.emplace(
203 std::make_pair(obj->getName(), obj->getProfiles()),
204 std::move(obj));
205 }
206 }
207 return config;
208 }
209
210 /**
Matthew Barth0206c722021-03-30 15:20:05 -0500211 * @brief Check if the given input configuration key matches with another
212 * configuration key that it's to be included in
213 *
214 * @param[in] input - Config key to be included in another config object
215 * @param[in] comp - Config key of the config object to compare with
216 *
217 * @return Whether the configuration object should be included
218 */
219 static bool inConfig(const configKey& input, const configKey& comp);
220
221 /**
Matthew Barth12cb1252021-03-08 16:47:30 -0600222 * @brief Check if the given path and inteface is owned by a dbus service
223 *
224 * @param[in] path - Dbus object path
225 * @param[in] intf - Dbus object interface
226 *
227 * @return - Whether the service has an owner for the given object path and
228 * interface
229 */
230 static bool hasOwner(const std::string& path, const std::string& intf);
231
232 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500233 * @brief Sets the dbus service owner state of a given object
234 *
235 * @param[in] path - Dbus object path
236 * @param[in] serv - Dbus service name
237 * @param[in] intf - Dbus object interface
238 * @param[in] isOwned - Dbus service owner state
239 */
240 void setOwner(const std::string& path, const std::string& serv,
241 const std::string& intf, bool isOwned);
242
243 /**
244 * @brief Add a set of services for a path and interface by retrieving all
245 * the path subtrees to the given depth from root for the interface
246 *
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500247 * @param[in] intf - Interface to add services for
248 * @param[in] depth - Depth of tree traversal from root path
249 *
250 * @throws - DBusMethodError
251 * Throws a DBusMethodError when the `getSubTree` method call fails
252 */
Matthew Barth98f6fc12021-04-16 10:48:37 -0500253 static void addServices(const std::string& intf, int32_t depth);
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500254
255 /**
256 * @brief Get the service for a given path and interface from cached
257 * dataset and attempt to add all the services for the given path/interface
258 * when it's not found
259 *
260 * @param[in] path - Path to get service for
261 * @param[in] intf - Interface to get service for
262 *
263 * @return - The now cached service name
264 *
265 * @throws - DBusMethodError
266 * Ripples up a DBusMethodError exception from calling addServices
267 */
268 static const std::string& getService(const std::string& path,
269 const std::string& intf);
270
271 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500272 * @brief Get all the object paths for a given service and interface from
273 * the cached dataset and try to add all the services for the given
274 * interface when no paths are found and then attempt to get all the object
275 * paths again
276 *
277 * @param[in] serv - Service name to get paths for
278 * @param[in] intf - Interface to get paths for
279 *
280 * @return The cached object paths
281 */
282 std::vector<std::string> getPaths(const std::string& serv,
283 const std::string& intf);
284
285 /**
286 * @brief Add objects to the cached dataset by first using
287 * `getManagedObjects` for the same service providing the given path and
288 * interface or just add the single object of the given path, interface, and
289 * property if that fails.
290 *
291 * @param[in] path - Dbus object's path
292 * @param[in] intf - Dbus object's interface
293 * @param[in] prop - Dbus object's property
294 *
295 * @throws - DBusMethodError
296 * Throws a DBusMethodError when the the service is failed to be found or
297 * when the `getManagedObjects` method call fails
298 */
299 void addObjects(const std::string& path, const std::string& intf,
300 const std::string& prop);
301
302 /**
303 * @brief Get an object's property value
304 *
305 * @param[in] path - Dbus object's path
306 * @param[in] intf - Dbus object's interface
307 * @param[in] prop - Dbus object's property
308 */
309 const std::optional<PropertyVariantType>
310 getProperty(const std::string& path, const std::string& intf,
311 const std::string& prop);
312
313 /**
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500314 * @brief Set/update an object's property value
315 *
316 * @param[in] path - Dbus object's path
317 * @param[in] intf - Dbus object's interface
318 * @param[in] prop - Dbus object's property
319 * @param[in] value - Dbus object's property value
320 */
321 void setProperty(const std::string& path, const std::string& intf,
322 const std::string& prop, PropertyVariantType value)
323 {
324 _objects[path][intf][prop] = std::move(value);
325 }
326
327 /**
Matthew Barthc2691402021-05-13 16:20:32 -0500328 * @brief Remove an object's interface
329 *
330 * @param[in] path - Dbus object's path
331 * @param[in] intf - Dbus object's interface
332 */
333 inline void removeInterface(const std::string& path,
334 const std::string& intf)
335 {
336 auto itPath = _objects.find(path);
337 if (itPath != std::end(_objects))
338 {
339 _objects[path].erase(intf);
340 }
341 }
342
343 /**
Matthew Barth07fecfc2021-01-29 09:04:43 -0600344 * @brief Get the object's property value as a variant
345 *
346 * @param[in] path - Path of the object containing the property
347 * @param[in] intf - Interface name containing the property
348 * @param[in] prop - Name of property
349 *
350 * @return - The object's property value as a variant
351 */
352 static inline auto getObjValueVariant(const std::string& path,
353 const std::string& intf,
354 const std::string& prop)
355 {
356 return _objects.at(path).at(intf).at(prop);
357 };
358
359 /**
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500360 * @brief Add a dbus timer
361 *
362 * @param[in] type - Type of timer
363 * @param[in] interval - Timer interval in microseconds
364 * @param[in] pkg - Packaged data for when timer expires
365 */
366 void addTimer(const TimerType type,
367 const std::chrono::microseconds interval,
368 std::unique_ptr<TimerPkg> pkg);
369
370 /**
371 * @brief Callback when a timer expires
372 *
373 * @param[in] data - Data to be used when the timer expired
374 */
375 void timerExpired(TimerData& data);
376
377 /**
Matthew Barthebabc042021-05-13 15:38:58 -0500378 * @brief Get the signal data for a given match string
379 *
380 * @param[in] sigMatch - Signal match string
381 *
382 * @return - Reference to the signal data for the given match string
383 */
384 std::vector<SignalData>& getSignal(const std::string& sigMatch)
385 {
386 return _signals[sigMatch];
387 }
388
389 /**
390 * @brief Handle receiving signals
391 *
392 * @param[in] msg - Signal message containing the signal's data
393 * @param[in] pkgs - Signal packages associated to the signal being handled
394 */
395 void handleSignal(sdbusplus::message::message& msg,
396 const std::vector<SignalPkg>* pkgs);
397
398 /**
Matthew Bartheebde062021-04-14 12:48:52 -0500399 * @brief Get the sdbusplus bus object
400 */
401 inline auto& getBus()
402 {
403 return _bus;
404 }
405
406 /**
Matthew Barth48f44da2021-05-27 15:43:34 -0500407 * @brief Is the power state on
408 *
409 * @return Current power state of the system
410 */
411 inline bool isPowerOn() const
412 {
413 return _powerState->isPowerOn();
414 }
415
Matthew Bartha227a162020-08-05 10:51:45 -0500416 private:
Matthew Barthacd737c2021-03-04 11:04:01 -0600417 /**
418 * The sdbusplus bus object to use
419 */
420 sdbusplus::bus::bus& _bus;
421
422 /**
423 * The sdeventplus even loop to use
424 */
425 sdeventplus::Event _event;
426
Matthew Barth48f44da2021-05-27 15:43:34 -0500427 /* The system's power state determination object */
428 std::unique_ptr<PowerState> _powerState;
429
Matthew Barth06764942021-03-04 09:28:40 -0600430 /* List of profiles configured */
Matthew Barthacd737c2021-03-04 11:04:01 -0600431 std::map<configKey, std::unique_ptr<Profile>> _profiles;
Matthew Barth06764942021-03-04 09:28:40 -0600432
433 /* List of active profiles */
Matthew Barthacd737c2021-03-04 11:04:01 -0600434 static std::vector<std::string> _activeProfiles;
435
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500436 /* Subtree map of paths to services of interfaces(with ownership state) */
437 static std::map<
438 std::string,
439 std::map<std::string, std::pair<bool, std::vector<std::string>>>>
Matthew Barth12cb1252021-03-08 16:47:30 -0600440 _servTree;
441
Matthew Barth07fecfc2021-01-29 09:04:43 -0600442 /* Object map of paths to interfaces of properties and their values */
443 static std::map<
444 std::string,
445 std::map<std::string, std::map<std::string, PropertyVariantType>>>
446 _objects;
447
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500448 /* List of timers and their data to be processed when expired */
449 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers;
450
Matthew Barthebabc042021-05-13 15:38:58 -0500451 /* Map of signal match strings to a list of signal handler data */
452 std::unordered_map<std::string, std::vector<SignalData>> _signals;
453
Matthew Barthacd737c2021-03-04 11:04:01 -0600454 /* List of zones configured */
455 std::map<configKey, std::unique_ptr<Zone>> _zones;
456
Matthew Barth44ab7692021-03-26 11:40:10 -0500457 /* List of events configured */
458 std::map<configKey, std::unique_ptr<Event>> _events;
459
Matthew Barthacd737c2021-03-04 11:04:01 -0600460 /**
Matthew Barthe91ac862021-05-25 16:22:17 -0500461 * @brief Load all the fan control JSON configuration files
462 *
463 * This is where all the fan control JSON configuration files are parsed and
464 * loaded into their associated objects. Anything that needs to be done when
465 * the Manager object is constructed or handling a SIGHUP to reload the
466 * configurations needs to be done here.
467 */
468 void load();
469
470 /**
Matthew Barth48f44da2021-05-27 15:43:34 -0500471 * @brief Callback for power state changes
472 *
473 * @param[in] powerStateOn - Whether the power state is on or not
474 *
475 * Callback function bound to the PowerState object instance to handle each
476 * time the power state changes.
477 */
478 void powerStateChanged(bool powerStateOn);
479
480 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500481 * @brief Find the service name for a given path and interface from the
482 * cached dataset
483 *
484 * @param[in] path - Path to get service for
485 * @param[in] intf - Interface to get service for
486 *
487 * @return - The cached service name
488 */
489 static const std::string& findService(const std::string& path,
490 const std::string& intf);
491
492 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500493 * @brief Find all the paths for a given service and interface from the
494 * cached dataset
495 *
496 * @param[in] serv - Service name to get paths for
497 * @param[in] intf - Interface to get paths for
498 *
499 * @return - The cached object paths
500 */
501 std::vector<std::string> findPaths(const std::string& serv,
502 const std::string& intf);
503
504 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600505 * @brief Parse and set the configured profiles from the profiles JSON file
506 *
507 * Retrieves the optional profiles JSON configuration file, parses it, and
508 * creates a list of configured profiles available to the other
509 * configuration files. These profiles can be used to remove or include
510 * entries within the other configuration files.
511 */
512 void setProfiles();
Matthew Bartha227a162020-08-05 10:51:45 -0500513};
514
515} // namespace phosphor::fan::control::json