blob: ce1c8699c670fba20a64a8039dd588d999f142bc [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>
Matthew Barthd9cb63b2021-03-24 14:36:49 -050035#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>
Matthew Bartha227a162020-08-05 10:51:45 -050044
45namespace phosphor::fan::control::json
46{
47
48using json = nlohmann::json;
Matthew Barth68ac0042021-06-01 15:38:36 -050049using namespace phosphor::logging;
Matthew Bartha227a162020-08-05 10:51:45 -050050
Matthew Barthacd737c2021-03-04 11:04:01 -060051/* Application name to be appended to the path for loading a JSON config file */
52constexpr auto confAppName = "control";
53
Matthew Barthd9cb63b2021-03-24 14:36:49 -050054/* Type of timers supported */
55enum 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
Matthew Barthd9cb63b2021-03-24 14:36:49 -050064 * std::vector<std::unique_ptr<ActionBase>> = List of pointers to actions
65 * that run when the timer expires
66 */
Matthew Barth00f6aa02021-04-09 10:49:47 -050067using TimerPkg =
68 std::tuple<std::string, std::vector<std::unique_ptr<ActionBase>>&>;
Matthew Barthd9cb63b2021-03-24 14:36:49 -050069/**
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 */
75using TimerData = std::pair<TimerType, TimerPkg>;
76/* Dbus event timer */
77using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>;
78
Matthew Barthebabc042021-05-13 15:38:58 -050079/* Dbus signal object */
80constexpr auto Path = 0;
81constexpr auto Intf = 1;
82constexpr auto Prop = 2;
83using SignalObject = std::tuple<std::string, std::string, std::string>;
84/* Dbus signal actions */
85using 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 */
91using 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 */
100using SignalPkg = std::tuple<SignalHandler, SignalObject, SignalActions>;
101/**
102 * Data associated to a subscribed signal
103 * Tuple constructed of:
104 * std::unique_ptr<std::vector<SignalPkg>> =
105 * Pointer to the signal's packages
106 * std::unique_ptr<sdbusplus::server::match::match> =
107 * Pointer to match holding the subscription to a signal
108 */
109using SignalData = std::tuple<std::unique_ptr<std::vector<SignalPkg>>,
110 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 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600150 * @brief Get the active profiles of the system where an empty list
151 * represents that only configuration entries without a profile defined will
152 * be loaded.
153 *
154 * @return - The list of active profiles
155 */
156 static const std::vector<std::string>& getActiveProfiles();
157
158 /**
159 * @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
Matthew Barth9403a212021-05-17 09:31:50 -0500175 auto confFile =
176 fan::JsonConfig::getConfFile(util::SDBusPlus::getBus(), confAppName,
177 T::confFileName, 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 }
Matthew Barth68ac0042021-06-01 15:38:36 -0500211 log<level::INFO>(
212 fmt::format("Configuration({}) loaded successfully",
213 T::confFileName)
214 .c_str());
Matthew Barthacd737c2021-03-04 11:04:01 -0600215 }
216 return config;
217 }
218
219 /**
Matthew Barth0206c722021-03-30 15:20:05 -0500220 * @brief Check if the given input configuration key matches with another
221 * configuration key that it's to be included in
222 *
223 * @param[in] input - Config key to be included in another config object
224 * @param[in] comp - Config key of the config object to compare with
225 *
226 * @return Whether the configuration object should be included
227 */
228 static bool inConfig(const configKey& input, const configKey& comp);
229
230 /**
Matthew Barth12cb1252021-03-08 16:47:30 -0600231 * @brief Check if the given path and inteface is owned by a dbus service
232 *
233 * @param[in] path - Dbus object path
234 * @param[in] intf - Dbus object interface
235 *
236 * @return - Whether the service has an owner for the given object path and
237 * interface
238 */
239 static bool hasOwner(const std::string& path, const std::string& intf);
240
241 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500242 * @brief Sets the dbus service owner state of a given object
243 *
244 * @param[in] path - Dbus object path
245 * @param[in] serv - Dbus service name
246 * @param[in] intf - Dbus object interface
247 * @param[in] isOwned - Dbus service owner state
248 */
249 void setOwner(const std::string& path, const std::string& serv,
250 const std::string& intf, bool isOwned);
251
252 /**
253 * @brief Add a set of services for a path and interface by retrieving all
254 * the path subtrees to the given depth from root for the interface
255 *
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500256 * @param[in] intf - Interface to add services for
257 * @param[in] depth - Depth of tree traversal from root path
258 *
259 * @throws - DBusMethodError
260 * Throws a DBusMethodError when the `getSubTree` method call fails
261 */
Matthew Barth98f6fc12021-04-16 10:48:37 -0500262 static void addServices(const std::string& intf, int32_t depth);
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500263
264 /**
265 * @brief Get the service for a given path and interface from cached
266 * dataset and attempt to add all the services for the given path/interface
267 * when it's not found
268 *
269 * @param[in] path - Path to get service for
270 * @param[in] intf - Interface to get service for
271 *
272 * @return - The now cached service name
273 *
274 * @throws - DBusMethodError
275 * Ripples up a DBusMethodError exception from calling addServices
276 */
277 static const std::string& getService(const std::string& path,
278 const std::string& intf);
279
280 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500281 * @brief Get all the object paths for a given service and interface from
282 * the cached dataset and try to add all the services for the given
283 * interface when no paths are found and then attempt to get all the object
284 * paths again
285 *
286 * @param[in] serv - Service name to get paths for
287 * @param[in] intf - Interface to get paths for
288 *
289 * @return The cached object paths
290 */
291 std::vector<std::string> getPaths(const std::string& serv,
292 const std::string& intf);
293
294 /**
295 * @brief Add objects to the cached dataset by first using
296 * `getManagedObjects` for the same service providing the given path and
297 * interface or just add the single object of the given path, interface, and
298 * property if that fails.
299 *
300 * @param[in] path - Dbus object's path
301 * @param[in] intf - Dbus object's interface
302 * @param[in] prop - Dbus object's property
303 *
304 * @throws - DBusMethodError
305 * Throws a DBusMethodError when the the service is failed to be found or
306 * when the `getManagedObjects` method call fails
307 */
308 void addObjects(const std::string& path, const std::string& intf,
309 const std::string& prop);
310
311 /**
312 * @brief Get an object's property value
313 *
314 * @param[in] path - Dbus object's path
315 * @param[in] intf - Dbus object's interface
316 * @param[in] prop - Dbus object's property
317 */
318 const std::optional<PropertyVariantType>
319 getProperty(const std::string& path, const std::string& intf,
320 const std::string& prop);
321
322 /**
Matthew Barthbaeeb8f2021-05-13 16:03:54 -0500323 * @brief Set/update an object's property value
324 *
325 * @param[in] path - Dbus object's path
326 * @param[in] intf - Dbus object's interface
327 * @param[in] prop - Dbus object's property
328 * @param[in] value - Dbus object's property value
329 */
330 void setProperty(const std::string& path, const std::string& intf,
331 const std::string& prop, PropertyVariantType value)
332 {
333 _objects[path][intf][prop] = std::move(value);
334 }
335
336 /**
Matthew Barthc2691402021-05-13 16:20:32 -0500337 * @brief Remove an object's interface
338 *
339 * @param[in] path - Dbus object's path
340 * @param[in] intf - Dbus object's interface
341 */
342 inline void removeInterface(const std::string& path,
343 const std::string& intf)
344 {
345 auto itPath = _objects.find(path);
346 if (itPath != std::end(_objects))
347 {
348 _objects[path].erase(intf);
349 }
350 }
351
352 /**
Matthew Barth07fecfc2021-01-29 09:04:43 -0600353 * @brief Get the object's property value as a variant
354 *
355 * @param[in] path - Path of the object containing the property
356 * @param[in] intf - Interface name containing the property
357 * @param[in] prop - Name of property
358 *
359 * @return - The object's property value as a variant
360 */
361 static inline auto getObjValueVariant(const std::string& path,
362 const std::string& intf,
363 const std::string& prop)
364 {
365 return _objects.at(path).at(intf).at(prop);
366 };
367
368 /**
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500369 * @brief Add a dbus timer
370 *
371 * @param[in] type - Type of timer
372 * @param[in] interval - Timer interval in microseconds
373 * @param[in] pkg - Packaged data for when timer expires
374 */
375 void addTimer(const TimerType type,
376 const std::chrono::microseconds interval,
377 std::unique_ptr<TimerPkg> pkg);
378
379 /**
380 * @brief Callback when a timer expires
381 *
382 * @param[in] data - Data to be used when the timer expired
383 */
384 void timerExpired(TimerData& data);
385
386 /**
Matthew Barthebabc042021-05-13 15:38:58 -0500387 * @brief Get the signal data for a given match string
388 *
389 * @param[in] sigMatch - Signal match string
390 *
391 * @return - Reference to the signal data for the given match string
392 */
393 std::vector<SignalData>& getSignal(const std::string& sigMatch)
394 {
395 return _signals[sigMatch];
396 }
397
398 /**
399 * @brief Handle receiving signals
400 *
401 * @param[in] msg - Signal message containing the signal's data
402 * @param[in] pkgs - Signal packages associated to the signal being handled
403 */
404 void handleSignal(sdbusplus::message::message& msg,
405 const std::vector<SignalPkg>* pkgs);
406
407 /**
Matthew Bartheebde062021-04-14 12:48:52 -0500408 * @brief Get the sdbusplus bus object
409 */
410 inline auto& getBus()
411 {
412 return _bus;
413 }
414
415 /**
Matthew Barth48f44da2021-05-27 15:43:34 -0500416 * @brief Is the power state on
417 *
418 * @return Current power state of the system
419 */
420 inline bool isPowerOn() const
421 {
422 return _powerState->isPowerOn();
423 }
424
Matthew Bartha227a162020-08-05 10:51:45 -0500425 private:
Matthew Barth1542fb52021-06-10 14:09:09 -0500426 /* The sdbusplus bus object to use */
Matthew Barthacd737c2021-03-04 11:04:01 -0600427 sdbusplus::bus::bus& _bus;
428
Matthew Barth1542fb52021-06-10 14:09:09 -0500429 /* The sdeventplus even loop to use */
Matthew Barthacd737c2021-03-04 11:04:01 -0600430 sdeventplus::Event _event;
431
Matthew Barth1542fb52021-06-10 14:09:09 -0500432 /* The sdbusplus manager object to set the ObjectManager interface */
433 sdbusplus::server::manager::manager _mgr;
434
Matthew Barth48f44da2021-05-27 15:43:34 -0500435 /* The system's power state determination object */
436 std::unique_ptr<PowerState> _powerState;
437
Matthew Barth06764942021-03-04 09:28:40 -0600438 /* List of profiles configured */
Matthew Barthacd737c2021-03-04 11:04:01 -0600439 std::map<configKey, std::unique_ptr<Profile>> _profiles;
Matthew Barth06764942021-03-04 09:28:40 -0600440
441 /* List of active profiles */
Matthew Barthacd737c2021-03-04 11:04:01 -0600442 static std::vector<std::string> _activeProfiles;
443
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500444 /* Subtree map of paths to services of interfaces(with ownership state) */
445 static std::map<
446 std::string,
447 std::map<std::string, std::pair<bool, std::vector<std::string>>>>
Matthew Barth12cb1252021-03-08 16:47:30 -0600448 _servTree;
449
Matthew Barth07fecfc2021-01-29 09:04:43 -0600450 /* Object map of paths to interfaces of properties and their values */
451 static std::map<
452 std::string,
453 std::map<std::string, std::map<std::string, PropertyVariantType>>>
454 _objects;
455
Matthew Barthd9cb63b2021-03-24 14:36:49 -0500456 /* List of timers and their data to be processed when expired */
457 std::vector<std::pair<std::unique_ptr<TimerData>, Timer>> _timers;
458
Matthew Barthebabc042021-05-13 15:38:58 -0500459 /* Map of signal match strings to a list of signal handler data */
460 std::unordered_map<std::string, std::vector<SignalData>> _signals;
461
Matthew Barthacd737c2021-03-04 11:04:01 -0600462 /* List of zones configured */
463 std::map<configKey, std::unique_ptr<Zone>> _zones;
464
Matthew Barth44ab7692021-03-26 11:40:10 -0500465 /* List of events configured */
466 std::map<configKey, std::unique_ptr<Event>> _events;
467
Matthew Barthacd737c2021-03-04 11:04:01 -0600468 /**
Matthew Barthe91ac862021-05-25 16:22:17 -0500469 * @brief Load all the fan control JSON configuration files
470 *
471 * This is where all the fan control JSON configuration files are parsed and
472 * loaded into their associated objects. Anything that needs to be done when
473 * the Manager object is constructed or handling a SIGHUP to reload the
474 * configurations needs to be done here.
475 */
476 void load();
477
478 /**
Matthew Barth48f44da2021-05-27 15:43:34 -0500479 * @brief Callback for power state changes
480 *
481 * @param[in] powerStateOn - Whether the power state is on or not
482 *
483 * Callback function bound to the PowerState object instance to handle each
484 * time the power state changes.
485 */
486 void powerStateChanged(bool powerStateOn);
487
488 /**
Matthew Barth4ca87fa2021-04-14 11:31:13 -0500489 * @brief Find the service name for a given path and interface from the
490 * cached dataset
491 *
492 * @param[in] path - Path to get service for
493 * @param[in] intf - Interface to get service for
494 *
495 * @return - The cached service name
496 */
497 static const std::string& findService(const std::string& path,
498 const std::string& intf);
499
500 /**
Matthew Barthf41e9472021-05-13 16:38:06 -0500501 * @brief Find all the paths for a given service and interface from the
502 * cached dataset
503 *
504 * @param[in] serv - Service name to get paths for
505 * @param[in] intf - Interface to get paths for
506 *
507 * @return - The cached object paths
508 */
509 std::vector<std::string> findPaths(const std::string& serv,
510 const std::string& intf);
511
512 /**
Matthew Barthacd737c2021-03-04 11:04:01 -0600513 * @brief Parse and set the configured profiles from the profiles JSON file
514 *
515 * Retrieves the optional profiles JSON configuration file, parses it, and
516 * creates a list of configured profiles available to the other
517 * configuration files. These profiles can be used to remove or include
518 * entries within the other configuration files.
519 */
520 void setProfiles();
Matthew Bartha227a162020-08-05 10:51:45 -0500521};
522
523} // namespace phosphor::fan::control::json