xref: /openbmc/bmcweb/include/dbus_monitor.hpp (revision 257f5795)
1 #pragma once
2 #include <dbus/filter.hpp>
3 #include <dbus/match.hpp>
4 #include <dbus_singleton.hpp>
5 #include <crow/app.h>
6 #include <boost/container/flat_map.hpp>
7 
8 namespace crow {
9 namespace dbus_monitor {
10 
11 struct DbusWebsocketSession {
12   std::vector<std::unique_ptr<dbus::match>> matches;
13   std::vector<dbus::filter> filters;
14 };
15 
16 static boost::container::flat_map<crow::websocket::connection*,
17                                   DbusWebsocketSession>
18     sessions;
19 
20 void on_property_update(dbus::filter& filter, boost::system::error_code ec,
21                         dbus::message s) {
22   if (!ec) {
23     std::string object_name;
24     std::vector<std::pair<std::string, dbus::dbus_variant>> values;
25     s.unpack(object_name, values);
26     nlohmann::json j;
27     for (auto& value : values) {
28       boost::apply_visitor([&](auto val) { j[s.get_path()] = val; },
29                            value.second);
30     }
31     auto data_to_send = j.dump();
32 
33     for (auto& session : sessions) {
34       session.first->send_text(data_to_send);
35     }
36   }
37   filter.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
38     on_property_update(filter, ec, s);
39   });
40 };
41 
42 template <typename... Middlewares>
43 void request_routes(Crow<Middlewares...>& app) {
44   CROW_ROUTE(app, "/dbus_monitor")
45       .websocket()
46       .onopen([&](crow::websocket::connection& conn) {
47         std::string path_namespace(conn.req.url_params.get("path_namespace"));
48         if (path_namespace.empty()) {
49           conn.send_text(
50               nlohmann::json({"error", "Did not specify path_namespace"}));
51           conn.close("error");
52         }
53         sessions[&conn] = DbusWebsocketSession();
54         std::string match_string(
55             "type='signal',"
56             "interface='org.freedesktop.DBus.Properties',"
57             "path_namespace='" +
58             path_namespace + "'");
59         sessions[&conn].matches.push_back(std::make_unique<dbus::match>(
60             crow::connections::system_bus, std::move(match_string)));
61 
62         sessions[&conn].filters.emplace_back(
63             crow::connections::system_bus, [path_namespace](dbus::message m) {
64               return m.get_member() == "PropertiesChanged" &&
65                      boost::starts_with(m.get_path(), path_namespace);
66             });
67         auto& this_filter = sessions[&conn].filters.back();
68         this_filter.async_dispatch(
69             [&](boost::system::error_code ec, dbus::message s) {
70               on_property_update(this_filter, ec, s);
71             });
72 
73       })
74       .onclose([&](crow::websocket::connection& conn,
75                    const std::string& reason) { sessions.erase(&conn); })
76       .onmessage([&](crow::websocket::connection& conn, const std::string& data,
77                      bool is_binary) {
78         CROW_LOG_ERROR << "Got unexpected message from client on sensorws";
79       });
80 }
81 }  // namespace redfish
82 }  // namespace crow
83