/* * Copyright (C) 2014 Pavel Kirienko */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace uavcan_linux { /** * This class can find and read machine ID from a text file, represented as 32-char (16-byte) long hexadecimal string, * possibly with separators (like dashes or colons). If the available ID is more than 16 bytes, extra bytes will be * ignored. A shorter ID will not be accepted as valid. * In order to be read, the ID must be located on the first line of the file and must not contain any whitespace * characters. * * Examples of valid ID: * 0123456789abcdef0123456789abcdef * 20CE0b1E-8C03-07C8-13EC-00242C491652 */ class MachineIDReader { public: static constexpr int MachineIDSize = 16; typedef std::array MachineID; static std::vector getDefaultSearchLocations() { return { "/etc/machine-id", "/var/lib/dbus/machine-id", "/sys/class/dmi/id/product_uuid" }; } private: const std::vector search_locations_; static std::vector mergeLists(const std::vector& a, const std::vector& b) { std::vector ab; ab.reserve(a.size() + b.size()); ab.insert(ab.end(), a.begin(), a.end()); ab.insert(ab.end(), b.begin(), b.end()); return ab; } bool tryRead(const std::string& location, MachineID& out_id) const { /* * Reading the file */ std::string token; try { std::ifstream infile(location); infile >> token; } catch (std::exception&) { return false; } /* * Preprocessing the input string - convert to lowercase, remove all non-hex characters, limit to 32 chars */ std::transform(token.begin(), token.end(), token.begin(), [](char x) { return std::tolower(x); }); token.erase(std::remove_if(token.begin(), token.end(), [](char x){ return (x < 'a' || x > 'f') && !std::isdigit(x); }), token.end()); if (token.length() < (MachineIDSize * 2)) { return false; } token.resize(MachineIDSize * 2); // Truncating /* * Parsing the string as hex bytes */ auto sym = std::begin(token); for (auto& byte : out_id) { assert(sym != std::end(token)); byte = std::stoi(std::string{*sym++, *sym++}, nullptr, 16); } return true; } public: /** * This class can use extra seach locations. If provided, they will be checked first, before default ones. */ MachineIDReader(const std::vector& extra_search_locations = {}) : search_locations_(mergeLists(extra_search_locations, getDefaultSearchLocations())) { } /** * Just like @ref readAndGetLocation(), but this one doesn't return location where this ID was obtained from. */ MachineID read() const { return readAndGetLocation().first; } /** * This function checks available search locations and reads the ID from the first valid location. * It returns std::pair<> with ID and the file path where it was read from. * In case if none of the search locations turned out to be valid, @ref uavcan_linux::Exception will be thrown. */ std::pair readAndGetLocation() const { for (auto x : search_locations_) { auto out = MachineID(); if (tryRead(x, out)) { return {out, x}; } } throw Exception("Failed to read machine ID"); } }; /** * This class computes unique ID for a UAVCAN node in a Linux application. * It takes the following inputs: * - Unique machine ID * - Node name string (e.g. "org.uavcan.linux_app.dynamic_node_id_server") * - Instance ID byte, e.g. node ID (optional) */ inline std::array makeApplicationID(const MachineIDReader::MachineID& machine_id, const std::string& node_name, const std::uint8_t instance_id = 0) { union HalfID { std::uint64_t num; std::uint8_t bytes[8]; HalfID(std::uint64_t arg_num) : num(arg_num) { } }; std::array out; // First 8 bytes of the application ID are CRC64 of the machine ID in native byte order { uavcan::DataTypeSignatureCRC crc; crc.add(machine_id.data(), static_cast(machine_id.size())); HalfID half(crc.get()); std::copy_n(half.bytes, 8, out.begin()); } // Last 8 bytes of the application ID are CRC64 of the node name and optionally node ID { uavcan::DataTypeSignatureCRC crc; crc.add(reinterpret_cast(node_name.c_str()), static_cast(node_name.length())); crc.add(instance_id); HalfID half(crc.get()); std::copy_n(half.bytes, 8, out.begin() + 8); } return out; } }