475 lines
16 KiB
C++
475 lines
16 KiB
C++
#include "SystemParamsProvider_linux.h"
|
|
|
|
#include <boost/process.hpp>
|
|
#include <boost/log/trivial.hpp>
|
|
#include <cpuid.h>
|
|
|
|
#include <future>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <regex>
|
|
#include <sstream>
|
|
|
|
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<std::string> splitString(const std::string& str, char c = ' ')
|
|
{
|
|
std::vector<std::string> 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<std::string> readFileLine(const std::string& fileName)
|
|
{
|
|
std::string line;
|
|
std::ifstream myfile(fileName);
|
|
if (myfile.is_open()) {
|
|
BOOST_LOG_TRIVIAL(debug) << "File " << fileName << " successfully open";
|
|
if (getline (myfile,line)) {
|
|
return line;
|
|
}
|
|
myfile.close();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::optional<std::string> loadOsId()
|
|
{
|
|
BOOST_LOG_TRIVIAL(debug) << "Trying to find Operating system ID...";
|
|
auto machineId = readFileLine("/var/lib/dbus/machine-id");
|
|
if (machineId.value_or("").empty()) {
|
|
machineId = readFileLine("/etc/machine-id");
|
|
}
|
|
return machineId;
|
|
}
|
|
|
|
std::optional<std::string> loadBoardSerial()
|
|
{
|
|
BOOST_LOG_TRIVIAL(debug) << "Trying to find mainboard serial number...";
|
|
return readFileLine("/sys/class/dmi/id/board_serial");
|
|
}
|
|
|
|
std::optional<std::string> loadComputerUUID()
|
|
{
|
|
BOOST_LOG_TRIVIAL(debug) << "Trying to find product UUID...";
|
|
return readFileLine("/sys/class/dmi/id/product_uuid");
|
|
}
|
|
|
|
std::optional<std::string> loadComputerSerial()
|
|
{
|
|
BOOST_LOG_TRIVIAL(debug) << "Trying to find computer serial number...";
|
|
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()
|
|
{
|
|
BOOST_LOG_TRIVIAL(debug) << "Trying to find processor idenfication...";
|
|
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<std::pair<std::string, std::string>> 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)) {
|
|
BOOST_LOG_TRIVIAL(trace) << "Matched parameter pair " << line_match[1] << ": " << line_match[2];
|
|
return std::make_pair<std::string, std::string>(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<std::string, std::string> read_dmidecode()
|
|
{
|
|
std::error_code ec;
|
|
std::map<std::string, std::string> 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());
|
|
BOOST_LOG_TRIVIAL(debug) << "Found section: " << section_match[1];
|
|
}
|
|
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<std::string, std::string> readMacAddresses()
|
|
{
|
|
std::error_code ec;
|
|
std::map<std::string, std::string> 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)) {
|
|
BOOST_LOG_TRIVIAL(debug) << "Found device : " << section_match[1] << " with MAC : " << section_match[2];
|
|
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<LsBlkResultEntry> 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<std::string> 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<LsBlkResultEntry> 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 {};
|
|
}
|
|
|
|
BOOST_LOG_TRIVIAL(debug) << "Found best mount point: " << bestEntry->mountPoint;
|
|
|
|
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();
|
|
BOOST_LOG_TRIVIAL(trace) << "Vendor ID: " << cpuIdInfo.vendorId << std::endl;
|
|
BOOST_LOG_TRIVIAL(trace) << "Model ID: " << cpuIdInfo.modelId << std::endl;
|
|
BOOST_LOG_TRIVIAL(trace) << "Vendor String: " << cpuIdInfo.vendorString << std::endl;
|
|
BOOST_LOG_TRIVIAL(trace) << "Is hypervisor: " << cpuIdInfo.isHypervisor << std::endl;
|
|
BOOST_LOG_TRIVIAL(trace) << "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<detail::SystemParamsProvider_linuxImpl>())
|
|
{
|
|
}
|
|
|
|
SystemParamsProvider_linux::~SystemParamsProvider_linux()
|
|
{
|
|
}
|
|
|
|
SystemParams SystemParamsProvider_linux::retrieveSystemParams()
|
|
{
|
|
return m_impl->retrieveSystemParams();
|
|
}
|