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