Руслан Урядинский / libuavcan

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers system_utils.hpp Source File

system_utils.hpp

00001 /*
00002  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #pragma once
00006 
00007 #include <array>
00008 #include <cassert>
00009 #include <cctype>
00010 #include <fstream>
00011 #include <string>
00012 #include <vector>
00013 #include <cstdint>
00014 #include <algorithm>
00015 #include <utility>
00016 #include <uavcan_linux/exception.hpp>
00017 #include <uavcan/data_type.hpp>
00018 
00019 namespace uavcan_linux
00020 {
00021 /**
00022  * This class can find and read machine ID from a text file, represented as 32-char (16-byte) long hexadecimal string,
00023  * possibly with separators (like dashes or colons). If the available ID is more than 16 bytes, extra bytes will be
00024  * ignored. A shorter ID will not be accepted as valid.
00025  * In order to be read, the ID must be located on the first line of the file and must not contain any whitespace
00026  * characters.
00027  *
00028  * Examples of valid ID:
00029  *   0123456789abcdef0123456789abcdef
00030  *   20CE0b1E-8C03-07C8-13EC-00242C491652
00031  */
00032 class MachineIDReader
00033 {
00034 public:
00035     static constexpr int MachineIDSize = 16;
00036 
00037     typedef std::array<std::uint8_t, MachineIDSize> MachineID;
00038 
00039     static std::vector<std::string> getDefaultSearchLocations()
00040     {
00041         return
00042         {
00043             "/etc/machine-id",
00044             "/var/lib/dbus/machine-id",
00045             "/sys/class/dmi/id/product_uuid"
00046         };
00047     }
00048 
00049 private:
00050     const std::vector<std::string> search_locations_;
00051 
00052     static std::vector<std::string> mergeLists(const std::vector<std::string>& a, const std::vector<std::string>& b)
00053     {
00054         std::vector<std::string> ab;
00055         ab.reserve(a.size() + b.size());
00056         ab.insert(ab.end(), a.begin(), a.end());
00057         ab.insert(ab.end(), b.begin(), b.end());
00058         return ab;
00059     }
00060 
00061     bool tryRead(const std::string& location, MachineID& out_id) const
00062     {
00063         /*
00064          * Reading the file
00065          */
00066         std::string token;
00067         try
00068         {
00069             std::ifstream infile(location);
00070             infile >> token;
00071         }
00072         catch (std::exception&)
00073         {
00074             return false;
00075         }
00076 
00077         /*
00078          * Preprocessing the input string - convert to lowercase, remove all non-hex characters, limit to 32 chars
00079          */
00080         std::transform(token.begin(), token.end(), token.begin(), [](char x) { return std::tolower(x); });
00081         token.erase(std::remove_if(token.begin(), token.end(),
00082                                    [](char x){ return (x < 'a' || x > 'f') && !std::isdigit(x); }),
00083                     token.end());
00084 
00085         if (token.length() < (MachineIDSize * 2))
00086         {
00087             return false;
00088         }
00089         token.resize(MachineIDSize * 2);        // Truncating
00090 
00091         /*
00092          * Parsing the string as hex bytes
00093          */
00094         auto sym = std::begin(token);
00095         for (auto& byte : out_id)
00096         {
00097             assert(sym != std::end(token));
00098             byte = std::stoi(std::string{*sym++, *sym++}, nullptr, 16);
00099         }
00100 
00101         return true;
00102     }
00103 
00104 public:
00105     /**
00106      * This class can use extra seach locations. If provided, they will be checked first, before default ones.
00107      */
00108     MachineIDReader(const std::vector<std::string>& extra_search_locations = {})
00109         : search_locations_(mergeLists(extra_search_locations, getDefaultSearchLocations()))
00110     { }
00111 
00112     /**
00113      * Just like @ref readAndGetLocation(), but this one doesn't return location where this ID was obtained from.
00114      */
00115     MachineID read() const { return readAndGetLocation().first; }
00116 
00117     /**
00118      * This function checks available search locations and reads the ID from the first valid location.
00119      * It returns std::pair<> with ID and the file path where it was read from.
00120      * In case if none of the search locations turned out to be valid, @ref uavcan_linux::Exception will be thrown.
00121      */
00122     std::pair<MachineID, std::string> readAndGetLocation() const
00123     {
00124         for (auto x : search_locations_)
00125         {
00126             auto out = MachineID();
00127             if (tryRead(x, out))
00128             {
00129                 return {out, x};
00130             }
00131         }
00132         throw Exception("Failed to read machine ID");
00133     }
00134 };
00135 
00136 /**
00137  * This class computes unique ID for a UAVCAN node in a Linux application.
00138  * It takes the following inputs:
00139  *  - Unique machine ID
00140  *  - Node name string (e.g. "org.uavcan.linux_app.dynamic_node_id_server")
00141  *  - Instance ID byte, e.g. node ID (optional)
00142  */
00143 inline std::array<std::uint8_t, 16> makeApplicationID(const MachineIDReader::MachineID& machine_id,
00144                                                       const std::string& node_name,
00145                                                       const std::uint8_t instance_id = 0)
00146 {
00147     union HalfID
00148     {
00149         std::uint64_t num;
00150         std::uint8_t bytes[8];
00151 
00152         HalfID(std::uint64_t arg_num) : num(arg_num) { }
00153     };
00154 
00155     std::array<std::uint8_t, 16> out;
00156 
00157     // First 8 bytes of the application ID are CRC64 of the machine ID in native byte order
00158     {
00159         uavcan::DataTypeSignatureCRC crc;
00160         crc.add(machine_id.data(), static_cast<unsigned>(machine_id.size()));
00161         HalfID half(crc.get());
00162         std::copy_n(half.bytes, 8, out.begin());
00163     }
00164 
00165     // Last 8 bytes of the application ID are CRC64 of the node name and optionally node ID
00166     {
00167         uavcan::DataTypeSignatureCRC crc;
00168         crc.add(reinterpret_cast<const std::uint8_t*>(node_name.c_str()), static_cast<unsigned>(node_name.length()));
00169         crc.add(instance_id);
00170         HalfID half(crc.get());
00171         std::copy_n(half.bytes, 8, out.begin() + 8);
00172     }
00173 
00174     return out;
00175 }
00176 
00177 }