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