diff --git a/libuavcan_drivers/linux/CMakeLists.txt b/libuavcan_drivers/linux/CMakeLists.txt index 51cbcf1c97..a88c7da13a 100644 --- a/libuavcan_drivers/linux/CMakeLists.txt +++ b/libuavcan_drivers/linux/CMakeLists.txt @@ -52,9 +52,11 @@ target_link_libraries(test_node ${UAVCAN_LIB} rt ${CMAKE_THREAD_LIBS_INIT}) add_executable(test_time_sync apps/test_time_sync.cpp) target_link_libraries(test_time_sync ${UAVCAN_LIB} rt ${CMAKE_THREAD_LIBS_INIT}) +add_executable(test_system_utils apps/test_system_utils.cpp) +target_link_libraries(test_system_utils ${UAVCAN_LIB} rt ${CMAKE_THREAD_LIBS_INIT}) + # # Tools -# Someday they will be replaced with Python scripts (pyuavcan is not finished at the moment) # add_executable(uavcan_status_monitor apps/uavcan_status_monitor.cpp) target_link_libraries(uavcan_status_monitor ${UAVCAN_LIB} rt ${CMAKE_THREAD_LIBS_INIT}) diff --git a/libuavcan_drivers/linux/apps/test_system_utils.cpp b/libuavcan_drivers/linux/apps/test_system_utils.cpp new file mode 100644 index 0000000000..719768fda5 --- /dev/null +++ b/libuavcan_drivers/linux/apps/test_system_utils.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015 Pavel Kirienko + */ + +#include +#include +#include +#include "debug.hpp" + +int main(int argc, const char** argv) +{ + try + { + const std::vector iface_names(argv + 1, argv + argc); + + const auto res = uavcan_linux::MachineIDReader(iface_names).readAndGetLocation(); + + const auto original_flags = std::cout.flags(); + + for (auto x : res.first) + { + std::cout << std::hex << std::setw(2) << std::setfill('0') << int(x); + } + + std::cout.width(0); + std::cout.flags(original_flags); + + std::cout << std::endl; + + std::cout << res.second << std::endl; + + return 0; + } + catch (const std::exception& ex) + { + std::cerr << "Exception: " << ex.what() << std::endl; + return 1; + } +} diff --git a/libuavcan_drivers/linux/include/uavcan_linux/system_utils.hpp b/libuavcan_drivers/linux/include/uavcan_linux/system_utils.hpp new file mode 100644 index 0000000000..14aa91ad63 --- /dev/null +++ b/libuavcan_drivers/linux/include/uavcan_linux/system_utils.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 Pavel Kirienko + */ + +#pragma once + +#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"); + } +}; + + +} diff --git a/libuavcan_drivers/linux/include/uavcan_linux/uavcan_linux.hpp b/libuavcan_drivers/linux/include/uavcan_linux/uavcan_linux.hpp index 3d68a42e99..4886fa66bd 100644 --- a/libuavcan_drivers/linux/include/uavcan_linux/uavcan_linux.hpp +++ b/libuavcan_drivers/linux/include/uavcan_linux/uavcan_linux.hpp @@ -9,3 +9,4 @@ #include #include #include +#include