1852db67bSMatt Spinler #include "association_manager.hpp"
2852db67bSMatt Spinler
3cf5d024bSGeorge Liu #include <phosphor-logging/lg2.hpp>
499e66a03SMatt Spinler
5a83db30eSBrad Bishop #include <filesystem>
6a83db30eSBrad Bishop #include <fstream>
7a83db30eSBrad Bishop
8852db67bSMatt Spinler namespace phosphor
9852db67bSMatt Spinler {
10852db67bSMatt Spinler namespace inventory
11852db67bSMatt Spinler {
12852db67bSMatt Spinler namespace manager
13852db67bSMatt Spinler {
14852db67bSMatt Spinler namespace associations
15852db67bSMatt Spinler {
1659521e87SMatt Spinler namespace fs = std::filesystem;
17852db67bSMatt Spinler
Manager(sdbusplus::bus_t & bus,const std::string & jsonPath)18563306f6SPatrick Williams Manager::Manager(sdbusplus::bus_t& bus, const std::string& jsonPath) :
19852db67bSMatt Spinler _bus(bus), _jsonFile(jsonPath)
20852db67bSMatt Spinler {
2159521e87SMatt Spinler // If there aren't any conditional associations files, look for
2259521e87SMatt Spinler // that default nonconditional one.
2359521e87SMatt Spinler if (!loadConditions())
2459521e87SMatt Spinler {
2559521e87SMatt Spinler if (fs::exists(_jsonFile))
2659521e87SMatt Spinler {
2759521e87SMatt Spinler std::ifstream file{_jsonFile};
2859521e87SMatt Spinler auto json = nlohmann::json::parse(file, nullptr, true);
2959521e87SMatt Spinler load(json);
3059521e87SMatt Spinler }
3159521e87SMatt Spinler }
3299e66a03SMatt Spinler }
3399e66a03SMatt Spinler
3499e66a03SMatt Spinler /**
3599e66a03SMatt Spinler * @brief Throws an exception if 'num' is zero. Used for JSON
3699e66a03SMatt Spinler * sanity checking.
3799e66a03SMatt Spinler *
3899e66a03SMatt Spinler * @param[in] num - the number to check
3999e66a03SMatt Spinler */
throwIfZero(int num)4099e66a03SMatt Spinler void throwIfZero(int num)
4199e66a03SMatt Spinler {
4299e66a03SMatt Spinler if (!num)
4399e66a03SMatt Spinler {
4499e66a03SMatt Spinler throw std::invalid_argument("Invalid empty field in JSON");
4599e66a03SMatt Spinler }
4699e66a03SMatt Spinler }
4799e66a03SMatt Spinler
loadConditions()4859521e87SMatt Spinler bool Manager::loadConditions()
4999e66a03SMatt Spinler {
5059521e87SMatt Spinler auto dir = _jsonFile.parent_path();
5199e66a03SMatt Spinler
5259521e87SMatt Spinler for (const auto& dirent : fs::recursive_directory_iterator(dir))
5359521e87SMatt Spinler {
5459521e87SMatt Spinler const auto& path = dirent.path();
5559521e87SMatt Spinler if (path.extension() == ".json")
5659521e87SMatt Spinler {
5759521e87SMatt Spinler std::ifstream file{path};
5899e66a03SMatt Spinler auto json = nlohmann::json::parse(file, nullptr, true);
5999e66a03SMatt Spinler
6059521e87SMatt Spinler if (json.is_object() && json.contains("condition"))
6159521e87SMatt Spinler {
6259521e87SMatt Spinler const auto& conditionJSON = json.at("condition");
6359521e87SMatt Spinler if (!conditionJSON.contains("path") ||
6459521e87SMatt Spinler !conditionJSON.contains("interface") ||
6559521e87SMatt Spinler !conditionJSON.contains("property") ||
6659521e87SMatt Spinler !conditionJSON.contains("values"))
6759521e87SMatt Spinler {
68cf5d024bSGeorge Liu lg2::error(
69cf5d024bSGeorge Liu "Invalid JSON in associations condition entry in {PATH}. Skipping file.",
70cf5d024bSGeorge Liu "PATH", path);
7159521e87SMatt Spinler continue;
7259521e87SMatt Spinler }
7359521e87SMatt Spinler
7459521e87SMatt Spinler Condition c;
7559521e87SMatt Spinler c.file = path;
7659521e87SMatt Spinler c.path = conditionJSON["path"].get<std::string>();
7759521e87SMatt Spinler if (c.path.front() != '/')
7859521e87SMatt Spinler {
7959521e87SMatt Spinler c.path = '/' + c.path;
8059521e87SMatt Spinler }
8159521e87SMatt Spinler fprintf(stderr, "found conditions file %s\n", c.file.c_str());
8259521e87SMatt Spinler c.interface = conditionJSON["interface"].get<std::string>();
8359521e87SMatt Spinler c.property = conditionJSON["property"].get<std::string>();
8459521e87SMatt Spinler
8559521e87SMatt Spinler // The values are in an array, and need to be
8659521e87SMatt Spinler // converted to an InterfaceVariantType.
8759521e87SMatt Spinler for (const auto& value : conditionJSON["values"])
8859521e87SMatt Spinler {
8959521e87SMatt Spinler if (value.is_array())
9059521e87SMatt Spinler {
9159521e87SMatt Spinler std::vector<uint8_t> variantValue;
9259521e87SMatt Spinler for (const auto& v : value)
9359521e87SMatt Spinler {
9459521e87SMatt Spinler variantValue.push_back(v.get<uint8_t>());
9559521e87SMatt Spinler }
9659521e87SMatt Spinler c.values.push_back(variantValue);
9759521e87SMatt Spinler continue;
9859521e87SMatt Spinler }
9959521e87SMatt Spinler
10059521e87SMatt Spinler // Try the remaining types
10159521e87SMatt Spinler auto s = value.get_ptr<const std::string*>();
10259521e87SMatt Spinler auto i = value.get_ptr<const int64_t*>();
10359521e87SMatt Spinler auto b = value.get_ptr<const bool*>();
10459521e87SMatt Spinler if (s)
10559521e87SMatt Spinler {
10659521e87SMatt Spinler c.values.push_back(*s);
10759521e87SMatt Spinler }
10859521e87SMatt Spinler else if (i)
10959521e87SMatt Spinler {
11059521e87SMatt Spinler c.values.push_back(*i);
11159521e87SMatt Spinler }
11259521e87SMatt Spinler else if (b)
11359521e87SMatt Spinler {
11459521e87SMatt Spinler c.values.push_back(*b);
11559521e87SMatt Spinler }
11659521e87SMatt Spinler else
11759521e87SMatt Spinler {
118cf5d024bSGeorge Liu lg2::error(
119cf5d024bSGeorge Liu "Invalid condition property value in {FILE}:",
120cf5d024bSGeorge Liu "FILE", c.file);
121cf5d024bSGeorge Liu throw std::runtime_error(
122cf5d024bSGeorge Liu "Invalid condition property value");
12359521e87SMatt Spinler }
12459521e87SMatt Spinler }
12559521e87SMatt Spinler
12659521e87SMatt Spinler _conditions.push_back(std::move(c));
12759521e87SMatt Spinler }
12859521e87SMatt Spinler }
12959521e87SMatt Spinler }
13059521e87SMatt Spinler
13159521e87SMatt Spinler return !_conditions.empty();
13259521e87SMatt Spinler }
13359521e87SMatt Spinler
conditionMatch(const sdbusplus::message::object_path & objectPath,const Object & object)13459521e87SMatt Spinler bool Manager::conditionMatch(const sdbusplus::message::object_path& objectPath,
13559521e87SMatt Spinler const Object& object)
13659521e87SMatt Spinler {
13759521e87SMatt Spinler fs::path foundPath;
13859521e87SMatt Spinler for (const auto& condition : _conditions)
13959521e87SMatt Spinler {
14059521e87SMatt Spinler if (condition.path != objectPath)
14159521e87SMatt Spinler {
14259521e87SMatt Spinler continue;
14359521e87SMatt Spinler }
14459521e87SMatt Spinler
145*d8fba8beSPatrick Williams auto interface = std::find_if(
146*d8fba8beSPatrick Williams object.begin(), object.end(), [&condition](const auto& i) {
14759521e87SMatt Spinler return i.first == condition.interface;
14859521e87SMatt Spinler });
14959521e87SMatt Spinler if (interface == object.end())
15059521e87SMatt Spinler {
15159521e87SMatt Spinler continue;
15259521e87SMatt Spinler }
15359521e87SMatt Spinler
154*d8fba8beSPatrick Williams auto property =
155*d8fba8beSPatrick Williams std::find_if(interface->second.begin(), interface->second.end(),
15659521e87SMatt Spinler [&condition](const auto& p) {
15759521e87SMatt Spinler return condition.property == p.first;
15859521e87SMatt Spinler });
15959521e87SMatt Spinler if (property == interface->second.end())
16059521e87SMatt Spinler {
16159521e87SMatt Spinler continue;
16259521e87SMatt Spinler }
16359521e87SMatt Spinler
16459521e87SMatt Spinler auto match = std::find(condition.values.begin(), condition.values.end(),
16559521e87SMatt Spinler property->second);
16659521e87SMatt Spinler if (match != condition.values.end())
16759521e87SMatt Spinler {
16859521e87SMatt Spinler foundPath = condition.file;
16959521e87SMatt Spinler break;
17059521e87SMatt Spinler }
17159521e87SMatt Spinler }
17259521e87SMatt Spinler
17359521e87SMatt Spinler if (!foundPath.empty())
17459521e87SMatt Spinler {
17559521e87SMatt Spinler std::ifstream file{foundPath};
17659521e87SMatt Spinler auto json = nlohmann::json::parse(file, nullptr, true);
17759521e87SMatt Spinler load(json["associations"]);
17859521e87SMatt Spinler _conditions.clear();
17959521e87SMatt Spinler return true;
18059521e87SMatt Spinler }
18159521e87SMatt Spinler
18259521e87SMatt Spinler return false;
18359521e87SMatt Spinler }
18459521e87SMatt Spinler
conditionMatch()18559521e87SMatt Spinler bool Manager::conditionMatch()
18659521e87SMatt Spinler {
18759521e87SMatt Spinler fs::path foundPath;
18859521e87SMatt Spinler
18959521e87SMatt Spinler for (const auto& condition : _conditions)
19059521e87SMatt Spinler {
19159521e87SMatt Spinler // Compare the actualValue field against the values in the
19259521e87SMatt Spinler // values vector to see if there is a condition match.
19359521e87SMatt Spinler auto found = std::find(condition.values.begin(), condition.values.end(),
19459521e87SMatt Spinler condition.actualValue);
19559521e87SMatt Spinler if (found != condition.values.end())
19659521e87SMatt Spinler {
19759521e87SMatt Spinler foundPath = condition.file;
19859521e87SMatt Spinler break;
19959521e87SMatt Spinler }
20059521e87SMatt Spinler }
20159521e87SMatt Spinler
20259521e87SMatt Spinler if (!foundPath.empty())
20359521e87SMatt Spinler {
20459521e87SMatt Spinler std::ifstream file{foundPath};
20559521e87SMatt Spinler auto json = nlohmann::json::parse(file, nullptr, true);
20659521e87SMatt Spinler load(json["associations"]);
20759521e87SMatt Spinler _conditions.clear();
20859521e87SMatt Spinler return true;
20959521e87SMatt Spinler }
21059521e87SMatt Spinler
21159521e87SMatt Spinler return false;
21259521e87SMatt Spinler }
21359521e87SMatt Spinler
load(const nlohmann::json & json)21459521e87SMatt Spinler void Manager::load(const nlohmann::json& json)
21559521e87SMatt Spinler {
21699e66a03SMatt Spinler const std::string root{INVENTORY_ROOT};
21799e66a03SMatt Spinler
21899e66a03SMatt Spinler for (const auto& jsonAssoc : json)
21999e66a03SMatt Spinler {
22099e66a03SMatt Spinler // Only add the slash if necessary
22199e66a03SMatt Spinler std::string path = jsonAssoc.at("path");
22299e66a03SMatt Spinler throwIfZero(path.size());
22399e66a03SMatt Spinler if (path.front() != '/')
22499e66a03SMatt Spinler {
22599e66a03SMatt Spinler path = root + "/" + path;
22699e66a03SMatt Spinler }
22799e66a03SMatt Spinler else
22899e66a03SMatt Spinler {
22999e66a03SMatt Spinler path = root + path;
23099e66a03SMatt Spinler }
23199e66a03SMatt Spinler
23299e66a03SMatt Spinler auto& assocEndpoints = _associations[path];
23399e66a03SMatt Spinler
23499e66a03SMatt Spinler for (const auto& endpoint : jsonAssoc.at("endpoints"))
23599e66a03SMatt Spinler {
23699e66a03SMatt Spinler std::string ftype = endpoint.at("types").at("fType");
23799e66a03SMatt Spinler std::string rtype = endpoint.at("types").at("rType");
23899e66a03SMatt Spinler throwIfZero(ftype.size());
23999e66a03SMatt Spinler throwIfZero(rtype.size());
24099e66a03SMatt Spinler Types types{std::move(ftype), std::move(rtype)};
24199e66a03SMatt Spinler
24299e66a03SMatt Spinler Paths paths = endpoint.at("paths");
24399e66a03SMatt Spinler throwIfZero(paths.size());
24499e66a03SMatt Spinler assocEndpoints.emplace_back(std::move(types), std::move(paths));
24599e66a03SMatt Spinler }
24699e66a03SMatt Spinler }
247852db67bSMatt Spinler }
248852db67bSMatt Spinler
createAssociations(const std::string & objectPath,bool deferSignal)249104ccba9SBrad Bishop void Manager::createAssociations(const std::string& objectPath,
250104ccba9SBrad Bishop bool deferSignal)
251852db67bSMatt Spinler {
252c47ca585SMatt Spinler auto endpoints = _associations.find(objectPath);
253c47ca585SMatt Spinler if (endpoints == _associations.end())
254c47ca585SMatt Spinler {
255c47ca585SMatt Spinler return;
256c47ca585SMatt Spinler }
257c47ca585SMatt Spinler
258c47ca585SMatt Spinler if (std::find(_handled.begin(), _handled.end(), objectPath) !=
259c47ca585SMatt Spinler _handled.end())
260c47ca585SMatt Spinler {
261c47ca585SMatt Spinler return;
262c47ca585SMatt Spinler }
263c47ca585SMatt Spinler
264c47ca585SMatt Spinler _handled.push_back(objectPath);
265c47ca585SMatt Spinler
266c47ca585SMatt Spinler for (const auto& endpoint : endpoints->second)
267c47ca585SMatt Spinler {
268c47ca585SMatt Spinler const auto& types = std::get<typesPos>(endpoint);
269c47ca585SMatt Spinler const auto& paths = std::get<pathsPos>(endpoint);
270c47ca585SMatt Spinler
271c47ca585SMatt Spinler for (const auto& endpointPath : paths)
272c47ca585SMatt Spinler {
273c47ca585SMatt Spinler const auto& forwardType = std::get<forwardTypePos>(types);
274c47ca585SMatt Spinler const auto& reverseType = std::get<reverseTypePos>(types);
275c47ca585SMatt Spinler
276c47ca585SMatt Spinler createAssociation(objectPath, forwardType, endpointPath,
277104ccba9SBrad Bishop reverseType, deferSignal);
278c47ca585SMatt Spinler }
279c47ca585SMatt Spinler }
280c47ca585SMatt Spinler }
281c47ca585SMatt Spinler
createAssociation(const std::string & forwardPath,const std::string & forwardType,const std::string & reversePath,const std::string & reverseType,bool deferSignal)282*d8fba8beSPatrick Williams void Manager::createAssociation(
283*d8fba8beSPatrick Williams const std::string& forwardPath, const std::string& forwardType,
284*d8fba8beSPatrick Williams const std::string& reversePath, const std::string& reverseType,
285104ccba9SBrad Bishop bool deferSignal)
286c47ca585SMatt Spinler {
287c47ca585SMatt Spinler auto object = _associationIfaces.find(forwardPath);
288c47ca585SMatt Spinler if (object == _associationIfaces.end())
289c47ca585SMatt Spinler {
2901bb06235SPatrick Williams auto a = std::make_unique<AssociationObject>(
2911bb06235SPatrick Williams _bus, forwardPath.c_str(), AssociationObject::action::defer_emit);
292c47ca585SMatt Spinler
293c47ca585SMatt Spinler using AssociationProperty =
294c47ca585SMatt Spinler std::vector<std::tuple<std::string, std::string, std::string>>;
295c47ca585SMatt Spinler AssociationProperty prop;
296c47ca585SMatt Spinler
297c47ca585SMatt Spinler prop.emplace_back(forwardType, reverseType, reversePath);
298c47ca585SMatt Spinler a->associations(std::move(prop));
299104ccba9SBrad Bishop if (!deferSignal)
300104ccba9SBrad Bishop {
301c47ca585SMatt Spinler a->emit_object_added();
302104ccba9SBrad Bishop }
303c47ca585SMatt Spinler _associationIfaces.emplace(forwardPath, std::move(a));
304c47ca585SMatt Spinler }
305c47ca585SMatt Spinler else
306c47ca585SMatt Spinler {
307c47ca585SMatt Spinler // Interface exists, just update the property
308c47ca585SMatt Spinler auto prop = object->second->associations();
309c47ca585SMatt Spinler prop.emplace_back(forwardType, reverseType, reversePath);
310104ccba9SBrad Bishop object->second->associations(std::move(prop), deferSignal);
311c47ca585SMatt Spinler }
312852db67bSMatt Spinler }
313852db67bSMatt Spinler } // namespace associations
314852db67bSMatt Spinler } // namespace manager
315852db67bSMatt Spinler } // namespace inventory
316852db67bSMatt Spinler } // namespace phosphor
317