gcontini
2020-10-24 7e4e14dde5fbfade46311fbf75386d5371062d7d
review of disk strategy linux
17个文件已修改
1个文件已删除
653 ■■■■■ 已修改文件
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.travis.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
build/.gitkeep 补丁 | 查看 | 原始文档 | blame | 历史
doc/conf.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/index.rst 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/usage/Hardware-identifiers.rst 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
doc/usage/issue-licenses.md 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/inspector/inspector.cpp 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/LicenseReader.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/hw_identifier/disk_strategy.cpp 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/hw_identifier/disk_strategy.hpp 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/hw_identifier/identification_strategy.cpp 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/os/linux/os_linux.cpp 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/os/os.h 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/library/os/windows/os_win.cpp 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/licensecc_properties.h.in 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test/functional/hw_identifier_it_test.cpp 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test/library/os_linux_test.cpp 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -36,3 +36,4 @@
**/CMakeCache.txt
**/CMakeFiles
/CMakeSettings.json
/.venv/
.travis.yml
@@ -278,7 +278,7 @@
             - doxygen
             - graphviz
     before_script: 
        - pip3 install setuptools sphinx sphinx_rtd_theme breathe sphinx-markdown-tables recommonmark sphinx-rtd-theme sphinx-sitemap sphinxemoji
        - pip3 install setuptools sphinx sphinx_rtd_theme breathe sphinx-markdown-tables recommonmark sphinx-sitemap sphinxemoji
        - cd build && cmake -DCMAKE_INSTALL_PREFIX=../../install ..
     script:
        - make documentation
build/.gitkeep
doc/conf.py
@@ -39,6 +39,7 @@
# Breathe Configuration
breathe_default_project = "licensecc"
breathe_domain_by_extension = {"h" : "cpp"}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
doc/index.rst
@@ -44,7 +44,7 @@
*******************
The software is made by 4 main sub-components:
* ``licensecc``    : the C++ library with a C api (the part you have to integrate in your software) with minimal (or no) external dependencies. This is the project you're currently looking at.
* `licensecc`    : the C++ library with a C api (the part you have to integrate in your software) with minimal (or no) external dependencies. This is the project you're currently looking at.
* ``lccinspector`` : a license debugger to be sent to the final customer to diagnose licensing problems or for calculating the hardware id before issuing the license.
* ``lccgen``       : a license generator (github project `lcc-license-generator`_ ) to initialize the library and generate the licenses.
* ``examples``     : usage samples (github project `examples <https://github.com/open-license-manager/examples>`_ ).
@@ -119,7 +119,7 @@
How to use
**************
The `examples`_ repository shows various ways to integrate ``licensecc`` into your project.
The `examples`_ repository shows various ways to integrate `licensecc` into your project.
.. _examples: https://github.com/open-license-manager/examples 
doc/usage/Hardware-identifiers.rst
@@ -44,16 +44,48 @@
For instance if containers are used to avoid to pollute the external distribution it makes perfect sense to have an 
hardware identifier, if users are running dockers in a kubernetes cluster in the cloud it makes no sense at all.
************************
Identifier Generation
************************
`Licensecc` is able to identify which virtual environment the user is running in and select the appropriate generation
strategy. Below the identifier generation workflow used by the :ref:`identify_pc <api/public_api:Public api>` method.
*************************************************
Hardware Identifier Generation
*************************************************
The licensed application will call the api method :ref:`identify_pc <api/public_api:Public api>` to generate an hardware
identifier and print it out to the user, the user then will contact the software licensor to get an appropriate license.
The licensed application can either decide an identification strategy by passing it in the ``identify_pc`` parameter ``hw_id_method``
(see: :cpp:enum:`LCC_API_HW_IDENTIFICATION_STRATEGY` ) or let `licensecc` automatically choose how to generate the
identifier (by passing `hw_id_method=STRATEGY_DEFAULT`).
In this case `licensecc` is able to identify which virtual environment the user is running in and select the appropriate generation
strategy.
Below the full identifier generation workflow used by the :ref:`identify_pc <api/public_api:Public api>` method.
.. figure:: ../_static/pc-id-selection.png
First of all it takes in account the application specified parameter
If the licensed software uses `STRATEGY_DEFAULT` and the strategy generates an unstable identifier it is possible to ask the user to
set the environemnt variable.
Default identifier generation (implementation details)
=======================================================
This section describes the inner working of the default hardware identifer strategy.
When the licensed software calls :ref:`identify_pc <api/public_api:Public api>` with :cpp:enumerator:`LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DEFAULT`
the identifier generation will follow these steps:
 - It will first look to the environment variable ``IDENTIFICATION_STRATEGY``. If set it will use the identification strategy in that variable.
 - It will try to determine which virtual environment the licensed software is running in.
    * If no virtual environment found it will use the strategies in :c:macro:`LCC_BARE_TO_METAL_STRATEGIES`, it will try them one by one until the first one succeeds.
    * If it detects it's running in a Virtual Machine it will try the strategies in :c:macro:`LCC_VM_STRATEGIES`, it will try them one by one until the first one succeeds.
if you're interested in implementing your own hardware identification strategy you can have a look to the library
 :ref:`extension points <api/extend:Tweak hardware signature generator>`.
.. TIP:
    If `licensecc` is generating a bad hardware identifier (eg. 'AAAA-AAAA-AAAA') software licensor can ask the user
    to set the environment variable ``IDENTIFICATION_STRATEGY`` and try again. Or he can send the user the `lccinspector`
    to generate all the possible identifiers for that machine.
.. NOTE::
    `licensecc` will try to validate the identifier using the same strategy that was used to generate it, regardless
    of what is the default method now in use. eg: disk identifiers will always be validated by ``DiskStrategy``.
doc/usage/issue-licenses.md
@@ -40,7 +40,7 @@
```
cd projects/DEFAULT #(or whatever your project name is) 
lcc license issue --client-signature XXXX-XXXX-XXXX-XXXX -o licenses/{license-file-name}.lic
lcc license issue --client-signature XXXX-XXXX-XXXX -o licenses/{license-file-name}.lic
```
usually this command is issued in the host machine where you compiled `licensecc`
@@ -52,7 +52,7 @@
|base64,b          | the license is encoded for inclusion in environment variables                                |
|valid-from        | Specify the start of the validity for this license. Format YYYY-MM-DD. If not specified defaults to today. |
|valid-to          | The expire date for this license. Format YYYY-MM-DD. If not specified the license won't expire |
|client-signature  | The signature of the hardware where the licensed software will run. It should be in the format XXXX-XXXX-XXXX-XXXX. If not specified the license won't be linked to a specific pc. |
|client-signature  | The signature of the hardware where the licensed software will run. It should be in the format XXXX-XXXX-XXXX. If not specified the license won't be linked to a specific pc. |
|output-file-name  | License output file path.                                                                    |
|extra-data        | Application specific data. They'll be returned when calling the `acquire_license` method   |
|feature-names     | Comma separated list of features to license. See `multi-feature` discussion.               |
src/inspector/inspector.cpp
@@ -10,15 +10,13 @@
#include "../library/os/dmi_info.hpp"
#include "../library/os/cpu_info.hpp"
#include "../library/os/dmi_info.hpp"
#include "../library/os/network.hpp"
using namespace std;
using namespace license::os;
const map<int, string> stringByStrategyId = {{STRATEGY_DEFAULT, "DEFAULT"},
                                             {STRATEGY_ETHERNET, "MAC"},
                                             {STRATEGY_IP_ADDRESS, "IP"},
                                             {STRATEGY_DISK_NUM, "Disk1"},
                                             {STRATEGY_DISK_LABEL, "Disk2"}};
const map<int, string> stringByStrategyId = {
    {STRATEGY_DEFAULT, "DEFAULT"}, {STRATEGY_ETHERNET, "MAC"}, {STRATEGY_IP_ADDRESS, "IP"}, {STRATEGY_DISK, "Disk"}};
const unordered_map<int, string> descByVirtDetail = {{BARE_TO_METAL, "No virtualization"},
                                                     {VMWARE, "Vmware"},
@@ -98,6 +96,24 @@
    cout << "Virtualiz. detail:" << descByVirtDetail.find(exec_env_info.virtualization_detail)->second << endl;
    cout << "Cloud provider   :" << descByCloudProvider.find(exec_env_info.cloud_provider)->second << endl;
    std::vector<license::os::OsAdapterInfo> adapterInfos;
    FUNCTION_RETURN ret = license::os::getAdapterInfos(adapterInfos);
    if (ret == FUNCTION_RETURN::FUNC_RET_OK) {
        for (auto osAdapter : adapterInfos) {
            cout << "Network adapter [" << osAdapter.id << "]: " << osAdapter.description << endl;
            cout << "   ip address [" << static_cast<unsigned int>(osAdapter.ipv4_address[3]) << "-"
                 << static_cast<unsigned int>(osAdapter.ipv4_address[2]) << "-"
                 << static_cast<unsigned int>(osAdapter.ipv4_address[1]) << "-"
                 << static_cast<unsigned int>(osAdapter.ipv4_address[0]) << "]" << endl;
            cout << "   mac address [";
            for (int i = 0; i < 8; i++) {
                // print mac
            }
        }
    } else {
        cout << "problem in getting adapter informations:" << ret << endl;
    }
    license::os::CpuInfo cpu;
    cout << "Cpu Vendor       :" << cpu.vendor() << endl;
    cout << "Cpu Brand        :" << cpu.brand() << endl;
src/library/LicenseReader.cpp
@@ -80,7 +80,7 @@
             *  sw_version_to = (optional int)
             *  from_date = YYYY-MM-DD (optional)
             *  to_date  = YYYY-MM-DD (optional)
             *  client_signature = XXXX-XXXX-XXXX-XXXX (optional string 16)
             *  client_signature = XXXX-XXXX-XXXX (optional string 16)
             *  sig = XXXXXXXXXX (mandatory, 1024)
             *  application_data = xxxxxxxxx (optional string 16)
             */
src/library/hw_identifier/disk_strategy.cpp
@@ -12,74 +12,63 @@
namespace license {
namespace hw_identifier {
static FUNCTION_RETURN generate_disk_pc_id(vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> &v_disk_id,
                                           bool use_id) {
    size_t disk_num = 0;
    size_t available_disk_info = 0;
    FUNCTION_RETURN result_diskinfos;
static array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> generate_id_by_sn(const DiskInfo &disk_info) {
    array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id;
    a_disk_id.fill(0);
    size_t size = min(a_disk_id.size(), sizeof(disk_info.disk_sn));
    memcpy(&a_disk_id[0], disk_info.disk_sn, size);
    result_diskinfos = getDiskInfos(nullptr, &disk_num);
    return a_disk_id;
}
static array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> generate_id_by_label(const DiskInfo &disk_info) {
    array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id;
    a_disk_id.fill(0);
    strncpy((char *)&a_disk_id[0], disk_info.label, a_disk_id.size() - 1);
    return a_disk_id;
}
static FUNCTION_RETURN generate_disk_pc_id(vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> &v_disk_id) {
    std::vector<DiskInfo> disk_infos;
    FUNCTION_RETURN result_diskinfos = getDiskInfos(disk_infos);
    if (result_diskinfos != FUNC_RET_OK && result_diskinfos != FUNC_RET_BUFFER_TOO_SMALL) {
        return result_diskinfos;
    }
    if (disk_num == 0) {
    if (disk_infos.size() == 0) {
        return FUNC_RET_NOT_AVAIL;
    }
    size_t mem = disk_num * sizeof(DiskInfo);
    DiskInfo *diskInfos = (DiskInfo *)malloc(mem);
    if (diskInfos == nullptr) {
        return FUNC_RET_NOT_AVAIL;
    }
    memset(diskInfos, 0, mem);
    result_diskinfos = getDiskInfos(diskInfos, &disk_num);
    if (result_diskinfos != FUNC_RET_OK) {
        free(diskInfos);
        return result_diskinfos;
    }
    for (unsigned int i = 0; i < disk_num; i++) {
        char firstChar = use_id ? diskInfos[i].label[0] : diskInfos[i].disk_sn[0];
        available_disk_info += firstChar == 0 ? 0 : 1;
    }
    if (available_disk_info == 0) {
        free(diskInfos);
        return FUNC_RET_NOT_AVAIL;
    }
    v_disk_id.reserve(available_disk_info);
    v_disk_id.reserve(disk_infos.size() * 2);
    for (int j = 0; j < 2; j++) {
        int preferred = (j == 0 ? 1 : 0);
        for (unsigned int i = 0; i < disk_num; i++) {
            array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id;
            a_disk_id.fill(0);
            if (use_id) {
                if (diskInfos[i].disk_sn[0] != 0 && diskInfos[i].preferred == preferred) {
                    size_t size = min(a_disk_id.size(), sizeof(&diskInfos[i].disk_sn));
                    memcpy(&a_disk_id[0], diskInfos[i].disk_sn, size);
        bool preferred = (j == 0);
        for (unsigned int i = 0; i < disk_infos.size(); i++) {
            if (disk_infos[i].preferred == preferred) {
                if (disk_infos[i].sn_initialized) {
                    array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> a_disk_id = generate_id_by_sn(disk_infos[i]);
                    v_disk_id.push_back(a_disk_id);
                }
            } else {
                if (diskInfos[i].label[0] != 0 && diskInfos[i].preferred == preferred) {
                    strncpy((char *)&a_disk_id[0], diskInfos[i].label, a_disk_id.size() - 1);
                    v_disk_id.push_back(a_disk_id);
                if (disk_infos[i].label_initialized) {
                    array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA> l_disk_id = generate_id_by_label(disk_infos[i]);
                    v_disk_id.push_back(l_disk_id);
                }
                if (preferred) {
                    break;
                }
            }
        }
    }
    free(diskInfos);
    return FUNC_RET_OK;
}
DiskStrategy::DiskStrategy(bool use_id) : m_use_id(use_id) {}
DiskStrategy::~DiskStrategy() {}
LCC_API_HW_IDENTIFICATION_STRATEGY DiskStrategy::identification_strategy() const {
    return m_use_id ? STRATEGY_DISK_NUM : STRATEGY_DISK_LABEL;
    return LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK;
}
std::vector<HwIdentifier> DiskStrategy::alternative_ids() const {
    vector<array<uint8_t, HW_IDENTIFIER_PROPRIETARY_DATA>> data;
    FUNCTION_RETURN result = generate_disk_pc_id(data, m_use_id);
    FUNCTION_RETURN result = generate_disk_pc_id(data);
    vector<HwIdentifier> identifiers;
    if (result == FUNC_RET_OK) {
        identifiers.reserve(data.size());
src/library/hw_identifier/disk_strategy.hpp
@@ -14,10 +14,8 @@
namespace hw_identifier {
class DiskStrategy : public IdentificationStrategy {
private:
    bool m_use_id;
public:
    DiskStrategy(bool use_id);
    inline DiskStrategy(){};
    virtual ~DiskStrategy();
    virtual LCC_API_HW_IDENTIFICATION_STRATEGY identification_strategy() const;
    virtual std::vector<HwIdentifier> alternative_ids() const;
src/library/hw_identifier/identification_strategy.cpp
@@ -45,11 +45,8 @@
        case STRATEGY_IP_ADDRESS:
            result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new Ethernet(true)));
            break;
        case STRATEGY_DISK_NUM:
            result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy(true)));
            break;
        case STRATEGY_DISK_LABEL:
            result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy(false)));
        case STRATEGY_DISK:
            result = unique_ptr<IdentificationStrategy>(dynamic_cast<IdentificationStrategy*>(new DiskStrategy()));
            break;
        default:
            throw logic_error("strategy not supported");
src/library/os/linux/os_linux.cpp
@@ -3,7 +3,13 @@
#include <stdio.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <fstream>
#include <unordered_map>
#include <string>
#include <stdio.h>
#include <string.h>
#include <sstream>
#include "../os.h"
#include "../../base/logger.h"
@@ -14,6 +20,13 @@
#include <valgrind/memcheck.h>
#endif
#ifdef USE_DISK_MODEL
#define PARSE_ID_FUNC parse_disk_id
#define ID_FOLDER "/dev/disk/by-id/"
#else
#define PARSE_ID_FUNC parseUUID
#define ID_FOLDER "/dev/disk/by-uuid/"
#endif
#ifdef USE_DBUS
#include <dbus-1.0/dbus/dbus.h>
#endif
@@ -55,138 +68,197 @@
    free(hexuuid);
}
#define MAX_UNITS 40
FUNCTION_RETURN getDiskInfos(DiskInfo *diskInfos, size_t *disk_info_size) {
    struct stat mount_stat, sym_stat;
    /*static char discard[1024];
     char device[64], name[64], type[64];
     */
    char cur_dir[MAX_PATH];
    struct mntent *ent;
static void parse_disk_id(const char *uuid, unsigned char *buffer_out, size_t out_size) {
    unsigned int i;
    size_t len = strlen(uuid);
    memset(buffer_out, 0, out_size);
    for (i = 0; i < len; i++) {
        buffer_out[i % out_size] = buffer_out[i % out_size] ^ uuid[i];
    }
}
    int maxDrives, currentDrive, i, drive_found;
    __ino64_t *statDrives = NULL;
    DiskInfo *tmpDrives = NULL;
    FILE *aFile = NULL;
    DIR *disk_by_uuid_dir = NULL, *disk_by_label = NULL;
/**
 *     int id;
    char device[MAX_PATH];
    unsigned char disk_sn[8];
    char label[255];
    int preferred;
 * @param blkidfile
 * @param diskInfos_out
 * @return
 */
static std::string getAttribute(const std::string &source, const std::string &attrName) {
    std::string attr_namefull = attrName + "=\"";
    std::size_t startpos = source.find(attr_namefull) + attr_namefull.size();
    std::size_t endpos = source.find("\"", startpos);
    return source.substr(startpos, endpos - startpos);
}
FUNCTION_RETURN parse_blkid(const std::string &blkid_file_content, std::vector<DiskInfo> &diskInfos_out) {
    DiskInfo diskInfo;
    int diskNum = 0;
    for (std::size_t oldpos = 0, pos = 0; (pos = blkid_file_content.find("</device>", oldpos)) != std::string::npos;
         oldpos = pos + 1) {
        std::string cur_dev = blkid_file_content.substr(oldpos, pos);
        diskInfo.id = diskNum++;
        std::string device = cur_dev.substr(cur_dev.find_last_of(">") + 1);
        strncpy(diskInfo.device, device.c_str(), MAX_PATH);
        std::string label = getAttribute(cur_dev, "PARTLABEL");
        strncpy(diskInfo.label, label.c_str(), 255);
        std::string disk_sn = getAttribute(cur_dev, "UUID");
        parseUUID(disk_sn.c_str(), diskInfo.disk_sn, sizeof(diskInfo.disk_sn));
        std::string disk_type = getAttribute(cur_dev, "TYPE");
        // unlikely that somebody put the swap on a removable disk.
        // this is a first rough guess on what can be a preferred disk for blkid devices
        // just in case /etc/fstab can't be accessed or it is not up to date.
        diskInfo.preferred = (disk_type == "swap");
        diskInfos_out.push_back(diskInfo);
    }
    return FUNCTION_RETURN::FUNC_RET_OK;
}
#define BLKID_LOCATIONS {"/run/blkid/blkid.tab", "/etc/blkid.tab"};
static FUNCTION_RETURN getDiskInfos_blkid(std::vector<DiskInfo> &diskInfos) {
    const char *strs[] = BLKID_LOCATIONS;
    bool can_read = false;
    std::stringstream buffer;
    for (int i = 0; i < sizeof(strs) / sizeof(const char *); i++) {
        const char *location = strs[i];
        std::ifstream t(location);
        if (t.is_open()) {
            buffer << t.rdbuf();
            can_read = true;
            break;
        }
    }
    if (!can_read) {
        return FUNCTION_RETURN::FUNC_RET_NOT_AVAIL;
    }
    return parse_blkid(buffer.str(), diskInfos);
}
#define MAX_UNITS 40
FUNCTION_RETURN getDiskInfos_dev(std::vector<DiskInfo> &diskInfos) {
    struct dirent *dir = NULL;
    struct stat sym_stat;
    FUNCTION_RETURN result;
    if (diskInfos != NULL) {
        maxDrives = *disk_info_size;
        tmpDrives = diskInfos;
    DIR *disk_by_uuid_dir = opendir(ID_FOLDER);
    if (disk_by_uuid_dir == nullptr) {
        LOG_DEBUG("Open " ID_FOLDER " fail");
    } else {
        maxDrives = MAX_UNITS;
        tmpDrives = (DiskInfo *)malloc(sizeof(DiskInfo) * maxDrives);
    }
    memset(tmpDrives, 0, sizeof(DiskInfo) * maxDrives);
    statDrives = (__ino64_t *)malloc(maxDrives * sizeof(__ino64_t));
    memset(statDrives, 0, sizeof(__ino64_t) * maxDrives);
    aFile = setmntent("/proc/mounts", "r");
    if (aFile == NULL) {
        /*proc not mounted*/
        free(tmpDrives);
        free(statDrives);
        return FUNC_RET_ERROR;
    }
    currentDrive = 0;
    while (NULL != (ent = getmntent(aFile)) && currentDrive < maxDrives) {
        if ((strncmp(ent->mnt_type, "ext", 3) == 0 || strncmp(ent->mnt_type, "xfs", 3) == 0 ||
             strncmp(ent->mnt_type, "vfat", 4) == 0 || strncmp(ent->mnt_type, "ntfs", 4) == 0 ||
             strncmp(ent->mnt_type, "btr", 3) == 0) &&
            ent->mnt_fsname != NULL && strncmp(ent->mnt_fsname, "/dev/", 5) == 0) {
            if (stat(ent->mnt_fsname, &mount_stat) == 0) {
                drive_found = -1;
                for (i = 0; i < currentDrive; i++) {
                    if (statDrives[i] == mount_stat.st_ino) {
                        drive_found = i;
                    }
                }
                if (drive_found == -1) {
                    LOG_DEBUG("mntent fs:[%s],dir:[%s],inode:[%d]\n", ent->mnt_fsname, ent->mnt_dir,
                              (unsigned long int)mount_stat.st_ino);
                    strncpy(tmpDrives[currentDrive].device, ent->mnt_fsname, 255 - 1);
                    statDrives[currentDrive] = mount_stat.st_ino;
                    drive_found = currentDrive;
                    currentDrive++;
                }
                if (strcmp(ent->mnt_dir, "/") == 0) {
                    strcpy(tmpDrives[drive_found].label, "root");
                    LOG_DEBUG("drive %s set to preferred\n", ent->mnt_fsname);
                    tmpDrives[drive_found].preferred = 1;
                } else {
                    tmpDrives[drive_found].preferred = 0;
                }
            } else {
                LOG_DEBUG("Error %s during stat of %s \n", std::strerror(errno), ent->mnt_fsname);
        const std::string base_dir(ID_FOLDER "/");
        while ((dir = readdir(disk_by_uuid_dir)) != nullptr && diskInfos.size() < MAX_UNITS) {
            if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0 ||
                strncmp(dir->d_name, "usb", 3) == 0) {
                continue;
            }
        }
    }
    endmntent(aFile);
    if (diskInfos == NULL) {
        *disk_info_size = currentDrive;
        free(tmpDrives);
        result = (currentDrive > 0) ? FUNC_RET_OK : FUNC_RET_NOT_AVAIL;
    } else if (*disk_info_size >= currentDrive) {
        disk_by_uuid_dir = opendir("/dev/disk/by-uuid");
        if (disk_by_uuid_dir == nullptr) {
            LOG_WARN("Open /dev/disk/by-uuid fail");
            free(statDrives);
            return FUNC_RET_ERROR;
        }
        result = FUNC_RET_OK;
        *disk_info_size = currentDrive;
        while ((dir = readdir(disk_by_uuid_dir)) != nullptr) {
            std::string cur_dir("/dev/disk/by-uuid/");
            cur_dir += dir->d_name;
            bool found = false;
            std::string cur_dir = base_dir + dir->d_name;
            if (stat(cur_dir.c_str(), &sym_stat) == 0) {
                for (i = 0; i < currentDrive; i++) {
                    if (sym_stat.st_ino == statDrives[i]) {
                        found = true;
                        parseUUID(dir->d_name, tmpDrives[i].disk_sn, sizeof(tmpDrives[i].disk_sn));
#ifndef NDEBUG
                        VALGRIND_CHECK_VALUE_IS_DEFINED(tmpDrives[i].device);
                        LOG_DEBUG("uuid %d %s %02x%02x%02x%02x", i, tmpDrives[i].device, tmpDrives[i].disk_sn[0],
                                  tmpDrives[i].disk_sn[1], tmpDrives[i].disk_sn[2], tmpDrives[i].disk_sn[3]);
#endif
                DiskInfo tmpDiskInfo;
                tmpDiskInfo.id = sym_stat.st_ino;
                ssize_t len = ::readlink(cur_dir.c_str(), tmpDiskInfo.device, sizeof(tmpDiskInfo.device) - 1);
                if (len != -1) {
                    tmpDiskInfo.device[len] = '\0';
                    PARSE_ID_FUNC(dir->d_name, tmpDiskInfo.disk_sn, sizeof(tmpDiskInfo.disk_sn));
                    tmpDiskInfo.sn_initialized = true;
                    tmpDiskInfo.label_initialized = false;
                    tmpDiskInfo.preferred = false;
                    bool found = false;
                    for (auto diskInfo : diskInfos) {
                        if (tmpDiskInfo.id == diskInfo.id) {
                            found = true;
                            break;
                        }
                    }
                }
                if (!found) {
                    LOG_DEBUG("Drive [%s], num [%d] inode [%d] did not match any existing drive", cur_dir.c_str(),
                              (unsigned long int)sym_stat.st_ino);
                    if (!found) {
                        diskInfos.push_back(tmpDiskInfo);
                    }
                } else {
                    LOG_DEBUG("Error %s during readlink of %s", std::strerror(errno), cur_dir.c_str());
                }
            } else {
                LOG_DEBUG("Error %s during stat of %s", std::strerror(errno), cur_dir.c_str());
            }
        }
        closedir(disk_by_uuid_dir);
    }
        disk_by_label = opendir("/dev/disk/by-label");
        if (disk_by_label != nullptr) {
            while ((dir = readdir(disk_by_label)) != nullptr) {
                strcpy(cur_dir, "/dev/disk/by-label/");
                strcat(cur_dir, dir->d_name);
                if (stat(cur_dir, &sym_stat) == 0) {
                    for (i = 0; i < currentDrive; i++) {
                        if (sym_stat.st_ino == statDrives[i]) {
                            strncpy(tmpDrives[i].label, dir->d_name, 255 - 1);
                            LOG_DEBUG("label %d %s %s", i, tmpDrives[i].label, tmpDrives[i].device);
                        }
    result = diskInfos.size() > 0 ? FUNCTION_RETURN::FUNC_RET_OK : FUNCTION_RETURN::FUNC_RET_NOT_AVAIL;
    const std::string label_dir("/dev/disk/by-label");
    DIR *disk_by_label = opendir(label_dir.c_str());
    if (disk_by_label != nullptr) {
        while ((dir = readdir(disk_by_label)) != nullptr) {
            if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
                continue;
            }
            std::string cur_disk_label = label_dir + "/" + dir->d_name;
            if (stat(cur_disk_label.c_str(), &sym_stat) == 0) {
                bool found = false;
                for (auto diskInfo : diskInfos) {
                    if (((int)sym_stat.st_ino) == diskInfo.id) {
                        strncpy(diskInfo.label, dir->d_name, 255 - 1);
                        diskInfo.label_initialized = true;
                        break;
                    }
                }
            } else {
                LOG_DEBUG("Stat %s for fail:F %s", cur_disk_label, std::strerror(errno));
            }
            closedir(disk_by_label);
        }
        closedir(disk_by_label);
    } else {
        result = FUNC_RET_BUFFER_TOO_SMALL;
        LOG_DEBUG("Open %s for reading disk labels fail", label_dir);
    }
    free(statDrives);
    return result;
}
/**
 * Try to determine removable devices: as a first guess removable devices doesn't have
 * an entry in /etc/fstab
 *
 * @param diskInfos
 */
static void set_preferred_disks(std::vector<DiskInfo> &diskInfos) {
    FILE *fstabFile = setmntent("/etc/fstab", "r");
    if (fstabFile == nullptr) {
        /*fstab not accessible*/
        return;
    }
    struct mntent *ent;
    while (nullptr != (ent = getmntent(fstabFile))) {
        bool found = false;
        for (auto disk_info : diskInfos) {
            if (strcmp(ent->mnt_fsname, disk_info.device) == 0) {
                disk_info.preferred = true;
                break;
            }
        }
    }
    endmntent(fstabFile);
    return;
}
/**
 * First try to read disk_infos from /dev/disk/by-id folder, if fails try to use
 * blkid cache to see what's in there, then try to exclude removable disks
 * looking at /etc/fstab
 * @param diskInfos_out vector used to output the disk informations
 * @return
 */
FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo> &disk_infos) {
    FUNCTION_RETURN result = getDiskInfos_dev(disk_infos);
    if (result != FUNCTION_RETURN::FUNC_RET_OK) {
        result = getDiskInfos_blkid(disk_infos);
    }
    if (result == FUNCTION_RETURN::FUNC_RET_OK) {
        set_preferred_disks(disk_infos);
    }
    return result;
}
src/library/os/os.h
@@ -8,31 +8,32 @@
#ifndef OS_DEPENDENT_HPP_
#define OS_DEPENDENT_HPP_
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
// definition of size_t
#include <stdlib.h>
#include <vector>
#ifdef __unix__
#include <unistd.h>
#include <stdbool.h>
#endif
#include <licensecc/datatypes.h>
#include "../base/base.h"
typedef struct {
    int id;
    char device[MAX_PATH];
    unsigned char disk_sn[8];
    bool sn_initialized;
    char label[255];
    int preferred;
    bool label_initialized;
    bool preferred;
} DiskInfo;
FUNCTION_RETURN getDiskInfos(DiskInfo* diskInfos, size_t* disk_info_size);
FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo>& diskInfos);
FUNCTION_RETURN getUserHomePath(char[MAX_PATH]);
FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]);
FUNCTION_RETURN getMachineName(unsigned char identifier[6]);
@@ -56,7 +57,6 @@
 */
FUNCTION_RETURN getOsSpecificIdentifier(unsigned char identifier[6]);
// FUNCTION_RETURN verifySignature(const char* stringToVerify, const char* signatureB64);
#ifdef _WIN32
#define SETENV(VAR, VAL) _putenv_s(VAR, VAL);
@@ -64,10 +64,6 @@
#else
#define SETENV(VAR, VAL) setenv(VAR, VAL, 1);
#define UNSETENV(P) unsetenv(P);
#endif
#ifdef __cplusplus
}
#endif
#endif /* OS_DEPENDENT_HPP_ */
src/library/os/windows/os_win.cpp
@@ -21,77 +21,63 @@
    return result;
}
//http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c
//http://stackoverflow.com/questions/3098696/same-code-returns-diffrent-result-on-windows7-32-bit-system
// http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c
// http://stackoverflow.com/questions/3098696/same-code-returns-diffrent-result-on-windows7-32-bit-system
#define MAX_UNITS 30
//bug check return with diskinfos == null func_ret_ok
FUNCTION_RETURN getDiskInfos(DiskInfo * diskInfos, size_t * disk_info_size) {
// bug check return with diskinfos == null func_ret_ok
FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo>& diskInfos) {
    DWORD fileMaxLen;
    size_t ndrives = 0;
    DWORD fileFlags;
    char volName[MAX_PATH], fileSysName[MAX_PATH];
    DWORD volSerial = 0;
    const DWORD dwSize = MAX_PATH;
    char szLogicalDrives[MAX_PATH] = { 0 };
    char szLogicalDrives[MAX_PATH] = {0};
    FUNCTION_RETURN return_value = FUNC_RET_NOT_AVAIL;
    const DWORD dwResult = GetLogicalDriveStrings(dwSize, szLogicalDrives);
    if (dwResult > 0 && dwResult <= MAX_PATH) {
    if (dwResult > 0) {
        return_value = FUNC_RET_OK;
        char* szSingleDrive = szLogicalDrives;
        while (*szSingleDrive && ndrives < MAX_UNITS) {
            // get the next drive
            UINT driveType = GetDriveType(szSingleDrive);
            if (driveType == DRIVE_FIXED) {
                BOOL success = GetVolumeInformation(szSingleDrive, volName, MAX_PATH,
                                                    &volSerial, &fileMaxLen, &fileFlags, fileSysName,
                                                    MAX_PATH);
                BOOL success = GetVolumeInformation(szSingleDrive, volName, MAX_PATH, &volSerial, &fileMaxLen,
                                                    &fileFlags, fileSysName, MAX_PATH);
                if (success) {
                    LOG_INFO("drive         : %s", szSingleDrive);
                    LOG_INFO("Volume Name   : %s", volName);
                    LOG_INFO("Volume Serial : 0x%x", volSerial);
                    LOG_DEBUG("Max file length : %d", fileMaxLen);
                    LOG_DEBUG("Filesystem      : %s", fileSysName);
                    if (diskInfos != NULL) {
                        if (ndrives < (int)*disk_info_size) {
                            diskInfos[ndrives].id = (int)ndrives;
                            strncpy(diskInfos[ndrives].device, volName,
                                    min(std::size_t{MAX_PATH}, sizeof(volName)) - 1);
                            strncpy(diskInfos[ndrives].label, fileSysName,
                                    min(sizeof(diskInfos[ndrives].label), sizeof(fileSysName)) - 1);
                            memcpy(diskInfos[ndrives].disk_sn, &volSerial, sizeof(DWORD));
                            diskInfos[ndrives].preferred = (szSingleDrive[0] == 'C');
                        } else {
                            return_value = FUNC_RET_BUFFER_TOO_SMALL;
                        }
                    }
                    DiskInfo diskInfo;
                    diskInfo.id = (int)ndrives;
                    strncpy(diskInfo.device, volName, min(std::size_t{MAX_PATH}, sizeof(volName)) - 1);
                    strncpy(diskInfo.label, fileSysName,
                            min(sizeof(diskInfos[ndrives].label), sizeof(fileSysName)) - 1);
                    memcpy(diskInfo.disk_sn, &volSerial, sizeof(DWORD));
                    diskInfo.preferred = (szSingleDrive[0] == 'C');
                    diskInfos.push_back(diskInfo);
                    ndrives++;
                } else {
                    LOG_WARN("Unable to retrieve information of '%s'", szSingleDrive);
                    LOG_DEBUG("Unable to retrieve information of '%s'", szSingleDrive);
                }
            } else {
                LOG_INFO("This volume is not fixed : %s, type: %d",    szSingleDrive);
                LOG_DEBUG("This volume is not fixed : %s, type: %d", szSingleDrive);
            }
            szSingleDrive += strlen(szSingleDrive) + 1;
        }
    }
    if (diskInfos == NULL || *disk_info_size == 0) {
        if (ndrives > 0) {
            return_value = FUNC_RET_OK;
        } else {
            return_value = FUNC_RET_NOT_AVAIL;
            LOG_INFO("No fixed drive was detected");
        }
        *disk_info_size = ndrives;
    if (diskInfos.size() > 0) {
        return_value = FUNC_RET_OK;
    } else {
        *disk_info_size = min(ndrives, *disk_info_size);
        return_value = FUNC_RET_NOT_AVAIL;
        LOG_INFO("No fixed drive were detected");
    }
    return return_value;
}
FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]) {
    FUNCTION_RETURN result = FUNC_RET_OK;
@@ -101,4 +87,3 @@
    }
    return result;
}
src/templates/licensecc_properties.h.in
@@ -78,8 +78,9 @@
enum LCC_API_HW_IDENTIFICATION_STRATEGY {
    /**
     * \brief Default strategy.
     *
     * This strategy try to detect which virtual environment the software is running in.
     *
     * This strategy first checks the content of the environment variable `IDENTIFICATION_STRATEGY`. If the variable is defined it will use the
     * strategy specified in there, if not defined it will try to detect which virtual environment the software is running in.
     *  - If no virtual environment is detected it will try the strategies defined in ::LCC_BARE_TO_METAL_STRATEGIES
     *  - If it detects the software is running in a virtual machine it will use ::LCC_VM_STRATEGIES
     *  - If it detects the software is running in a docker or in an LXC it will use ::LCC_DOCKER_STRATEGIES or
@@ -89,10 +90,15 @@
    STRATEGY_DEFAULT = -1,
    STRATEGY_ETHERNET = 0,
    STRATEGY_IP_ADDRESS = 1,
    STRATEGY_DISK_NUM = 2,
    STRATEGY_DISK_LABEL = 3,
    STRATEGY_MEMORY_CPU_SIZE = 4,
    STRATEGY_HOST_NAME = 5,
    STRATEGY_DISK = 2,
    /**
    * Not yet implemented
    */
    STRATEGY_CPU_SIZE = 3,
    /**
    * Not yet implemented
    */
    STRATEGY_HOST_NAME = 4,
    STRATEGY_NONE = -2
};
@@ -103,7 +109,7 @@
/**
 * List the strategies used when no virtual envrionment is detected
 */
#define LCC_BARE_TO_METAL_STRATEGIES { STRATEGY_ETHERNET, STRATEGY_DISK_LABEL, STRATEGY_NONE }
#define LCC_BARE_TO_METAL_STRATEGIES { STRATEGY_DISK, STRATEGY_HOST_NAME, STRATEGY_NONE }
/**
 * List the strategies used when the software is executing in a virtual machine
 */
test/functional/hw_identifier_it_test.cpp
@@ -49,23 +49,12 @@
BOOST_AUTO_TEST_CASE(volid_lic_file) {
    HwIdentifier identifier_out;
    size_t disk_num;
    FUNCTION_RETURN result_diskinfos = getDiskInfos(nullptr, &disk_num);
    if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && disk_num > 0) {
        generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK_NUM, "volid_lic_file");
    vector<DiskInfo> diskInfos;
    FUNCTION_RETURN result_diskinfos = getDiskInfos(diskInfos);
    if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && diskInfos.size() > 0) {
        generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK, "volid_lic_file");
    } else {
        BOOST_TEST_MESSAGE("No disk found skipping testing disk hardware identifier");
    }
}
BOOST_AUTO_TEST_CASE(volume_name_lic_file) {
    HwIdentifier identifier_out;
    size_t disk_num;
    FUNCTION_RETURN result_diskinfos = getDiskInfos(nullptr, &disk_num);
    if ((result_diskinfos == FUNC_RET_BUFFER_TOO_SMALL || result_diskinfos == FUNC_RET_OK) && disk_num > 0) {
        generate_and_verify_license(LCC_API_HW_IDENTIFICATION_STRATEGY::STRATEGY_DISK_LABEL, "volume_name_lic_file");
    } else {
        BOOST_TEST_MESSAGE("No disk found skipping testing volume name disk hardware identifier");
    }
}
test/library/os_linux_test.cpp
@@ -9,39 +9,64 @@
#include "../../src/library/os/os.h"
#include "../../src/library/os/execution_environment.hpp"
FUNCTION_RETURN parse_blkid(const std::string &blkid_file_content, std::vector<DiskInfo> &diskInfos_out);
namespace license {
namespace test {
using namespace std;
using namespace os;
// using namespace os;
BOOST_AUTO_TEST_CASE(read_disk_id) {
    os::ExecutionEnvironment exec_env;
    LCC_API_VIRTUALIZATION_SUMMARY virt = exec_env.virtualization();
    vector<DiskInfo> disk_infos;
    FUNCTION_RETURN result = getDiskInfos(disk_infos);
    if (virt == LCC_API_VIRTUALIZATION_SUMMARY::NONE || virt == LCC_API_VIRTUALIZATION_SUMMARY::VM) {
        DiskInfo *diskInfos = NULL;
        size_t disk_info_size = 0;
        FUNCTION_RETURN result = getDiskInfos(NULL, &disk_info_size);
        BOOST_CHECK_EQUAL(result, FUNC_RET_OK);
        BOOST_CHECK_GT(disk_info_size, 0);
        diskInfos = (DiskInfo*) malloc(sizeof(DiskInfo) * disk_info_size);
        result = getDiskInfos(diskInfos, &disk_info_size);
        BOOST_CHECK_EQUAL(result, FUNC_RET_OK);
        BOOST_CHECK_GT(mstrnlen_s(diskInfos[0].device, sizeof(diskInfos[0].device)), 0);
        BOOST_CHECK_GT(mstrnlen_s(diskInfos[0].label, sizeof diskInfos[0].label), 0);
        bool all_zero = true;
        for (int i = 0; i < sizeof(diskInfos[0].disk_sn) && all_zero; i++) {
            all_zero = (diskInfos[0].disk_sn[i] == 0);
        BOOST_REQUIRE_MESSAGE(disk_infos.size() > 0, "Found some disk");
        bool preferred_found = false;
        bool uuid_found = false;
        bool label_found = false;
        for (auto disk_info : disk_infos) {
            uuid_found |= disk_info.sn_initialized;
            preferred_found |= disk_info.preferred;
            label_found |= disk_info.label_initialized;
            if (disk_info.sn_initialized) {
                bool all_zero = true;
                for (int i = 0; i < sizeof(disk_info.disk_sn) && all_zero; i++) {
                    all_zero = (disk_info.disk_sn[i] == 0);
                }
                BOOST_CHECK_MESSAGE(!all_zero, "disksn is not all zero");
            }
        }
        BOOST_CHECK_MESSAGE(!all_zero, "disksn is not all zero");
        free(diskInfos);
        BOOST_CHECK_MESSAGE(uuid_found, "At least one UUID initialized");
        BOOST_CHECK_MESSAGE(label_found, "At least one label found");
        BOOST_CHECK_MESSAGE(preferred_found, "At least one standard 'root' file system");
    } else if (virt == LCC_API_VIRTUALIZATION_SUMMARY::CONTAINER) {
        // docker or lxc diskInfo is not meaningful
        DiskInfo *diskInfos = NULL;
        size_t disk_info_size = 0;
        FUNCTION_RETURN result = getDiskInfos(NULL, &disk_info_size);
        // in docker or lxc diskInfo is very likely not to find any good disk.
        BOOST_CHECK_EQUAL(result, FUNC_RET_NOT_AVAIL);
        BOOST_REQUIRE_MESSAGE(disk_infos.size() == 0, "Found no disk");
    }
}
} // namespace test
} // namespace license
BOOST_AUTO_TEST_CASE(parse_blkid_file) {
    const string blkid_content =
        "<device DEVNO=\"0x0803\" TIME=\"1603155692.238672\" "
        "UUID=\"baccfd49-5203-4e34-9b8b-a2bbaf9b4e24\" TYPE=\"swap\" PARTLABEL=\"Linux swap\" "
        "PARTUUID=\"7d84b1a8-5492-4651-b720-61c723fb8c69\">/dev/sda3</device>"
        "<device DEVNO=\"0x10302\" TIME=\"1603155692.253094\" UUID=\"d1b5b096-5e58-4e4f-af39-be12038c9bed\" "
        "TYPE=\"ext4\" PARTLABEL=\"Linux filesystem\" PARTUUID=\"3d742821-3167-43fa-9f22-e9bea9a9ce64\">"
        "/dev/nvme0n1p2</device>";
    vector<DiskInfo> disk_infos;
    FUNCTION_RETURN result = parse_blkid(blkid_content, disk_infos);
    BOOST_CHECK_EQUAL(result, FUNC_RET_OK);
    BOOST_CHECK_MESSAGE(disk_infos.size() == 2, "Two disks found");
    BOOST_CHECK_MESSAGE(string("Linux swap") == disk_infos[0].label, "Label parsed OK");
    BOOST_CHECK_MESSAGE(string("/dev/sda3") == disk_infos[0].device, "device parsed");
    BOOST_CHECK_MESSAGE(disk_infos[0].preferred, "Preferred found");
}
}  // namespace test
}  // namespace license