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