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 <boost/container/flat_map.hpp>
18 #include <iostream>
19 #include <sdbusplus/asio/connection.hpp>
20 #include <sdbusplus/asio/object_server.hpp>
21 #include <variant>
22 
23 constexpr const char* fatalLedPath =
24     "/xyz/openbmc_project/led/groups/status_critical";
25 constexpr const char* criticalLedPath =
26     "/xyz/openbmc_project/led/groups/status_non_critical";
27 constexpr const char* warningLedPath =
28     "/xyz/openbmc_project/led/groups/status_degraded";
29 
30 constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
31 constexpr const char* ledAssertProp = "Asserted";
32 constexpr const char* ledManagerBusname =
33     "xyz.openbmc_project.LED.GroupManager";
34 
35 std::shared_ptr<sdbusplus::asio::dbus_interface> assertedIface = nullptr;
36 
37 constexpr const bool debug = false;
38 
39 // final led state tracking
40 bool fatalState = false;
41 bool criticalState = false;
42 bool warnState = false;
43 
44 // maps of <object-path, <property, asserted>>
45 boost::container::flat_map<std::string,
46                            boost::container::flat_map<std::string, bool>>
47     fatalAssertMap;
48 boost::container::flat_map<std::string,
49                            boost::container::flat_map<std::string, bool>>
50     criticalAssertMap;
51 boost::container::flat_map<std::string,
52                            boost::container::flat_map<std::string, bool>>
53     warningAssertMap;
54 
55 std::vector<std::string> assertedInMap(
56     const boost::container::flat_map<
57         std::string, boost::container::flat_map<std::string, bool>>& map)
58 {
59     std::vector<std::string> ret;
60     // if any of the properties are true, return true
61     for (const auto& pair : map)
62     {
63         for (const auto& item : pair.second)
64         {
65             if (item.second)
66             {
67                 ret.push_back(pair.first);
68             }
69         }
70     }
71     return ret;
72 }
73 
74 void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn)
75 {
76     std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
77 
78     std::vector<std::string> assertedVector = assertedInMap(fatalAssertMap);
79     assertedIface->set_property("Fatal", assertedVector);
80 
81     bool fatal = assertedVector.size();
82     if (fatal != fatalState)
83     {
84         fatalState = fatal;
85         ledsToSet.push_back(std::make_pair(fatalLedPath, fatalState));
86     }
87 
88     assertedVector = assertedInMap(criticalAssertMap);
89     assertedIface->set_property("Critical", assertedVector);
90 
91     bool critical = assertedVector.size();
92     if (critical != criticalState)
93     {
94         criticalState = critical;
95         ledsToSet.push_back(std::make_pair(criticalLedPath, criticalState));
96     }
97 
98     assertedVector = assertedInMap(warningAssertMap);
99     assertedIface->set_property("Warning", assertedVector);
100 
101     bool warn = assertedVector.size();
102     if (warn != warnState)
103     {
104         warnState = warn;
105         ledsToSet.push_back(std::make_pair(warningLedPath, warnState));
106     }
107 
108     for (const auto& ledPair : ledsToSet)
109     {
110         conn->async_method_call(
111             [ledPair](const boost::system::error_code ec) {
112                 if (ec)
113                 {
114                     std::cerr << "Cannot set " << ledPair.first << " to "
115                               << std::boolalpha
116                               << std::get<bool>(ledPair.second) << "\n";
117                 }
118                 if constexpr (debug)
119                 {
120                     std::cerr << "Set " << ledPair.first << " to "
121                               << std::boolalpha
122                               << std::get<bool>(ledPair.second) << "\n";
123                 }
124             },
125             ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
126             "Set", ledIface, ledAssertProp, ledPair.second);
127     }
128 }
129 
130 void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
131 {
132     static std::unique_ptr<sdbusplus::bus::match::match> match = nullptr;
133 
134     std::function<void(sdbusplus::message::message&)> thresholdCallback =
135         [&conn](sdbusplus::message::message& message) {
136             std::string objectName;
137             boost::container::flat_map<std::string, std::variant<bool>> values;
138             message.read(objectName, values);
139 
140             if constexpr (debug)
141             {
142                 std::cerr << "Threshold callback " << message.get_path()
143                           << "\n";
144             }
145 
146             auto findCriticalLow = values.find("CriticalAlarmLow");
147             auto findCriticalHigh = values.find("CriticalAlarmHigh");
148 
149             auto findWarnLow = values.find("WarningAlarmLow");
150             auto findWarnHigh = values.find("WarningAlarmHigh");
151 
152             if (findCriticalLow != values.end())
153             {
154                 criticalAssertMap[message.get_path()]["Low"] =
155                     std::get<bool>(findCriticalLow->second);
156             }
157             if (findCriticalHigh != values.end())
158             {
159                 criticalAssertMap[message.get_path()]["High"] =
160                     std::get<bool>(findCriticalHigh->second);
161             }
162             if (findWarnLow != values.end())
163             {
164                 warningAssertMap[message.get_path()]["Low"] =
165                     std::get<bool>(findWarnLow->second);
166             }
167             if (findWarnHigh != values.end())
168             {
169                 warningAssertMap[message.get_path()]["High"] =
170                     std::get<bool>(findWarnHigh->second);
171             }
172             updateLedStatus(conn);
173         };
174 
175     match = std::make_unique<sdbusplus::bus::match::match>(
176         static_cast<sdbusplus::bus::bus&>(*conn),
177         "type='signal',interface='org.freedesktop.DBus.Properties',path_"
178         "namespace='/xyz/openbmc_project/"
179         "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
180         thresholdCallback);
181 }
182 
183 int main(int argc, char** argv)
184 {
185     boost::asio::io_service io;
186     auto conn = std::make_shared<sdbusplus::asio::connection>(io);
187     conn->request_name("xyz.openbmc_project.CallbackManager");
188     sdbusplus::asio::object_server objServer(conn);
189     assertedIface =
190         objServer.add_interface("/xyz/openbmc_project/CallbackManager",
191                                 "xyz.openbmc_project.CallbackManager");
192     assertedIface->register_property("Warning", std::vector<std::string>());
193     assertedIface->register_property("Critical", std::vector<std::string>());
194     assertedIface->register_property("Fatal", std::vector<std::string>());
195     assertedIface->initialize();
196 
197     createThresholdMatch(conn);
198 
199     io.run();
200 
201     return 0;
202 }
203