Add callback manager

Callback manager sets up matches and sets properties on
dbus.

Tested: used sensor override to make sensors assert
critical and warning interfaces and saw led change. also
caught a few buggy threshold settings using led.

Change-Id: I06c164b749febbc3e81cb3db5f7a0a2b72b3678f
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/callback-manager/src/callback_manager.cpp b/callback-manager/src/callback_manager.cpp
new file mode 100644
index 0000000..706c607
--- /dev/null
+++ b/callback-manager/src/callback_manager.cpp
@@ -0,0 +1,202 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include <boost/container/flat_map.hpp>
+#include <iostream>
+#include <sdbusplus/asio/connection.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+#include <variant>
+
+constexpr const char* fatalLedPath =
+    "/xyz/openbmc_project/led/groups/status_critical";
+constexpr const char* criticalLedPath =
+    "/xyz/openbmc_project/led/groups/status_non_critical";
+constexpr const char* warningLedPath =
+    "/xyz/openbmc_project/led/groups/status_degraded";
+
+constexpr const char* ledIface = "xyz.openbmc_project.Led.Group";
+constexpr const char* ledAssertProp = "Asserted";
+constexpr const char* ledManagerBusname =
+    "xyz.openbmc_project.LED.GroupManager";
+
+std::shared_ptr<sdbusplus::asio::dbus_interface> assertedIface = nullptr;
+
+constexpr const bool debug = false;
+
+// final led state tracking
+bool fatalState = false;
+bool criticalState = false;
+bool warnState = false;
+
+// maps of <object-path, <property, asserted>>
+boost::container::flat_map<std::string,
+                           boost::container::flat_map<std::string, bool>>
+    fatalAssertMap;
+boost::container::flat_map<std::string,
+                           boost::container::flat_map<std::string, bool>>
+    criticalAssertMap;
+boost::container::flat_map<std::string,
+                           boost::container::flat_map<std::string, bool>>
+    warningAssertMap;
+
+std::vector<std::string> assertedInMap(
+    const boost::container::flat_map<
+        std::string, boost::container::flat_map<std::string, bool>>& map)
+{
+    std::vector<std::string> ret;
+    // if any of the properties are true, return true
+    for (const auto& pair : map)
+    {
+        for (const auto& item : pair.second)
+        {
+            if (item.second)
+            {
+                ret.push_back(pair.first);
+            }
+        }
+    }
+    return ret;
+}
+
+void updateLedStatus(std::shared_ptr<sdbusplus::asio::connection>& conn)
+{
+    std::vector<std::pair<std::string, std::variant<bool>>> ledsToSet;
+
+    std::vector<std::string> assertedVector = assertedInMap(fatalAssertMap);
+    assertedIface->set_property("Fatal", assertedVector);
+
+    bool fatal = assertedVector.size();
+    if (fatal != fatalState)
+    {
+        fatalState = fatal;
+        ledsToSet.push_back(std::make_pair(fatalLedPath, fatalState));
+    }
+
+    assertedVector = assertedInMap(criticalAssertMap);
+    assertedIface->set_property("Critical", assertedVector);
+
+    bool critical = assertedVector.size();
+    if (critical != criticalState)
+    {
+        criticalState = critical;
+        ledsToSet.push_back(std::make_pair(criticalLedPath, criticalState));
+    }
+
+    assertedVector = assertedInMap(warningAssertMap);
+    assertedIface->set_property("Warning", assertedVector);
+
+    bool warn = assertedVector.size();
+    if (warn != warnState)
+    {
+        warnState = warn;
+        ledsToSet.push_back(std::make_pair(warningLedPath, warnState));
+    }
+
+    for (const auto& ledPair : ledsToSet)
+    {
+        conn->async_method_call(
+            [ledPair](const boost::system::error_code ec) {
+                if (ec)
+                {
+                    std::cerr << "Cannot set " << ledPair.first << " to "
+                              << std::boolalpha
+                              << std::get<bool>(ledPair.second) << "\n";
+                }
+                if constexpr (debug)
+                {
+                    std::cerr << "Set " << ledPair.first << " to "
+                              << std::boolalpha
+                              << std::get<bool>(ledPair.second) << "\n";
+                }
+            },
+            ledManagerBusname, ledPair.first, "org.freedesktop.DBus.Properties",
+            "Set", ledIface, ledAssertProp, ledPair.second);
+    }
+}
+
+void createThresholdMatch(std::shared_ptr<sdbusplus::asio::connection>& conn)
+{
+    static std::unique_ptr<sdbusplus::bus::match::match> match = nullptr;
+
+    std::function<void(sdbusplus::message::message&)> thresholdCallback =
+        [&conn](sdbusplus::message::message& message) {
+            std::string objectName;
+            boost::container::flat_map<std::string, std::variant<bool>> values;
+            message.read(objectName, values);
+
+            if constexpr (debug)
+            {
+                std::cerr << "Threshold callback " << message.get_path()
+                          << "\n";
+            }
+
+            auto findCriticalLow = values.find("CriticalAlarmLow");
+            auto findCriticalHigh = values.find("CriticalAlarmHigh");
+
+            auto findWarnLow = values.find("WarningAlarmLow");
+            auto findWarnHigh = values.find("WarningAlarmHigh");
+
+            if (findCriticalLow != values.end())
+            {
+                criticalAssertMap[message.get_path()]["Low"] =
+                    std::get<bool>(findCriticalLow->second);
+            }
+            if (findCriticalHigh != values.end())
+            {
+                criticalAssertMap[message.get_path()]["High"] =
+                    std::get<bool>(findCriticalHigh->second);
+            }
+            if (findWarnLow != values.end())
+            {
+                warningAssertMap[message.get_path()]["Low"] =
+                    std::get<bool>(findWarnLow->second);
+            }
+            if (findWarnHigh != values.end())
+            {
+                warningAssertMap[message.get_path()]["High"] =
+                    std::get<bool>(findWarnHigh->second);
+            }
+            updateLedStatus(conn);
+        };
+
+    match = std::make_unique<sdbusplus::bus::match::match>(
+        static_cast<sdbusplus::bus::bus&>(*conn),
+        "type='signal',interface='org.freedesktop.DBus.Properties',path_"
+        "namespace='/xyz/openbmc_project/"
+        "sensors',arg0namespace='xyz.openbmc_project.Sensor.Threshold'",
+        thresholdCallback);
+}
+
+int main(int argc, char** argv)
+{
+    boost::asio::io_service io;
+    auto conn = std::make_shared<sdbusplus::asio::connection>(io);
+    conn->request_name("xyz.openbmc_project.CallbackManager");
+    sdbusplus::asio::object_server objServer(conn);
+    assertedIface =
+        objServer.add_interface("/xyz/openbmc_project/CallbackManager",
+                                "xyz.openbmc_project.CallbackManager");
+    assertedIface->register_property("Warning", std::vector<std::string>());
+    assertedIface->register_property("Critical", std::vector<std::string>());
+    assertedIface->register_property("Fatal", std::vector<std::string>());
+    assertedIface->initialize();
+
+    createThresholdMatch(conn);
+
+    io.run();
+
+    return 0;
+}