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