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 
17 #include "manager.hpp"
18 
19 #include "chassis.hpp"
20 #include "config_file_parser.hpp"
21 #include "exception_utils.hpp"
22 #include "journal.hpp"
23 #include "rule.hpp"
24 #include "utility.hpp"
25 
26 #include <sdbusplus/bus.hpp>
27 
28 #include <chrono>
29 #include <exception>
30 #include <stdexcept>
31 #include <tuple>
32 #include <utility>
33 #include <variant>
34 
35 namespace phosphor::power::regulators
36 {
37 
38 namespace fs = std::filesystem;
39 
40 /**
41  * Standard configuration file directory.  This directory is part of the
42  * firmware install image.  It contains the standard version of the config file.
43  */
44 const fs::path standardConfigFileDir{"/usr/share/phosphor-regulators"};
45 
46 /**
47  * Test configuration file directory.  This directory can contain a test version
48  * of the config file.  The test version will override the standard version.
49  */
50 const fs::path testConfigFileDir{"/etc/phosphor-regulators"};
51 
52 Manager::Manager(sdbusplus::bus::bus& bus, const sdeventplus::Event& event) :
53     ManagerObject{bus, objPath, true}, bus{bus}, eventLoop{event}
54 {
55     /* Temporarily comment out until D-Bus interface is defined and available.
56         // Subscribe to interfacesAdded signal for filename property
57         std::unique_ptr<sdbusplus::server::match::match> matchPtr =
58             std::make_unique<sdbusplus::server::match::match>(
59                 bus,
60                 sdbusplus::bus::match::rules::interfacesAdded(sysDbusObj).c_str(),
61                 std::bind(std::mem_fn(&Manager::signalHandler), this,
62                           std::placeholders::_1));
63         signals.emplace_back(std::move(matchPtr));
64 
65         // Attempt to get the filename property from dbus
66         setFileName(getFileNameDbus());
67     */
68 
69     // Temporarily hard-code JSON config file name to first system that will use
70     // this application.  Remove this when D-Bus interface is available.
71     fileName = "ibm_rainier.json";
72 
73     if (!fileName.empty())
74     {
75         loadConfigFile();
76     }
77 
78     // Obtain dbus service name
79     bus.request_name(busName);
80 }
81 
82 void Manager::configure()
83 {
84     // Verify System object exists; this means config file has been loaded
85     if (system)
86     {
87         // Configure the regulator devices in the system
88         system->configure();
89     }
90     else
91     {
92         journal::logErr("Unable to configure regulator devices: Configuration "
93                         "file not loaded");
94         // TODO: Log error
95     }
96 
97     // TODO Configuration errors that should halt poweron,
98     // throw InternalFailure exception (or similar) to
99     // fail the call(busctl) to this method
100 }
101 
102 void Manager::monitor(bool enable)
103 {
104     if (enable)
105     {
106         /* Temporarily comment out until monitoring is supported.
107             Timer timer(eventLoop, std::bind(&Manager::timerExpired, this));
108             // Set timer as a repeating 1sec timer
109             timer.restart(std::chrono::milliseconds(1000));
110             timers.emplace_back(std::move(timer));
111         */
112     }
113     else
114     {
115         /* Temporarily comment out until monitoring is supported.
116             // Delete all timers to disable monitoring
117             timers.clear();
118         */
119 
120         // Verify System object exists; this means config file has been loaded
121         if (system)
122         {
123             // Close the regulator devices in the system.  Monitoring is
124             // normally disabled because the system is being powered off.  The
125             // devices should be closed in case hardware is removed or replaced
126             // while the system is at standby.
127             system->closeDevices();
128         }
129     }
130 }
131 
132 void Manager::timerExpired()
133 {
134     // TODO Analyze, refresh sensor status, and
135     // collect/update telemetry for each regulator
136 }
137 
138 void Manager::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
139                             const struct signalfd_siginfo* /*sigInfo*/)
140 {
141     if (!fileName.empty())
142     {
143         loadConfigFile();
144     }
145 }
146 
147 void Manager::signalHandler(sdbusplus::message::message& msg)
148 {
149     if (msg)
150     {
151         sdbusplus::message::object_path op;
152         msg.read(op);
153         if (static_cast<const std::string&>(op) != sysDbusPath)
154         {
155             // Object path does not match the path
156             return;
157         }
158 
159         // An interfacesAdded signal returns a dictionary of interface
160         // names to a dictionary of properties and their values
161         // https://dbus.freedesktop.org/doc/dbus-specification.html
162         std::map<std::string, std::map<std::string, std::variant<std::string>>>
163             intfProp;
164         msg.read(intfProp);
165         auto itIntf = intfProp.find(sysDbusIntf);
166         if (itIntf == intfProp.cend())
167         {
168             // Interface not found on the path
169             return;
170         }
171         auto itProp = itIntf->second.find(sysDbusProp);
172         if (itProp == itIntf->second.cend())
173         {
174             // Property not found on the interface
175             return;
176         }
177         // Set fileName and call parse json function
178         setFileName(std::get<std::string>(itProp->second));
179         if (!fileName.empty())
180         {
181             loadConfigFile();
182         }
183     }
184 }
185 
186 const std::string Manager::getFileNameDbus()
187 {
188     std::string fileName = "";
189     using namespace phosphor::power::util;
190 
191     try
192     {
193         // Do not log an error when service or property are not found
194         auto service = getService(sysDbusPath, sysDbusIntf, bus, false);
195         if (!service.empty())
196         {
197             getProperty(sysDbusIntf, sysDbusProp, sysDbusPath, service, bus,
198                         fileName);
199         }
200     }
201     catch (const sdbusplus::exception::SdBusError&)
202     {
203         // File name property not available on dbus
204         fileName = "";
205     }
206 
207     return fileName;
208 }
209 
210 fs::path Manager::findConfigFile()
211 {
212     // First look in the test directory
213     fs::path pathName{testConfigFileDir / fileName};
214     if (!fs::exists(pathName))
215     {
216         // Look in the standard directory
217         pathName = standardConfigFileDir / fileName;
218         if (!fs::exists(pathName))
219         {
220             throw std::runtime_error{"Configuration file does not exist: " +
221                                      pathName.string()};
222         }
223     }
224 
225     return pathName;
226 }
227 
228 void Manager::loadConfigFile()
229 {
230     try
231     {
232         // Find the absolute path to the config file
233         fs::path pathName = findConfigFile();
234 
235         // Log info message in journal; config file path is important
236         journal::logInfo("Loading configuration file " + pathName.string());
237 
238         // Parse the config file
239         std::vector<std::unique_ptr<Rule>> rules{};
240         std::vector<std::unique_ptr<Chassis>> chassis{};
241         std::tie(rules, chassis) = config_file_parser::parse(pathName);
242 
243         // Store config file information in a new System object.  The old System
244         // object, if any, is automatically deleted.
245         system = std::make_unique<System>(std::move(rules), std::move(chassis));
246     }
247     catch (const std::exception& e)
248     {
249         // Log error messages in journal
250         exception_utils::log(e);
251         journal::logErr("Unable to load configuration file");
252 
253         // TODO: Create error log entry
254     }
255 }
256 
257 } // namespace phosphor::power::regulators
258