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