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