1 #include "utils.hpp" 2 3 #include "../utils.hpp" 4 #include "../variant_visitors.hpp" 5 #include "expression.hpp" 6 #include "phosphor-logging/lg2.hpp" 7 8 #include <boost/algorithm/string/case_conv.hpp> 9 #include <boost/algorithm/string/classification.hpp> 10 #include <boost/algorithm/string/replace.hpp> 11 #include <boost/algorithm/string/split.hpp> 12 #include <phosphor-logging/lg2.hpp> 13 #include <sdbusplus/bus/match.hpp> 14 15 #include <fstream> 16 #include <regex> 17 18 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]"); 19 20 namespace em_utils 21 { 22 23 constexpr const char* templateChar = "$"; 24 25 bool fwVersionIsSame() 26 { 27 std::ifstream version(versionFile); 28 if (!version.good()) 29 { 30 lg2::error("Can't read {PATH}", "PATH", versionFile); 31 return false; 32 } 33 34 std::string versionData; 35 std::string line; 36 while (std::getline(version, line)) 37 { 38 versionData += line; 39 } 40 41 std::string expectedHash = 42 std::to_string(std::hash<std::string>{}(versionData)); 43 44 std::error_code ec; 45 std::filesystem::create_directory(configurationOutDir, ec); 46 47 if (ec) 48 { 49 lg2::error("could not create directory {DIR}", "DIR", 50 configurationOutDir); 51 return false; 52 } 53 54 std::ifstream hashFile(versionHashFile); 55 if (hashFile.good()) 56 { 57 std::string hashString; 58 hashFile >> hashString; 59 60 if (expectedHash == hashString) 61 { 62 return true; 63 } 64 hashFile.close(); 65 } 66 67 std::ofstream output(versionHashFile); 68 output << expectedHash; 69 return false; 70 } 71 72 void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair) 73 { 74 if (keyPair.value().type() == nlohmann::json::value_t::object || 75 keyPair.value().type() == nlohmann::json::value_t::array) 76 { 77 for (auto nextLayer = keyPair.value().begin(); 78 nextLayer != keyPair.value().end(); nextLayer++) 79 { 80 handleLeftOverTemplateVars(nextLayer); 81 } 82 return; 83 } 84 85 std::string* strPtr = keyPair.value().get_ptr<std::string*>(); 86 if (strPtr == nullptr) 87 { 88 return; 89 } 90 91 // Walking through the string to find $<templateVar> 92 while (true) 93 { 94 auto [firstIndex, lastIndex] = iFindFirst(*strPtr, templateChar); 95 if (firstIndex == std::string_view::npos) 96 { 97 break; 98 } 99 100 size_t templateVarEndIndex = 0; 101 auto [firstSpaceIndex, _] = iFindFirst(strPtr->substr(lastIndex), " "); 102 if (firstSpaceIndex == std::string_view::npos) 103 { 104 // No space means the template var spans to the end of 105 // of the keyPair value 106 templateVarEndIndex = strPtr->size(); 107 } 108 else 109 { 110 // A space marks the end of a template var 111 templateVarEndIndex = lastIndex + firstSpaceIndex; 112 } 113 114 lg2::error( 115 "There's still template variable {VAR} un-replaced. Removing it from the string.\n", 116 "VAR", 117 strPtr->substr(firstIndex, templateVarEndIndex - firstIndex)); 118 strPtr->erase(firstIndex, templateVarEndIndex - firstIndex); 119 } 120 } 121 122 // Replaces the template character like the other version of this function, 123 // but checks all properties on all interfaces provided to do the substitution 124 // with. 125 std::optional<std::string> templateCharReplace( 126 nlohmann::json::iterator& keyPair, const DBusObject& object, 127 const size_t index, const std::optional<std::string>& replaceStr) 128 { 129 for (const auto& [_, interface] : object) 130 { 131 auto ret = templateCharReplace(keyPair, interface, index, replaceStr); 132 if (ret) 133 { 134 handleLeftOverTemplateVars(keyPair); 135 return ret; 136 } 137 } 138 handleLeftOverTemplateVars(keyPair); 139 return std::nullopt; 140 } 141 142 // finds the template character (currently set to $) and replaces the value with 143 // the field found in a dbus object i.e. $ADDRESS would get populated with the 144 // ADDRESS field from a object on dbus 145 std::optional<std::string> templateCharReplace( 146 nlohmann::json::iterator& keyPair, const DBusInterface& interface, 147 const size_t index, const std::optional<std::string>& replaceStr) 148 { 149 std::optional<std::string> ret = std::nullopt; 150 151 if (keyPair.value().type() == nlohmann::json::value_t::object || 152 keyPair.value().type() == nlohmann::json::value_t::array) 153 { 154 for (auto nextLayer = keyPair.value().begin(); 155 nextLayer != keyPair.value().end(); nextLayer++) 156 { 157 templateCharReplace(nextLayer, interface, index, replaceStr); 158 } 159 return ret; 160 } 161 162 std::string* strPtr = keyPair.value().get_ptr<std::string*>(); 163 if (strPtr == nullptr) 164 { 165 return ret; 166 } 167 168 boost::replace_all(*strPtr, std::string(templateChar) + "index", 169 std::to_string(index)); 170 if (replaceStr) 171 { 172 boost::replace_all(*strPtr, *replaceStr, std::to_string(index)); 173 } 174 175 for (const auto& [propName, propValue] : interface) 176 { 177 std::string templateName = templateChar + propName; 178 auto [start, endIdx] = iFindFirst(*strPtr, templateName); 179 if (start == std::string::npos) 180 { 181 continue; 182 } 183 184 // check for additional operations 185 if ((start == 0U) && endIdx == strPtr->size()) 186 { 187 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue); 188 return ret; 189 } 190 191 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*', 192 '/'}; 193 size_t nextItemIdx = start + templateName.size() + 1; 194 195 if (nextItemIdx > strPtr->size() || 196 std::find(mathChars.begin(), mathChars.end(), 197 strPtr->at(nextItemIdx)) == mathChars.end()) 198 { 199 std::string val = std::visit(VariantToStringVisitor(), propValue); 200 boost::ireplace_all(*strPtr, templateName, val); 201 continue; 202 } 203 204 // save the prefix 205 std::string prefix = strPtr->substr(0, start); 206 207 // operate on the rest 208 std::string end = strPtr->substr(nextItemIdx); 209 210 std::vector<std::string> split; 211 boost::split(split, end, boost::is_any_of(" ")); 212 213 // need at least 1 operation and number 214 if (split.size() < 2) 215 { 216 lg2::error("Syntax error on template replacement of {STR}", "STR", 217 *strPtr); 218 for (const std::string& data : split) 219 { 220 lg2::error("{SPLIT} ", "SPLIT", data); 221 } 222 lg2::error(""); 223 continue; 224 } 225 226 // we assume that the replacement is a number, because we can 227 // only do math on numbers.. we might concatenate strings in the 228 // future, but thats later 229 int number = std::visit(VariantToIntVisitor(), propValue); 230 auto exprBegin = split.begin(); 231 auto exprEnd = split.end(); 232 233 number = expression::evaluate(number, exprBegin, exprEnd); 234 235 std::string replaced(strPtr->begin() + start, strPtr->begin() + endIdx); 236 while (exprBegin != exprEnd) 237 { 238 replaced.append(" ").append(*exprBegin++); 239 } 240 ret = replaced; 241 242 std::string result = prefix + std::to_string(number); 243 while (exprEnd != split.end()) 244 { 245 result.append(" ").append(*exprEnd++); 246 } 247 keyPair.value() = result; 248 249 // We probably just invalidated the pointer abovei, 250 // reset and continue to handle multiple templates 251 strPtr = keyPair.value().get_ptr<std::string*>(); 252 if (strPtr == nullptr) 253 { 254 break; 255 } 256 } 257 258 strPtr = keyPair.value().get_ptr<std::string*>(); 259 if (strPtr == nullptr) 260 { 261 return ret; 262 } 263 264 std::string_view strView = *strPtr; 265 int base = 10; 266 if (strView.starts_with("0x")) 267 { 268 strView.remove_prefix(2); 269 base = 16; 270 } 271 272 uint64_t temp = 0; 273 bool fullMatch = false; 274 const std::from_chars_result res = 275 fromCharsWrapper(strView, temp, fullMatch, base); 276 if (res.ec == std::errc{} && fullMatch) 277 { 278 keyPair.value() = temp; 279 } 280 281 return ret; 282 } 283 284 std::string buildInventorySystemPath(std::string& boardName, 285 const std::string& boardType) 286 { 287 std::string path = "/xyz/openbmc_project/inventory/system/"; 288 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType); 289 290 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(), 291 illegalDbusMemberRegex, "_"); 292 293 return std::format("{}{}/{}", path, boardTypeLower, boardName); 294 } 295 } // namespace em_utils 296