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 <boost/algorithm/string/case_conv.hpp> 9 #include <phosphor-logging/lg2.hpp> 10 #include <sdbusplus/bus/match.hpp> 11 12 #include <fstream> 13 #include <regex> 14 15 const std::regex illegalDbusMemberRegex("[^A-Za-z0-9_]"); 16 17 namespace em_utils 18 { 19 20 constexpr const char* templateChar = "$"; 21 22 bool fwVersionIsSame() 23 { 24 std::ifstream version(versionFile); 25 if (!version.good()) 26 { 27 lg2::error("Can't read {PATH}", "PATH", versionFile); 28 return false; 29 } 30 31 std::string versionData; 32 std::string line; 33 while (std::getline(version, line)) 34 { 35 versionData += line; 36 } 37 38 std::string expectedHash = 39 std::to_string(std::hash<std::string>{}(versionData)); 40 41 std::error_code ec; 42 std::filesystem::create_directory(configurationOutDir, ec); 43 44 if (ec) 45 { 46 lg2::error("could not create directory {DIR}", "DIR", 47 configurationOutDir); 48 return false; 49 } 50 51 std::ifstream hashFile(versionHashFile); 52 if (hashFile.good()) 53 { 54 std::string hashString; 55 hashFile >> hashString; 56 57 if (expectedHash == hashString) 58 { 59 return true; 60 } 61 hashFile.close(); 62 } 63 64 std::ofstream output(versionHashFile); 65 output << expectedHash; 66 return false; 67 } 68 69 void handleLeftOverTemplateVars(nlohmann::json::iterator& keyPair) 70 { 71 if (keyPair.value().type() == nlohmann::json::value_t::object || 72 keyPair.value().type() == nlohmann::json::value_t::array) 73 { 74 for (auto nextLayer = keyPair.value().begin(); 75 nextLayer != keyPair.value().end(); nextLayer++) 76 { 77 handleLeftOverTemplateVars(nextLayer); 78 } 79 return; 80 } 81 82 std::string* strPtr = keyPair.value().get_ptr<std::string*>(); 83 if (strPtr == nullptr) 84 { 85 return; 86 } 87 88 // Walking through the string to find $<templateVar> 89 while (true) 90 { 91 std::ranges::subrange<std::string::const_iterator> findStart = 92 iFindFirst(*strPtr, std::string_view(templateChar)); 93 94 if (!findStart) 95 { 96 break; 97 } 98 99 std::ranges::subrange<std::string::iterator> searchRange( 100 strPtr->begin() + (findStart.end() - strPtr->begin()), 101 strPtr->end()); 102 std::ranges::subrange<std::string::const_iterator> findSpace = 103 iFindFirst(searchRange, " "); 104 105 std::string::const_iterator templateVarEnd; 106 107 if (!findSpace) 108 { 109 // No space means the template var spans to the end of 110 // of the keyPair value 111 templateVarEnd = strPtr->end(); 112 } 113 else 114 { 115 // A space marks the end of a template var 116 templateVarEnd = findSpace.begin(); 117 } 118 119 lg2::error( 120 "There's still template variable {VAR} un-replaced. Removing it from the string.\n", 121 "VAR", std::string(findStart.begin(), templateVarEnd)); 122 strPtr->erase(findStart.begin(), templateVarEnd); 123 } 124 } 125 126 // Replaces the template character like the other version of this function, 127 // but checks all properties on all interfaces provided to do the substitution 128 // with. 129 std::optional<std::string> templateCharReplace( 130 nlohmann::json::iterator& keyPair, const DBusObject& object, 131 const size_t index, const std::optional<std::string>& replaceStr) 132 { 133 for (const auto& [_, interface] : object) 134 { 135 auto ret = templateCharReplace(keyPair, interface, index, replaceStr); 136 if (ret) 137 { 138 handleLeftOverTemplateVars(keyPair); 139 return ret; 140 } 141 } 142 handleLeftOverTemplateVars(keyPair); 143 return std::nullopt; 144 } 145 146 // finds the template character (currently set to $) and replaces the value with 147 // the field found in a dbus object i.e. $ADDRESS would get populated with the 148 // ADDRESS field from a object on dbus 149 std::optional<std::string> templateCharReplace( 150 nlohmann::json::iterator& keyPair, const DBusInterface& interface, 151 const size_t index, const std::optional<std::string>& replaceStr) 152 { 153 std::optional<std::string> ret = std::nullopt; 154 155 if (keyPair.value().type() == nlohmann::json::value_t::object || 156 keyPair.value().type() == nlohmann::json::value_t::array) 157 { 158 for (auto nextLayer = keyPair.value().begin(); 159 nextLayer != keyPair.value().end(); nextLayer++) 160 { 161 templateCharReplace(nextLayer, interface, index, replaceStr); 162 } 163 return ret; 164 } 165 166 std::string* strPtr = keyPair.value().get_ptr<std::string*>(); 167 if (strPtr == nullptr) 168 { 169 return ret; 170 } 171 172 replaceAll(*strPtr, std::string(templateChar) + "index", 173 std::to_string(index)); 174 if (replaceStr) 175 { 176 replaceAll(*strPtr, *replaceStr, std::to_string(index)); 177 } 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) { keyPair.value() = val; }, propValue); 195 return ret; 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 keyPair.value() = result; 254 255 // We probably just invalidated the pointer abovei, 256 // reset and continue to handle multiple templates 257 strPtr = keyPair.value().get_ptr<std::string*>(); 258 if (strPtr == nullptr) 259 { 260 break; 261 } 262 } 263 264 strPtr = keyPair.value().get_ptr<std::string*>(); 265 if (strPtr == nullptr) 266 { 267 return ret; 268 } 269 270 std::string_view strView = *strPtr; 271 int base = 10; 272 if (strView.starts_with("0x")) 273 { 274 strView.remove_prefix(2); 275 base = 16; 276 } 277 278 uint64_t temp = 0; 279 bool fullMatch = false; 280 const std::from_chars_result res = 281 fromCharsWrapper(strView, temp, fullMatch, base); 282 if (res.ec == std::errc{} && fullMatch) 283 { 284 keyPair.value() = temp; 285 } 286 287 return ret; 288 } 289 290 std::string buildInventorySystemPath(std::string& boardName, 291 const std::string& boardType) 292 { 293 std::string path = "/xyz/openbmc_project/inventory/system/"; 294 std::string boardTypeLower = boost::algorithm::to_lower_copy(boardType); 295 296 std::regex_replace(boardName.begin(), boardName.begin(), boardName.end(), 297 illegalDbusMemberRegex, "_"); 298 299 return std::format("{}{}/{}", path, boardTypeLower, boardName); 300 } 301 } // namespace em_utils 302