blob: e8b1a327b1605f1e6b1478a59eeb32a3f8a0ac31 [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -07002#include <dbus_singleton.hpp>
Ed Tanousaa2e59c2018-04-12 12:17:20 -07003#include <sdbusplus/bus/match.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07004#include <crow/app.h>
Ed Tanous9b243a42018-08-03 14:33:10 -07005#include <crow/websocket.h>
Ed Tanous911ac312017-08-15 09:37:42 -07006#include <boost/container/flat_map.hpp>
Ed Tanous9b243a42018-08-03 14:33:10 -07007#include <boost/container/flat_set.hpp>
8
9namespace nlohmann {
10template <typename... Args>
11struct adl_serializer<sdbusplus::message::variant<Args...>> {
12 static void to_json(json& j, const sdbusplus::message::variant<Args...>& v) {
13 mapbox::util::apply_visitor([&](auto&& val) { j = val; }, v);
14 }
15};
16} // namespace nlohmann
Ed Tanous911ac312017-08-15 09:37:42 -070017
18namespace crow {
19namespace dbus_monitor {
20
21struct DbusWebsocketSession {
Ed Tanousaa2e59c2018-04-12 12:17:20 -070022 std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches;
Ed Tanous9b243a42018-08-03 14:33:10 -070023 boost::container::flat_set<std::string> interfaces;
Ed Tanous911ac312017-08-15 09:37:42 -070024};
25
Ed Tanous55c7b7a2018-05-22 15:27:24 -070026static boost::container::flat_map<crow::websocket::Connection*,
Ed Tanous911ac312017-08-15 09:37:42 -070027 DbusWebsocketSession>
28 sessions;
29
Ed Tanous9b243a42018-08-03 14:33:10 -070030inline int onPropertyUpdate(sd_bus_message* m, void* userdata,
31 sd_bus_error* ret_error) {
Ed Tanousaa2e59c2018-04-12 12:17:20 -070032 if (ret_error == nullptr || sd_bus_error_is_set(ret_error)) {
Ed Tanous9b243a42018-08-03 14:33:10 -070033 BMCWEB_LOG_ERROR << "Got sdbus error on match";
34 return 0;
35 }
36 crow::websocket::Connection* connection =
37 static_cast<crow::websocket::Connection*>(userdata);
38 auto thisSession = sessions.find(connection);
39 if (thisSession == sessions.end()) {
40 BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 return 0;
Ed Tanous911ac312017-08-15 09:37:42 -070042 }
Ed Tanousaa2e59c2018-04-12 12:17:20 -070043 sdbusplus::message::message message(m);
Ed Tanous9b243a42018-08-03 14:33:10 -070044 using VariantType =
45 sdbusplus::message::variant<std::string, bool, int64_t, uint64_t, double>;
46 nlohmann::json j{{"event", message.get_member()},
47 {"path", message.get_path()}};
48 if (strcmp(message.get_member(), "PropertiesChanged") == 0) {
49 std::string interface_name;
50 boost::container::flat_map<std::string, VariantType> values;
51 message.read(interface_name, values);
52 j["properties"] = values;
53 j["interface"] = std::move(interface_name);
Ed Tanousaa2e59c2018-04-12 12:17:20 -070054
Ed Tanous9b243a42018-08-03 14:33:10 -070055 } else if (strcmp(message.get_member(), "InterfacesAdded") == 0) {
56 std::string object_name;
57 boost::container::flat_map<
58 std::string, boost::container::flat_map<std::string, VariantType>>
59 values;
60 message.read(object_name, values);
61 for (const std::pair<std::string,
62 boost::container::flat_map<std::string, VariantType>>&
63 paths : values) {
64 auto it = thisSession->second.interfaces.find(paths.first);
65 if (it != thisSession->second.interfaces.end()) {
66 j["interfaces"][paths.first] = paths.second;
67 }
68 }
69 } else {
70 BMCWEB_LOG_CRITICAL << "message " << message.get_member()
71 << " was unexpected";
72 return 0;
Ed Tanousaa2e59c2018-04-12 12:17:20 -070073 }
Ed Tanous9b243a42018-08-03 14:33:10 -070074
75 connection->sendText(j.dump());
76 return 0;
Ed Tanous911ac312017-08-15 09:37:42 -070077};
78
79template <typename... Middlewares>
Ed Tanous55c7b7a2018-05-22 15:27:24 -070080void requestRoutes(Crow<Middlewares...>& app) {
Ed Tanous9b243a42018-08-03 14:33:10 -070081 BMCWEB_ROUTE(app, "/subscribe")
Ed Tanous911ac312017-08-15 09:37:42 -070082 .websocket()
Ed Tanous55c7b7a2018-05-22 15:27:24 -070083 .onopen([&](crow::websocket::Connection& conn) {
Ed Tanous9b243a42018-08-03 14:33:10 -070084 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
Ed Tanous911ac312017-08-15 09:37:42 -070085 sessions[&conn] = DbusWebsocketSession();
Ed Tanous911ac312017-08-15 09:37:42 -070086 })
Ed Tanous55c7b7a2018-05-22 15:27:24 -070087 .onclose([&](crow::websocket::Connection& conn,
Ed Tanous911ac312017-08-15 09:37:42 -070088 const std::string& reason) { sessions.erase(&conn); })
Ed Tanous55c7b7a2018-05-22 15:27:24 -070089 .onmessage([&](crow::websocket::Connection& conn, const std::string& data,
Ed Tanous911ac312017-08-15 09:37:42 -070090 bool is_binary) {
Ed Tanous9b243a42018-08-03 14:33:10 -070091 DbusWebsocketSession& thisSession = sessions[&conn];
92 BMCWEB_LOG_DEBUG << "Connection " << &conn << " recevied " << data;
93 nlohmann::json j = nlohmann::json::parse(data, nullptr, false);
94 if (j.is_discarded()) {
95 BMCWEB_LOG_ERROR << "Unable to parse json data for monitor";
96 conn.close("Unable to parse json request");
97 return;
98 }
99 nlohmann::json::iterator interfaces = j.find("interfaces");
100 if (interfaces != j.end()) {
101 thisSession.interfaces.reserve(interfaces->size());
102 for (auto& interface : *interfaces) {
103 const std::string* str = interface.get_ptr<const std::string*>();
104 if (str != nullptr) {
105 thisSession.interfaces.insert(*str);
106 }
107 }
108 }
109
110 nlohmann::json::iterator paths = j.find("paths");
111 if (paths != j.end()) {
112 int interfaceCount = thisSession.interfaces.size();
113 if (interfaceCount == 0) {
114 interfaceCount = 1;
115 }
116 // Reserve our matches upfront. For each path there is 1 for
117 // interfacesAdded, and InterfaceCount number for PropertiesChanged
118 thisSession.matches.reserve(thisSession.matches.size() +
119 paths->size() * (1 + interfaceCount));
120 }
121 std::string object_manager_match_string;
122 std::string properties_match_string;
123 std::string object_manager_interfaces_match_string;
124 // These regexes derived on the rules here:
125 // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names
126 std::regex validPath("^/([A-Za-z0-9_]+/?)*$");
127 std::regex validInterface(
128 "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$");
129
130 for (const auto& thisPath : *paths) {
131 const std::string* thisPathString =
132 thisPath.get_ptr<const std::string*>();
133 if (thisPathString == nullptr) {
134 BMCWEB_LOG_ERROR << "subscribe path isn't a string?";
135 conn.close();
136 return;
137 }
138 if (!std::regex_match(*thisPathString, validPath)) {
139 BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString;
140 conn.close();
141 return;
142 }
143 properties_match_string =
144 ("type='signal',"
145 "interface='org.freedesktop.DBus.Properties',"
146 "path_namespace='" +
147 *thisPathString +
148 "',"
149 "member='PropertiesChanged'");
150 // If interfaces weren't specified, add a single match for all
151 // interfaces
152 if (thisSession.interfaces.size() == 0) {
153 BMCWEB_LOG_DEBUG << "Creating match " << properties_match_string;
154
155 thisSession.matches.emplace_back(
156 std::make_unique<sdbusplus::bus::match::match>(
157 *crow::connections::systemBus, properties_match_string,
158 onPropertyUpdate, &conn));
159 } else {
160 // If interfaces were specified, add a match for each interface
161 for (const std::string& interface : thisSession.interfaces) {
162 if (!std::regex_match(interface, validInterface)) {
163 BMCWEB_LOG_ERROR << "Invalid interface name " << interface;
164 conn.close();
165 return;
166 }
167 std::string ifaceMatchString =
168 properties_match_string + ",arg0='" + interface + "'";
169 BMCWEB_LOG_DEBUG << "Creating match " << ifaceMatchString;
170 thisSession.matches.emplace_back(
171 std::make_unique<sdbusplus::bus::match::match>(
172 *crow::connections::systemBus, ifaceMatchString,
173 onPropertyUpdate, &conn));
174 }
175 }
176 object_manager_match_string =
177 ("type='signal',"
178 "interface='org.freedesktop.DBus.ObjectManager',"
179 "path_namespace='" +
180 *thisPathString +
181 "',"
182 "member='InterfacesAdded'");
183 BMCWEB_LOG_DEBUG << "Creating match " << object_manager_match_string;
184 thisSession.matches.emplace_back(
185 std::make_unique<sdbusplus::bus::match::match>(
186 *crow::connections::systemBus, object_manager_match_string,
187 onPropertyUpdate, &conn));
188 }
Ed Tanous911ac312017-08-15 09:37:42 -0700189 });
190}
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700191} // namespace dbus_monitor
Ed Tanous911ac312017-08-15 09:37:42 -0700192} // namespace crow