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( 40 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> getAdditionalDataItem( 65 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( 95 const DbusPropertyMap& properties) 96 { 97 // The modifier may be one of several things within the 98 // AdditionalData property. Try them all until one 99 // is found. 100 101 auto data = 102 getProperty<std::vector<std::string>>(properties, "AdditionalData"); 103 104 if (!data) 105 { 106 return std::string{}; 107 } 108 109 // AdditionalData fields where the value is the modifier 110 static const std::vector<std::string> ADFields{"CALLOUT_INVENTORY_PATH", 111 "RAIL_NAME", "INPUT_NAME"}; 112 113 optional_ns::optional<std::string> mod; 114 for (const auto& field : ADFields) 115 { 116 mod = getAdditionalDataItem(*data, field); 117 if (mod && !(*mod).empty()) 118 { 119 return *mod; 120 } 121 } 122 123 // Next are the AdditionalData fields where the value needs 124 // to be massaged to get the modifier. 125 126 // A device path, but we only care about the type 127 mod = getAdditionalDataItem(*data, "CALLOUT_DEVICE_PATH"); 128 if (mod) 129 { 130 // The table only handles I2C and FSI 131 if ((*mod).find("i2c") != std::string::npos) 132 { 133 return std::string{"I2C"}; 134 } 135 else if ((*mod).find("fsi") != std::string::npos) 136 { 137 return std::string{"FSI"}; 138 } 139 } 140 141 // A hostboot procedure ID 142 mod = getAdditionalDataItem(*data, "PROCEDURE"); 143 if (mod) 144 { 145 // Convert decimal (e.g. 109) to hex (e.g. 6D) 146 std::ostringstream stream; 147 try 148 { 149 stream << std::hex << std::stoul((*mod).c_str()); 150 auto value = stream.str(); 151 152 if (!value.empty()) 153 { 154 std::transform( 155 value.begin(), value.end(), value.begin(), toupper); 156 return value; 157 } 158 } 159 catch (std::exception& e) 160 { 161 using namespace phosphor::logging; 162 log<level::ERR>("Invalid PROCEDURE value found", 163 entry("PROCEDURE=%s", *mod)); 164 } 165 } 166 167 return std::string{}; 168 } 169 170 PolicyProps find( 171 const policy::Table& policy, 172 const DbusPropertyMap& errorLogProperties) 173 { 174 auto errorMsg = getProperty<std::string>( 175 errorLogProperties, "Message"); //e.g. xyz.X.Error.Y 176 if (errorMsg) 177 { 178 auto modifier = getSearchModifier(errorLogProperties); 179 180 auto result = policy.find(*errorMsg, modifier); 181 182 if (result) 183 { 184 return {(*result).get().ceid, (*result).get().msg}; 185 } 186 } 187 else 188 { 189 using namespace phosphor::logging; 190 log<level::ERR>("No Message metadata found in an error"); 191 } 192 193 return {policy.defaultEID(), policy.defaultMsg()}; 194 } 195 196 } 197 } 198 } 199