blob: 657f51a41e57fecce175453b095e2ccc5a8861a5 [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 Barth9403a212021-05-17 09:31:50 -050024#include "sdbusplus.hpp"
Matthew Barthacd737c2021-03-04 11:04:01 -060025#include "zone.hpp"
Matthew Barth06764942021-03-04 09:28:40 -060026
Matthew Bartha227a162020-08-05 10:51:45 -050027#include <nlohmann/json.hpp>
28#include <sdbusplus/bus.hpp>
Matthew Barth06764942021-03-04 09:28:40 -060029#include <sdeventplus/event.hpp>
Matthew Barthd9cb63b2021-03-24 14:36:49 -050030#include <sdeventplus/utility/timer.hpp>
31
32#include <chrono>
33#include <map>
34#include <memory>
35#include <optional>
36#include <tuple>
37#include <utility>
38#include <vector>
Matthew Bartha227a162020-08-05 10:51:45 -050039
40namespace phosphor::fan::control::json
41{
42
43using json = nlohmann::json;
44
Matthew Barthacd737c2021-03-04 11:04:01 -060045/* Application name to be appended to the path for loading a JSON config file */
46constexpr auto confAppName = "control";
47
Matthew Barthd9cb63b2021-03-24 14:36:49 -050048/* Type of timers supported */
49enum class TimerType
50{
51 oneshot,
52 repeating,
53};
54/**
55 * Package of data required when a timer expires
56 * Tuple constructed of:
57 * std::string = Timer package unique identifier
Matthew Barthd9cb63b2021-03-24 14:36:49 -050058 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions
59 * that run when the timer expires
60 */
Matthew Barth00f6aa02021-04-09 10:49:47 -050061using TimerPkg =
62 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>;
Matthew Barthd9cb63b2021-03-24 14:36:49 -050063/**
64 * Data associated with a running timer that's used when it expires
65 * Pair constructed of:
66 * TimerType = Type of timer to manage expired timer instances
67 * TimerPkg = Package of data required when the timer expires
68 */
69using TimerData = std::pair<TimerType, TimerPkg>;
70/* Dbus event timer */
71using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
72
Matthew Barthebabc042021-05-13 15:38:58 -050073/* Dbus signal object */
74constexpr auto Path = 0;
75constexpr auto Intf = 1;
76constexpr auto Prop = 2;
77using SignalObject = std::tuple<std::string, std::string, std::string>;
78/* Dbus signal actions */
79using SignalActions =
80 std::vector<std::reference_wrapper<std::unique_ptr<ActionBase>>>;
81/**
82 * Signal handler function that handles parsing a signal's message for a
83 * particular signal object and stores the results in the manager
84 */
85using SignalHandler = std::function<bool(sdbusplus::message::message&,
86 const SignalObject&, Manager&)>;
87/**
88 * Package of data required when a signal is received
89 * Tuple constructed of:
90 * SignalHandler = Signal handler function
91 * SignalObject = Dbus signal object
92 * SignalActions = List of actions that are run when the signal is received
93 */
94using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>;
95/**
96 * Data associated to a subscribed signal
97 * Tuple constructed of:
98 * std::unique_ptr<std::vector<SignalPkg>> =
99 * Pointer to the signal's packages
100 * std::unique_ptr<sdbusplus::server::match::match> =
101 * Pointer to match holding the subscription to a signal
102 */
103using SignalData = std::tuple<std::unique_ptr<std::vector<SignalPkg>>,
104 std::unique_ptr<sdbusplus::server::match::match>>;
105
Matthew Bartha227a162020-08-05 10:51:45 -0500106/**
107 * @class Manager - Represents the fan control manager's configuration
108 *
109 * A fan control manager configuration is optional, therefore the "manager.json"
110 * file is also optional. The manager configuration is used to populate
111 * fan control's manager parameters which are used in how the application
112 * operates, not in how the fans are controlled.
113 *
114 * When no manager configuration exists, the fan control application starts,
115 * processes any configured events and then begins controlling fans according
116 * to those events.
117 */
118class Manager
119{
120 public:
121 Manager() = delete;
122 Manager(const Manager&) = delete;
123 Manager(Manager&&) = delete;
124 Manager& operator=(const Manager&) = delete;
125 Manager& operator=(Manager&&) = delete;
126 ~Manager() = default;
127
128 /**
129 * Constructor
130 * Parses and populates the fan control manager attributes from a json file
131 *
Matthew Barth06764942021-03-04 09:28:40 -0600132 * @param[in] event - sdeventplus event loop
Matthew Bartha227a162020-08-05 10:51:45 -0500133 */
Matthew Barth9403a212021-05-17 09:31:50 -0500134 Manager(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 /**
146 * @brief Load the configuration of a given JSON class object based on the
147 * active profiles
148 *
Matthew Barthacd737c2021-03-04 11:04:01 -0600149 * @param[in] isOptional - JSON configuration file is optional or not
Matthew Barth603ef162021-03-24 15:34:53 -0500150 * @param[in] args - Arguments to be forwarded to each instance of `T`
Matthew Barthe6d1f782021-05-14 12:52:20 -0500151 * (*Note that a sdbusplus bus object is required as the first argument)
Matthew Barthacd737c2021-03-04 11:04:01 -0600152 *
153 * @return Map of configuration entries
154 * Map of configuration keys to their corresponding configuration object
155 */
Matthew Barth603ef162021-03-24 15:34:53 -0500156 template <typename T, typename... Args>
Matthew Barthe6d1f782021-05-14 12:52:20 -0500157 static std::map<configKey, std::unique_ptr<T>> getConfig(bool isOptional,
158 Args&&... args)
Matthew Barthacd737c2021-03-04 11:04:01 -0600159 {
160 std::map<configKey, std::unique_ptr<T>> config;
161
Matthew Barth9403a212021-05-17 09:31:50 -0500162 auto confFile =
163 fan::JsonConfig::getConfFile(util::SDBusPlus::getBus(), confAppName,
164 T::confFileName, isOptional);
Matthew Barthacd737c2021-03-04 11:04:01 -0600165 if (!confFile.empty())
166 {
167 for (const auto& entry : fan::JsonConfig::load(confFile))
168 {
169 if (entry.contains("profiles"))
170 {
171 std::vector<std::string> profiles;
172 for (const auto& profile : entry["profiles"])
173 {
174 profiles.emplace_back(
175 profile.template get<std::string>());
176 }
177 // Do not create the object if its profiles are not in the
178 // list of active profiles
Matthew Barth42428052021-03-30 12:50:59 -0500179 if (!profiles.empty() &&
180 !std::any_of(profiles.begin(), profiles.end(),
Matthew Barthacd737c2021-03-04 11:04:01 -0600181 [](const auto& name) {
182 return std::find(
183 getActiveProfiles().begin(),
184 getActiveProfiles().end(),
185 name) !=
186 getActiveProfiles().end();
187 }))
188 {
189 continue;
190 }
191 }
Matthew Barth603ef162021-03-24 15:34:53 -0500192 auto obj =
193 std::make_unique<T>(entry, std::forward<Args>(args)...);
Matthew Barthacd737c2021-03-04 11:04:01 -0600194 config.emplace(
195 std::make_pair(obj->getName(), obj->getProfiles()),
196 std::move(obj));
197 }
198 }
199 return config;
200 }
201
202 /**
Matthew Barth0206c722021-03-30 15:20:05 -0500203 * @brief Check if the given input configuration key matches with another
204 * configuration key that it's to be included in
205 *
206 * @param[in] input - Config key to be included in another config object
207 * @param[in] comp - Config key of the config object to compare with
208 *
209 * @return Whether the configuration object should be included
210 */
211 static bool inConfig(const configKey& input, const configKey& comp);
212
213 /**
Matthew Barth12cb1252021-03-08 16:47:30 -0600214 * @brief Check if the given path and inteface is owned by a dbus service
215 *
216 * @param[in] path - Dbus object path
217 * @param[in] intf - Dbus object interface
218 *
219 * @return - Whether the service has an owner for the given object path and
220 * interface
221 */
222 static bool hasOwner(const std::string& path, const std::string& intf);
223
224 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500225 * @brief Sets the dbus service owner state of a given object
226 *
227 * @param[in] path - Dbus object path
228 * @param[in] serv - Dbus service name
229 * @param[in] intf - Dbus object interface
230 * @param[in] isOwned - Dbus service owner state
231 */
232 void setOwner(const std::string& path, const std::string& serv,
233 const std::string& intf, bool isOwned);
234
235 /**
236 * @brief Add a set of services for a path and interface by retrieving all
237 * the path subtrees to the given depth from root for the interface
238 *
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500239 * @param[in] intf - Interface to add services for
240 * @param[in] depth - Depth of tree traversal from root path
241 *
242 * @throws - DBusMethodError
243 * Throws a DBusMethodError when the `getSubTree` method call fails
244 */
Matthew Barth98f6fc12021-04-16 10:48:37 -0500245 static void addServices(const std::string& intf, int32_t depth);
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500246
247 /**
248 * @brief Get the service for a given path and interface from cached
249 * dataset and attempt to add all the services for the given path/interface
250 * when it's not found
251 *
252 * @param[in] path - Path to get service for
253 * @param[in] intf - Interface to get service for
254 *
255 * @return - The now cached service name
256 *
257 * @throws - DBusMethodError
258 * Ripples up a DBusMethodError exception from calling addServices
259 */
260 static const std::string& getService(const std::string& path,
261 const std::string& intf);
262
263 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500264 * @brief Get all the object paths for a given service and interface from
265 * the cached dataset and try to add all the services for the given
266 * interface when no paths are found and then attempt to get all the object
267 * paths again
268 *
269 * @param[in] serv - Service name to get paths for
270 * @param[in] intf - Interface to get paths for
271 *
272 * @return The cached object paths
273 */
274 std::vector<std::string> getPaths(const std::string& serv,
275 const std::string& intf);
276
277 /**
278 * @brief Add objects to the cached dataset by first using
279 * `getManagedObjects` for the same service providing the given path and
280 * interface or just add the single object of the given path, interface, and
281 * property if that fails.
282 *
283 * @param[in] path - Dbus object's path
284 * @param[in] intf - Dbus object's interface
285 * @param[in] prop - Dbus object's property
286 *
287 * @throws - DBusMethodError
288 * Throws a DBusMethodError when the the service is failed to be found or
289 * when the `getManagedObjects` method call fails
290 */
291 void addObjects(const std::string& path, const std::string& intf,
292 const std::string& prop);
293
294 /**
295 * @brief Get an object's property value
296 *
297 * @param[in] path - Dbus object's path
298 * @param[in] intf - Dbus object's interface
299 * @param[in] prop - Dbus object's property
300 */
301 const std::optional<PropertyVariantType>
302 getProperty(const std::string& path, const std::string& intf,
303 const std::string& prop);
304
305 /**
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500306 * @brief Set/update an object's property value
307 *
308 * @param[in] path - Dbus object's path
309 * @param[in] intf - Dbus object's interface
310 * @param[in] prop - Dbus object's property
311 * @param[in] value - Dbus object's property value
312 */
313 void setProperty(const std::string& path, const std::string& intf,
314 const std::string& prop, PropertyVariantType value)
315 {
316 _objects[path][intf][prop] = std::move(value);
317 }
318
319 /**
Matthew Barthc2691402021-05-13 16:20:32 -0500320 * @brief Remove an object's interface
321 *
322 * @param[in] path - Dbus object's path
323 * @param[in] intf - Dbus object's interface
324 */
325 inline void removeInterface(const std::string& path,
326 const std::string& intf)
327 {
328 auto itPath = _objects.find(path);
329 if (itPath != std::end(_objects))
330 {
331 _objects[path].erase(intf);
332 }
333 }
334
335 /**
Matthew Barth07fecfc2021-01-29 09:04:43 -0600336 * @brief Get the object's property value as a variant
337 *
338 * @param[in] path - Path of the object containing the property
339 * @param[in] intf - Interface name containing the property
340 * @param[in] prop - Name of property
341 *
342 * @return - The object's property value as a variant
343 */
344 static inline auto getObjValueVariant(const std::string& path,
345 const std::string& intf,
346 const std::string& prop)
347 {
348 return _objects.at(path).at(intf).at(prop);
349 };
350
351 /**
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500352 * @brief Add a dbus timer
353 *
354 * @param[in] type - Type of timer
355 * @param[in] interval - Timer interval in microseconds
356 * @param[in] pkg - Packaged data for when timer expires
357 */
358 void addTimer(const TimerType type,
359 const std::chrono::microseconds interval,
360 std::unique_ptr<TimerPkg> pkg);
361
362 /**
363 * @brief Callback when a timer expires
364 *
365 * @param[in] data - Data to be used when the timer expired
366 */
367 void timerExpired(TimerData& data);
368
369 /**
Matthew Barthebabc042021-05-13 15:38:58 -0500370 * @brief Get the signal data for a given match string
371 *
372 * @param[in] sigMatch - Signal match string
373 *
374 * @return - Reference to the signal data for the given match string
375 */
376 std::vector<SignalData>& getSignal(const std::string& sigMatch)
377 {
378 return _signals[sigMatch];
379 }
380
381 /**
382 * @brief Handle receiving signals
383 *
384 * @param[in] msg - Signal message containing the signal's data
385 * @param[in] pkgs - Signal packages associated to the signal being handled
386 */
387 void handleSignal(sdbusplus::message::message& msg,
388 const std::vector<SignalPkg>* pkgs);
389
390 /**
Matthew Bartheebde062021-04-14 12:48:52 -0500391 * @brief Get the sdbusplus bus object
392 */
393 inline auto& getBus()
394 {
395 return _bus;
396 }
397
398 /**
Matthew Bartha227a162020-08-05 10:51:45 -0500399 * @brief Get the configured power on delay(OPTIONAL)
400 *
401 * @return Power on delay in seconds
402 * Configured power on delay in seconds, otherwise 0
403 */
404 unsigned int getPowerOnDelay();
405
406 private:
407 /* JSON file name for manager configuration attributes */
408 static constexpr auto confFileName = "manager.json";
409
410 /* The parsed JSON object */
411 json _jsonObj;
Matthew Barth06764942021-03-04 09:28:40 -0600412
Matthew Barthacd737c2021-03-04 11:04:01 -0600413 /**
414 * The sdbusplus bus object to use
415 */
416 sdbusplus::bus::bus& _bus;
417
418 /**
419 * The sdeventplus even loop to use
420 */
421 sdeventplus::Event _event;
422
Matthew Barth06764942021-03-04 09:28:40 -0600423 /* List of profiles configured */
Matthew Barthacd737c2021-03-04 11:04:01 -0600424 std::map<configKey, std::unique_ptr<Profile>> _profiles;
Matthew Barth06764942021-03-04 09:28:40 -0600425
426 /* List of active profiles */
Matthew Barthacd737c2021-03-04 11:04:01 -0600427 static std::vector<std::string> _activeProfiles;
428
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500429 /* Subtree map of paths to services of interfaces(with ownership state) */
430 static std::map<
431 std::string,
432 std::map<std::string, std::pair<bool, std::vector<std::string>>>>
Matthew Barth12cb1252021-03-08 16:47:30 -0600433 _servTree;
434
Matthew Barth07fecfc2021-01-29 09:04:43 -0600435 /* Object map of paths to interfaces of properties and their values */
436 static std::map<
437 std::string,
438 std::map<std::string, std::map<std::string, PropertyVariantType>>>
439 _objects;
440
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500441 /* List of timers and their data to be processed when expired */
442 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers;
443
Matthew Barthebabc042021-05-13 15:38:58 -0500444 /* Map of signal match strings to a list of signal handler data */
445 std::unordered_map<std::string, std::vector<SignalData>> _signals;
446
Matthew Barthacd737c2021-03-04 11:04:01 -0600447 /* List of zones configured */
448 std::map<configKey, std::unique_ptr<Zone>> _zones;
449
Matthew Barth44ab7692021-03-26 11:40:10 -0500450 /* List of events configured */
451 std::map<configKey, std::unique_ptr<Event>> _events;
452
Matthew Barthacd737c2021-03-04 11:04:01 -0600453 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500454 * @brief Find the service name for a given path and interface from the
455 * cached dataset
456 *
457 * @param[in] path - Path to get service for
458 * @param[in] intf - Interface to get service for
459 *
460 * @return - The cached service name
461 */
462 static const std::string& findService(const std::string& path,
463 const std::string& intf);
464
465 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500466 * @brief Find all the paths for a given service and interface from the
467 * cached dataset
468 *
469 * @param[in] serv - Service name to get paths for
470 * @param[in] intf - Interface to get paths for
471 *
472 * @return - The cached object paths
473 */
474 std::vector<std::string> findPaths(const std::string& serv,
475 const std::string& intf);
476
477 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600478 * @brief Parse and set the configured profiles from the profiles JSON file
479 *
480 * Retrieves the optional profiles JSON configuration file, parses it, and
481 * creates a list of configured profiles available to the other
482 * configuration files. These profiles can be used to remove or include
483 * entries within the other configuration files.
484 */
485 void setProfiles();
Matthew Bartha227a162020-08-05 10:51:45 -0500486};
487
488} // namespace phosphor::fan::control::json