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