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