1 /**
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 #include "config.h"
17 
18 #include "manager.hpp"
19 
20 #include "action.hpp"
21 #include "event.hpp"
22 #include "fan.hpp"
23 #include "group.hpp"
24 #include "json_config.hpp"
25 #include "power_state.hpp"
26 #include "profile.hpp"
27 #include "sdbusplus.hpp"
28 #include "zone.hpp"
29 
30 #include <nlohmann/json.hpp>
31 #include <sdbusplus/bus.hpp>
32 #include <sdbusplus/server/manager.hpp>
33 #include <sdeventplus/event.hpp>
34 #include <sdeventplus/utility/timer.hpp>
35 
36 #include <algorithm>
37 #include <chrono>
38 #include <filesystem>
39 #include <functional>
40 #include <map>
41 #include <memory>
42 #include <tuple>
43 #include <utility>
44 #include <vector>
45 
46 namespace phosphor::fan::control::json
47 {
48 
49 using json = nlohmann::json;
50 
51 std::vector<std::string> Manager::_activeProfiles;
52 std::map<std::string,
53          std::map<std::string, std::pair<bool, std::vector<std::string>>>>
54     Manager::_servTree;
55 std::map<std::string,
56          std::map<std::string, std::map<std::string, PropertyVariantType>>>
57     Manager::_objects;
58 
59 Manager::Manager(const sdeventplus::Event& event) :
60     _bus(util::SDBusPlus::getBus()), _event(event),
61     _mgr(util::SDBusPlus::getBus(), CONTROL_OBJPATH), _loadAllowed(true),
62     _powerState(std::make_unique<PGoodState>(
63         util::SDBusPlus::getBus(),
64         std::bind(std::mem_fn(&Manager::powerStateChanged), this,
65                   std::placeholders::_1)))
66 {}
67 
68 void Manager::sighupHandler(sdeventplus::source::Signal&,
69                             const struct signalfd_siginfo*)
70 {
71     // Save current set of available and active profiles
72     std::map<configKey, std::unique_ptr<Profile>> profiles;
73     profiles.swap(_profiles);
74     std::vector<std::string> activeProfiles;
75     activeProfiles.swap(_activeProfiles);
76 
77     try
78     {
79         _loadAllowed = true;
80         load();
81     }
82     catch (std::runtime_error& re)
83     {
84         // Restore saved available and active profiles
85         _loadAllowed = false;
86         _profiles.swap(profiles);
87         _activeProfiles.swap(activeProfiles);
88         log<level::ERR>("Error reloading configs, no changes made",
89                         entry("LOAD_ERROR=%s", re.what()));
90     }
91 }
92 
93 void Manager::load()
94 {
95     if (_loadAllowed)
96     {
97         // Load the available profiles and which are active
98         setProfiles();
99 
100         // Load the zone configurations
101         auto zones = getConfig<Zone>(false, _event, this);
102         // Load the fan configurations and move each fan into its zone
103         auto fans = getConfig<Fan>(false);
104         for (auto& fan : fans)
105         {
106             configKey fanProfile =
107                 std::make_pair(fan.second->getZone(), fan.first.second);
108             auto itZone = std::find_if(
109                 zones.begin(), zones.end(), [&fanProfile](const auto& zone) {
110                     return Manager::inConfig(fanProfile, zone.first);
111                 });
112             if (itZone != zones.end())
113             {
114                 if (itZone->second->getTarget() != fan.second->getTarget() &&
115                     fan.second->getTarget() != 0)
116                 {
117                     // Update zone target to current target of the fan in the
118                     // zone
119                     itZone->second->setTarget(fan.second->getTarget());
120                 }
121                 itZone->second->addFan(std::move(fan.second));
122             }
123         }
124 
125         // Load any events configured
126         auto events = getConfig<Event>(true, this, zones);
127 
128         // Enable zones
129         _zones = std::move(zones);
130         std::for_each(_zones.begin(), _zones.end(),
131                       [](const auto& entry) { entry.second->enable(); });
132 
133         // Clear current timers and signal subscriptions before enabling events
134         // To save reloading services and/or objects into cache, do not clear
135         // cache
136         _timers.clear();
137         _signals.clear();
138 
139         // Enable events
140         _events = std::move(events);
141         std::for_each(_events.begin(), _events.end(),
142                       [](const auto& entry) { entry.second->enable(); });
143 
144         _loadAllowed = false;
145     }
146 }
147 
148 void Manager::powerStateChanged(bool powerStateOn)
149 {
150     if (powerStateOn)
151     {
152         std::for_each(_zones.begin(), _zones.end(), [](const auto& entry) {
153             entry.second->setTarget(entry.second->getPoweronTarget());
154         });
155     }
156 }
157 
158 const std::vector<std::string>& Manager::getActiveProfiles()
159 {
160     return _activeProfiles;
161 }
162 
163 bool Manager::inConfig(const configKey& input, const configKey& comp)
164 {
165     // Config names dont match, do not include in config
166     if (input.first != comp.first)
167     {
168         return false;
169     }
170     // No profiles specified by input config, can be used in any config
171     if (input.second.empty())
172     {
173         return true;
174     }
175     else
176     {
177         // Profiles must have one match in the other's profiles(and they must be
178         // an active profile) to be used in the config
179         return std::any_of(
180             input.second.begin(), input.second.end(),
181             [&comp](const auto& lProfile) {
182                 return std::any_of(
183                     comp.second.begin(), comp.second.end(),
184                     [&lProfile](const auto& rProfile) {
185                         if (lProfile != rProfile)
186                         {
187                             return false;
188                         }
189                         auto activeProfs = getActiveProfiles();
190                         return std::find(activeProfs.begin(), activeProfs.end(),
191                                          lProfile) != activeProfs.end();
192                     });
193             });
194     }
195 }
196 
197 bool Manager::hasOwner(const std::string& path, const std::string& intf)
198 {
199     auto itServ = _servTree.find(path);
200     if (itServ == _servTree.end())
201     {
202         // Path not found in cache, therefore owner missing
203         return false;
204     }
205     for (const auto& service : itServ->second)
206     {
207         auto itIntf = std::find_if(
208             service.second.second.begin(), service.second.second.end(),
209             [&intf](const auto& interface) { return intf == interface; });
210         if (itIntf != std::end(service.second.second))
211         {
212             // Service found, return owner state
213             return service.second.first;
214         }
215     }
216     // Interface not found in cache, therefore owner missing
217     return false;
218 }
219 
220 void Manager::setOwner(const std::string& path, const std::string& serv,
221                        const std::string& intf, bool isOwned)
222 {
223     // Set owner state for specific object given
224     auto& ownIntf = _servTree[path][serv];
225     ownIntf.first = isOwned;
226     auto itIntf = std::find_if(
227         ownIntf.second.begin(), ownIntf.second.end(),
228         [&intf](const auto& interface) { return intf == interface; });
229     if (itIntf == std::end(ownIntf.second))
230     {
231         ownIntf.second.emplace_back(intf);
232     }
233 
234     // Update owner state on all entries of the same `serv` & `intf`
235     for (auto& itPath : _servTree)
236     {
237         if (itPath.first == path)
238         {
239             // Already set/updated owner on this path for `serv` & `intf`
240             continue;
241         }
242         for (auto& itServ : itPath.second)
243         {
244             if (itServ.first != serv)
245             {
246                 continue;
247             }
248             auto itIntf = std::find_if(
249                 itServ.second.second.begin(), itServ.second.second.end(),
250                 [&intf](const auto& interface) { return intf == interface; });
251             if (itIntf != std::end(itServ.second.second))
252             {
253                 itServ.second.first = isOwned;
254             }
255         }
256     }
257 }
258 
259 const std::string& Manager::findService(const std::string& path,
260                                         const std::string& intf)
261 {
262     static const std::string empty = "";
263 
264     auto itServ = _servTree.find(path);
265     if (itServ != _servTree.end())
266     {
267         for (const auto& service : itServ->second)
268         {
269             auto itIntf = std::find_if(
270                 service.second.second.begin(), service.second.second.end(),
271                 [&intf](const auto& interface) { return intf == interface; });
272             if (itIntf != std::end(service.second.second))
273             {
274                 // Service found, return service name
275                 return service.first;
276             }
277         }
278     }
279 
280     return empty;
281 }
282 
283 void Manager::addServices(const std::string& intf, int32_t depth)
284 {
285     // Get all subtree objects for the given interface
286     auto objects = util::SDBusPlus::getSubTreeRaw(util::SDBusPlus::getBus(),
287                                                   "/", intf, depth);
288     // Add what's returned to the cache of path->services
289     for (auto& itPath : objects)
290     {
291         auto pathIter = _servTree.find(itPath.first);
292         if (pathIter != _servTree.end())
293         {
294             // Path found in cache
295             for (auto& itServ : itPath.second)
296             {
297                 auto servIter = pathIter->second.find(itServ.first);
298                 if (servIter != pathIter->second.end())
299                 {
300                     // Service found in cache
301                     for (auto& itIntf : itServ.second)
302                     {
303                         if (std::find(servIter->second.second.begin(),
304                                       servIter->second.second.end(),
305                                       itIntf) == servIter->second.second.end())
306                         {
307                             // Add interface to cache
308                             servIter->second.second.emplace_back(itIntf);
309                         }
310                     }
311                 }
312                 else
313                 {
314                     // Service not found in cache
315                     auto intfs = {intf};
316                     pathIter->second[itServ.first] =
317                         std::make_pair(true, intfs);
318                 }
319             }
320         }
321         else
322         {
323             // Path not found in cache
324             auto intfs = {intf};
325             _servTree[itPath.first] = {
326                 {itPath.second.begin()->first, std::make_pair(true, intfs)}};
327         }
328     }
329 }
330 
331 const std::string& Manager::getService(const std::string& path,
332                                        const std::string& intf)
333 {
334     // Retrieve service from cache
335     const auto& serviceName = findService(path, intf);
336     if (serviceName.empty())
337     {
338         addServices(intf, 0);
339         return findService(path, intf);
340     }
341 
342     return serviceName;
343 }
344 
345 std::vector<std::string> Manager::findPaths(const std::string& serv,
346                                             const std::string& intf)
347 {
348     std::vector<std::string> paths;
349 
350     for (const auto& path : _servTree)
351     {
352         auto itServ = path.second.find(serv);
353         if (itServ != path.second.end())
354         {
355             if (std::find(itServ->second.second.begin(),
356                           itServ->second.second.end(),
357                           intf) != itServ->second.second.end())
358             {
359                 if (std::find(paths.begin(), paths.end(), path.first) ==
360                     paths.end())
361                 {
362                     paths.push_back(path.first);
363                 }
364             }
365         }
366     }
367 
368     return paths;
369 }
370 
371 std::vector<std::string> Manager::getPaths(const std::string& serv,
372                                            const std::string& intf)
373 {
374     auto paths = findPaths(serv, intf);
375     if (paths.empty())
376     {
377         addServices(intf, 0);
378         return findPaths(serv, intf);
379     }
380 
381     return paths;
382 }
383 
384 void Manager::addObjects(const std::string& path, const std::string& intf,
385                          const std::string& prop)
386 {
387     auto service = getService(path, intf);
388     if (service.empty())
389     {
390         // Log service not found for object
391         log<level::DEBUG>(
392             fmt::format("Unable to get service name for path {}, interface {}",
393                         path, intf)
394                 .c_str());
395         return;
396     }
397 
398     auto objMgrPaths = getPaths(service, "org.freedesktop.DBus.ObjectManager");
399     if (objMgrPaths.empty())
400     {
401         // No object manager interface provided by service?
402         // Attempt to retrieve property directly
403         auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>(
404             _bus, service, path, intf, prop);
405         _objects[path][intf][prop] = variant;
406         return;
407     }
408 
409     for (const auto& objMgrPath : objMgrPaths)
410     {
411         // Get all managed objects of service
412         auto objects = util::SDBusPlus::getManagedObjects<PropertyVariantType>(
413             _bus, service, objMgrPath);
414 
415         // Add what's returned to the cache of objects
416         for (auto& object : objects)
417         {
418             auto itPath = _objects.find(object.first);
419             if (itPath != _objects.end())
420             {
421                 // Path found in cache
422                 for (auto& interface : itPath->second)
423                 {
424                     auto itIntf = itPath->second.find(interface.first);
425                     if (itIntf != itPath->second.end())
426                     {
427                         // Interface found in cache
428                         for (auto& property : itIntf->second)
429                         {
430                             auto itProp = itIntf->second.find(property.first);
431                             if (itProp != itIntf->second.end())
432                             {
433                                 // Property found, update value
434                                 itProp->second = property.second;
435                             }
436                             else
437                             {
438                                 itIntf->second.insert(property);
439                             }
440                         }
441                     }
442                     else
443                     {
444                         // Interface not found in cache
445                         itPath->second.insert(interface);
446                     }
447                 }
448             }
449             else
450             {
451                 // Path not found in cache
452                 _objects.insert(object);
453             }
454         }
455     }
456 }
457 
458 const std::optional<PropertyVariantType>
459     Manager::getProperty(const std::string& path, const std::string& intf,
460                          const std::string& prop)
461 {
462     // TODO Objects hosted by fan control (i.e. ThermalMode) are required to
463     // update the cache upon being set/updated
464     auto itPath = _objects.find(path);
465     if (itPath != _objects.end())
466     {
467         auto itIntf = itPath->second.find(intf);
468         if (itIntf != itPath->second.end())
469         {
470             auto itProp = itIntf->second.find(prop);
471             if (itProp != itIntf->second.end())
472             {
473                 return itProp->second;
474             }
475         }
476     }
477 
478     return std::nullopt;
479 }
480 
481 void Manager::addTimer(const TimerType type,
482                        const std::chrono::microseconds interval,
483                        std::unique_ptr<TimerPkg> pkg)
484 {
485     auto dataPtr =
486         std::make_unique<TimerData>(std::make_pair(type, std::move(*pkg)));
487     Timer timer(_event,
488                 std::bind(&Manager::timerExpired, this, std::ref(*dataPtr)));
489     if (type == TimerType::repeating)
490     {
491         timer.restart(interval);
492     }
493     else if (type == TimerType::oneshot)
494     {
495         timer.restartOnce(interval);
496     }
497     else
498     {
499         throw std::invalid_argument("Invalid Timer Type");
500     }
501     _timers.emplace_back(std::move(dataPtr), std::move(timer));
502 }
503 
504 void Manager::timerExpired(TimerData& data)
505 {
506     auto& actions =
507         std::get<std::vector<std::unique_ptr<ActionBase>>&>(data.second);
508     // Perform the actions in the timer data
509     std::for_each(actions.begin(), actions.end(),
510                   [](auto& action) { action->run(); });
511 
512     // Remove oneshot timers after they expired
513     if (data.first == TimerType::oneshot)
514     {
515         auto itTimer = std::find_if(
516             _timers.begin(), _timers.end(), [&data](const auto& timer) {
517                 return (data.first == timer.first->first &&
518                         (std::get<std::string>(data.second) ==
519                          std::get<std::string>(timer.first->second)));
520             });
521         if (itTimer != std::end(_timers))
522         {
523             _timers.erase(itTimer);
524         }
525     }
526 }
527 
528 void Manager::handleSignal(sdbusplus::message::message& msg,
529                            const std::vector<SignalPkg>& pkgs)
530 {
531     for (auto& pkg : pkgs)
532     {
533         // Handle the signal callback and only run the actions if the handler
534         // updated the cache for the given SignalObject
535         if (std::get<SignalHandler>(pkg)(msg, std::get<SignalObject>(pkg),
536                                          *this))
537         {
538             // Perform the actions in the handler package
539             auto& actions = std::get<SignalActions>(pkg);
540             std::for_each(actions.begin(), actions.end(),
541                           [](auto& action) { action.get()->run(); });
542         }
543     }
544 }
545 
546 void Manager::setProfiles()
547 {
548     // Profiles JSON config file is optional
549     auto confFile = fan::JsonConfig::getConfFile(_bus, confAppName,
550                                                  Profile::confFileName, true);
551 
552     _profiles.clear();
553     if (!confFile.empty())
554     {
555         for (const auto& entry : fan::JsonConfig::load(confFile))
556         {
557             auto obj = std::make_unique<Profile>(entry);
558             _profiles.emplace(
559                 std::make_pair(obj->getName(), obj->getProfiles()),
560                 std::move(obj));
561         }
562     }
563 
564     // Ensure all configurations use the same set of active profiles
565     // (In case a profile's active state changes during configuration)
566     _activeProfiles.clear();
567     for (const auto& profile : _profiles)
568     {
569         if (profile.second->isActive())
570         {
571             _activeProfiles.emplace_back(profile.first.first);
572         }
573     }
574 }
575 
576 } // namespace phosphor::fan::control::json
577