| extern "C" |
| { |
| #include <i2c/smbus.h> |
| #include <linux/i2c-dev.h> |
| #include <linux/i2c.h> |
| } |
| #include "include/common_utility.hpp" |
| |
| namespace util |
| { |
| |
| std::vector<fs::path> findFiles(const fs::path& dirPath, |
| const std::string& matchString) |
| { |
| std::vector<fs::path> foundPaths; |
| if (!fs::exists(dirPath)) |
| { |
| lg2::error("Directory {PATH} does not exist", "PATH", dirPath); |
| return foundPaths; |
| } |
| |
| std::regex search(matchString); |
| std::smatch match; |
| for (const auto& p : fs::directory_iterator(dirPath)) |
| { |
| std::string path = p.path().string(); |
| if (std::regex_search(path, match, search)) |
| { |
| foundPaths.emplace_back(p.path()); |
| } |
| } |
| if (foundPaths.empty()) |
| { |
| lg2::error("No file found in {PATH} with {STRING}", "PATH", dirPath, |
| "STRING", matchString); |
| } |
| |
| return foundPaths; |
| } |
| |
| bool checkPartNumber(std::string_view fileName, const json& configuration) |
| { |
| auto [boardName, |
| partNumberPrefixes] = getBoardAndPartNumberPrefixes(configuration); |
| if (boardName.empty() || partNumberPrefixes.empty()) |
| { |
| lg2::error("Board or PartNumberPrefix is empty in {NAME}", "NAME", |
| fileName); |
| return false; |
| } |
| |
| const std::string fruService = "xyz.openbmc_project.FruDevice"; |
| const std::string fruObject = "/xyz/openbmc_project/FruDevice/" + boardName; |
| const std::string fruInterface = "xyz.openbmc_project.FruDevice"; |
| const std::string property = "BOARD_PART_NUMBER"; |
| |
| auto [success, value] = getProperty(fruService, fruObject, fruInterface, |
| property); |
| if (!success) |
| { |
| lg2::info("{FILENAME} PartNumber is not available", "FILENAME", |
| fileName); |
| return false; |
| } |
| |
| const auto* strVal = std::get_if<std::string>(&value); |
| if (strVal != nullptr) |
| { |
| const std::string dbusPartNumberPrefix = strVal->substr(0, 7); |
| auto it = std::find(partNumberPrefixes.begin(), |
| partNumberPrefixes.end(), dbusPartNumberPrefix); |
| if (it == partNumberPrefixes.end()) |
| { |
| lg2::error("{FILENAME} PartNumberPrefix is not match", "FILENAME", |
| fileName); |
| return false; |
| } |
| } |
| else |
| { |
| lg2::error("{FILENAME} PartNumber is not a string", "FILENAME", |
| fileName); |
| } |
| |
| std::vector<std::string> properties = { |
| "BOARD_INFO_AM1", "BOARD_INFO_AM2", "BOARD_INFO_AM3", |
| "PRODUCT_INFO_AM1", "PRODUCT_INFO_AM2", "PRODUCT_INFO_AM3"}; |
| for (const auto& property : properties) |
| { |
| if (configuration.contains(property)) |
| { |
| std::string jsonValue = configuration[property]; |
| auto [success, dbusValue] = getProperty(fruService, fruObject, |
| fruInterface, property); |
| if (!success) |
| { |
| continue; |
| } |
| auto* strVal = std::get_if<std::string>(&dbusValue); |
| if (strVal != nullptr) |
| { |
| std::string dbusProperty = strVal->substr(); |
| if (jsonValue != dbusProperty) |
| { |
| lg2::error("{PROPERTY} does not match", "PROPERTY", |
| property); |
| return false; |
| } |
| } |
| else |
| { |
| lg2::error("{FILENAME} {PROPERTY} is not a string", "FILENAME", |
| fileName, "PROPERTY", property); |
| return false; |
| } |
| } |
| } |
| |
| lg2::info("Vr config Added: {FILENAME}", "FILENAME", fileName); |
| return true; |
| } |
| |
| std::string getBoodID() |
| { |
| const std::string bootIdPath = "/proc/sys/kernel/random/boot_id"; |
| std::ifstream bootIdFile(bootIdPath); |
| |
| if (!bootIdFile.is_open()) |
| { |
| lg2::error("Unable to open boot_id file"); |
| return ""; |
| } |
| |
| std::string bootId; |
| std::getline(bootIdFile, bootId); |
| |
| bootIdFile.close(); |
| |
| lg2::info("Boot ID: {ID}", "ID", bootId); |
| |
| return bootId; |
| } |
| |
| void summarizeLog(DbusUtil* dbusUtil, std::string_view msg, |
| const fs::path& logDirPath, const std::string_view filename) |
| { |
| std::time_t currentTime = |
| std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| |
| const std::string kCurrentBoot = "currentBoot"; |
| const std::string kPreviousBoot = "previousBoot"; |
| const std::string kBootIdFile = "boot_id.log"; |
| |
| const fs::path logDirPathCurrent = logDirPath / kCurrentBoot; |
| const fs::path logDirPathPrevious = logDirPath / kPreviousBoot; |
| |
| if (!fs::exists(logDirPathCurrent)) |
| { |
| fs::create_directories(logDirPathCurrent); |
| std::ofstream ofsCurrent(logDirPathCurrent / kBootIdFile); |
| ofsCurrent << getBoodID(); |
| ofsCurrent.close(); |
| } |
| else |
| { |
| std::ifstream ifsCurrent(logDirPathCurrent / kBootIdFile); |
| std::string bootIdCurrent; |
| ifsCurrent >> bootIdCurrent; |
| ifsCurrent.close(); |
| |
| std::string bootId = getBoodID(); |
| if (bootIdCurrent != bootId) |
| { |
| fs::remove_all(logDirPathPrevious); |
| fs::rename(logDirPathCurrent, logDirPathPrevious); |
| |
| fs::create_directories(logDirPathCurrent); |
| std::ofstream ofsCurrent(logDirPathCurrent / kBootIdFile); |
| ofsCurrent << bootId; |
| ofsCurrent.close(); |
| } |
| } |
| |
| size_t fileCount = 0; |
| for (const auto& entry : fs::directory_iterator(logDirPathCurrent)) |
| { |
| if (entry.is_regular_file() && |
| entry.path().filename().string().starts_with(filename)) |
| { |
| fileCount++; |
| } |
| } |
| |
| if (fileCount > 100) |
| { |
| return; |
| } |
| |
| std::string filenameStr = std::string(filename) + "_" + |
| std::to_string(fileCount); |
| |
| fs::path logFilePath = logDirPathCurrent / filenameStr; |
| |
| std::ofstream ofs(logFilePath, std::ios::binary | std::ios::app); |
| if (!ofs.is_open()) |
| { |
| lg2::error("Error: Unable to open file."); |
| return; |
| } |
| |
| ofs.seekp(0, std::ios::end); |
| uint64_t fileSize = ofs.tellp(); |
| uint64_t msgSize = |
| msg.size() + |
| 25; // 25 for timestamp length: "Www Mmm dd hh:mm:ss yyyy\n" |
| |
| std::string now = std::ctime(¤tTime); |
| std::replace(now.begin(), now.end(), '\n', ':'); |
| |
| if (msgSize > kMaxLogSize) |
| { |
| std::string_view truncatedMsg = msg.substr(0, kMaxLogSize - 25); |
| |
| ofs.close(); |
| ofs.open(logFilePath, std::ios::binary | std::ios::trunc); |
| ofs << now << "\n" << truncatedMsg << '\n'; |
| } |
| else |
| { |
| if (fileSize + msgSize > kMaxLogSize) |
| { |
| ofs.close(); |
| |
| uint64_t bytesToKeep = kMaxLogSize - msgSize; |
| |
| if (bytesToKeep > fileSize) |
| { |
| bytesToKeep = fileSize; |
| } |
| |
| std::ifstream ifs(logFilePath, std::ios::binary); |
| ifs.seekg(static_cast<int64_t>(fileSize - bytesToKeep), |
| std::ios::beg); |
| std::vector<char> buffer(bytesToKeep); |
| ifs.read(buffer.data(), static_cast<int64_t>(buffer.size())); |
| ifs.close(); |
| |
| ofs.open(logFilePath, std::ios::binary | std::ios::trunc); |
| ofs.write(buffer.data(), static_cast<int64_t>(buffer.size())); |
| } |
| |
| ofs << now << "\n" << msg << '\n'; |
| } |
| |
| ofs.close(); |
| |
| lg2::info("{TIME}", "TIME", now); |
| lg2::info("{MSG}", "MSG", msg); |
| lg2::info("Stored log file: {FILE}", "FILE", logFilePath); |
| std::cerr << logFilePath << "<logFilePath\n"; |
| |
| dbusUtil->dbusAddStoredLog(filenameStr); |
| } |
| |
| std::pair<bool, PropertyValue> getProperty(const std::string& serviceName, |
| const std::string& objectPath, |
| const std::string& interface, |
| const std::string& propertyName) |
| { |
| try |
| { |
| auto bus = sdbusplus::bus::new_default(); |
| auto method = |
| bus.new_method_call(serviceName.c_str(), objectPath.c_str(), |
| "org.freedesktop.DBus.Properties", "Get"); |
| method.append(interface, propertyName); |
| auto reply = bus.call(method); |
| PropertyValue value; |
| reply.read(value); |
| return {true, value}; |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| lg2::error("Unable to get property: {ERR}", "ERR", e); |
| return {false, {}}; |
| } |
| } |
| |
| int i2cOpenDevice(const uint16_t bus, const uint16_t address) |
| { |
| std::string i2cDev = "/dev/i2c-" + std::to_string(bus); |
| int fd = open(i2cDev.c_str(), O_RDWR); |
| if (fd < 0) |
| { |
| lg2::error("Unable to open i2c device."); |
| return -1; |
| } |
| if (ioctl(fd, I2C_SLAVE_FORCE, address) < 0) |
| { |
| close(fd); |
| lg2::error("Unable to set i2c slave address."); |
| return -1; |
| } |
| return fd; |
| } |
| |
| void i2cWriteByte(const uint8_t bus, const uint8_t address, |
| const uint8_t command, const uint8_t value) |
| { |
| int fd = i2cOpenDevice(bus, address); |
| if (fd < 0) |
| { |
| lg2::error("i2cWriteByte failed"); |
| return; |
| } |
| |
| i2c_smbus_write_byte_data(fd, command, value); |
| close(fd); |
| } |
| |
| std::vector<uint8_t> i2cWriteRead(const uint8_t bus, const uint8_t address, |
| const std::vector<uint8_t>& writeBuf, |
| const uint16_t writeLen, |
| const uint16_t recvLen) |
| { |
| std::vector<uint8_t> recvData; |
| |
| int fd = i2cOpenDevice(bus, address); |
| if (fd < 0) |
| { |
| lg2::error("i2cWriteRead failed"); |
| close(fd); |
| return {}; |
| } |
| |
| struct i2c_rdwr_ioctl_data data |
| {}; |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) |
| struct i2c_msg messages[2]; |
| |
| messages[0].addr = address; |
| messages[0].flags = 0; |
| messages[0].len = writeLen; |
| messages[0].buf = const_cast<uint8_t*>(writeBuf.data()); // NOLINT |
| |
| recvData.resize(recvLen); |
| messages[1].addr = address; |
| messages[1].flags = I2C_M_RD; |
| messages[1].len = recvLen; |
| messages[1].buf = recvData.data(); |
| |
| data.msgs = static_cast<i2c_msg*>(messages); |
| data.nmsgs = 2; |
| |
| if (ioctl(fd, I2C_RDWR, &data) < 0) |
| { |
| lg2::error("Unable to write and read data from i2c device."); |
| } |
| close(fd); |
| |
| return recvData; |
| } |
| |
| std::uint8_t i2cReadByte(const uint8_t bus, const uint8_t address, |
| const uint8_t reg) |
| { |
| int fd = i2cOpenDevice(bus, address); |
| if (fd < 0) |
| { |
| lg2::error("i2cReadByte failed"); |
| return 0; |
| } |
| |
| uint8_t value = 0; |
| uint8_t buf = reg; |
| struct i2c_rdwr_ioctl_data data |
| {}; |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) |
| struct i2c_msg messages[2]; |
| |
| messages[0].addr = address; |
| messages[0].flags = 0; |
| messages[0].len = 1; |
| messages[0].buf = &buf; |
| |
| messages[1].addr = address; |
| messages[1].flags = I2C_M_RD; |
| messages[1].len = 1; |
| messages[1].buf = &value; |
| |
| data.msgs = static_cast<i2c_msg*>(messages); |
| data.nmsgs = 2; |
| |
| if (ioctl(fd, I2C_RDWR, &data) < 0) |
| { |
| lg2::error("Unable to read byte from i2c device."); |
| } |
| close(fd); |
| return 0; |
| } |
| |
| std::vector<uint8_t> i2cReadBytes(const uint8_t bus, const uint8_t address, |
| const uint8_t reg, const uint16_t length) |
| { |
| int fd = i2cOpenDevice(bus, address); |
| if (fd < 0) |
| { |
| lg2::error("i2cReadBytes failed"); |
| return {}; |
| } |
| |
| std::vector<uint8_t> buffer(length); |
| uint8_t buf = reg; |
| struct i2c_rdwr_ioctl_data data |
| {}; |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) |
| struct i2c_msg messages[2]; |
| |
| messages[0].addr = address; |
| messages[0].flags = 0; |
| messages[0].len = 1; |
| messages[0].buf = &buf; |
| |
| messages[1].addr = address; |
| messages[1].flags = I2C_M_RD; |
| messages[1].len = length; |
| messages[1].buf = buffer.data(); |
| |
| data.msgs = static_cast<i2c_msg*>(messages); |
| data.nmsgs = 2; |
| |
| if (ioctl(fd, I2C_RDWR, &data) < 0) |
| { |
| lg2::error("Unable to read bytes from i2c device."); |
| } |
| close(fd); |
| return {}; |
| } |
| |
| double convertFromLinear(const uint16_t value) |
| { |
| uint8_t exponentField = value >> 11; |
| uint16_t mantissaField = value & 0x7FFU; |
| bool negativeExp = exponentField > 0x0FU; |
| if (negativeExp) |
| { |
| exponentField = 32 - exponentField; |
| } |
| if (mantissaField > 0x03FFU) |
| { |
| mantissaField = 2048 - mantissaField; |
| } |
| |
| int8_t exponent = static_cast<int8_t>(exponentField); |
| int16_t mantissa = static_cast<int16_t>(mantissaField); |
| return negativeExp ? mantissa / std::pow(2.0, exponent) |
| : mantissa * std::pow(2.0, exponent); |
| } |
| |
| double convertFromVoutLinear(const uint16_t value, const int8_t exponent) |
| { |
| return value * std::pow(2.0, exponent); |
| } |
| |
| double convertFromDirect(const uint16_t value, const int16_t m, const int16_t b, |
| const int8_t r) |
| { |
| if (m == 0) |
| { |
| lg2::error("Error: Divided by zero"); |
| return 0; |
| } |
| return (value * std::pow(10.0, -r) - b) / m; |
| } |
| |
| uint16_t parseDec(const json& element) |
| { |
| if (!element.is_string()) |
| { |
| lg2::error("Element is not a string"); |
| return 0; |
| } |
| std::string value = element.get<std::string>(); |
| std::regex decimalRegex("^-?[0-9]+$"); |
| if (!std::regex_match(value, decimalRegex)) |
| { |
| lg2::error("Element is not decimal string"); |
| return 0; |
| } |
| return static_cast<uint16_t>(std::stoul(value, nullptr, 0)); |
| } |
| |
| std::vector<uint8_t> parseHexByteArray(const json& element) |
| { |
| if (!element.is_array()) |
| { |
| return {}; |
| } |
| |
| std::vector<uint8_t> values; |
| for (const auto& valueElement : element) |
| { |
| values.emplace_back(parseHexByte(valueElement)); |
| } |
| return values; |
| } |
| |
| uint8_t parseHexByte(const json& element) |
| { |
| if (!element.is_string()) |
| { |
| lg2::error("Element is not a string"); |
| return 0; |
| } |
| std::string value = element.get<std::string>(); |
| std::regex hexPattern("^0x[0-9a-fA-F]{1,3}$"); |
| if (!std::regex_match(value, hexPattern)) |
| { |
| lg2::error("Element is not hexadecimal string"); |
| return 0; |
| } |
| return static_cast<uint8_t>(std::stoul(value, nullptr, 16)); |
| } |
| |
| uint16_t bytesIntoWord(std::string_view upperByte, std::string_view lowerByte) |
| { |
| return (parseHexByte(upperByte) << 8) | parseHexByte(lowerByte); |
| } |
| |
| std::string byteArrayToHexString(uint8_t* buf, const uint16_t len) |
| { |
| std::stringstream result; |
| for (uint16_t i = 0; i < len; ++i) |
| { |
| result << "0x" << std::setw(2) << std::setfill('0') << std::hex |
| << static_cast<int>(buf[i]) << std::dec << " "; // NOLINT |
| } |
| return result.str(); |
| } |
| |
| std::string getCurrentTimestamp() |
| { |
| std::time_t currentTime = |
| std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); |
| std::tm* gmTime = std::gmtime(¤tTime); |
| gmTime->tm_hour -= 8; |
| std::mktime(gmTime); |
| std::ostringstream oss; |
| oss << std::put_time(gmTime, "%Y-%m-%d %H:%M:%S"); |
| return oss.str(); |
| } |
| |
| } // namespace util |