1 #include "trigger_manager.hpp" 2 3 #include "trigger.hpp" 4 #include "types/trigger_types.hpp" 5 #include "utils/conversion_trigger.hpp" 6 #include "utils/transform.hpp" 7 8 #include <phosphor-logging/log.hpp> 9 10 TriggerManager::TriggerManager( 11 std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn, 12 std::unique_ptr<interfaces::JsonStorage> triggerStorageIn, 13 const std::shared_ptr<sdbusplus::asio::object_server>& objServer) : 14 triggerFactory(std::move(triggerFactoryIn)), 15 triggerStorage(std::move(triggerStorageIn)) 16 { 17 loadFromPersistent(); 18 19 managerIface = objServer->add_unique_interface( 20 triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) { 21 iface.register_method( 22 "AddTrigger", 23 [this](boost::asio::yield_context& yield, const std::string& id, 24 const std::string& name, 25 const std::vector<std::string>& triggerActions, 26 const SensorsInfo& sensors, 27 const std::vector<std::string>& reportNames, 28 const TriggerThresholdParamsExt& thresholds) { 29 LabeledTriggerThresholdParams 30 labeledTriggerThresholdParams = std::visit( 31 utils::ToLabeledThresholdParamConversion(), 32 thresholds); 33 34 std::vector<LabeledSensorInfo> labeledSensorsInfo = 35 triggerFactory->getLabeledSensorsInfo(yield, sensors); 36 37 return addTrigger(id, name, triggerActions, 38 labeledSensorsInfo, reportNames, 39 labeledTriggerThresholdParams) 40 .getPath(); 41 }); 42 }); 43 } 44 45 void TriggerManager::removeTrigger(const interfaces::Trigger* trigger) 46 { 47 triggers.erase( 48 std::remove_if(triggers.begin(), triggers.end(), 49 [trigger](const auto& x) { return trigger == x.get(); }), 50 triggers.end()); 51 } 52 53 void TriggerManager::verifyAddTrigger(const std::string& triggerId, 54 const std::string& triggerName) const 55 { 56 if (triggers.size() >= maxTriggers) 57 { 58 throw sdbusplus::exception::SdBusError( 59 static_cast<int>(std::errc::too_many_files_open), 60 "Reached maximal trigger count"); 61 } 62 63 verifyTriggerIdLength(triggerId); 64 verifyIdCharacters(triggerId); 65 66 for (const auto& trigger : triggers) 67 { 68 if (trigger->getId() == triggerId) 69 { 70 throw sdbusplus::exception::SdBusError( 71 static_cast<int>(std::errc::file_exists), "Duplicate trigger"); 72 } 73 } 74 } 75 76 void TriggerManager::verifyTriggerIdLength(const std::string& triggerId) 77 { 78 if (triggerId.length() > maxTriggerIdLength) 79 { 80 throw sdbusplus::exception::SdBusError( 81 static_cast<int>(std::errc::invalid_argument), 82 "Trigger id exceeds maximum length"); 83 } 84 } 85 86 void TriggerManager::verifyIdCharacters(const std::string& triggerId) 87 { 88 if (triggerId.find_first_not_of(allowedCharactersInId) != std::string::npos) 89 { 90 throw sdbusplus::exception::SdBusError( 91 static_cast<int>(std::errc::invalid_argument), 92 "Invalid character in trigger id"); 93 } 94 } 95 96 std::string TriggerManager::generateId(const std::string& prefix, 97 const std::string& triggerName) const 98 { 99 verifyIdCharacters(prefix); 100 std::string strippedId(triggerName); 101 strippedId.erase(std::remove_if(strippedId.begin(), strippedId.end(), 102 [](char c) { 103 return c == '/' || 104 allowedCharactersInId.find(c) == 105 std::string_view::npos; 106 }), 107 strippedId.end()); 108 109 strippedId = prefix + strippedId; 110 strippedId = strippedId.substr( 111 0, maxTriggerIdLength - std::to_string(maxTriggers - 1).length()); 112 113 size_t idx = 0; 114 std::string tmpId(strippedId); 115 while (std::find_if(triggers.begin(), triggers.end(), 116 [&tmpId](const auto& trigger) { 117 return trigger->getId() == tmpId; 118 }) != triggers.end()) 119 { 120 tmpId = strippedId + std::to_string(idx++); 121 } 122 return tmpId; 123 } 124 125 interfaces::Trigger& TriggerManager::addTrigger( 126 const std::string& triggerIdIn, const std::string& triggerNameIn, 127 const std::vector<std::string>& triggerActions, 128 const std::vector<LabeledSensorInfo>& labeledSensorsInfo, 129 const std::vector<std::string>& reportNames, 130 const LabeledTriggerThresholdParams& labeledThresholdParams) 131 { 132 std::string triggerName = triggerNameIn; 133 if (triggerName.empty()) 134 { 135 triggerName = triggerNameDefault; 136 } 137 138 std::string triggerId = triggerIdIn; 139 if (triggerId.empty() || triggerId.ends_with('/')) 140 { 141 triggerId = generateId(triggerId, triggerName); 142 } 143 144 verifyAddTrigger(triggerId, triggerName); 145 146 triggers.emplace_back(triggerFactory->make( 147 triggerId, triggerName, triggerActions, reportNames, *this, 148 *triggerStorage, labeledThresholdParams, labeledSensorsInfo)); 149 150 return *triggers.back(); 151 } 152 153 void TriggerManager::loadFromPersistent() 154 { 155 std::vector<interfaces::JsonStorage::FilePath> paths = 156 triggerStorage->list(); 157 158 for (const auto& path : paths) 159 { 160 std::optional<nlohmann::json> data = triggerStorage->load(path); 161 try 162 { 163 if (!data.has_value()) 164 { 165 throw std::runtime_error("Empty storage"); 166 } 167 size_t version = data->at("Version").get<size_t>(); 168 if (version != Trigger::triggerVersion) 169 { 170 throw std::runtime_error("Invalid version"); 171 } 172 const std::string& id = data->at("Id").get_ref<std::string&>(); 173 const std::string& name = data->at("Name").get_ref<std::string&>(); 174 int thresholdParamsDiscriminator = 175 data->at("ThresholdParamsDiscriminator").get<int>(); 176 const std::vector<std::string> triggerActions = 177 data->at("TriggerActions").get<std::vector<std::string>>(); 178 179 LabeledTriggerThresholdParams labeledThresholdParams; 180 if (0 == thresholdParamsDiscriminator) 181 { 182 labeledThresholdParams = 183 data->at("ThresholdParams") 184 .get<std::vector<numeric::LabeledThresholdParam>>(); 185 } 186 else 187 { 188 labeledThresholdParams = 189 data->at("ThresholdParams") 190 .get<std::vector<discrete::LabeledThresholdParam>>(); 191 } 192 193 auto reportNames = 194 data->at("ReportNames").get<std::vector<std::string>>(); 195 196 auto labeledSensorsInfo = 197 data->at("Sensors").get<std::vector<LabeledSensorInfo>>(); 198 199 addTrigger(id, name, triggerActions, labeledSensorsInfo, 200 reportNames, labeledThresholdParams); 201 } 202 catch (const std::exception& e) 203 { 204 phosphor::logging::log<phosphor::logging::level::ERR>( 205 "Failed to load trigger from storage", 206 phosphor::logging::entry( 207 "FILENAME=%s", 208 static_cast<std::filesystem::path>(path).c_str()), 209 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what())); 210 triggerStorage->remove(path); 211 } 212 } 213 } 214