Adedeji Adebisi | 12c5f11 | 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 | |
| 108 | |
| 109 | // Only when service is known (during playback) |
| 110 | void AddConnection(const std::string& _s) |
| 111 | { |
| 112 | connections_.push_back(new DBusConnection(_s, "", "", "", INVALID)); |
| 113 | } |
| 114 | |
| 115 | // When all 5 pieces of details are known (during actual capture) |
| 116 | void AddConnection(const std::string& _s, const std::string& _connection, |
| 117 | const std::string& _cmd, const std::string& _unit, |
| 118 | int _pid) |
| 119 | { |
| 120 | DBusConnection* cxn = |
| 121 | new DBusConnection(_s, _connection, _cmd, _unit, _pid); |
| 122 | connections_.push_back(cxn); |
| 123 | unique_name_to_cxn[_connection] = cxn; |
| 124 | } |
| 125 | |
| 126 | int GetConnectionPIDFromNameOrUniqueName(const std::string& key) |
| 127 | { |
| 128 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 129 | { |
| 130 | return INVALID; |
| 131 | } |
| 132 | else |
| 133 | { |
| 134 | return unique_name_to_cxn[key]->pid; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | std::string GetConnectionCMDFromNameOrUniqueName(const std::string& key) |
| 139 | { |
| 140 | if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end()) |
| 141 | { |
| 142 | return "(unknown)"; |
| 143 | } |
| 144 | else |
| 145 | { |
| 146 | return unique_name_to_cxn[key]->cmd; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | std::string GetUniqueNameIfExists(const std::string service) |
| 151 | { |
| 152 | for (DBusConnection* cxn : connections_) |
| 153 | { |
| 154 | if (cxn->service == service) |
| 155 | return cxn->connection; |
| 156 | } |
| 157 | return service; |
| 158 | } |
| 159 | |
| 160 | }; |
| 161 | |
| 162 | // Each sensor might have different units, for example current and voltage |
| 163 | class Sensor |
| 164 | { |
| 165 | public: |
| 166 | DBusConnection* connection_; |
| 167 | // Example: "/xyz/openbmc_project/sensors/temperature/powerseq_temp" |
| 168 | std::string object_path_; |
| 169 | std::string SensorID() |
| 170 | { |
| 171 | const size_t idx = object_path_.rfind('/'); |
| 172 | if (idx != std::string::npos) |
| 173 | { |
| 174 | return object_path_.substr(idx + 1); |
| 175 | } |
| 176 | else |
| 177 | return ("unknown sensor"); |
| 178 | } |
| 179 | |
| 180 | std::string ServiceName() |
| 181 | { |
| 182 | if (connection_ == nullptr) |
| 183 | return ""; |
| 184 | else |
| 185 | return connection_->service; |
| 186 | } |
| 187 | |
| 188 | std::string ConnectionName() |
| 189 | { |
| 190 | if (connection_ == nullptr) |
| 191 | return ""; |
| 192 | else |
| 193 | return connection_->connection; |
| 194 | } |
| 195 | |
| 196 | std::string ObjectPath() |
| 197 | { |
| 198 | return object_path_; |
| 199 | } |
| 200 | |
| 201 | // Should contain the following: |
| 202 | // 1. "org.freedesktop.DBus.Introspectable" |
| 203 | // 2. "org.freedesktop.DBus.Peer" |
| 204 | // 3. "org.freedesktop.DBus.Properties" |
| 205 | // 4. "xyz.openbmc_project.Sensor.Value" |
| 206 | // 5. "xyz.openbmc_project.State.Decorator.OperationalStatus" |
| 207 | std::set<std::string> interfaces_; |
| 208 | std::bitset<4> visibility_flags_; |
| 209 | std::set<std::string> associations_; |
| 210 | }; |
| 211 | |
| 212 | class SensorSnapshot |
| 213 | { |
| 214 | public: |
| 215 | std::vector<std::string> GetDistinctSensorNames() |
| 216 | { |
| 217 | std::unordered_set<std::string> seen; |
| 218 | std::vector<std::string> ret; |
| 219 | for (Sensor* s : sensors_) |
| 220 | { |
| 221 | std::string sn = s->SensorID(); |
| 222 | if (seen.find(sn) == seen.end()) |
| 223 | { |
| 224 | ret.push_back(sn); |
| 225 | seen.insert(sn); |
| 226 | } |
| 227 | } |
| 228 | return ret; |
| 229 | } |
| 230 | |
| 231 | explicit SensorSnapshot(DBusConnectionSnapshot* cs) |
| 232 | { |
| 233 | connection_snapshot_ = cs; |
| 234 | } |
| 235 | |
| 236 | ~SensorSnapshot() |
| 237 | { |
| 238 | for (Sensor* s : sensors_) |
| 239 | { |
| 240 | delete s; |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | int SensorCount() |
| 245 | { |
| 246 | return int(sensors_.size()); |
| 247 | } |
| 248 | |
| 249 | Sensor* FindOrCreateSensorByServiceAndObject(const std::string& service, |
| 250 | const std::string& object) |
| 251 | { |
| 252 | Sensor* ret = nullptr; |
| 253 | for (Sensor* s : sensors_) |
| 254 | { |
| 255 | if (s->ServiceName() == service && s->object_path_ == object) |
| 256 | { |
| 257 | ret = s; |
| 258 | break; |
| 259 | } |
| 260 | } |
| 261 | if (ret == nullptr) |
| 262 | { |
| 263 | DBusConnection* cxn = |
| 264 | connection_snapshot_->FindDBusConnectionByService(service); |
| 265 | ret = new Sensor(); |
| 266 | ret->connection_ = cxn; |
| 267 | ret->object_path_ = object; |
| 268 | sensors_.push_back(ret); |
| 269 | } |
| 270 | return ret; |
| 271 | } |
| 272 | |
| 273 | // Note: one sensor_id might correspond to multiple sensors. |
| 274 | // Example: "VDD" can have all 3 of power, current and voltage. |
| 275 | std::vector<Sensor*> FindSensorsBySensorID(const std::string& sensor_id) |
| 276 | { |
| 277 | std::vector<Sensor*> ret; |
| 278 | for (Sensor* s : sensors_) |
| 279 | { |
| 280 | const std::string& p = s->object_path_; |
| 281 | if (p.find(sensor_id) == p.size() - sensor_id.size()) |
| 282 | { |
| 283 | ret.push_back(s); |
| 284 | } |
| 285 | } |
| 286 | return ret; |
| 287 | } |
| 288 | |
| 289 | // This sensor is visible from Object Mapper |
| 290 | void SerSensorVisibleFromObjectMapper(const std::string& service, |
| 291 | const std::string& object) |
| 292 | { |
| 293 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 294 | s->visibility_flags_.set(VISIBILITY_OBJECT_MAPPER); |
| 295 | } |
| 296 | |
| 297 | // This sensor is visible from Hwmon |
| 298 | void SetSensorVisibleFromHwmon(const std::string& service, |
| 299 | const std::string& object) |
| 300 | { |
| 301 | Sensor* s = FindOrCreateSensorByServiceAndObject(service, object); |
| 302 | s->visibility_flags_.set(VISIBILITY_HWMON); |
| 303 | } |
| 304 | |
| 305 | // This sensor is visible from `ipmitool sdr` |
| 306 | // The first column is referred to as "sensorid". |
| 307 | void SetSensorVisibleFromIpmitoolSdr(const std::string& sensor_id) |
| 308 | { |
| 309 | std::vector<Sensor*> sensors = FindSensorsBySensorID(sensor_id); |
| 310 | for (Sensor* s : sensors) |
| 311 | s->visibility_flags_.set(VISIBILITY_IPMITOOL_SDR); |
| 312 | } |
| 313 | |
| 314 | void PrintSummary() |
| 315 | { |
| 316 | for (Sensor* s : sensors_) |
| 317 | { |
| 318 | printf("%50s %50s %9s\n", s->ServiceName().c_str(), |
| 319 | s->object_path_.c_str(), |
| 320 | s->visibility_flags_.to_string().c_str()); |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | Sensor* FindSensorByDBusUniqueNameOrServiceName(const std::string& key) |
| 325 | { |
| 326 | for (Sensor* s : sensors_) |
| 327 | { |
| 328 | if (s->ConnectionName() == key || s->ServiceName() == key) |
| 329 | return s; |
| 330 | } |
| 331 | return nullptr; |
| 332 | } |
| 333 | |
| 334 | private: |
| 335 | std::vector<Sensor*> sensors_; |
| 336 | std::unordered_map<std::string, int> conn2pid_; |
| 337 | DBusConnectionSnapshot* connection_snapshot_; |
| 338 | }; |
| 339 | |
| 340 | bool IsSensorObjectPath(const std::string& s); |
| 341 | bool IsUniqueName(const std::string& x); |
| 342 | std::string Trim(const std::string& s); |