blob: a619c4ab3d5ddedca15d7ec2022334968b8b3bcc [file] [log] [blame]
Adedeji Adebisi12c5f112021-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>
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?
28constexpr int VISIBILITY_OBJECT_MAPPER = 0;
29constexpr int VISIBILITY_HWMON = 1;
30constexpr int VISIBILITY_IPMITOOL_SDR = 2;
31class 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
62class 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
163class 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
212class 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
340bool IsSensorObjectPath(const std::string& s);
341bool IsUniqueName(const std::string& x);
342std::string Trim(const std::string& s);