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 { 131 for (const auto& [_, interface] : object) 132 { 133 auto ret = templateCharReplace(keyPair, interface, index, replaceStr); 134 if (ret) 135 { 136 handleLeftOverTemplateVars(keyPair); 137 return ret; 138 } 139 } 140 handleLeftOverTemplateVars(keyPair); 141 return std::nullopt; 142 } 143 144 // finds the template character (currently set to $) and replaces the value with 145 // the field found in a dbus object i.e. $ADDRESS would get populated with the 146 // ADDRESS field from a object on dbus 147 std::optional<std::string> templateCharReplace( 148 nlohmann::json::iterator& keyPair, const DBusInterface& interface, 149 const size_t index, const std::optional<std::string>& replaceStr) 150 { 151 std::optional<std::string> ret = std::nullopt; 152 153 if (keyPair.value().type() == nlohmann::json::value_t::object || 154 keyPair.value().type() == nlohmann::json::value_t::array) 155 { 156 for (auto nextLayer = keyPair.value().begin(); 157 nextLayer != keyPair.value().end(); nextLayer++) 158 { 159 templateCharReplace(nextLayer, interface, index, replaceStr); 160 } 161 return ret; 162 } 163 164 std::string* strPtr = keyPair.value().get_ptr<std::string*>(); 165 if (strPtr == nullptr) 166 { 167 return ret; 168 } 169 170 replaceAll(*strPtr, std::string(templateChar) + "index", 171 std::to_string(index)); 172 if (replaceStr) 173 { 174 replaceAll(*strPtr, *replaceStr, std::to_string(index)); 175 } 176 177 for (const auto& [propName, propValue] : interface) 178 { 179 std::string templateName = templateChar + propName; 180 std::ranges::subrange<std::string::const_iterator> find = 181 iFindFirst(*strPtr, templateName); 182 if (!find) 183 { 184 continue; 185 } 186 187 size_t start = find.begin() - strPtr->begin(); 188 189 // check for additional operations 190 if ((start == 0U) && find.end() == strPtr->end()) 191 { 192 std::visit([&](auto&& val) { keyPair.value() = val; }, propValue); 193 return ret; 194 } 195 196 constexpr const std::array<char, 5> mathChars = {'+', '-', '%', '*', 197 '/'}; 198 size_t nextItemIdx = start + templateName.size() + 1; 199 200 if (nextItemIdx > strPtr->size() || 201 std::find(mathChars.begin(), mathChars.end(), 202 strPtr->at(nextItemIdx)) == mathChars.end()) 203 { 204 std::string val = std::visit(VariantToStringVisitor(), propValue); 205 iReplaceAll(*strPtr, templateName, val); 206 continue; 207 } 208 209 // save the prefix 210 std::string prefix = strPtr->substr(0, start); 211 212 // operate on the rest 213 std::string end = strPtr->substr(nextItemIdx); 214 215 std::vector<std::string> splitResult = split(end, ' '); 216 217 // need at least 1 operation and number 218 if (splitResult.size() < 2) 219 { 220 lg2::error("Syntax error on template replacement of {STR}", "STR", 221 *strPtr); 222 for (const std::string& data : splitResult) 223 { 224 lg2::error("{SPLIT} ", "SPLIT", data); 225 } 226 lg2::error(""); 227 continue; 228 } 229 230 // we assume that the replacement is a number, because we can 231 // only do math on numbers.. we might concatenate strings in the 232 // future, but thats later 233 int number = std::visit(VariantToIntVisitor(), propValue); 234 auto exprBegin = splitResult.begin(); 235 auto exprEnd = splitResult.end(); 236 237 number = expression::evaluate(number, exprBegin, exprEnd); 238 239 std::string replaced(find.begin(), find.end()); 240 while (exprBegin != exprEnd) 241 { 242 replaced.append(" ").append(*exprBegin++); 243 } 244 ret = replaced; 245 246 std::string result = prefix + std::to_string(number); 247 while (exprEnd != splitResult.end()) 248 { 249 result.append(" ").append(*exprEnd++); 250 } 251 keyPair.value() = result; 252 253 // We probably just invalidated the pointer abovei, 254 // reset and continue to handle multiple templates 255 strPtr = keyPair.value().get_ptr<std::string*>(); 256 if (strPtr == nullptr) 257 { 258 break; 259 } 260 } 261 262 strPtr = keyPair.value().get_ptr<std::string*>(); 263 if (strPtr == nullptr) 264 { 265 return ret; 266 } 267 268 std::string_view strView = *strPtr; 269 int base = 10; 270 if (strView.starts_with("0x")) 271 { 272 strView.remove_prefix(2); 273 base = 16; 274 } 275 276 uint64_t temp = 0; 277 bool fullMatch = false; 278 const std::from_chars_result res = 279 fromCharsWrapper(strView, temp, fullMatch, base); 280 if (res.ec == std::errc{} && fullMatch) 281 { 282 keyPair.value() = temp; 283 } 284 285 return ret; 286 } 287 288 std::string buildInventorySystemPath(std::string& boardName, 289 const std::string& boardType) 290 { 291 std::string path = "/xyz/openbmc_project/inventory/system/"; 292 std::string boardTypeLower = toLowerCopy(boardType); 293 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(), 294 illegalDbusMemberRegex, "_"); 295 296 return std::format("{}{}/{}", path, boardTypeLower, boardName); 297 } 298 } // namespace em_utils 299