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