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