|  | #include "trigger_manager.hpp" | 
|  |  | 
|  | #include "trigger.hpp" | 
|  | #include "types/trigger_types.hpp" | 
|  | #include "utils/conversion_trigger.hpp" | 
|  | #include "utils/transform.hpp" | 
|  |  | 
|  | #include <phosphor-logging/log.hpp> | 
|  |  | 
|  | TriggerManager::TriggerManager( | 
|  | std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn, | 
|  | std::unique_ptr<interfaces::JsonStorage> triggerStorageIn, | 
|  | const std::shared_ptr<sdbusplus::asio::object_server>& objServer) : | 
|  | triggerFactory(std::move(triggerFactoryIn)), | 
|  | triggerStorage(std::move(triggerStorageIn)) | 
|  | { | 
|  | loadFromPersistent(); | 
|  |  | 
|  | managerIface = objServer->add_unique_interface( | 
|  | triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) { | 
|  | iface.register_method( | 
|  | "AddTrigger", | 
|  | [this](boost::asio::yield_context& yield, const std::string& id, | 
|  | const std::string& name, | 
|  | const std::vector<std::string>& triggerActions, | 
|  | const SensorsInfo& sensors, | 
|  | const std::vector<std::string>& reportNames, | 
|  | const TriggerThresholdParamsExt& thresholds) { | 
|  | LabeledTriggerThresholdParams | 
|  | labeledTriggerThresholdParams = std::visit( | 
|  | utils::ToLabeledThresholdParamConversion(), | 
|  | thresholds); | 
|  |  | 
|  | std::vector<LabeledSensorInfo> labeledSensorsInfo = | 
|  | triggerFactory->getLabeledSensorsInfo(yield, sensors); | 
|  |  | 
|  | return addTrigger(id, name, triggerActions, | 
|  | labeledSensorsInfo, reportNames, | 
|  | labeledTriggerThresholdParams) | 
|  | .getPath(); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void TriggerManager::removeTrigger(const interfaces::Trigger* trigger) | 
|  | { | 
|  | triggers.erase( | 
|  | std::remove_if(triggers.begin(), triggers.end(), | 
|  | [trigger](const auto& x) { return trigger == x.get(); }), | 
|  | triggers.end()); | 
|  | } | 
|  |  | 
|  | void TriggerManager::verifyAddTrigger(const std::string& triggerId, | 
|  | const std::string& triggerName) const | 
|  | { | 
|  | if (triggers.size() >= maxTriggers) | 
|  | { | 
|  | throw sdbusplus::exception::SdBusError( | 
|  | static_cast<int>(std::errc::too_many_files_open), | 
|  | "Reached maximal trigger count"); | 
|  | } | 
|  |  | 
|  | verifyTriggerIdLength(triggerId); | 
|  | verifyIdCharacters(triggerId); | 
|  |  | 
|  | for (const auto& trigger : triggers) | 
|  | { | 
|  | if (trigger->getId() == triggerId) | 
|  | { | 
|  | throw sdbusplus::exception::SdBusError( | 
|  | static_cast<int>(std::errc::file_exists), "Duplicate trigger"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void TriggerManager::verifyTriggerIdLength(const std::string& triggerId) | 
|  | { | 
|  | if (triggerId.length() > maxTriggerIdLength) | 
|  | { | 
|  | throw sdbusplus::exception::SdBusError( | 
|  | static_cast<int>(std::errc::invalid_argument), | 
|  | "Trigger id exceeds maximum length"); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TriggerManager::verifyIdCharacters(const std::string& triggerId) | 
|  | { | 
|  | if (triggerId.find_first_not_of(allowedCharactersInId) != std::string::npos) | 
|  | { | 
|  | throw sdbusplus::exception::SdBusError( | 
|  | static_cast<int>(std::errc::invalid_argument), | 
|  | "Invalid character in trigger id"); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string TriggerManager::generateId(const std::string& prefix, | 
|  | const std::string& triggerName) const | 
|  | { | 
|  | verifyIdCharacters(prefix); | 
|  | std::string strippedId(triggerName); | 
|  | strippedId.erase(std::remove_if(strippedId.begin(), strippedId.end(), | 
|  | [](char c) { | 
|  | return c == '/' || | 
|  | allowedCharactersInId.find(c) == | 
|  | std::string_view::npos; | 
|  | }), | 
|  | strippedId.end()); | 
|  |  | 
|  | strippedId = prefix + strippedId; | 
|  | strippedId = strippedId.substr( | 
|  | 0, maxTriggerIdLength - std::to_string(maxTriggers - 1).length()); | 
|  |  | 
|  | size_t idx = 0; | 
|  | std::string tmpId(strippedId); | 
|  | while (std::find_if(triggers.begin(), triggers.end(), | 
|  | [&tmpId](const auto& trigger) { | 
|  | return trigger->getId() == tmpId; | 
|  | }) != triggers.end()) | 
|  | { | 
|  | tmpId = strippedId + std::to_string(idx++); | 
|  | } | 
|  | return tmpId; | 
|  | } | 
|  |  | 
|  | interfaces::Trigger& TriggerManager::addTrigger( | 
|  | const std::string& triggerIdIn, const std::string& triggerNameIn, | 
|  | const std::vector<std::string>& triggerActions, | 
|  | const std::vector<LabeledSensorInfo>& labeledSensorsInfo, | 
|  | const std::vector<std::string>& reportNames, | 
|  | const LabeledTriggerThresholdParams& labeledThresholdParams) | 
|  | { | 
|  | std::string triggerName = triggerNameIn; | 
|  | if (triggerName.empty()) | 
|  | { | 
|  | triggerName = triggerNameDefault; | 
|  | } | 
|  |  | 
|  | std::string triggerId = triggerIdIn; | 
|  | if (triggerId.empty() || triggerId.ends_with('/')) | 
|  | { | 
|  | triggerId = generateId(triggerId, triggerName); | 
|  | } | 
|  |  | 
|  | verifyAddTrigger(triggerId, triggerName); | 
|  |  | 
|  | triggers.emplace_back(triggerFactory->make( | 
|  | triggerId, triggerName, triggerActions, reportNames, *this, | 
|  | *triggerStorage, labeledThresholdParams, labeledSensorsInfo)); | 
|  |  | 
|  | return *triggers.back(); | 
|  | } | 
|  |  | 
|  | void TriggerManager::loadFromPersistent() | 
|  | { | 
|  | std::vector<interfaces::JsonStorage::FilePath> paths = | 
|  | triggerStorage->list(); | 
|  |  | 
|  | for (const auto& path : paths) | 
|  | { | 
|  | std::optional<nlohmann::json> data = triggerStorage->load(path); | 
|  | try | 
|  | { | 
|  | if (!data.has_value()) | 
|  | { | 
|  | throw std::runtime_error("Empty storage"); | 
|  | } | 
|  | size_t version = data->at("Version").get<size_t>(); | 
|  | if (version != Trigger::triggerVersion) | 
|  | { | 
|  | throw std::runtime_error("Invalid version"); | 
|  | } | 
|  | const std::string& id = data->at("Id").get_ref<std::string&>(); | 
|  | const std::string& name = data->at("Name").get_ref<std::string&>(); | 
|  | int thresholdParamsDiscriminator = | 
|  | data->at("ThresholdParamsDiscriminator").get<int>(); | 
|  | const std::vector<std::string> triggerActions = | 
|  | data->at("TriggerActions").get<std::vector<std::string>>(); | 
|  |  | 
|  | LabeledTriggerThresholdParams labeledThresholdParams; | 
|  | if (0 == thresholdParamsDiscriminator) | 
|  | { | 
|  | labeledThresholdParams = | 
|  | data->at("ThresholdParams") | 
|  | .get<std::vector<numeric::LabeledThresholdParam>>(); | 
|  | } | 
|  | else | 
|  | { | 
|  | labeledThresholdParams = | 
|  | data->at("ThresholdParams") | 
|  | .get<std::vector<discrete::LabeledThresholdParam>>(); | 
|  | } | 
|  |  | 
|  | auto reportNames = | 
|  | data->at("ReportNames").get<std::vector<std::string>>(); | 
|  |  | 
|  | auto labeledSensorsInfo = | 
|  | data->at("Sensors").get<std::vector<LabeledSensorInfo>>(); | 
|  |  | 
|  | addTrigger(id, name, triggerActions, labeledSensorsInfo, | 
|  | reportNames, labeledThresholdParams); | 
|  | } | 
|  | catch (const std::exception& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "Failed to load trigger from storage", | 
|  | phosphor::logging::entry( | 
|  | "FILENAME=%s", | 
|  | static_cast<std::filesystem::path>(path).c_str()), | 
|  | phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); | 
|  | triggerStorage->remove(path); | 
|  | } | 
|  | } | 
|  | } |