CCEngineServer/CMakeProject1/SystemParamsProvider_linux.cpp
2021-08-31 05:28:39 +02:00

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();
}