review of disk strategy linux
| | |
| | | **/CMakeCache.txt |
| | | **/CMakeFiles |
| | | /CMakeSettings.json |
| | | /.venv/ |
| | |
| | | - 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 |
| | |
| | | |
| | | # 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'] |
| | |
| | | ******************* |
| | | 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>`_ ). |
| | |
| | | |
| | | 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 |
| | | |
| | |
| | | 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``. |
| | |
| | | |
| | | ``` |
| | | 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` |
| | | |
| | |
| | | |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. | |
| | |
| | | #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"}, |
| | |
| | | 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; |
| | |
| | | * 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) |
| | | */ |
| | |
| | | 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()); |
| | |
| | | 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; |
| | |
| | | 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"); |
| | |
| | | #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" |
| | | |
| | |
| | | #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 |
| | |
| | | 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; |
| | | } 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); |
| | | } |
| | | } |
| | | } |
| | | 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"); |
| | | DIR *disk_by_uuid_dir = opendir(ID_FOLDER); |
| | | if (disk_by_uuid_dir == nullptr) { |
| | | LOG_WARN("Open /dev/disk/by-uuid fail"); |
| | | free(statDrives); |
| | | return FUNC_RET_ERROR; |
| | | LOG_DEBUG("Open " ID_FOLDER " fail"); |
| | | } else { |
| | | 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; |
| | | } |
| | | 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]) { |
| | | 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; |
| | | 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 |
| | | 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); |
| | | 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"); |
| | | 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) { |
| | | 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); |
| | | 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); |
| | | } |
| | | } 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; |
| | | } |
| | | |
| | |
| | | #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]); |
| | |
| | | */ |
| | | 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); |
| | |
| | | #else |
| | | #define SETENV(VAR, VAL) setenv(VAR, VAL, 1); |
| | | #define UNSETENV(P) unsetenv(P); |
| | | #endif |
| | | |
| | | #ifdef __cplusplus |
| | | } |
| | | #endif |
| | | |
| | | #endif /* OS_DEPENDENT_HPP_ */ |
| | |
| | | //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) { |
| | | FUNCTION_RETURN getDiskInfos(std::vector<DiskInfo>& diskInfos) { |
| | | DWORD fileMaxLen; |
| | | size_t ndrives = 0; |
| | | DWORD fileFlags; |
| | |
| | | const DWORD dwSize = MAX_PATH; |
| | | 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, |
| | | 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(diskInfos[ndrives].disk_sn, &volSerial, sizeof(DWORD)); |
| | | diskInfos[ndrives].preferred = (szSingleDrive[0] == 'C'); |
| | | } else { |
| | | return_value = FUNC_RET_BUFFER_TOO_SMALL; |
| | | } |
| | | } |
| | | 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); |
| | | } |
| | | szSingleDrive += strlen(szSingleDrive) + 1; |
| | | LOG_DEBUG("This volume is not fixed : %s, type: %d", szSingleDrive); |
| | | } |
| | | } |
| | | if (diskInfos == NULL || *disk_info_size == 0) { |
| | | if (ndrives > 0) { |
| | | } |
| | | if (diskInfos.size() > 0) { |
| | | return_value = FUNC_RET_OK; |
| | | } else { |
| | | return_value = FUNC_RET_NOT_AVAIL; |
| | | LOG_INFO("No fixed drive was detected"); |
| | | } |
| | | *disk_info_size = ndrives; |
| | | } else { |
| | | *disk_info_size = min(ndrives, *disk_info_size); |
| | | } |
| | | return return_value; |
| | | LOG_INFO("No fixed drive were detected"); |
| | | } |
| | | |
| | | return return_value; |
| | | } |
| | | |
| | | FUNCTION_RETURN getModuleName(char buffer[MAX_PATH]) { |
| | | FUNCTION_RETURN result = FUNC_RET_OK; |
| | |
| | | } |
| | | return result; |
| | | } |
| | | |
| | |
| | | /** |
| | | * \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 |
| | |
| | | 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 |
| | | }; |
| | | |
| | |
| | | /** |
| | | * 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 |
| | | */ |
| | |
| | | |
| | | 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"); |
| | | } |
| | | } |
| | | |
| | |
| | | #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); |
| | | 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(diskInfos[0].disk_sn) && all_zero; i++) { |
| | | all_zero = (diskInfos[0].disk_sn[i] == 0); |
| | | 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"); |
| | | free(diskInfos); |
| | | } 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); |
| | | BOOST_CHECK_EQUAL(result, FUNC_RET_NOT_AVAIL); |
| | | } |
| | | } |
| | | 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) { |
| | | // 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"); |
| | | } |
| | | } |
| | | |
| | | 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 |