| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 1 | #pragma once | 
| Ed Tanous | 04e438c | 2020-10-03 08:06:26 -0700 | [diff] [blame] | 2 | #include <app.hpp> | 
| Iwona Klimaszewska | c0a1c8a | 2019-07-12 18:26:38 +0200 | [diff] [blame] | 3 | #include <async_resp.hpp> | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 4 | #include <boost/container/flat_map.hpp> | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 5 | #include <boost/container/flat_set.hpp> | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 6 | #include <dbus_singleton.hpp> | 
| Matt Spinler | 715748a | 2019-01-30 13:22:28 -0600 | [diff] [blame] | 7 | #include <openbmc_dbus_rest.hpp> | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 8 | #include <sdbusplus/bus/match.hpp> | 
| William A. Kennington III | 0a63b1c | 2018-10-18 13:37:19 -0700 | [diff] [blame] | 9 | #include <sdbusplus/message/types.hpp> | 
| Ed Tanous | 04e438c | 2020-10-03 08:06:26 -0700 | [diff] [blame] | 10 | #include <websocket.hpp> | 
| Gunnar Mills | 1214b7e | 2020-06-04 10:11:30 -0500 | [diff] [blame] | 11 |  | 
| Ed Tanous | abf2add | 2019-01-22 16:40:12 -0800 | [diff] [blame] | 12 | #include <variant> | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 13 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 14 | namespace crow | 
|  | 15 | { | 
|  | 16 | namespace dbus_monitor | 
|  | 17 | { | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 18 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 19 | struct DbusWebsocketSession | 
|  | 20 | { | 
|  | 21 | std::vector<std::unique_ptr<sdbusplus::bus::match::match>> matches; | 
|  | 22 | boost::container::flat_set<std::string> interfaces; | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 23 | }; | 
|  | 24 |  | 
| Ed Tanous | 55c7b7a | 2018-05-22 15:27:24 -0700 | [diff] [blame] | 25 | static boost::container::flat_map<crow::websocket::Connection*, | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 26 | DbusWebsocketSession> | 
|  | 27 | sessions; | 
|  | 28 |  | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 29 | inline int onPropertyUpdate(sd_bus_message* m, void* userdata, | 
| Ed Tanous | 81ce609 | 2020-12-17 16:54:55 +0000 | [diff] [blame] | 30 | sd_bus_error* retError) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 31 | { | 
| Ed Tanous | 81ce609 | 2020-12-17 16:54:55 +0000 | [diff] [blame] | 32 | if (retError == nullptr || sd_bus_error_is_set(retError)) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 33 | { | 
|  | 34 | BMCWEB_LOG_ERROR << "Got sdbus error on match"; | 
|  | 35 | return 0; | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 36 | } | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 37 | crow::websocket::Connection* connection = | 
|  | 38 | static_cast<crow::websocket::Connection*>(userdata); | 
|  | 39 | auto thisSession = sessions.find(connection); | 
|  | 40 | if (thisSession == sessions.end()) | 
|  | 41 | { | 
|  | 42 | BMCWEB_LOG_ERROR << "Couldn't find dbus connection " << connection; | 
|  | 43 | return 0; | 
|  | 44 | } | 
|  | 45 | sdbusplus::message::message message(m); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 46 | nlohmann::json j{{"event", message.get_member()}, | 
|  | 47 | {"path", message.get_path()}}; | 
|  | 48 | if (strcmp(message.get_member(), "PropertiesChanged") == 0) | 
|  | 49 | { | 
| Matt Spinler | 715748a | 2019-01-30 13:22:28 -0600 | [diff] [blame] | 50 | nlohmann::json data; | 
|  | 51 | int r = openbmc_mapper::convertDBusToJSON("sa{sv}as", message, data); | 
|  | 52 | if (r < 0) | 
|  | 53 | { | 
|  | 54 | BMCWEB_LOG_ERROR << "convertDBusToJSON failed with " << r; | 
|  | 55 | return 0; | 
|  | 56 | } | 
|  | 57 | if (!data.is_array()) | 
|  | 58 | { | 
|  | 59 | BMCWEB_LOG_ERROR << "No data in PropertiesChanged signal"; | 
|  | 60 | return 0; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | // data is type sa{sv}as and is an array[3] of string, object, array | 
|  | 64 | j["interface"] = data[0]; | 
|  | 65 | j["properties"] = data[1]; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 66 | } | 
|  | 67 | else if (strcmp(message.get_member(), "InterfacesAdded") == 0) | 
|  | 68 | { | 
| Matt Spinler | 715748a | 2019-01-30 13:22:28 -0600 | [diff] [blame] | 69 | nlohmann::json data; | 
|  | 70 | int r = openbmc_mapper::convertDBusToJSON("oa{sa{sv}}", message, data); | 
|  | 71 | if (r < 0) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 72 | { | 
| Matt Spinler | 715748a | 2019-01-30 13:22:28 -0600 | [diff] [blame] | 73 | BMCWEB_LOG_ERROR << "convertDBusToJSON failed with " << r; | 
|  | 74 | return 0; | 
|  | 75 | } | 
|  | 76 |  | 
|  | 77 | if (!data.is_array()) | 
|  | 78 | { | 
|  | 79 | BMCWEB_LOG_ERROR << "No data in InterfacesAdded signal"; | 
|  | 80 | return 0; | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 | // data is type oa{sa{sv}} which is an array[2] of string, object | 
|  | 84 | for (auto& entry : data[1].items()) | 
|  | 85 | { | 
|  | 86 | auto it = thisSession->second.interfaces.find(entry.key()); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 87 | if (it != thisSession->second.interfaces.end()) | 
|  | 88 | { | 
| Matt Spinler | 715748a | 2019-01-30 13:22:28 -0600 | [diff] [blame] | 89 | j["interfaces"][entry.key()] = entry.value(); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 90 | } | 
|  | 91 | } | 
|  | 92 | } | 
|  | 93 | else | 
|  | 94 | { | 
|  | 95 | BMCWEB_LOG_CRITICAL << "message " << message.get_member() | 
|  | 96 | << " was unexpected"; | 
|  | 97 | return 0; | 
|  | 98 | } | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 99 |  | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 100 | connection->sendText(j.dump()); | 
|  | 101 | return 0; | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 102 | } | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 103 |  | 
| Ed Tanous | 23a21a1 | 2020-07-25 04:45:05 +0000 | [diff] [blame] | 104 | inline void requestRoutes(App& app) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 105 | { | 
|  | 106 | BMCWEB_ROUTE(app, "/subscribe") | 
| Ed Tanous | 23a21a1 | 2020-07-25 04:45:05 +0000 | [diff] [blame] | 107 | .privileges({"Login"}) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 108 | .websocket() | 
| Iwona Klimaszewska | c0a1c8a | 2019-07-12 18:26:38 +0200 | [diff] [blame] | 109 | .onopen([&](crow::websocket::Connection& conn, | 
| Ed Tanous | b5a7693 | 2020-09-29 16:16:58 -0700 | [diff] [blame] | 110 | const std::shared_ptr<bmcweb::AsyncResp>&) { | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 111 | BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; | 
|  | 112 | sessions[&conn] = DbusWebsocketSession(); | 
|  | 113 | }) | 
| Ed Tanous | cb13a39 | 2020-07-25 19:02:03 +0000 | [diff] [blame] | 114 | .onclose([&](crow::websocket::Connection& conn, const std::string&) { | 
|  | 115 | sessions.erase(&conn); | 
|  | 116 | }) | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 117 | .onmessage([&](crow::websocket::Connection& conn, | 
| Ed Tanous | cb13a39 | 2020-07-25 19:02:03 +0000 | [diff] [blame] | 118 | const std::string& data, bool) { | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 119 | DbusWebsocketSession& thisSession = sessions[&conn]; | 
| Gunnar Mills | caa3ce3 | 2020-07-08 14:46:53 -0500 | [diff] [blame] | 120 | BMCWEB_LOG_DEBUG << "Connection " << &conn << " received " << data; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 121 | nlohmann::json j = nlohmann::json::parse(data, nullptr, false); | 
|  | 122 | if (j.is_discarded()) | 
|  | 123 | { | 
|  | 124 | BMCWEB_LOG_ERROR << "Unable to parse json data for monitor"; | 
|  | 125 | conn.close("Unable to parse json request"); | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 126 | return; | 
| Ed Tanous | 9b243a4 | 2018-08-03 14:33:10 -0700 | [diff] [blame] | 127 | } | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 128 | nlohmann::json::iterator interfaces = j.find("interfaces"); | 
|  | 129 | if (interfaces != j.end()) | 
|  | 130 | { | 
|  | 131 | thisSession.interfaces.reserve(interfaces->size()); | 
|  | 132 | for (auto& interface : *interfaces) | 
|  | 133 | { | 
|  | 134 | const std::string* str = | 
|  | 135 | interface.get_ptr<const std::string*>(); | 
|  | 136 | if (str != nullptr) | 
|  | 137 | { | 
|  | 138 | thisSession.interfaces.insert(*str); | 
|  | 139 | } | 
|  | 140 | } | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | nlohmann::json::iterator paths = j.find("paths"); | 
|  | 144 | if (paths != j.end()) | 
|  | 145 | { | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 146 | size_t interfaceCount = thisSession.interfaces.size(); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 147 | if (interfaceCount == 0) | 
|  | 148 | { | 
|  | 149 | interfaceCount = 1; | 
|  | 150 | } | 
|  | 151 | // Reserve our matches upfront.  For each path there is 1 for | 
|  | 152 | // interfacesAdded, and InterfaceCount number for | 
|  | 153 | // PropertiesChanged | 
|  | 154 | thisSession.matches.reserve(thisSession.matches.size() + | 
|  | 155 | paths->size() * | 
| Ed Tanous | 271584a | 2019-07-09 16:24:22 -0700 | [diff] [blame] | 156 | (1U + interfaceCount)); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 157 | } | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 158 | std::string objectManagerMatchString; | 
|  | 159 | std::string propertiesMatchString; | 
|  | 160 | std::string objectManagerInterfacesMatchString; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 161 | // These regexes derived on the rules here: | 
|  | 162 | // https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names | 
|  | 163 | std::regex validPath("^/([A-Za-z0-9_]+/?)*$"); | 
|  | 164 | std::regex validInterface( | 
|  | 165 | "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)+$"); | 
|  | 166 |  | 
|  | 167 | for (const auto& thisPath : *paths) | 
|  | 168 | { | 
|  | 169 | const std::string* thisPathString = | 
|  | 170 | thisPath.get_ptr<const std::string*>(); | 
|  | 171 | if (thisPathString == nullptr) | 
|  | 172 | { | 
|  | 173 | BMCWEB_LOG_ERROR << "subscribe path isn't a string?"; | 
|  | 174 | conn.close(); | 
|  | 175 | return; | 
|  | 176 | } | 
|  | 177 | if (!std::regex_match(*thisPathString, validPath)) | 
|  | 178 | { | 
|  | 179 | BMCWEB_LOG_ERROR << "Invalid path name " << *thisPathString; | 
|  | 180 | conn.close(); | 
|  | 181 | return; | 
|  | 182 | } | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 183 | propertiesMatchString = | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 184 | ("type='signal'," | 
|  | 185 | "interface='org.freedesktop.DBus.Properties'," | 
|  | 186 | "path_namespace='" + | 
|  | 187 | *thisPathString + | 
|  | 188 | "'," | 
|  | 189 | "member='PropertiesChanged'"); | 
|  | 190 | // If interfaces weren't specified, add a single match for all | 
|  | 191 | // interfaces | 
|  | 192 | if (thisSession.interfaces.size() == 0) | 
|  | 193 | { | 
|  | 194 | BMCWEB_LOG_DEBUG << "Creating match " | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 195 | << propertiesMatchString; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 196 |  | 
|  | 197 | thisSession.matches.emplace_back( | 
|  | 198 | std::make_unique<sdbusplus::bus::match::match>( | 
|  | 199 | *crow::connections::systemBus, | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 200 | propertiesMatchString, onPropertyUpdate, &conn)); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 201 | } | 
|  | 202 | else | 
|  | 203 | { | 
|  | 204 | // If interfaces were specified, add a match for each | 
|  | 205 | // interface | 
|  | 206 | for (const std::string& interface : thisSession.interfaces) | 
|  | 207 | { | 
|  | 208 | if (!std::regex_match(interface, validInterface)) | 
|  | 209 | { | 
|  | 210 | BMCWEB_LOG_ERROR << "Invalid interface name " | 
|  | 211 | << interface; | 
|  | 212 | conn.close(); | 
|  | 213 | return; | 
|  | 214 | } | 
| Ed Tanous | f23b729 | 2020-10-15 09:41:17 -0700 | [diff] [blame] | 215 | std::string ifaceMatchString = propertiesMatchString; | 
|  | 216 | ifaceMatchString += ",arg0='"; | 
|  | 217 | ifaceMatchString += interface; | 
|  | 218 | ifaceMatchString += "'"; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 219 | BMCWEB_LOG_DEBUG << "Creating match " | 
|  | 220 | << ifaceMatchString; | 
|  | 221 | thisSession.matches.emplace_back( | 
|  | 222 | std::make_unique<sdbusplus::bus::match::match>( | 
|  | 223 | *crow::connections::systemBus, ifaceMatchString, | 
|  | 224 | onPropertyUpdate, &conn)); | 
|  | 225 | } | 
|  | 226 | } | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 227 | objectManagerMatchString = | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 228 | ("type='signal'," | 
|  | 229 | "interface='org.freedesktop.DBus.ObjectManager'," | 
|  | 230 | "path_namespace='" + | 
|  | 231 | *thisPathString + | 
|  | 232 | "'," | 
|  | 233 | "member='InterfacesAdded'"); | 
|  | 234 | BMCWEB_LOG_DEBUG << "Creating match " | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 235 | << objectManagerMatchString; | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 236 | thisSession.matches.emplace_back( | 
|  | 237 | std::make_unique<sdbusplus::bus::match::match>( | 
| Ed Tanous | 2c70f80 | 2020-09-28 14:29:23 -0700 | [diff] [blame] | 238 | *crow::connections::systemBus, objectManagerMatchString, | 
|  | 239 | onPropertyUpdate, &conn)); | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 240 | } | 
|  | 241 | }); | 
| Ed Tanous | 911ac31 | 2017-08-15 09:37:42 -0700 | [diff] [blame] | 242 | } | 
| Ed Tanous | 1abe55e | 2018-09-05 08:30:59 -0700 | [diff] [blame] | 243 | } // namespace dbus_monitor | 
|  | 244 | } // namespace crow |