blob: 12283398738b56724fcc6c44170d646931f84b6a [file] [log] [blame]
Adedeji Adebisi684ec912021-07-22 18:07:52 +00001// 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 Chen8c5208f2023-04-21 14:10:05 -070021#include <map>
Adedeji Adebisi684ec912021-07-22 18:07:52 +000022#include <optional>
23#include <set>
24#include <string>
25#include <unordered_map>
26#include <unordered_set>
27#include <vector>
Sui Chen8c5208f2023-04-21 14:10:05 -070028
29std::pair<std::string, std::string> ExtractFileName(std::string x);
Adedeji Adebisi684ec912021-07-22 18:07:52 +000030// Where is this sensor seen?
31constexpr int VISIBILITY_OBJECT_MAPPER = 0;
32constexpr int VISIBILITY_HWMON = 1;
33constexpr int VISIBILITY_IPMITOOL_SDR = 2;
34class 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
65class 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 Adebisi684ec912021-07-22 18:07:52 +0000111 // 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 Williamsf5c0b9d2023-03-14 09:31:01 -0500122 DBusConnection* cxn = new DBusConnection(_s, _connection, _cmd, _unit,
123 _pid);
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000124 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 Adebisi684ec912021-07-22 18:07:52 +0000161};
162
163// Each sensor might have different units, for example current and voltage
164class 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
213class 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 Chen8c5208f2023-04-21 14:10:05 -0700232 explicit SensorSnapshot()
233 {
234 connection_snapshot_ = nullptr;
235 }
236
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000237 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 Chen8c5208f2023-04-21 14:10:05 -0700296 void SetSensorVisibleFromObjectMapper(const std::string& service,
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000297 const std::string& object)
298 {
299 Sensor* s = FindOrCreateSensorByServiceAndObject(service, object);
300 s->visibility_flags_.set(VISIBILITY_OBJECT_MAPPER);
301 }
kuiyingdfb0cd92023-03-14 11:43:23 +0800302
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000303 // 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 Chen8c5208f2023-04-21 14:10:05 -0700340 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
375 void AddAssociationDefinition(const std::string& path,
376 const std::string& forward,
377 const std::string& reverse,
378 const std::string& endpoint)
379 {
380 std::vector<std::string> d = {path, forward, reverse, endpoint};
381 association_definitions_.push_back(d);
382 }
383
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000384 private:
385 std::vector<Sensor*> sensors_;
386 std::unordered_map<std::string, int> conn2pid_;
387 DBusConnectionSnapshot* connection_snapshot_;
Sui Chen8c5208f2023-04-21 14:10:05 -0700388
389 // Associations seen from Inventory objects
390 // Key: the path of the object that has the Association interface
391 // Value: all the endpoints
392 std::unordered_map<std::string, std::set<std::string>> associations_;
393
394 // Association Definitions
395 // Ideally, this should match associations_ above
396 std::vector<std::vector<std::string>> association_definitions_;
Adedeji Adebisi684ec912021-07-22 18:07:52 +0000397};
398
399bool IsSensorObjectPath(const std::string& s);
400bool IsUniqueName(const std::string& x);
401std::string Trim(const std::string& s);