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