From e82a5d07d9af25d63577eebf274273e8cd61f21a Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Fri, 12 Jul 2024 23:57:53 +0300 Subject: [PATCH] src/system: use /etc/machine-id as machine identifier At least on my raspberrypi 3, with a mainline kernel, /proc/cpuinfo does not contain a `Serial:` line. It neither does on my other system, so it was impossible to run the application in release mode. This now instead reads from `/etc/machine-id`, as per https://www.freedesktop.org/software/systemd/man/latest/machine-id.html That one is guaranteed to be stable across reboots, as long as we don't loose the root filesystem, and works on pretty much any Linux system out there. It's also possible to influence its generation, as described in https://www.freedesktop.org/software/systemd/man/latest/machine-id.html#Initialization This would allow passing some unique identifier determined in u-boot to be passed to the kernel cmdline, for example, or a early-boot systemd unit file that interacts with https://www.freedesktop.org/software/systemd/man/latest/systemd-machine-id-setup.html# Point being we don't need to make this too hardware-specific. I kept the `id` thing in the config around for now, so when testing on MacOS, it can be provided in the config to get a stable identifier there. --- Cargo.lock | 10 ++++++++++ Cargo.nix | 46 ++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 6 ++---- src/system.rs | 40 +++++++++++++--------------------------- 5 files changed, 72 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac6a462..004d638 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -610,6 +610,7 @@ dependencies = [ "thiserror", "tracing", "tracing-subscriber", + "uuid", "wry", ] @@ -2561,6 +2562,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom 0.2.15", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.nix b/Cargo.nix index 635e67c..4742a32 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -1734,6 +1734,11 @@ rec { packageId = "tracing-subscriber"; features = [ "env-filter" ]; } + { + name = "uuid"; + packageId = "uuid"; + features = [ "v4" ]; + } { name = "wry"; packageId = "wry"; @@ -7363,6 +7368,47 @@ rec { features = { }; resolvedDefaultFeatures = [ "default" ]; }; + "uuid" = rec { + crateName = "uuid"; + version = "1.10.0"; + edition = "2018"; + sha256 = "0503gvp08dh5mnm3f0ffqgisj6x3mbs53dmnn1lm19pga43a1pw1"; + authors = [ + "Ashley Mannix" + "Dylan DPC" + "Hunar Roop Kahlon" + ]; + dependencies = [ + { + name = "getrandom"; + packageId = "getrandom 0.2.15"; + optional = true; + } + ]; + features = { + "arbitrary" = [ "dep:arbitrary" ]; + "atomic" = [ "dep:atomic" ]; + "borsh" = [ "dep:borsh" "dep:borsh-derive" ]; + "bytemuck" = [ "dep:bytemuck" ]; + "default" = [ "std" ]; + "fast-rng" = [ "rng" "dep:rand" ]; + "js" = [ "dep:wasm-bindgen" "getrandom?/js" ]; + "macro-diagnostics" = [ "dep:uuid-macro-internal" ]; + "md5" = [ "dep:md-5" ]; + "rng" = [ "dep:getrandom" ]; + "serde" = [ "dep:serde" ]; + "sha1" = [ "dep:sha1_smol" ]; + "slog" = [ "dep:slog" ]; + "v1" = [ "atomic" ]; + "v3" = [ "md5" ]; + "v4" = [ "rng" ]; + "v5" = [ "sha1" ]; + "v6" = [ "atomic" ]; + "v7" = [ "rng" ]; + "zerocopy" = [ "dep:zerocopy" ]; + }; + resolvedDefaultFeatures = [ "default" "rng" "std" "v4" ]; + }; "valuable" = rec { crateName = "valuable"; version = "0.1.0"; diff --git a/Cargo.toml b/Cargo.toml index 0295a0a..c4d9156 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ tao = "0.28.1" thiserror = "1.0.62" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +uuid = { version = "1.10.0", features = ["v4"] } wry = "0.41.0" # FUTUREWORK: Somehow rustc wants to link this against zlib. diff --git a/src/main.rs b/src/main.rs index 88a105e..dae92ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,9 +31,7 @@ fn main() -> color_eyre::eyre::Result<()> { let cli = Cli::parse(); - debug!("Acquiring the CPU serial number..."); - let serial = system::get_cpu_serial().context("getting cpu serial")?; - debug!(%serial, "got CPU serial number"); + let machine_id = system::get_machine_id().context("getting machine id")?; debug!("Loading the config"); let config = Config::load(cli.default_config_path.map(|p| PathBuf::from(p))) @@ -44,7 +42,7 @@ fn main() -> color_eyre::eyre::Result<()> { let (sender, receiver) = channel(); let listener = mqtt::Listener { - id: config.id.unwrap_or(serial), + id: config.id.unwrap_or(machine_id), host: config.host, port: config.port, sender, diff --git a/src/system.rs b/src/system.rs index 377e44e..ce727ea 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,31 +1,17 @@ -use std::{ - fs::File, - io::{BufRead, BufReader}, -}; +use tracing::{instrument, warn}; -use crate::common::Error; +const MACHINE_ID_PATH: &'static str = "/etc/machine-id"; -#[cfg(debug_assertions)] -const CPU_INFO_PATH: &'static str = "./cpuinfo"; - -#[cfg(not(debug_assertions))] -const CPU_INFO_PATH: &'static str = "/proc/cpuinfo"; - -pub(crate) fn get_cpu_serial() -> Result { - let file = - File::open(CPU_INFO_PATH).map_err(|e| Error::FileIoError(CPU_INFO_PATH.into(), e))?; - let reader = BufReader::new(file); - let mut lines = reader.lines(); - - while let Some(Ok(line)) = lines.next() { - if line.starts_with("Serial") { - let (_, serial) = line - .split_once(':') - .ok_or(Error::ParsingError(CPU_INFO_PATH.into()))?; - - return Ok(serial.trim().to_string()); - } +/// Returns the system machine id +/// (https://www.freedesktop.org/software/systemd/man/latest/machine-id.html) +/// Falls back to a random uuid if the file doesn't exist, or another error +/// occurs during retrieval. It logs a warning in this case. +#[instrument(ret, err)] +pub(crate) fn get_machine_id() -> std::io::Result { + if let Ok(machine_id) = std::fs::read_to_string(MACHINE_ID_PATH) { + Ok(machine_id.trim().to_string()) + } else { + warn!("unable to read machine id, setting a random one"); + Ok(uuid::Uuid::new_v4().to_string()) } - - Err(Error::ParsingError(CPU_INFO_PATH.into())) }