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