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