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