#include "SystemParamsProvider_linux.h" #include #include #include #include #include #include #include #include namespace bp = boost::process; //we will assume this for all further examples namespace { template< typename T > std::string intToHex( T i ) { std::ostringstream stream; stream << std::setfill ('0') << std::setw(sizeof(T)*2) << std::hex << i; return stream.str(); } std::vector splitString(const std::string& str, char c = ' ') { std::vector result; auto it = str.cbegin(); do { auto firstIt = it; while(*it != c && it != str.cend()) { ++it; } result.emplace_back(firstIt, it); } while (it++ != str.cend()); return result; } std::optional readFileLine(const std::string& fileName) { std::string line; std::ifstream myfile(fileName); if (myfile.is_open()) { std::cout << "File " << fileName << " successfully open" << std::endl; if (getline (myfile,line)) { return line; } myfile.close(); } return {}; } std::optional loadOsId() { auto machineId = readFileLine("/var/lib/dbus/machine-id"); if (machineId.value_or("").empty()) { machineId = readFileLine("/etc/machine-id"); } return machineId; } std::optional loadBoardSerial() { return readFileLine("/sys/class/dmi/id/board_serial"); } std::optional loadComputerUUID() { return readFileLine("/sys/class/dmi/id/product_uuid"); } std::optional loadComputerSerial() { auto productSerial = readFileLine("/sys/class/dmi/id/product_serial"); if(!productSerial.value_or("").empty()) { auto productFamily = readFileLine("/sys/class/dmi/id/product_family"); auto productName = readFileLine("/sys/class/dmi/id/product_name"); std::ostringstream os; os << productFamily.value() << "/" << productName.value() << "/" << productSerial.value(); return os.str(); } return {}; } struct CpuIdInfo { uint32_t vendorId; std::string vendorString; uint32_t modelId; bool isHypervisor; std::string serialNumber; }; CpuIdInfo loadCpuId() { uint32_t a, b, c, d; __cpuid (0 /* vendor string */, a, b, c, d); std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n"; uint32_t vendorId = a; uint32_t vendorString[] = {b, d, c, 0}; __cpuid (1 /* Processor Info and Feature Bits */, a, b, c, d); std::cout << "EAX: " << std::hex << std::setw(8) << std::setfill('0') << a << "\nEBX: " << b << "\nECX: " << c << "\nEDX: " << d << "\n"; bool isHypervisor = c & 0x80000000; uint32_t modelId = a & 0b00001111111111110011111111111111; __cpuid (3 /* Processor Serial Number */, a, b, c, d); std::ostringstream oss; oss << std::hex << std::setw(8) << std::setfill('0') << a << "-" << std::hex << std::setw(8) << std::setfill('0') << b << "-" << std::hex << std::setw(8) << std::setfill('0') << c << "-" << std::hex << std::setw(8) << std::setfill('0') << d; return CpuIdInfo {vendorId, std::string((const char*)vendorString), modelId, isHypervisor, oss.str()}; } std::optional> matchParameter( const std::string& line, const std::string& parameterRegex, const std::string& valueRegex = "([0-9a-fA-F\\-_]+)$" ) { std::regex line_regex(parameterRegex + ": " + valueRegex); std::smatch line_match; if(std::regex_search(line, line_match, line_regex)) { std::cout << "DEBUG: Found " << line_match[1] << ": " << line_match[2] << std::endl; return std::make_pair(line_match[1], line_match[2]); } return {}; } struct DmiDecodeParamTypes { static const std::string biosSerialNum; static const std::string systemUUID; static const std::string systemSerialNum; static const std::string systemFamily; static const std::string systemProductNumber; static const std::string baseboardSerialNumber; }; const std::string DmiDecodeParamTypes::systemUUID = "systemUUID"; const std::string DmiDecodeParamTypes::systemSerialNum = "systemSerialNum"; const std::string DmiDecodeParamTypes::systemFamily = "systemFamily"; const std::string DmiDecodeParamTypes::systemProductNumber = "systemProductNumber"; const std::string DmiDecodeParamTypes::baseboardSerialNumber = "baseboardSerialNumber"; std::map read_dmidecode() { std::error_code ec; std::map output; bp::ipstream is; //reading pipe-stream bp::child c("/usr/sbin/dmidecode", ec, bp::std_out > is, bp::std_err > bp::null); if(ec) { return output; } std::regex section_regex("DMI type ([0-9]+)"); std::string line; int section = -1; while (std::getline(is, line)) { if (!line.empty() && line[0] != '\t') { std::smatch section_match; if(std::regex_search(line, section_match, section_regex)) { section = std::atoi(section_match[1].str().c_str()); std::cout << "DEBUG: Found section: " << section_match[1] << std::endl; } continue; } if (section == 1) { { const auto matchedParameter = matchParameter(line, "(UUID)"); if (matchedParameter.has_value()) { output["systemUUID"] = matchedParameter.value().second; continue; } } { const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$"); if (matchedParameter.has_value()) { output["systemSerialNumber"] = matchedParameter.value().second; continue; } } { const auto matchedParameter = matchParameter(line, "(Family)", "([0-9a-zA-Z\\-_ ]+)$"); if (matchedParameter.has_value()) { output["systemFamily"] = matchedParameter.value().second; continue; } } { const auto matchedParameter = matchParameter(line, "(Product name)", "([0-9a-zA-Z\\-_ ]+)$"); if (matchedParameter.has_value()) { output["systemProductName"] = matchedParameter.value().second; continue; } } } else if (section == 2) { { const auto matchedParameter = matchParameter(line, "(Serial Number)", "([0-9a-zA-Z\\-_]+)$"); if (matchedParameter.has_value()) { output["baseboardSerialNumber"] = matchedParameter.value().second; continue; } } } } c.wait(ec); return output; } std::map readMacAddresses() { std::error_code ec; std::map output; bp::ipstream is; //reading pipe-stream bp::child c("/usr/bin/find /sys/class/net -mindepth 1 -maxdepth 1 ! -name lo -printf \"%P: \" -execdir /usr/bin/cat {}/address ;", ec, bp::std_out > is, bp::std_err > bp::null); if (ec) { return output; } std::regex section_regex("([0-9a-zA-Z\\-_]+): ([0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2})"); std::string line; int section = -1; while (std::getline(is, line)) { if (!line.empty() && line[0] == 'e') { std::smatch section_match; if(std::regex_match(line, section_match, section_regex)) { std::cout << "DEBUG: Found device: " << section_match[1] << " with MAC: " << section_match[2] << std::endl; output[section_match[1]] = section_match[2]; } continue; } } c.wait(ec); return output; } struct LsBlkResultEntry{ std::string mountPoint; std::string parentName; std::string name; std::string model; std::string serial; std::string type; }; std::optional parseLsBlkResultEntry(const std::string& line) { auto cols = splitString(line, ' '); if(cols.size() == 6) { return LsBlkResultEntry{ cols[0],cols[1],cols[2],cols[3],cols[4],cols[5] }; } return {}; } std::optional readDiskSerial() { std::error_code ec; bp::ipstream is; //reading pipe-stream bp::child c("/usr/bin/lsblk -oMOUNTPOINT,PKNAME,NAME,MODEL,SERIAL,TYPE --raw", ec, bp::std_out > is, bp::std_err > bp::null); if (ec) { return {}; } std::vector cmdOut; std::string line; int section = -1; while (std::getline(is, line)) { if (!line.empty()) { const auto entry = parseLsBlkResultEntry(line); if (entry.has_value()) { cmdOut.push_back(entry.value()); } } } c.wait(ec); if(ec) { return {}; } // Find best mountpoint LsBlkResultEntry const* bestEntry = nullptr; for (const auto& entry: cmdOut) { if(entry.mountPoint == "/") { bestEntry = &entry; } else if(entry.mountPoint == "/boot" && (bestEntry == nullptr || bestEntry->mountPoint == "/sysroot")) { bestEntry = &entry; } else if(entry.mountPoint == "/sysroot" && bestEntry == nullptr) { bestEntry = &entry; } } if(bestEntry == nullptr) { return {}; } std::cout << "Found best mount point: " << bestEntry->mountPoint << std::endl; int depth = 0; while(depth < 10 && !bestEntry->parentName.empty()) { ++depth; bool found = false; for (const auto& entry: cmdOut) { if (entry.name == bestEntry->parentName) { bestEntry = &entry; found = true; break; } } if (!found) { break; } } if (bestEntry->type == "disk" && !bestEntry->serial.empty()) { return bestEntry->serial; } return {}; } } namespace detail { class SystemParamsProvider_linuxImpl final { public: SystemParamsProvider_linuxImpl() { } ~SystemParamsProvider_linuxImpl() { } public: SystemParams retrieveSystemParams() { SystemParams result; // result[SystemParamTypes::biosSerialNum] = processiosData(biosFuture.get()); { const auto val = loadComputerUUID(); if (val.has_value()) { result[SystemParamTypes::computerUUID] = val.value(); } } { const auto val = loadComputerSerial(); if (val.has_value()) { result[SystemParamTypes::computerSerial] = val.value(); } } { const auto val = loadOsId(); if (val.has_value()) { result[SystemParamTypes::osId] = val.value(); } } { const auto val = loadBoardSerial(); if (val.has_value()) { result[SystemParamTypes::mainboardSerialNum] = val.value(); } } auto cpuIdInfo = loadCpuId(); std::cout << "Vendor ID: " << cpuIdInfo.vendorId << std::endl; std::cout << "Model ID: " << cpuIdInfo.modelId << std::endl; std::cout << "Vendor String: " << cpuIdInfo.vendorString << std::endl; std::cout << "Is hypervisor: " << cpuIdInfo.isHypervisor << std::endl; std::cout << "Serial number: " << cpuIdInfo.serialNumber << std::endl; result[SystemParamTypes::cpuIdModel] = intToHex(cpuIdInfo.vendorId) + intToHex(cpuIdInfo.modelId); if(cpuIdInfo.isHypervisor) { result[SystemParamTypes::cpuIdHypervisor] = "true"; } const auto macAddresses = readMacAddresses(); if(macAddresses.size() > 0) { result[SystemParamTypes::nicMac] = macAddresses.begin()->second; } const auto diskSerial = readDiskSerial(); if (diskSerial.has_value()) { result[SystemParamTypes::diskSerialNum] = diskSerial.value(); } auto result2 = read_dmidecode(); for (auto it = result2.cbegin(); it != result2.cend(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } if (result.find(SystemParamTypes::mainboardSerialNum) == result.end() && result2.find(DmiDecodeParamTypes::baseboardSerialNumber) != result2.end()) { result[SystemParamTypes::mainboardSerialNum] = result2[DmiDecodeParamTypes::baseboardSerialNumber]; } if (result.find(SystemParamTypes::computerUUID) == result.end() && result2.find(DmiDecodeParamTypes::systemUUID) != result2.end()) { result[SystemParamTypes::computerUUID] = result2[DmiDecodeParamTypes::systemUUID]; } if (result.find(SystemParamTypes::computerSerial) == result.end() && result2.find(DmiDecodeParamTypes::systemSerialNum) != result2.end()) { if(!result2[DmiDecodeParamTypes::systemUUID].empty()) { auto productFamily = result2.find(DmiDecodeParamTypes::systemFamily); auto productName = result2.find(DmiDecodeParamTypes::systemProductNumber); std::ostringstream os; os << ((productFamily != result2.end()) ? productFamily->second : "") << "/" << ((productName != result2.end()) ? productName->second : "") << "/" << result2[DmiDecodeParamTypes::systemUUID]; result[SystemParamTypes::computerSerial] = os.str(); } } return result; } private: private: private: }; } SystemParamsProvider_linux::SystemParamsProvider_linux() : m_impl(std::make_unique()) { } SystemParamsProvider_linux::~SystemParamsProvider_linux() { } SystemParams SystemParamsProvider_linux::retrieveSystemParams() { return m_impl->retrieveSystemParams(); }