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 for (auto& nextLayer : *objPtr) 74 { 75 handleLeftOverTemplateVars(nextLayer.second); 76 } 77 return; 78 } 79 80 nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>(); 81 if (arrPtr != nullptr) 82 { 83 for (auto& nextLayer : *arrPtr) 84 { 85 handleLeftOverTemplateVars(nextLayer); 86 } 87 return; 88 } 89 90 std::string* strPtr = value.get_ptr<std::string*>(); 91 if (strPtr == nullptr) 92 { 93 return; 94 } 95 96 // Walking through the string to find $<templateVar> 97 while (true) 98 { 99 std::ranges::subrange<std::string::const_iterator> findStart = 100 iFindFirst(*strPtr, std::string_view(templateChar)); 101 102 if (!findStart) 103 { 104 break; 105 } 106 107 std::ranges::subrange<std::string::iterator> searchRange( 108 strPtr->begin() + (findStart.end() - strPtr->begin()), 109 strPtr->end()); 110 std::ranges::subrange<std::string::const_iterator> findSpace = 111 iFindFirst(searchRange, " "); 112 113 std::string::const_iterator templateVarEnd; 114 115 if (!findSpace) 116 { 117 // No space means the template var spans to the end of 118 // of the keyPair value 119 templateVarEnd = strPtr->end(); 120 } 121 else 122 { 123 // A space marks the end of a template var 124 templateVarEnd = findSpace.begin(); 125 } 126 127 lg2::error( 128 "There's still template variable {VAR} un-replaced. Removing it from the string.\n", 129 "VAR", std::string(findStart.begin(), templateVarEnd)); 130 strPtr->erase(findStart.begin(), templateVarEnd); 131 } 132 } 133 134 // Replaces the template character like the other version of this function, 135 // but checks all properties on all interfaces provided to do the substitution 136 // with. 137 std::optional<std::string> templateCharReplace( 138 nlohmann::json& value, const DBusObject& object, const size_t index, 139 const std::optional<std::string>& replaceStr, bool handleLeftOver) 140 { 141 for (const auto& [_, interface] : object) 142 { 143 auto ret = templateCharReplace(value, interface, index, replaceStr); 144 if (ret) 145 { 146 if (handleLeftOver) 147 { 148 handleLeftOverTemplateVars(value); 149 } 150 return ret; 151 } 152 } 153 if (handleLeftOver) 154 { 155 handleLeftOverTemplateVars(value); 156 } 157 return std::nullopt; 158 } 159 160 // finds the template character (currently set to $) and replaces the value with 161 // the field found in a dbus object i.e. $ADDRESS would get populated with the 162 // ADDRESS field from a object on dbus 163 std::optional<std::string> templateCharReplace( 164 nlohmann::json& value, const DBusInterface& interface, const size_t index, 165 const std::optional<std::string>& replaceStr) 166 { 167 std::optional<std::string> ret = std::nullopt; 168 169 nlohmann::json::object_t* objPtr = 170 value.get_ptr<nlohmann::json::object_t*>(); 171 if (objPtr != nullptr) 172 { 173 for (auto& [key, value] : *objPtr) 174 { 175 templateCharReplace(value, interface, index, replaceStr); 176 } 177 return ret; 178 } 179 180 nlohmann::json::array_t* arrPtr = value.get_ptr<nlohmann::json::array_t*>(); 181 if (arrPtr != nullptr) 182 { 183 for (auto& value : *arrPtr) 184 { 185 templateCharReplace(value, interface, index, replaceStr); 186 } 187 return ret; 188 } 189 190 std::string* strPtr = value.get_ptr<std::string*>(); 191 if (strPtr == nullptr) 192 { 193 return ret; 194 } 195 196 replaceAll(*strPtr, std::string(templateChar) + "index", 197 std::to_string(index)); 198 if (replaceStr) 199 { 200 replaceAll(*strPtr, *replaceStr, std::to_string(index)); 201 } 202 203 for (const auto& [propName, propValue] : interface) 204 { 205 std::string templateName = templateChar + propName; 206 std::ranges::subrange<std::string::const_iterator> find = 207 iFindFirst(*strPtr, templateName); 208 if (!find) 209 { 210 continue; 211 } 212 213 size_t start = find.begin() - strPtr->begin(); 214 215 // check for additional operations 216 if ((start == 0U) && find.end() == strPtr->end()) 217 { 218 std::visit([&](auto&& val) { value = val; }, propValue); 219 return ret; 220 } 221 222 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*', 223 '/'}; 224 size_t nextItemIdx = start + templateName.size() + 1; 225 226 if (nextItemIdx > strPtr->size() || 227 std::find(mathChars.begin(), mathChars.end(), 228 strPtr->at(nextItemIdx)) == mathChars.end()) 229 { 230 std::string val = std::visit(VariantToStringVisitor(), propValue); 231 iReplaceAll(*strPtr, templateName, val); 232 continue; 233 } 234 235 // save the prefix 236 std::string prefix = strPtr->substr(0, start); 237 238 // operate on the rest 239 std::string end = strPtr->substr(nextItemIdx); 240 241 std::vector<std::string> splitResult = split(end, ' '); 242 243 // need at least 1 operation and number 244 if (splitResult.size() < 2) 245 { 246 lg2::error("Syntax error on template replacement of {STR}", "STR", 247 *strPtr); 248 for (const std::string& data : splitResult) 249 { 250 lg2::error("{SPLIT} ", "SPLIT", data); 251 } 252 lg2::error(""); 253 continue; 254 } 255 256 // we assume that the replacement is a number, because we can 257 // only do math on numbers.. we might concatenate strings in the 258 // future, but thats later 259 int number = std::visit(VariantToIntVisitor(), propValue); 260 auto exprBegin = splitResult.begin(); 261 auto exprEnd = splitResult.end(); 262 263 number = expression::evaluate(number, exprBegin, exprEnd); 264 265 std::string replaced(find.begin(), find.end()); 266 while (exprBegin != exprEnd) 267 { 268 replaced.append(" ").append(*exprBegin++); 269 } 270 ret = replaced; 271 272 std::string result = prefix + std::to_string(number); 273 while (exprEnd != splitResult.end()) 274 { 275 result.append(" ").append(*exprEnd++); 276 } 277 value = result; 278 279 // We probably just invalidated the pointer abovei, 280 // reset and continue to handle multiple templates 281 strPtr = value.get_ptr<std::string*>(); 282 if (strPtr == nullptr) 283 { 284 break; 285 } 286 } 287 288 strPtr = value.get_ptr<std::string*>(); 289 if (strPtr == nullptr) 290 { 291 return ret; 292 } 293 294 std::string_view strView = *strPtr; 295 int base = 10; 296 if (strView.starts_with("0x")) 297 { 298 strView.remove_prefix(2); 299 base = 16; 300 } 301 302 uint64_t temp = 0; 303 bool fullMatch = false; 304 const std::from_chars_result res = 305 fromCharsWrapper(strView, temp, fullMatch, base); 306 if (res.ec == std::errc{} && fullMatch) 307 { 308 value = temp; 309 } 310 311 return ret; 312 } 313 314 std::string buildInventorySystemPath(std::string& boardName, 315 const std::string& boardType) 316 { 317 std::string path = "/xyz/openbmc_project/inventory/system/"; 318 std::string boardTypeLower = toLowerCopy(boardType); 319 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(), 320 illegalDbusMemberRegex, "_"); 321 322 return std::format("{}{}/{}", path, boardTypeLower, boardName); 323 } 324 } // namespace em_utils 325