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