1*e83604beSCheng C Yang /*
2*e83604beSCheng C Yang // Copyright (c) 2019 Intel Corporation
3*e83604beSCheng C Yang //
4*e83604beSCheng C Yang // Licensed under the Apache License, Version 2.0 (the "License");
5*e83604beSCheng C Yang // you may not use this file except in compliance with the License.
6*e83604beSCheng C Yang // You may obtain a copy of the License at
7*e83604beSCheng C Yang //
8*e83604beSCheng C Yang //      http://www.apache.org/licenses/LICENSE-2.0
9*e83604beSCheng C Yang //
10*e83604beSCheng C Yang // Unless required by applicable law or agreed to in writing, software
11*e83604beSCheng C Yang // distributed under the License is distributed on an "AS IS" BASIS,
12*e83604beSCheng C Yang // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*e83604beSCheng C Yang // See the License for the specific language governing permissions and
14*e83604beSCheng C Yang // limitations under the License.
15*e83604beSCheng C Yang */
16*e83604beSCheng C Yang 
17*e83604beSCheng C Yang #include "types.hpp"
18*e83604beSCheng C Yang 
19*e83604beSCheng C Yang #include <array>
20*e83604beSCheng C Yang #include <boost/algorithm/string/predicate.hpp>
21*e83604beSCheng C Yang #include <boost/algorithm/string/replace.hpp>
22*e83604beSCheng C Yang #include <boost/asio/post.hpp>
23*e83604beSCheng C Yang #include <boost/container/flat_set.hpp>
24*e83604beSCheng C Yang #include <cold_redundancy.hpp>
25*e83604beSCheng C Yang #include <filesystem>
26*e83604beSCheng C Yang #include <fstream>
27*e83604beSCheng C Yang #include <iostream>
28*e83604beSCheng C Yang #include <phosphor-logging/elog-errors.hpp>
29*e83604beSCheng C Yang #include <regex>
30*e83604beSCheng C Yang #include <sdbusplus/asio/connection.hpp>
31*e83604beSCheng C Yang #include <sdbusplus/asio/object_server.hpp>
32*e83604beSCheng C Yang #include <sdbusplus/asio/sd_event.hpp>
33*e83604beSCheng C Yang #include <sdbusplus/bus/match.hpp>
34*e83604beSCheng C Yang 
35*e83604beSCheng C Yang namespace
36*e83604beSCheng C Yang {
37*e83604beSCheng C Yang constexpr const std::array<const char*, 1> psuInterfaceTypes = {
38*e83604beSCheng C Yang     "xyz.openbmc_project.Configuration.pmbus"};
39*e83604beSCheng C Yang std::string inventoryPath = std::string(INVENTORY_OBJ_PATH) + "/system";
40*e83604beSCheng C Yang const constexpr char* eventPath = "/xyz/openbmc_project/State/Decorator";
41*e83604beSCheng C Yang std::vector<std::unique_ptr<PowerSupply>> powerSupplies;
42*e83604beSCheng C Yang } // namespace
43*e83604beSCheng C Yang 
44*e83604beSCheng C Yang ColdRedundancy::ColdRedundancy(
45*e83604beSCheng C Yang     boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
46*e83604beSCheng C Yang     std::shared_ptr<sdbusplus::asio::connection>& systemBus) :
47*e83604beSCheng C Yang     filterTimer(io),
48*e83604beSCheng C Yang     systemBus(systemBus)
49*e83604beSCheng C Yang {
50*e83604beSCheng C Yang     post(io,
51*e83604beSCheng C Yang          [this, &io, &objectServer, &systemBus]() { createPSU(systemBus); });
52*e83604beSCheng C Yang     std::function<void(sdbusplus::message::message&)> eventHandler =
53*e83604beSCheng C Yang         [this, &io, &objectServer,
54*e83604beSCheng C Yang          &systemBus](sdbusplus::message::message& message) {
55*e83604beSCheng C Yang             if (message.is_method_error())
56*e83604beSCheng C Yang             {
57*e83604beSCheng C Yang                 std::cerr << "callback method error\n";
58*e83604beSCheng C Yang                 return;
59*e83604beSCheng C Yang             }
60*e83604beSCheng C Yang             filterTimer.expires_after(std::chrono::seconds(1));
61*e83604beSCheng C Yang             filterTimer.async_wait([this, &io, &objectServer, &systemBus](
62*e83604beSCheng C Yang                                        const boost::system::error_code& ec) {
63*e83604beSCheng C Yang                 if (ec == boost::asio::error::operation_aborted)
64*e83604beSCheng C Yang                 {
65*e83604beSCheng C Yang                     return;
66*e83604beSCheng C Yang                 }
67*e83604beSCheng C Yang                 else if (ec)
68*e83604beSCheng C Yang                 {
69*e83604beSCheng C Yang                     std::cerr << "timer error\n";
70*e83604beSCheng C Yang                 }
71*e83604beSCheng C Yang                 createPSU(systemBus);
72*e83604beSCheng C Yang             });
73*e83604beSCheng C Yang         };
74*e83604beSCheng C Yang 
75*e83604beSCheng C Yang     std::function<void(sdbusplus::message::message&)> eventCollect =
76*e83604beSCheng C Yang         [&](sdbusplus::message::message& message) {
77*e83604beSCheng C Yang             std::string objectName;
78*e83604beSCheng C Yang             boost::container::flat_map<std::string, std::variant<bool>> values;
79*e83604beSCheng C Yang             std::string path = message.get_path();
80*e83604beSCheng C Yang             std::size_t slantingPos = path.find_last_of("/\\");
81*e83604beSCheng C Yang             if ((slantingPos == std::string::npos) ||
82*e83604beSCheng C Yang                 ((slantingPos + 1) >= path.size()))
83*e83604beSCheng C Yang             {
84*e83604beSCheng C Yang                 std::cerr << "Unable to get PSU state name from path\n";
85*e83604beSCheng C Yang                 return;
86*e83604beSCheng C Yang             }
87*e83604beSCheng C Yang             std::string statePSUName = path.substr(slantingPos + 1);
88*e83604beSCheng C Yang 
89*e83604beSCheng C Yang             std::size_t hypenPos = statePSUName.find("_");
90*e83604beSCheng C Yang             if (hypenPos == std::string::npos)
91*e83604beSCheng C Yang             {
92*e83604beSCheng C Yang                 std::cerr << "Unable to get PSU name from PSU path\n";
93*e83604beSCheng C Yang                 return;
94*e83604beSCheng C Yang             }
95*e83604beSCheng C Yang             std::string psuName = statePSUName.substr(0, hypenPos);
96*e83604beSCheng C Yang 
97*e83604beSCheng C Yang             try
98*e83604beSCheng C Yang             {
99*e83604beSCheng C Yang                 message.read(objectName, values);
100*e83604beSCheng C Yang             }
101*e83604beSCheng C Yang             catch (const sdbusplus::exception::exception& e)
102*e83604beSCheng C Yang             {
103*e83604beSCheng C Yang                 std::cerr << "Failed to read message from PSU Event\n";
104*e83604beSCheng C Yang                 return;
105*e83604beSCheng C Yang             }
106*e83604beSCheng C Yang 
107*e83604beSCheng C Yang             for (auto& psu : powerSupplies)
108*e83604beSCheng C Yang             {
109*e83604beSCheng C Yang                 if (psu->name != psuName)
110*e83604beSCheng C Yang                 {
111*e83604beSCheng C Yang                     continue;
112*e83604beSCheng C Yang                 }
113*e83604beSCheng C Yang 
114*e83604beSCheng C Yang                 std::string psuEventName = "functional";
115*e83604beSCheng C Yang                 auto findEvent = values.find(psuEventName);
116*e83604beSCheng C Yang                 if (findEvent != values.end())
117*e83604beSCheng C Yang                 {
118*e83604beSCheng C Yang                     bool* functional = std::get_if<bool>(&(findEvent->second));
119*e83604beSCheng C Yang                     if (functional == nullptr)
120*e83604beSCheng C Yang                     {
121*e83604beSCheng C Yang                         std::cerr << "Unable to get valid functional status\n";
122*e83604beSCheng C Yang                         continue;
123*e83604beSCheng C Yang                     }
124*e83604beSCheng C Yang                     if (*functional)
125*e83604beSCheng C Yang                     {
126*e83604beSCheng C Yang                         psu->state = CR::PSUState::normal;
127*e83604beSCheng C Yang                     }
128*e83604beSCheng C Yang                     else
129*e83604beSCheng C Yang                     {
130*e83604beSCheng C Yang                         psu->state = CR::PSUState::acLost;
131*e83604beSCheng C Yang                     }
132*e83604beSCheng C Yang                 }
133*e83604beSCheng C Yang             }
134*e83604beSCheng C Yang         };
135*e83604beSCheng C Yang 
136*e83604beSCheng C Yang     using namespace sdbusplus::bus::match::rules;
137*e83604beSCheng C Yang     for (const char* type : psuInterfaceTypes)
138*e83604beSCheng C Yang     {
139*e83604beSCheng C Yang         auto match = std::make_unique<sdbusplus::bus::match::match>(
140*e83604beSCheng C Yang             static_cast<sdbusplus::bus::bus&>(*systemBus),
141*e83604beSCheng C Yang             type::signal() + member("PropertiesChanged") +
142*e83604beSCheng C Yang                 path_namespace(inventoryPath) + arg0namespace(type),
143*e83604beSCheng C Yang             eventHandler);
144*e83604beSCheng C Yang         matches.emplace_back(std::move(match));
145*e83604beSCheng C Yang     }
146*e83604beSCheng C Yang 
147*e83604beSCheng C Yang     for (const char* eventType : psuEventInterface)
148*e83604beSCheng C Yang     {
149*e83604beSCheng C Yang         auto eventMatch = std::make_unique<sdbusplus::bus::match::match>(
150*e83604beSCheng C Yang             static_cast<sdbusplus::bus::bus&>(*systemBus),
151*e83604beSCheng C Yang             type::signal() + member("PropertiesChanged") +
152*e83604beSCheng C Yang                 path_namespace(eventPath) + arg0namespace(eventType),
153*e83604beSCheng C Yang             eventCollect);
154*e83604beSCheng C Yang         matches.emplace_back(std::move(eventMatch));
155*e83604beSCheng C Yang     }
156*e83604beSCheng C Yang }
157*e83604beSCheng C Yang 
158*e83604beSCheng C Yang static const constexpr int psuDepth = 3;
159*e83604beSCheng C Yang // Check PSU information from entity-manager D-Bus interface and use the bus
160*e83604beSCheng C Yang // address to create PSU Class for cold redundancy.
161*e83604beSCheng C Yang void ColdRedundancy::createPSU(
162*e83604beSCheng C Yang     std::shared_ptr<sdbusplus::asio::connection>& conn)
163*e83604beSCheng C Yang {
164*e83604beSCheng C Yang     numberOfPSU = 0;
165*e83604beSCheng C Yang     powerSupplies.clear();
166*e83604beSCheng C Yang 
167*e83604beSCheng C Yang     // call mapper to get matched obj paths
168*e83604beSCheng C Yang     conn->async_method_call(
169*e83604beSCheng C Yang         [this, &conn](const boost::system::error_code ec,
170*e83604beSCheng C Yang                       CR::GetSubTreeType subtree) {
171*e83604beSCheng C Yang             if (ec)
172*e83604beSCheng C Yang             {
173*e83604beSCheng C Yang                 std::cerr << "Exception happened when communicating to "
174*e83604beSCheng C Yang                              "ObjectMapper\n";
175*e83604beSCheng C Yang                 return;
176*e83604beSCheng C Yang             }
177*e83604beSCheng C Yang             for (const auto& object : subtree)
178*e83604beSCheng C Yang             {
179*e83604beSCheng C Yang                 std::string pathName = object.first;
180*e83604beSCheng C Yang                 for (const auto& serviceIface : object.second)
181*e83604beSCheng C Yang                 {
182*e83604beSCheng C Yang                     std::string serviceName = serviceIface.first;
183*e83604beSCheng C Yang                     for (const auto& interface : serviceIface.second)
184*e83604beSCheng C Yang                     {
185*e83604beSCheng C Yang                         // only get property of matched interface
186*e83604beSCheng C Yang                         bool isIfaceMatched = false;
187*e83604beSCheng C Yang                         for (const auto& type : psuInterfaceTypes)
188*e83604beSCheng C Yang                         {
189*e83604beSCheng C Yang                             if (type == interface)
190*e83604beSCheng C Yang                             {
191*e83604beSCheng C Yang                                 isIfaceMatched = true;
192*e83604beSCheng C Yang                                 break;
193*e83604beSCheng C Yang                             }
194*e83604beSCheng C Yang                         }
195*e83604beSCheng C Yang                         if (!isIfaceMatched)
196*e83604beSCheng C Yang                             continue;
197*e83604beSCheng C Yang 
198*e83604beSCheng C Yang                         conn->async_method_call(
199*e83604beSCheng C Yang                             [this, &conn,
200*e83604beSCheng C Yang                              interface](const boost::system::error_code ec,
201*e83604beSCheng C Yang                                         CR::PropertyMapType propMap) {
202*e83604beSCheng C Yang                                 if (ec)
203*e83604beSCheng C Yang                                 {
204*e83604beSCheng C Yang                                     std::cerr
205*e83604beSCheng C Yang                                         << "Exception happened when get all "
206*e83604beSCheng C Yang                                            "properties\n";
207*e83604beSCheng C Yang                                     return;
208*e83604beSCheng C Yang                                 }
209*e83604beSCheng C Yang 
210*e83604beSCheng C Yang                                 auto configName =
211*e83604beSCheng C Yang                                     std::get_if<std::string>(&propMap["Name"]);
212*e83604beSCheng C Yang                                 if (configName == nullptr)
213*e83604beSCheng C Yang                                 {
214*e83604beSCheng C Yang                                     std::cerr << "error finding necessary "
215*e83604beSCheng C Yang                                                  "entry in configuration\n";
216*e83604beSCheng C Yang                                     return;
217*e83604beSCheng C Yang                                 }
218*e83604beSCheng C Yang 
219*e83604beSCheng C Yang                                 auto configBus =
220*e83604beSCheng C Yang                                     std::get_if<uint64_t>(&propMap["Bus"]);
221*e83604beSCheng C Yang                                 auto configAddress =
222*e83604beSCheng C Yang                                     std::get_if<uint64_t>(&propMap["Address"]);
223*e83604beSCheng C Yang 
224*e83604beSCheng C Yang                                 if (configBus == nullptr ||
225*e83604beSCheng C Yang                                     configAddress == nullptr)
226*e83604beSCheng C Yang                                 {
227*e83604beSCheng C Yang                                     std::cerr << "error finding necessary "
228*e83604beSCheng C Yang                                                  "entry in configuration\n";
229*e83604beSCheng C Yang                                     return;
230*e83604beSCheng C Yang                                 }
231*e83604beSCheng C Yang                                 for (auto& psu : powerSupplies)
232*e83604beSCheng C Yang                                 {
233*e83604beSCheng C Yang                                     if ((static_cast<uint8_t>(*configBus) ==
234*e83604beSCheng C Yang                                          psu->bus) &&
235*e83604beSCheng C Yang                                         (static_cast<uint8_t>(*configAddress) ==
236*e83604beSCheng C Yang                                          psu->address))
237*e83604beSCheng C Yang                                     {
238*e83604beSCheng C Yang                                         return;
239*e83604beSCheng C Yang                                     }
240*e83604beSCheng C Yang                                 }
241*e83604beSCheng C Yang 
242*e83604beSCheng C Yang                                 uint8_t order = 0;
243*e83604beSCheng C Yang 
244*e83604beSCheng C Yang                                 powerSupplies.emplace_back(
245*e83604beSCheng C Yang                                     std::make_unique<PowerSupply>(
246*e83604beSCheng C Yang                                         *configName,
247*e83604beSCheng C Yang                                         static_cast<uint8_t>(*configBus),
248*e83604beSCheng C Yang                                         static_cast<uint8_t>(*configAddress),
249*e83604beSCheng C Yang                                         order, conn));
250*e83604beSCheng C Yang 
251*e83604beSCheng C Yang                                 numberOfPSU++;
252*e83604beSCheng C Yang                                 std::vector<uint8_t> orders = {};
253*e83604beSCheng C Yang                                 for (auto& psu : powerSupplies)
254*e83604beSCheng C Yang                                 {
255*e83604beSCheng C Yang                                     orders.push_back(psu->order);
256*e83604beSCheng C Yang                                 }
257*e83604beSCheng C Yang                             },
258*e83604beSCheng C Yang                             serviceName.c_str(), pathName.c_str(),
259*e83604beSCheng C Yang                             "org.freedesktop.DBus.Properties", "GetAll",
260*e83604beSCheng C Yang                             interface);
261*e83604beSCheng C Yang                     }
262*e83604beSCheng C Yang                 }
263*e83604beSCheng C Yang             }
264*e83604beSCheng C Yang         },
265*e83604beSCheng C Yang         "xyz.openbmc_project.ObjectMapper",
266*e83604beSCheng C Yang         "/xyz/openbmc_project/object_mapper",
267*e83604beSCheng C Yang         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
268*e83604beSCheng C Yang         "/xyz/openbmc_project/inventory/system", psuDepth, psuInterfaceTypes);
269*e83604beSCheng C Yang }
270*e83604beSCheng C Yang 
271*e83604beSCheng C Yang PowerSupply::PowerSupply(
272*e83604beSCheng C Yang     std::string& name, uint8_t bus, uint8_t address, uint8_t order,
273*e83604beSCheng C Yang     const std::shared_ptr<sdbusplus::asio::connection>& dbusConnection) :
274*e83604beSCheng C Yang     name(name),
275*e83604beSCheng C Yang     bus(bus), address(address), order(order)
276*e83604beSCheng C Yang {
277*e83604beSCheng C Yang     CR::getPSUEvent(dbusConnection, name, state);
278*e83604beSCheng C Yang }
279