123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /*
- * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
- */
- #pragma once
- #include <array>
- #include <cassert>
- #include <cctype>
- #include <fstream>
- #include <string>
- #include <vector>
- #include <cstdint>
- #include <algorithm>
- #include <utility>
- #include <uavcan_linux/exception.hpp>
- #include <uavcan/data_type.hpp>
- 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<std::uint8_t, MachineIDSize> MachineID;
- static std::vector<std::string> getDefaultSearchLocations()
- {
- return
- {
- "/etc/machine-id",
- "/var/lib/dbus/machine-id",
- "/sys/class/dmi/id/product_uuid"
- };
- }
- private:
- const std::vector<std::string> search_locations_;
- static std::vector<std::string> mergeLists(const std::vector<std::string>& a, const std::vector<std::string>& b)
- {
- std::vector<std::string> 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<std::string>& 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<MachineID, std::string> 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<std::uint8_t, 16> 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<std::uint8_t, 16> 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<unsigned>(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<const std::uint8_t*>(node_name.c_str()), static_cast<unsigned>(node_name.length()));
- crc.add(instance_id);
- HalfID half(crc.get());
- std::copy_n(half.bytes, 8, out.begin() + 8);
- }
- return out;
- }
- }
|