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