1 /** 2 * Copyright © 2018 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "policy_find.hpp" 17 18 #include <phosphor-logging/log.hpp> 19 #include <sstream> 20 21 namespace ibm 22 { 23 namespace logging 24 { 25 namespace policy 26 { 27 28 namespace optional_ns = std::experimental; 29 30 /** 31 * Returns a property value from a map of properties. 32 * 33 * @tparam - T the property data type 34 * @param[in] properties - the property map 35 * @param[in] name - the property name 36 * 37 * @return optional<T> - the property value 38 */ 39 template <typename T> 40 optional_ns::optional<T> getProperty(const DbusPropertyMap& properties, 41 const std::string& name) 42 { 43 auto prop = properties.find(name); 44 45 if (prop != properties.end()) 46 { 47 return prop->second.template get<T>(); 48 } 49 50 return {}; 51 } 52 53 /** 54 * Finds a value in the AdditionalData property, which is 55 * an array of strings in the form of: 56 * 57 * NAME=VALUE 58 * 59 * @param[in] additionalData - the AdditionalData property contents 60 * @param[in] name - the name of the value to find 61 * 62 * @return optional<std::string> - the data value 63 */ 64 optional_ns::optional<std::string> 65 getAdditionalDataItem(const std::vector<std::string>& additionalData, 66 const std::string& name) 67 { 68 for (const auto& item : additionalData) 69 { 70 if (item.find(name + "=") != std::string::npos) 71 { 72 return item.substr(item.find('=') + 1); 73 } 74 } 75 76 return {}; 77 } 78 79 /** 80 * Returns the search modifier to use. 81 * 82 * The modifier is used when the error name itself isn't granular 83 * enough to find a policy table entry. The modifier is determined 84 * using rules provided by the IBM service team. 85 * 86 * Not all errors need a modifier, so this function isn't 87 * guaranteed to find one. 88 * 89 * @param[in] properties - the property map for the error 90 * 91 * @return string - the search modifier 92 * may be empty if none found 93 */ 94 auto getSearchModifier(const DbusPropertyMap& properties) 95 { 96 // The modifier may be one of several things within the 97 // AdditionalData property. Try them all until one 98 // is found. 99 100 auto data = 101 getProperty<std::vector<std::string>>(properties, "AdditionalData"); 102 103 if (!data) 104 { 105 return std::string{}; 106 } 107 108 // AdditionalData fields where the value is the modifier 109 static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH", 110 "RAIL_NAME", "INPUT_NAME"}; 111 112 optional_ns::optional<std::string> mod; 113 for (const auto& field : ADFields) 114 { 115 mod = getAdditionalDataItem(*data, field); 116 if (mod && !(*mod).empty()) 117 { 118 return *mod; 119 } 120 } 121 122 // Next are the AdditionalData fields where the value needs 123 // to be massaged to get the modifier. 124 125 // A device path, but we only care about the type 126 mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH"); 127 if (mod) 128 { 129 // The table only handles I2C and FSI 130 if ((*mod).find("i2c") != std::string::npos) 131 { 132 return std::string{"I2C"}; 133 } 134 else if ((*mod).find("fsi") != std::string::npos) 135 { 136 return std::string{"FSI"}; 137 } 138 } 139 140 // A hostboot procedure ID 141 mod = getAdditionalDataItem(*data, "PROCEDURE"); 142 if (mod) 143 { 144 // Convert decimal (e.g. 109) to hex (e.g. 6D) 145 std::ostringstream stream; 146 try 147 { 148 stream << std::hex << std::stoul((*mod).c_str()); 149 auto value = stream.str(); 150 151 if (!value.empty()) 152 { 153 std::transform(value.begin(), value.end(), value.begin(), 154 toupper); 155 return value; 156 } 157 } 158 catch (std::exception& e) 159 { 160 using namespace phosphor::logging; 161 log<level::ERR>("Invalid PROCEDURE value found", 162 entry("PROCEDURE=%s", mod->c_str())); 163 } 164 } 165 166 return std::string{}; 167 } 168 169 PolicyProps find(const policy::Table& policy, 170 const DbusPropertyMap& errorLogProperties) 171 { 172 auto errorMsg = getProperty<std::string>(errorLogProperties, 173 "Message"); // e.g. xyz.X.Error.Y 174 if (errorMsg) 175 { 176 auto modifier = getSearchModifier(errorLogProperties); 177 178 auto result = policy.find(*errorMsg, modifier); 179 180 if (result) 181 { 182 return {(*result).get().ceid, (*result).get().msg}; 183 } 184 } 185 else 186 { 187 using namespace phosphor::logging; 188 log<level::ERR>("No Message metadata found in an error"); 189 } 190 191 return {policy.defaultEID(), policy.defaultMsg()}; 192 } 193 } // namespace policy 194 } // namespace logging 195 } // namespace ibm 196