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 "dbuspassiveredundancy.hpp"
18 
19 #include <sdbusplus/bus.hpp>
20 #include <sdbusplus/bus/match.hpp>
21 
22 #include <iostream>
23 #include <set>
24 #include <unordered_map>
25 #include <variant>
26 
27 namespace properties
28 {
29 
30 constexpr const char* interface = "org.freedesktop.DBus.Properties";
31 constexpr const char* get = "Get";
32 constexpr const char* getAll = "GetAll";
33 
34 } // namespace properties
35 
36 namespace redundancy
37 {
38 
39 constexpr const char* collection = "Collection";
40 constexpr const char* status = "Status";
41 constexpr const char* interface = "xyz.openbmc_project.Control.FanRedundancy";
42 
43 } // namespace redundancy
44 
45 DbusPassiveRedundancy::DbusPassiveRedundancy(sdbusplus::bus::bus& bus) :
46     match(bus,
47           "type='signal',member='PropertiesChanged',arg0namespace='" +
48               std::string(redundancy::interface) + "'",
49           std::move([this](sdbusplus::message::message& message) {
50               std::string objectName;
51               std::unordered_map<
52                   std::string,
53                   std::variant<std::string, std::vector<std::string>>>
54                   result;
55               try
56               {
57                   message.read(objectName, result);
58               }
59               catch (sdbusplus::exception_t&)
60               {
61                   std::cerr << "Error reading match data";
62                   return;
63               }
64               auto findStatus = result.find("Status");
65               if (findStatus == result.end())
66               {
67                   return;
68               }
69               std::string status = std::get<std::string>(findStatus->second);
70 
71               auto methodCall = passiveBus.new_method_call(
72                   message.get_sender(), message.get_path(),
73                   properties::interface, properties::get);
74               methodCall.append(redundancy::interface, redundancy::collection);
75               std::variant<std::vector<std::string>> collection;
76 
77               try
78               {
79                   auto reply = passiveBus.call(methodCall);
80                   reply.read(collection);
81               }
82               catch (sdbusplus::exception_t&)
83               {
84                   std::cerr << "Error reading match data";
85                   return;
86               }
87 
88               auto data = std::get<std::vector<std::string>>(collection);
89               if (status.rfind("Failed") != std::string::npos)
90               {
91                   failed.insert(data.begin(), data.end());
92               }
93               else
94               {
95                   for (const auto& d : data)
96                   {
97                       failed.erase(d);
98                   }
99               }
100           })),
101     passiveBus(bus)
102 {
103     populateFailures();
104 }
105 
106 void DbusPassiveRedundancy::populateFailures(void)
107 {
108     auto mapper = passiveBus.new_method_call(
109         "xyz.openbmc_project.ObjectMapper",
110         "/xyz/openbmc_project/object_mapper",
111         "xyz.openbmc_project.ObjectMapper", "GetSubTree");
112     mapper.append("/", 0, std::array<const char*, 1>{redundancy::interface});
113     std::unordered_map<
114         std::string, std::unordered_map<std::string, std::vector<std::string>>>
115         respData;
116     try
117     {
118         auto resp = passiveBus.call(mapper);
119         resp.read(respData);
120     }
121     catch (sdbusplus::exception_t&)
122     {
123         std::cerr << "Populate Failures Mapper Error\n";
124         return;
125     }
126 
127     /*
128      * The subtree response looks like:
129      * {path :
130      *     {busname:
131      *        {interface, interface, interface, ...}
132      *     }
133      * }
134      *
135      * This loops through this structure to pre-poulate the already failed items
136      */
137 
138     for (const auto& [path, interfaceDict] : respData)
139     {
140         for (const auto& [owner, _] : interfaceDict)
141         {
142             auto call = passiveBus.new_method_call(owner.c_str(), path.c_str(),
143                                                    properties::interface,
144                                                    properties::getAll);
145             call.append(redundancy::interface);
146 
147             std::unordered_map<
148                 std::string,
149                 std::variant<std::string, std::vector<std::string>>>
150                 getAll;
151             try
152             {
153                 auto data = passiveBus.call(call);
154                 data.read(getAll);
155             }
156             catch (sdbusplus::exception_t&)
157             {
158                 std::cerr << "Populate Failures Mapper Error\n";
159                 return;
160             }
161             std::string status =
162                 std::get<std::string>(getAll[redundancy::status]);
163             if (status.rfind("Failed") == std::string::npos)
164             {
165                 continue;
166             }
167             std::vector<std::string> collection =
168                 std::get<std::vector<std::string>>(
169                     getAll[redundancy::collection]);
170             failed.insert(collection.begin(), collection.end());
171         }
172     }
173 }
174 
175 const std::set<std::string>& DbusPassiveRedundancy::getFailed()
176 {
177     return failed;
178 }