blob: 74683a9bd8ed5556e1de6756bbb45b3f25dc0657 [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 Barth06764942021-03-04 09:28:40 -060023#include "profile.hpp"
Matthew Barthacd737c2021-03-04 11:04:01 -060024#include "zone.hpp"
Matthew Barth06764942021-03-04 09:28:40 -060025
Matthew Bartha227a162020-08-05 10:51:45 -050026#include <nlohmann/json.hpp>
27#include <sdbusplus/bus.hpp>
Matthew Barth06764942021-03-04 09:28:40 -060028#include <sdeventplus/event.hpp>
Matthew Barthd9cb63b2021-03-24 14:36:49 -050029#include <sdeventplus/utility/timer.hpp>
30
31#include <chrono>
32#include <map>
33#include <memory>
34#include <optional>
35#include <tuple>
36#include <utility>
37#include <vector>
Matthew Bartha227a162020-08-05 10:51:45 -050038
39namespace phosphor::fan::control::json
40{
41
42using json = nlohmann::json;
43
Matthew Barthacd737c2021-03-04 11:04:01 -060044/* Application name to be appended to the path for loading a JSON config file */
45constexpr auto confAppName = "control";
46
Matthew Barthd9cb63b2021-03-24 14:36:49 -050047/* Type of timers supported */
48enum class TimerType
49{
50 oneshot,
51 repeating,
52};
53/**
54 * Package of data required when a timer expires
55 * Tuple constructed of:
56 * std::string = Timer package unique identifier
Matthew Barthd9cb63b2021-03-24 14:36:49 -050057 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions
58 * that run when the timer expires
59 */
Matthew Barth00f6aa02021-04-09 10:49:47 -050060using TimerPkg =
61 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>;
Matthew Barthd9cb63b2021-03-24 14:36:49 -050062/**
63 * Data associated with a running timer that's used when it expires
64 * Pair constructed of:
65 * TimerType = Type of timer to manage expired timer instances
66 * TimerPkg = Package of data required when the timer expires
67 */
68using TimerData = std::pair<TimerType, TimerPkg>;
69/* Dbus event timer */
70using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
71
Matthew Barthebabc042021-05-13 15:38:58 -050072/* Dbus signal object */
73constexpr auto Path = 0;
74constexpr auto Intf = 1;
75constexpr auto Prop = 2;
76using SignalObject = std::tuple<std::string, std::string, std::string>;
77/* Dbus signal actions */
78using SignalActions =
79 std::vector<std::reference_wrapper<std::unique_ptr<ActionBase>>>;
80/**
81 * Signal handler function that handles parsing a signal's message for a
82 * particular signal object and stores the results in the manager
83 */
84using SignalHandler = std::function<bool(sdbusplus::message::message&,
85 const SignalObject&, Manager&)>;
86/**
87 * Package of data required when a signal is received
88 * Tuple constructed of:
89 * SignalHandler = Signal handler function
90 * SignalObject = Dbus signal object
91 * SignalActions = List of actions that are run when the signal is received
92 */
93using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>;
94/**
95 * Data associated to a subscribed signal
96 * Tuple constructed of:
97 * std::unique_ptr<std::vector<SignalPkg>> =
98 * Pointer to the signal's packages
99 * std::unique_ptr<sdbusplus::server::match::match> =
100 * Pointer to match holding the subscription to a signal
101 */
102using SignalData = std::tuple<std::unique_ptr<std::vector<SignalPkg>>,
103 std::unique_ptr<sdbusplus::server::match::match>>;
104
Matthew Bartha227a162020-08-05 10:51:45 -0500105/**
106 * @class Manager - Represents the fan control manager's configuration
107 *
108 * A fan control manager configuration is optional, therefore the "manager.json"
109 * file is also optional. The manager configuration is used to populate
110 * fan control's manager parameters which are used in how the application
111 * operates, not in how the fans are controlled.
112 *
113 * When no manager configuration exists, the fan control application starts,
114 * processes any configured events and then begins controlling fans according
115 * to those events.
116 */
117class Manager
118{
119 public:
120 Manager() = delete;
121 Manager(const Manager&) = delete;
122 Manager(Manager&&) = delete;
123 Manager& operator=(const Manager&) = delete;
124 Manager& operator=(Manager&&) = delete;
125 ~Manager() = default;
126
127 /**
128 * Constructor
129 * Parses and populates the fan control manager attributes from a json file
130 *
131 * @param[in] bus - sdbusplus bus object
Matthew Barth06764942021-03-04 09:28:40 -0600132 * @param[in] event - sdeventplus event loop
Matthew Bartha227a162020-08-05 10:51:45 -0500133 */
Matthew Barth06764942021-03-04 09:28:40 -0600134 Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event);
Matthew Bartha227a162020-08-05 10:51:45 -0500135
136 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600137 * @brief Get the active profiles of the system where an empty list
138 * represents that only configuration entries without a profile defined will
139 * be loaded.
140 *
141 * @return - The list of active profiles
142 */
143 static const std::vector<std::string>& getActiveProfiles();
144
145 /**
Matthew Barthe6d1f782021-05-14 12:52:20 -0500146 * @brief Extract bus from first location in argument pack
147 *
148 * @param[in] args - Argument pack
149 *
150 * @return - The first argument(sdbusplus bus object) from argument pack
151 */
152 template <typename... Args>
153 static decltype(auto) getBus(Args&&... args)
154 {
155 return std::get<0>(std::forward_as_tuple(args...));
156 }
157
158 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600159 * @brief Load the configuration of a given JSON class object based on the
160 * active profiles
161 *
Matthew Barthacd737c2021-03-04 11:04:01 -0600162 * @param[in] isOptional - JSON configuration file is optional or not
Matthew Barth603ef162021-03-24 15:34:53 -0500163 * @param[in] args - Arguments to be forwarded to each instance of `T`
Matthew Barthe6d1f782021-05-14 12:52:20 -0500164 * (*Note that a sdbusplus bus object is required as the first argument)
Matthew Barthacd737c2021-03-04 11:04:01 -0600165 *
166 * @return Map of configuration entries
167 * Map of configuration keys to their corresponding configuration object
168 */
Matthew Barth603ef162021-03-24 15:34:53 -0500169 template <typename T, typename... Args>
Matthew Barthe6d1f782021-05-14 12:52:20 -0500170 static std::map<configKey, std::unique_ptr<T>> getConfig(bool isOptional,
171 Args&&... args)
Matthew Barthacd737c2021-03-04 11:04:01 -0600172 {
173 std::map<configKey, std::unique_ptr<T>> config;
174
175 auto confFile = fan::JsonConfig::getConfFile(
Matthew Barthe6d1f782021-05-14 12:52:20 -0500176 getBus(std::forward<Args>(args)...), confAppName, T::confFileName,
177 isOptional);
Matthew Barthacd737c2021-03-04 11:04:01 -0600178 if (!confFile.empty())
179 {
180 for (const auto& entry : fan::JsonConfig::load(confFile))
181 {
182 if (entry.contains("profiles"))
183 {
184 std::vector<std::string> profiles;
185 for (const auto& profile : entry["profiles"])
186 {
187 profiles.emplace_back(
188 profile.template get<std::string>());
189 }
190 // Do not create the object if its profiles are not in the
191 // list of active profiles
Matthew Barth42428052021-03-30 12:50:59 -0500192 if (!profiles.empty() &&
193 !std::any_of(profiles.begin(), profiles.end(),
Matthew Barthacd737c2021-03-04 11:04:01 -0600194 [](const auto& name) {
195 return std::find(
196 getActiveProfiles().begin(),
197 getActiveProfiles().end(),
198 name) !=
199 getActiveProfiles().end();
200 }))
201 {
202 continue;
203 }
204 }
Matthew Barth603ef162021-03-24 15:34:53 -0500205 auto obj =
206 std::make_unique<T>(entry, std::forward<Args>(args)...);
Matthew Barthacd737c2021-03-04 11:04:01 -0600207 config.emplace(
208 std::make_pair(obj->getName(), obj->getProfiles()),
209 std::move(obj));
210 }
211 }
212 return config;
213 }
214
215 /**
Matthew Barth0206c722021-03-30 15:20:05 -0500216 * @brief Check if the given input configuration key matches with another
217 * configuration key that it's to be included in
218 *
219 * @param[in] input - Config key to be included in another config object
220 * @param[in] comp - Config key of the config object to compare with
221 *
222 * @return Whether the configuration object should be included
223 */
224 static bool inConfig(const configKey& input, const configKey& comp);
225
226 /**
Matthew Barth12cb1252021-03-08 16:47:30 -0600227 * @brief Check if the given path and inteface is owned by a dbus service
228 *
229 * @param[in] path - Dbus object path
230 * @param[in] intf - Dbus object interface
231 *
232 * @return - Whether the service has an owner for the given object path and
233 * interface
234 */
235 static bool hasOwner(const std::string& path, const std::string& intf);
236
237 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500238 * @brief Sets the dbus service owner state of a given object
239 *
240 * @param[in] path - Dbus object path
241 * @param[in] serv - Dbus service name
242 * @param[in] intf - Dbus object interface
243 * @param[in] isOwned - Dbus service owner state
244 */
245 void setOwner(const std::string& path, const std::string& serv,
246 const std::string& intf, bool isOwned);
247
248 /**
249 * @brief Add a set of services for a path and interface by retrieving all
250 * the path subtrees to the given depth from root for the interface
251 *
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500252 * @param[in] intf - Interface to add services for
253 * @param[in] depth - Depth of tree traversal from root path
254 *
255 * @throws - DBusMethodError
256 * Throws a DBusMethodError when the `getSubTree` method call fails
257 */
Matthew Barth98f6fc12021-04-16 10:48:37 -0500258 static void addServices(const std::string& intf, int32_t depth);
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500259
260 /**
261 * @brief Get the service for a given path and interface from cached
262 * dataset and attempt to add all the services for the given path/interface
263 * when it's not found
264 *
265 * @param[in] path - Path to get service for
266 * @param[in] intf - Interface to get service for
267 *
268 * @return - The now cached service name
269 *
270 * @throws - DBusMethodError
271 * Ripples up a DBusMethodError exception from calling addServices
272 */
273 static const std::string& getService(const std::string& path,
274 const std::string& intf);
275
276 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500277 * @brief Get all the object paths for a given service and interface from
278 * the cached dataset and try to add all the services for the given
279 * interface when no paths are found and then attempt to get all the object
280 * paths again
281 *
282 * @param[in] serv - Service name to get paths for
283 * @param[in] intf - Interface to get paths for
284 *
285 * @return The cached object paths
286 */
287 std::vector<std::string> getPaths(const std::string& serv,
288 const std::string& intf);
289
290 /**
291 * @brief Add objects to the cached dataset by first using
292 * `getManagedObjects` for the same service providing the given path and
293 * interface or just add the single object of the given path, interface, and
294 * property if that fails.
295 *
296 * @param[in] path - Dbus object's path
297 * @param[in] intf - Dbus object's interface
298 * @param[in] prop - Dbus object's property
299 *
300 * @throws - DBusMethodError
301 * Throws a DBusMethodError when the the service is failed to be found or
302 * when the `getManagedObjects` method call fails
303 */
304 void addObjects(const std::string& path, const std::string& intf,
305 const std::string& prop);
306
307 /**
308 * @brief Get an object's property value
309 *
310 * @param[in] path - Dbus object's path
311 * @param[in] intf - Dbus object's interface
312 * @param[in] prop - Dbus object's property
313 */
314 const std::optional<PropertyVariantType>
315 getProperty(const std::string& path, const std::string& intf,
316 const std::string& prop);
317
318 /**
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500319 * @brief Set/update 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 * @param[in] value - Dbus object's property value
325 */
326 void setProperty(const std::string& path, const std::string& intf,
327 const std::string& prop, PropertyVariantType value)
328 {
329 _objects[path][intf][prop] = std::move(value);
330 }
331
332 /**
Matthew Barthc2691402021-05-13 16:20:32 -0500333 * @brief Remove an object's interface
334 *
335 * @param[in] path - Dbus object's path
336 * @param[in] intf - Dbus object's interface
337 */
338 inline void removeInterface(const std::string& path,
339 const std::string& intf)
340 {
341 auto itPath = _objects.find(path);
342 if (itPath != std::end(_objects))
343 {
344 _objects[path].erase(intf);
345 }
346 }
347
348 /**
Matthew Barth07fecfc2021-01-29 09:04:43 -0600349 * @brief Get the object's property value as a variant
350 *
351 * @param[in] path - Path of the object containing the property
352 * @param[in] intf - Interface name containing the property
353 * @param[in] prop - Name of property
354 *
355 * @return - The object's property value as a variant
356 */
357 static inline auto getObjValueVariant(const std::string& path,
358 const std::string& intf,
359 const std::string& prop)
360 {
361 return _objects.at(path).at(intf).at(prop);
362 };
363
364 /**
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500365 * @brief Add a dbus timer
366 *
367 * @param[in] type - Type of timer
368 * @param[in] interval - Timer interval in microseconds
369 * @param[in] pkg - Packaged data for when timer expires
370 */
371 void addTimer(const TimerType type,
372 const std::chrono::microseconds interval,
373 std::unique_ptr<TimerPkg> pkg);
374
375 /**
376 * @brief Callback when a timer expires
377 *
378 * @param[in] data - Data to be used when the timer expired
379 */
380 void timerExpired(TimerData& data);
381
382 /**
Matthew Barthebabc042021-05-13 15:38:58 -0500383 * @brief Get the signal data for a given match string
384 *
385 * @param[in] sigMatch - Signal match string
386 *
387 * @return - Reference to the signal data for the given match string
388 */
389 std::vector<SignalData>& getSignal(const std::string& sigMatch)
390 {
391 return _signals[sigMatch];
392 }
393
394 /**
395 * @brief Handle receiving signals
396 *
397 * @param[in] msg - Signal message containing the signal's data
398 * @param[in] pkgs - Signal packages associated to the signal being handled
399 */
400 void handleSignal(sdbusplus::message::message& msg,
401 const std::vector<SignalPkg>* pkgs);
402
403 /**
Matthew Bartheebde062021-04-14 12:48:52 -0500404 * @brief Get the sdbusplus bus object
405 */
406 inline auto& getBus()
407 {
408 return _bus;
409 }
410
411 /**
Matthew Bartha227a162020-08-05 10:51:45 -0500412 * @brief Get the configured power on delay(OPTIONAL)
413 *
414 * @return Power on delay in seconds
415 * Configured power on delay in seconds, otherwise 0
416 */
417 unsigned int getPowerOnDelay();
418
419 private:
420 /* JSON file name for manager configuration attributes */
421 static constexpr auto confFileName = "manager.json";
422
423 /* The parsed JSON object */
424 json _jsonObj;
Matthew Barth06764942021-03-04 09:28:40 -0600425
Matthew Barthacd737c2021-03-04 11:04:01 -0600426 /**
427 * The sdbusplus bus object to use
428 */
429 sdbusplus::bus::bus& _bus;
430
431 /**
432 * The sdeventplus even loop to use
433 */
434 sdeventplus::Event _event;
435
Matthew Barth06764942021-03-04 09:28:40 -0600436 /* List of profiles configured */
Matthew Barthacd737c2021-03-04 11:04:01 -0600437 std::map<configKey, std::unique_ptr<Profile>> _profiles;
Matthew Barth06764942021-03-04 09:28:40 -0600438
439 /* List of active profiles */
Matthew Barthacd737c2021-03-04 11:04:01 -0600440 static std::vector<std::string> _activeProfiles;
441
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500442 /* Subtree map of paths to services of interfaces(with ownership state) */
443 static std::map<
444 std::string,
445 std::map<std::string, std::pair<bool, std::vector<std::string>>>>
Matthew Barth12cb1252021-03-08 16:47:30 -0600446 _servTree;
447
Matthew Barth07fecfc2021-01-29 09:04:43 -0600448 /* Object map of paths to interfaces of properties and their values */
449 static std::map<
450 std::string,
451 std::map<std::string, std::map<std::string, PropertyVariantType>>>
452 _objects;
453
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500454 /* List of timers and their data to be processed when expired */
455 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers;
456
Matthew Barthebabc042021-05-13 15:38:58 -0500457 /* Map of signal match strings to a list of signal handler data */
458 std::unordered_map<std::string, std::vector<SignalData>> _signals;
459
Matthew Barthacd737c2021-03-04 11:04:01 -0600460 /* List of zones configured */
461 std::map<configKey, std::unique_ptr<Zone>> _zones;
462
Matthew Barth44ab7692021-03-26 11:40:10 -0500463 /* List of events configured */
464 std::map<configKey, std::unique_ptr<Event>> _events;
465
Matthew Barthacd737c2021-03-04 11:04:01 -0600466 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500467 * @brief Find the service name for a given path and interface from the
468 * cached dataset
469 *
470 * @param[in] path - Path to get service for
471 * @param[in] intf - Interface to get service for
472 *
473 * @return - The cached service name
474 */
475 static const std::string& findService(const std::string& path,
476 const std::string& intf);
477
478 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500479 * @brief Find all the paths for a given service and interface from the
480 * cached dataset
481 *
482 * @param[in] serv - Service name to get paths for
483 * @param[in] intf - Interface to get paths for
484 *
485 * @return - The cached object paths
486 */
487 std::vector<std::string> findPaths(const std::string& serv,
488 const std::string& intf);
489
490 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600491 * @brief Parse and set the configured profiles from the profiles JSON file
492 *
493 * Retrieves the optional profiles JSON configuration file, parses it, and
494 * creates a list of configured profiles available to the other
495 * configuration files. These profiles can be used to remove or include
496 * entries within the other configuration files.
497 */
498 void setProfiles();
Matthew Bartha227a162020-08-05 10:51:45 -0500499};
500
501} // namespace phosphor::fan::control::json