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