Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 1 | // Copyright 2021 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #pragma once |
| 16 | |
| 17 | #include "main.hpp" |
| 18 | // This is the form a sensor assumes on DBus. |
| 19 | // Aggregates their view from all other daemons. |
| 20 | #include <bitset> |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 21 | #include <map> |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 22 | #include <optional> |
| 23 | #include <set> |
| 24 | #include <string> |
| 25 | #include <unordered_map> |
| 26 | #include <unordered_set> |
| 27 | #include <vector> |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 28 | |
| 29 | std::pair<std::string, std::string> ExtractFileName(std::string x); |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 30 | // Where is this sensor seen? |
| 31 | constexpr int VISIBILITY_OBJECT_MAPPER = 0; |
| 32 | constexpr int VISIBILITY_HWMON = 1; |
| 33 | constexpr int VISIBILITY_IPMITOOL_SDR = 2; |
| 34 | class DBusConnection |
| 35 | { |
| 36 | public: |
| 37 | std::string service; // example: "systemd-resolved" |
| 38 | std::string connection; // example: ":1.1" |
| 39 | std::string cmd; // the comm line |
| 40 | std::string unit; // example: "systemd-resolved.service" |
| 41 | int pid; |
| 42 | // For actual DBus capture: service name, connection name, command line, |
| 43 | // systmed unit and PID are all known |
| 44 | DBusConnection(const std::string& _s, const std::string& _c, |
| 45 | const std::string& _cmd, const std::string& _u, int _pid) |
| 46 | { |
| 47 | service = _s; |
| 48 | connection = _c; |
| 49 | cmd = _cmd; |
| 50 | unit = _u; |
| 51 | pid = _pid; |
| 52 | } |
| 53 | |
| 54 | // During PCap replay: only service name, connection name, and PID are known |
| 55 | // cmd and unit are not known since they are not |
| 56 | // stored in the PCap file |
| 57 | DBusConnection(const std::string& _s, const std::string& _c, int _pid) |
| 58 | { |
| 59 | service = _s; |
| 60 | connection = _c; |
| 61 | pid = _pid; |
| 62 | } |
| 63 | }; |
| 64 | |
| 65 | class DBusConnectionSnapshot |
| 66 | { |
| 67 | public: |
| 68 | std::vector<DBusConnection*> connections_; |
| 69 | std::unordered_map<std::string, DBusConnection*> unique_name_to_cxn; |
| 70 | DBusConnection* FindDBusConnectionByService(const std::string& service) |
| 71 | { |
| 72 | for (DBusConnection* cxn : connections_) |
| 73 | { |
| 74 | if (cxn->service == service) |
| 75 | return cxn; |
| 76 | } |
| 77 | return nullptr; |
| 78 | } |
| 79 | |
| 80 | void SetConnectionPID(const std::string& connection, int pid) |
| 81 | { |
| 82 | DBusConnection* cxn = FindDBusConnectionByService(connection); |
| 83 | if (cxn != nullptr) |
| 84 | { |
| 85 | cxn->pid = pid; |
| 86 | unique_name_to_cxn[connection] = cxn; // Just to make sure |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | void SetConnectionUniqueName(const std::string& service, |
| 91 | const std::string& unique_name) |
| 92 | { |
| 93 | DBusConnection* cxn = FindDBusConnectionByService(service); |
| 94 | if (cxn != nullptr) |
| 95 | { |
| 96 | cxn->connection = unique_name; |
| 97 | unique_name_to_cxn[unique_name] = cxn; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | DBusConnection* FindDBusConnectionByConnection(const std::string& conn) |
| 102 | { |
| 103 | for (DBusConnection* cxn : connections_) |
| 104 | { |
| 105 | if (cxn->connection == conn) |
| 106 | return cxn; |
| 107 | } |
| 108 | return nullptr; |
| 109 | } |
| 110 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 111 | // Only when service is known (during playback) |
| 112 | void AddConnection(const std::string& _s) |
| 113 | { |
| 114 | connections_.push_back(new DBusConnection(_s, "", "", "", INVALID)); |
| 115 | } |
| 116 | |
| 117 | // When all 5 pieces of details are known (during actual capture) |
| 118 | void AddConnection(const std::string& _s, const std::string& _connection, |
| 119 | const std::string& _cmd, const std::string& _unit, |
| 120 | int _pid) |
| 121 | { |
Patrick Williams | 3efd0b9 | 2024-08-16 15:22:31 -0400 | [diff] [blame] | 122 | DBusConnection* cxn = |
| 123 | new DBusConnection(_s, _connection, _cmd, _unit, _pid); |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 124 | connections_.push_back(cxn); |
| 125 | unique_name_to_cxn[_connection] = cxn; |
| 126 | } |
| 127 | |
| 128 | int GetConnectionPIDFromNameOrUniqueName(const std::string& key) |
| 129 | { |
| 130 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 131 | { |
| 132 | return INVALID; |
| 133 | } |
| 134 | else |
| 135 | { |
| 136 | return unique_name_to_cxn[key]->pid; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | std::string GetConnectionCMDFromNameOrUniqueName(const std::string& key) |
| 141 | { |
| 142 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 143 | { |
| 144 | return "(unknown)"; |
| 145 | } |
| 146 | else |
| 147 | { |
| 148 | return unique_name_to_cxn[key]->cmd; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | std::string GetUniqueNameIfExists(const std::string service) |
| 153 | { |
| 154 | for (DBusConnection* cxn : connections_) |
| 155 | { |
| 156 | if (cxn->service == service) |
| 157 | return cxn->connection; |
| 158 | } |
| 159 | return service; |
| 160 | } |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 161 | }; |
| 162 | |
| 163 | // Each sensor might have different units, for example current and voltage |
| 164 | class Sensor |
| 165 | { |
| 166 | public: |
| 167 | DBusConnection* connection_; |
| 168 | // Example: "/xyz/openbmc_project/sensors/temperature/powerseq_temp" |
| 169 | std::string object_path_; |
| 170 | std::string SensorID() |
| 171 | { |
| 172 | const size_t idx = object_path_.rfind('/'); |
| 173 | if (idx != std::string::npos) |
| 174 | { |
| 175 | return object_path_.substr(idx + 1); |
| 176 | } |
| 177 | else |
| 178 | return ("unknown sensor"); |
| 179 | } |
| 180 | |
| 181 | std::string ServiceName() |
| 182 | { |
| 183 | if (connection_ == nullptr) |
| 184 | return ""; |
| 185 | else |
| 186 | return connection_->service; |
| 187 | } |
| 188 | |
| 189 | std::string ConnectionName() |
| 190 | { |
| 191 | if (connection_ == nullptr) |
| 192 | return ""; |
| 193 | else |
| 194 | return connection_->connection; |
| 195 | } |
| 196 | |
| 197 | std::string ObjectPath() |
| 198 | { |
| 199 | return object_path_; |
| 200 | } |
| 201 | |
| 202 | // Should contain the following: |
| 203 | // 1. "org.freedesktop.DBus.Introspectable" |
| 204 | // 2. "org.freedesktop.DBus.Peer" |
| 205 | // 3. "org.freedesktop.DBus.Properties" |
| 206 | // 4. "xyz.openbmc_project.Sensor.Value" |
| 207 | // 5. "xyz.openbmc_project.State.Decorator.OperationalStatus" |
| 208 | std::set<std::string> interfaces_; |
| 209 | std::bitset<4> visibility_flags_; |
| 210 | std::set<std::string> associations_; |
| 211 | }; |
| 212 | |
| 213 | class SensorSnapshot |
| 214 | { |
| 215 | public: |
| 216 | std::vector<std::string> GetDistinctSensorNames() |
| 217 | { |
| 218 | std::unordered_set<std::string> seen; |
| 219 | std::vector<std::string> ret; |
| 220 | for (Sensor* s : sensors_) |
| 221 | { |
| 222 | std::string sn = s->SensorID(); |
| 223 | if (seen.find(sn) == seen.end()) |
| 224 | { |
| 225 | ret.push_back(sn); |
| 226 | seen.insert(sn); |
| 227 | } |
| 228 | } |
| 229 | return ret; |
| 230 | } |
| 231 | |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 232 | explicit SensorSnapshot() |
| 233 | { |
| 234 | connection_snapshot_ = nullptr; |
| 235 | } |
| 236 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 237 | explicit SensorSnapshot(DBusConnectionSnapshot* cs) |
| 238 | { |
| 239 | connection_snapshot_ = cs; |
| 240 | } |
| 241 | |
| 242 | ~SensorSnapshot() |
| 243 | { |
| 244 | for (Sensor* s : sensors_) |
| 245 | { |
| 246 | delete s; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | int SensorCount() |
| 251 | { |
| 252 | return int(sensors_.size()); |
| 253 | } |
| 254 | |
| 255 | Sensor* FindOrCreateSensorByServiceAndObject(const std::string& service, |
| 256 | const std::string& object) |
| 257 | { |
| 258 | Sensor* ret = nullptr; |
| 259 | for (Sensor* s : sensors_) |
| 260 | { |
| 261 | if (s->ServiceName() == service && s->object_path_ == object) |
| 262 | { |
| 263 | ret = s; |
| 264 | break; |
| 265 | } |
| 266 | } |
| 267 | if (ret == nullptr) |
| 268 | { |
| 269 | DBusConnection* cxn = |
| 270 | connection_snapshot_->FindDBusConnectionByService(service); |
| 271 | ret = new Sensor(); |
| 272 | ret->connection_ = cxn; |
| 273 | ret->object_path_ = object; |
| 274 | sensors_.push_back(ret); |
| 275 | } |
| 276 | return ret; |
| 277 | } |
| 278 | |
| 279 | // Note: one sensor_id might correspond to multiple sensors. |
| 280 | // Example: "VDD" can have all 3 of power, current and voltage. |
| 281 | std::vector<Sensor*> FindSensorsBySensorID(const std::string& sensor_id) |
| 282 | { |
| 283 | std::vector<Sensor*> ret; |
| 284 | for (Sensor* s : sensors_) |
| 285 | { |
| 286 | const std::string& p = s->object_path_; |
| 287 | if (p.find(sensor_id) == p.size() - sensor_id.size()) |
| 288 | { |
| 289 | ret.push_back(s); |
| 290 | } |
| 291 | } |
| 292 | return ret; |
| 293 | } |
| 294 | |
| 295 | // This sensor is visible from Object Mapper |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 296 | void SetSensorVisibleFromObjectMapper(const std::string& service, |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 297 | const std::string& object) |
| 298 | { |
| 299 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 300 | s->visibility_flags_.set(VISIBILITY_OBJECT_MAPPER); |
| 301 | } |
kuiying | dfb0cd9 | 2023-03-14 11:43:23 +0800 | [diff] [blame] | 302 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 303 | // This sensor is visible from Hwmon |
| 304 | void SetSensorVisibleFromHwmon(const std::string& service, |
| 305 | const std::string& object) |
| 306 | { |
| 307 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 308 | s->visibility_flags_.set(VISIBILITY_HWMON); |
| 309 | } |
| 310 | |
| 311 | // This sensor is visible from `ipmitool sdr` |
| 312 | // The first column is referred to as "sensorid". |
| 313 | void SetSensorVisibleFromIpmitoolSdr(const std::string& sensor_id) |
| 314 | { |
| 315 | std::vector<Sensor*> sensors = FindSensorsBySensorID(sensor_id); |
| 316 | for (Sensor* s : sensors) |
| 317 | s->visibility_flags_.set(VISIBILITY_IPMITOOL_SDR); |
| 318 | } |
| 319 | |
| 320 | void PrintSummary() |
| 321 | { |
| 322 | for (Sensor* s : sensors_) |
| 323 | { |
| 324 | printf("%50s %50s %9s\n", s->ServiceName().c_str(), |
| 325 | s->object_path_.c_str(), |
| 326 | s->visibility_flags_.to_string().c_str()); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | Sensor* FindSensorByDBusUniqueNameOrServiceName(const std::string& key) |
| 331 | { |
| 332 | for (Sensor* s : sensors_) |
| 333 | { |
| 334 | if (s->ConnectionName() == key || s->ServiceName() == key) |
| 335 | return s; |
| 336 | } |
| 337 | return nullptr; |
| 338 | } |
| 339 | |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 340 | void AddAssociationEndpoints(const std::string& path, |
| 341 | const std::set<std::string>& entries) |
| 342 | { |
| 343 | associations_[path] = entries; |
| 344 | } |
| 345 | |
| 346 | // Input: object path (regardless of what service name) |
| 347 | // out_edges: what associations does `path` declare? |
| 348 | // in_edges: what associations have `path` as endpoints? |
| 349 | void FindAssociationEndpoints( |
| 350 | const std::string& path, |
| 351 | std::map<std::string, std::set<std::string>>* out_edges, |
| 352 | std::map<std::string, std::set<std::string>>* in_edges) |
| 353 | { |
| 354 | std::map<std::string, std::set<std::string>> out, in; |
| 355 | for (const auto& [k, v] : associations_) |
| 356 | { |
| 357 | if (k.find(path) == 0) |
| 358 | { |
| 359 | out[k.substr(path.size())] = v; |
| 360 | } |
| 361 | for (const std::string& entry : v) |
| 362 | { |
| 363 | if (entry.find(path) == 0) |
| 364 | { |
| 365 | std::pair<std::string, std::string> p = ExtractFileName(k); |
| 366 | in[p.second].insert(k); |
| 367 | } |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | *out_edges = out; |
| 372 | *in_edges = in; |
| 373 | } |
| 374 | |
Patrick Williams | 3efd0b9 | 2024-08-16 15:22:31 -0400 | [diff] [blame] | 375 | void AddAssociationDefinition( |
| 376 | const std::string& path, const std::string& forward, |
| 377 | const std::string& reverse, const std::string& endpoint) |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 378 | { |
| 379 | std::vector<std::string> d = {path, forward, reverse, endpoint}; |
| 380 | association_definitions_.push_back(d); |
| 381 | } |
| 382 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 383 | private: |
| 384 | std::vector<Sensor*> sensors_; |
| 385 | std::unordered_map<std::string, int> conn2pid_; |
| 386 | DBusConnectionSnapshot* connection_snapshot_; |
Sui Chen | 8c5208f | 2023-04-21 14:10:05 -0700 | [diff] [blame] | 387 | |
| 388 | // Associations seen from Inventory objects |
| 389 | // Key: the path of the object that has the Association interface |
| 390 | // Value: all the endpoints |
| 391 | std::unordered_map<std::string, std::set<std::string>> associations_; |
| 392 | |
| 393 | // Association Definitions |
| 394 | // Ideally, this should match associations_ above |
| 395 | std::vector<std::vector<std::string>> association_definitions_; |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 396 | }; |
| 397 | |
| 398 | bool IsSensorObjectPath(const std::string& s); |
| 399 | bool IsUniqueName(const std::string& x); |
| 400 | std::string Trim(const std::string& s); |