blob: 12283398738b56724fcc6c44170d646931f84b6a [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "main.hpp"
// This is the form a sensor assumes on DBus.
// Aggregates their view from all other daemons.
#include <bitset>
#include <map>
#include <optional>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
std::pair<std::string, std::string> ExtractFileName(std::string x);
// Where is this sensor seen?
constexpr int VISIBILITY_OBJECT_MAPPER = 0;
constexpr int VISIBILITY_HWMON = 1;
constexpr int VISIBILITY_IPMITOOL_SDR = 2;
class DBusConnection
{
public:
std::string service; // example: "systemd-resolved"
std::string connection; // example: ":1.1"
std::string cmd; // the comm line
std::string unit; // example: "systemd-resolved.service"
int pid;
// For actual DBus capture: service name, connection name, command line,
// systmed unit and PID are all known
DBusConnection(const std::string& _s, const std::string& _c,
const std::string& _cmd, const std::string& _u, int _pid)
{
service = _s;
connection = _c;
cmd = _cmd;
unit = _u;
pid = _pid;
}
// During PCap replay: only service name, connection name, and PID are known
// cmd and unit are not known since they are not
// stored in the PCap file
DBusConnection(const std::string& _s, const std::string& _c, int _pid)
{
service = _s;
connection = _c;
pid = _pid;
}
};
class DBusConnectionSnapshot
{
public:
std::vector<DBusConnection*> connections_;
std::unordered_map<std::string, DBusConnection*> unique_name_to_cxn;
DBusConnection* FindDBusConnectionByService(const std::string& service)
{
for (DBusConnection* cxn : connections_)
{
if (cxn->service == service)
return cxn;
}
return nullptr;
}
void SetConnectionPID(const std::string& connection, int pid)
{
DBusConnection* cxn = FindDBusConnectionByService(connection);
if (cxn != nullptr)
{
cxn->pid = pid;
unique_name_to_cxn[connection] = cxn; // Just to make sure
}
}
void SetConnectionUniqueName(const std::string& service,
const std::string& unique_name)
{
DBusConnection* cxn = FindDBusConnectionByService(service);
if (cxn != nullptr)
{
cxn->connection = unique_name;
unique_name_to_cxn[unique_name] = cxn;
}
}
DBusConnection* FindDBusConnectionByConnection(const std::string& conn)
{
for (DBusConnection* cxn : connections_)
{
if (cxn->connection == conn)
return cxn;
}
return nullptr;
}
// Only when service is known (during playback)
void AddConnection(const std::string& _s)
{
connections_.push_back(new DBusConnection(_s, "", "", "", INVALID));
}
// When all 5 pieces of details are known (during actual capture)
void AddConnection(const std::string& _s, const std::string& _connection,
const std::string& _cmd, const std::string& _unit,
int _pid)
{
DBusConnection* cxn = new DBusConnection(_s, _connection, _cmd, _unit,
_pid);
connections_.push_back(cxn);
unique_name_to_cxn[_connection] = cxn;
}
int GetConnectionPIDFromNameOrUniqueName(const std::string& key)
{
if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end())
{
return INVALID;
}
else
{
return unique_name_to_cxn[key]->pid;
}
}
std::string GetConnectionCMDFromNameOrUniqueName(const std::string& key)
{
if (unique_name_to_cxn.find(key) == unique_name_to_cxn.end())
{
return "(unknown)";
}
else
{
return unique_name_to_cxn[key]->cmd;
}
}
std::string GetUniqueNameIfExists(const std::string service)
{
for (DBusConnection* cxn : connections_)
{
if (cxn->service == service)
return cxn->connection;
}
return service;
}
};
// Each sensor might have different units, for example current and voltage
class Sensor
{
public:
DBusConnection* connection_;
// Example: "/xyz/openbmc_project/sensors/temperature/powerseq_temp"
std::string object_path_;
std::string SensorID()
{
const size_t idx = object_path_.rfind('/');
if (idx != std::string::npos)
{
return object_path_.substr(idx + 1);
}
else
return ("unknown sensor");
}
std::string ServiceName()
{
if (connection_ == nullptr)
return "";
else
return connection_->service;
}
std::string ConnectionName()
{
if (connection_ == nullptr)
return "";
else
return connection_->connection;
}
std::string ObjectPath()
{
return object_path_;
}
// Should contain the following:
// 1. "org.freedesktop.DBus.Introspectable"
// 2. "org.freedesktop.DBus.Peer"
// 3. "org.freedesktop.DBus.Properties"
// 4. "xyz.openbmc_project.Sensor.Value"
// 5. "xyz.openbmc_project.State.Decorator.OperationalStatus"
std::set<std::string> interfaces_;
std::bitset<4> visibility_flags_;
std::set<std::string> associations_;
};
class SensorSnapshot
{
public:
std::vector<std::string> GetDistinctSensorNames()
{
std::unordered_set<std::string> seen;
std::vector<std::string> ret;
for (Sensor* s : sensors_)
{
std::string sn = s->SensorID();
if (seen.find(sn) == seen.end())
{
ret.push_back(sn);
seen.insert(sn);
}
}
return ret;
}
explicit SensorSnapshot()
{
connection_snapshot_ = nullptr;
}
explicit SensorSnapshot(DBusConnectionSnapshot* cs)
{
connection_snapshot_ = cs;
}
~SensorSnapshot()
{
for (Sensor* s : sensors_)
{
delete s;
}
}
int SensorCount()
{
return int(sensors_.size());
}
Sensor* FindOrCreateSensorByServiceAndObject(const std::string& service,
const std::string& object)
{
Sensor* ret = nullptr;
for (Sensor* s : sensors_)
{
if (s->ServiceName() == service && s->object_path_ == object)
{
ret = s;
break;
}
}
if (ret == nullptr)
{
DBusConnection* cxn =
connection_snapshot_->FindDBusConnectionByService(service);
ret = new Sensor();
ret->connection_ = cxn;
ret->object_path_ = object;
sensors_.push_back(ret);
}
return ret;
}
// Note: one sensor_id might correspond to multiple sensors.
// Example: "VDD" can have all 3 of power, current and voltage.
std::vector<Sensor*> FindSensorsBySensorID(const std::string& sensor_id)
{
std::vector<Sensor*> ret;
for (Sensor* s : sensors_)
{
const std::string& p = s->object_path_;
if (p.find(sensor_id) == p.size() - sensor_id.size())
{
ret.push_back(s);
}
}
return ret;
}
// This sensor is visible from Object Mapper
void SetSensorVisibleFromObjectMapper(const std::string& service,
const std::string& object)
{
Sensor* s = FindOrCreateSensorByServiceAndObject(service, object);
s->visibility_flags_.set(VISIBILITY_OBJECT_MAPPER);
}
// This sensor is visible from Hwmon
void SetSensorVisibleFromHwmon(const std::string& service,
const std::string& object)
{
Sensor* s = FindOrCreateSensorByServiceAndObject(service, object);
s->visibility_flags_.set(VISIBILITY_HWMON);
}
// This sensor is visible from `ipmitool sdr`
// The first column is referred to as "sensorid".
void SetSensorVisibleFromIpmitoolSdr(const std::string& sensor_id)
{
std::vector<Sensor*> sensors = FindSensorsBySensorID(sensor_id);
for (Sensor* s : sensors)
s->visibility_flags_.set(VISIBILITY_IPMITOOL_SDR);
}
void PrintSummary()
{
for (Sensor* s : sensors_)
{
printf("%50s %50s %9s\n", s->ServiceName().c_str(),
s->object_path_.c_str(),
s->visibility_flags_.to_string().c_str());
}
}
Sensor* FindSensorByDBusUniqueNameOrServiceName(const std::string& key)
{
for (Sensor* s : sensors_)
{
if (s->ConnectionName() == key || s->ServiceName() == key)
return s;
}
return nullptr;
}
void AddAssociationEndpoints(const std::string& path,
const std::set<std::string>& entries)
{
associations_[path] = entries;
}
// Input: object path (regardless of what service name)
// out_edges: what associations does `path` declare?
// in_edges: what associations have `path` as endpoints?
void FindAssociationEndpoints(
const std::string& path,
std::map<std::string, std::set<std::string>>* out_edges,
std::map<std::string, std::set<std::string>>* in_edges)
{
std::map<std::string, std::set<std::string>> out, in;
for (const auto& [k, v] : associations_)
{
if (k.find(path) == 0)
{
out[k.substr(path.size())] = v;
}
for (const std::string& entry : v)
{
if (entry.find(path) == 0)
{
std::pair<std::string, std::string> p = ExtractFileName(k);
in[p.second].insert(k);
}
}
}
*out_edges = out;
*in_edges = in;
}
void AddAssociationDefinition(const std::string& path,
const std::string& forward,
const std::string& reverse,
const std::string& endpoint)
{
std::vector<std::string> d = {path, forward, reverse, endpoint};
association_definitions_.push_back(d);
}
private:
std::vector<Sensor*> sensors_;
std::unordered_map<std::string, int> conn2pid_;
DBusConnectionSnapshot* connection_snapshot_;
// Associations seen from Inventory objects
// Key: the path of the object that has the Association interface
// Value: all the endpoints
std::unordered_map<std::string, std::set<std::string>> associations_;
// Association Definitions
// Ideally, this should match associations_ above
std::vector<std::vector<std::string>> association_definitions_;
};
bool IsSensorObjectPath(const std::string& s);
bool IsUniqueName(const std::string& x);
std::string Trim(const std::string& s);