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