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> |
| 21 | #include <optional> |
| 22 | #include <set> |
| 23 | #include <string> |
| 24 | #include <unordered_map> |
| 25 | #include <unordered_set> |
| 26 | #include <vector> |
| 27 | // Where is this sensor seen? |
| 28 | constexpr int VISIBILITY_OBJECT_MAPPER = 0; |
| 29 | constexpr int VISIBILITY_HWMON = 1; |
| 30 | constexpr int VISIBILITY_IPMITOOL_SDR = 2; |
| 31 | class DBusConnection |
| 32 | { |
| 33 | public: |
| 34 | std::string service; // example: "systemd-resolved" |
| 35 | std::string connection; // example: ":1.1" |
| 36 | std::string cmd; // the comm line |
| 37 | std::string unit; // example: "systemd-resolved.service" |
| 38 | int pid; |
| 39 | // For actual DBus capture: service name, connection name, command line, |
| 40 | // systmed unit and PID are all known |
| 41 | DBusConnection(const std::string& _s, const std::string& _c, |
| 42 | const std::string& _cmd, const std::string& _u, int _pid) |
| 43 | { |
| 44 | service = _s; |
| 45 | connection = _c; |
| 46 | cmd = _cmd; |
| 47 | unit = _u; |
| 48 | pid = _pid; |
| 49 | } |
| 50 | |
| 51 | // During PCap replay: only service name, connection name, and PID are known |
| 52 | // cmd and unit are not known since they are not |
| 53 | // stored in the PCap file |
| 54 | DBusConnection(const std::string& _s, const std::string& _c, int _pid) |
| 55 | { |
| 56 | service = _s; |
| 57 | connection = _c; |
| 58 | pid = _pid; |
| 59 | } |
| 60 | }; |
| 61 | |
| 62 | class DBusConnectionSnapshot |
| 63 | { |
| 64 | public: |
| 65 | std::vector<DBusConnection*> connections_; |
| 66 | std::unordered_map<std::string, DBusConnection*> unique_name_to_cxn; |
| 67 | DBusConnection* FindDBusConnectionByService(const std::string& service) |
| 68 | { |
| 69 | for (DBusConnection* cxn : connections_) |
| 70 | { |
| 71 | if (cxn->service == service) |
| 72 | return cxn; |
| 73 | } |
| 74 | return nullptr; |
| 75 | } |
| 76 | |
| 77 | void SetConnectionPID(const std::string& connection, int pid) |
| 78 | { |
| 79 | DBusConnection* cxn = FindDBusConnectionByService(connection); |
| 80 | if (cxn != nullptr) |
| 81 | { |
| 82 | cxn->pid = pid; |
| 83 | unique_name_to_cxn[connection] = cxn; // Just to make sure |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | void SetConnectionUniqueName(const std::string& service, |
| 88 | const std::string& unique_name) |
| 89 | { |
| 90 | DBusConnection* cxn = FindDBusConnectionByService(service); |
| 91 | if (cxn != nullptr) |
| 92 | { |
| 93 | cxn->connection = unique_name; |
| 94 | unique_name_to_cxn[unique_name] = cxn; |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | DBusConnection* FindDBusConnectionByConnection(const std::string& conn) |
| 99 | { |
| 100 | for (DBusConnection* cxn : connections_) |
| 101 | { |
| 102 | if (cxn->connection == conn) |
| 103 | return cxn; |
| 104 | } |
| 105 | return nullptr; |
| 106 | } |
| 107 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 108 | // Only when service is known (during playback) |
| 109 | void AddConnection(const std::string& _s) |
| 110 | { |
| 111 | connections_.push_back(new DBusConnection(_s, "", "", "", INVALID)); |
| 112 | } |
| 113 | |
| 114 | // When all 5 pieces of details are known (during actual capture) |
| 115 | void AddConnection(const std::string& _s, const std::string& _connection, |
| 116 | const std::string& _cmd, const std::string& _unit, |
| 117 | int _pid) |
| 118 | { |
Patrick Williams | f5c0b9d | 2023-03-14 09:31:01 -0500 | [diff] [blame] | 119 | DBusConnection* cxn = new DBusConnection(_s, _connection, _cmd, _unit, |
| 120 | _pid); |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 121 | connections_.push_back(cxn); |
| 122 | unique_name_to_cxn[_connection] = cxn; |
| 123 | } |
| 124 | |
| 125 | int GetConnectionPIDFromNameOrUniqueName(const std::string& key) |
| 126 | { |
| 127 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 128 | { |
| 129 | return INVALID; |
| 130 | } |
| 131 | else |
| 132 | { |
| 133 | return unique_name_to_cxn[key]->pid; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | std::string GetConnectionCMDFromNameOrUniqueName(const std::string& key) |
| 138 | { |
| 139 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 140 | { |
| 141 | return "(unknown)"; |
| 142 | } |
| 143 | else |
| 144 | { |
| 145 | return unique_name_to_cxn[key]->cmd; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | std::string GetUniqueNameIfExists(const std::string service) |
| 150 | { |
| 151 | for (DBusConnection* cxn : connections_) |
| 152 | { |
| 153 | if (cxn->service == service) |
| 154 | return cxn->connection; |
| 155 | } |
| 156 | return service; |
| 157 | } |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 158 | }; |
| 159 | |
| 160 | // Each sensor might have different units, for example current and voltage |
| 161 | class Sensor |
| 162 | { |
| 163 | public: |
| 164 | DBusConnection* connection_; |
| 165 | // Example: "/xyz/openbmc_project/sensors/temperature/powerseq_temp" |
| 166 | std::string object_path_; |
| 167 | std::string SensorID() |
| 168 | { |
| 169 | const size_t idx = object_path_.rfind('/'); |
| 170 | if (idx != std::string::npos) |
| 171 | { |
| 172 | return object_path_.substr(idx + 1); |
| 173 | } |
| 174 | else |
| 175 | return ("unknown sensor"); |
| 176 | } |
| 177 | |
| 178 | std::string ServiceName() |
| 179 | { |
| 180 | if (connection_ == nullptr) |
| 181 | return ""; |
| 182 | else |
| 183 | return connection_->service; |
| 184 | } |
| 185 | |
| 186 | std::string ConnectionName() |
| 187 | { |
| 188 | if (connection_ == nullptr) |
| 189 | return ""; |
| 190 | else |
| 191 | return connection_->connection; |
| 192 | } |
| 193 | |
| 194 | std::string ObjectPath() |
| 195 | { |
| 196 | return object_path_; |
| 197 | } |
| 198 | |
| 199 | // Should contain the following: |
| 200 | // 1. "org.freedesktop.DBus.Introspectable" |
| 201 | // 2. "org.freedesktop.DBus.Peer" |
| 202 | // 3. "org.freedesktop.DBus.Properties" |
| 203 | // 4. "xyz.openbmc_project.Sensor.Value" |
| 204 | // 5. "xyz.openbmc_project.State.Decorator.OperationalStatus" |
| 205 | std::set<std::string> interfaces_; |
| 206 | std::bitset<4> visibility_flags_; |
| 207 | std::set<std::string> associations_; |
| 208 | }; |
| 209 | |
| 210 | class SensorSnapshot |
| 211 | { |
| 212 | public: |
| 213 | std::vector<std::string> GetDistinctSensorNames() |
| 214 | { |
| 215 | std::unordered_set<std::string> seen; |
| 216 | std::vector<std::string> ret; |
| 217 | for (Sensor* s : sensors_) |
| 218 | { |
| 219 | std::string sn = s->SensorID(); |
| 220 | if (seen.find(sn) == seen.end()) |
| 221 | { |
| 222 | ret.push_back(sn); |
| 223 | seen.insert(sn); |
| 224 | } |
| 225 | } |
| 226 | return ret; |
| 227 | } |
| 228 | |
| 229 | explicit SensorSnapshot(DBusConnectionSnapshot* cs) |
| 230 | { |
| 231 | connection_snapshot_ = cs; |
| 232 | } |
| 233 | |
| 234 | ~SensorSnapshot() |
| 235 | { |
| 236 | for (Sensor* s : sensors_) |
| 237 | { |
| 238 | delete s; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | int SensorCount() |
| 243 | { |
| 244 | return int(sensors_.size()); |
| 245 | } |
| 246 | |
| 247 | Sensor* FindOrCreateSensorByServiceAndObject(const std::string& service, |
| 248 | const std::string& object) |
| 249 | { |
| 250 | Sensor* ret = nullptr; |
| 251 | for (Sensor* s : sensors_) |
| 252 | { |
| 253 | if (s->ServiceName() == service && s->object_path_ == object) |
| 254 | { |
| 255 | ret = s; |
| 256 | break; |
| 257 | } |
| 258 | } |
| 259 | if (ret == nullptr) |
| 260 | { |
| 261 | DBusConnection* cxn = |
| 262 | connection_snapshot_->FindDBusConnectionByService(service); |
| 263 | ret = new Sensor(); |
| 264 | ret->connection_ = cxn; |
| 265 | ret->object_path_ = object; |
| 266 | sensors_.push_back(ret); |
| 267 | } |
| 268 | return ret; |
| 269 | } |
| 270 | |
| 271 | // Note: one sensor_id might correspond to multiple sensors. |
| 272 | // Example: "VDD" can have all 3 of power, current and voltage. |
| 273 | std::vector<Sensor*> FindSensorsBySensorID(const std::string& sensor_id) |
| 274 | { |
| 275 | std::vector<Sensor*> ret; |
| 276 | for (Sensor* s : sensors_) |
| 277 | { |
| 278 | const std::string& p = s->object_path_; |
| 279 | if (p.find(sensor_id) == p.size() - sensor_id.size()) |
| 280 | { |
| 281 | ret.push_back(s); |
| 282 | } |
| 283 | } |
| 284 | return ret; |
| 285 | } |
| 286 | |
| 287 | // This sensor is visible from Object Mapper |
| 288 | void SerSensorVisibleFromObjectMapper(const std::string& service, |
| 289 | const std::string& object) |
| 290 | { |
| 291 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 292 | s->visibility_flags_.set(VISIBILITY_OBJECT_MAPPER); |
| 293 | } |
kuiying | dfb0cd9 | 2023-03-14 11:43:23 +0800 | [diff] [blame] | 294 | |
Adedeji Adebisi | 684ec91 | 2021-07-22 18:07:52 +0000 | [diff] [blame] | 295 | // This sensor is visible from Hwmon |
| 296 | void SetSensorVisibleFromHwmon(const std::string& service, |
| 297 | const std::string& object) |
| 298 | { |
| 299 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 300 | s->visibility_flags_.set(VISIBILITY_HWMON); |
| 301 | } |
| 302 | |
| 303 | // This sensor is visible from `ipmitool sdr` |
| 304 | // The first column is referred to as "sensorid". |
| 305 | void SetSensorVisibleFromIpmitoolSdr(const std::string& sensor_id) |
| 306 | { |
| 307 | std::vector<Sensor*> sensors = FindSensorsBySensorID(sensor_id); |
| 308 | for (Sensor* s : sensors) |
| 309 | s->visibility_flags_.set(VISIBILITY_IPMITOOL_SDR); |
| 310 | } |
| 311 | |
| 312 | void PrintSummary() |
| 313 | { |
| 314 | for (Sensor* s : sensors_) |
| 315 | { |
| 316 | printf("%50s %50s %9s\n", s->ServiceName().c_str(), |
| 317 | s->object_path_.c_str(), |
| 318 | s->visibility_flags_.to_string().c_str()); |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | Sensor* FindSensorByDBusUniqueNameOrServiceName(const std::string& key) |
| 323 | { |
| 324 | for (Sensor* s : sensors_) |
| 325 | { |
| 326 | if (s->ConnectionName() == key || s->ServiceName() == key) |
| 327 | return s; |
| 328 | } |
| 329 | return nullptr; |
| 330 | } |
| 331 | |
| 332 | private: |
| 333 | std::vector<Sensor*> sensors_; |
| 334 | std::unordered_map<std::string, int> conn2pid_; |
| 335 | DBusConnectionSnapshot* connection_snapshot_; |
| 336 | }; |
| 337 | |
| 338 | bool IsSensorObjectPath(const std::string& s); |
| 339 | bool IsUniqueName(const std::string& x); |
| 340 | std::string Trim(const std::string& s); |