1use std::error::Error;
72use std::fs::File;
73use std::io::prelude::*;
74
75#[allow(dead_code)]
76fn read_file(file_path: &str) -> Result<String, Box<dyn Error>> {
77 let mut fd = File::open(file_path)?;
78 let mut content = String::new();
79 fd.read_to_string(&mut content)?;
80 Ok(content.trim().to_string())
81}
82
83#[cfg(target_os = "linux")]
84pub mod machine_id {
85 use super::read_file;
86 use std::error::Error;
87
88 const DBUS_PATH: &str = "/var/lib/dbus/machine-id";
90 const DBUS_PATH_ETC: &str = "/etc/machine-id";
92
93 pub fn get_machine_id() -> Result<String, Box<dyn Error>> {
95 match read_file(DBUS_PATH) {
96 Ok(machine_id) => Ok(machine_id),
97 Err(_) => Ok(read_file(DBUS_PATH_ETC)?),
98 }
99 }
100}
101
102#[cfg(any(
103 target_os = "freebsd",
104 target_os = "dragonfly",
105 target_os = "openbsd",
106 target_os = "netbsd"
107))]
108pub mod machine_id {
109 use super::read_file;
110 use std::error::Error;
111 use std::process::Command;
112
113 const HOST_ID_PATH: &str = "/etc/hostid";
114
115 pub fn get_machine_id() -> Result<String, Box<dyn Error>> {
117 match read_file(HOST_ID_PATH) {
118 Ok(machine_id) => Ok(machine_id),
119 Err(_) => Ok(read_from_kenv()?),
120 }
121 }
122
123 fn read_from_kenv() -> Result<String, Box<dyn Error>> {
124 let output = Command::new("kenv")
125 .args(&["-q", "smbios.system.uuid"])
126 .output()?;
127 let content = String::from_utf8_lossy(&output.stdout);
128 Ok(content.trim().to_string())
129 }
130}
131
132#[cfg(target_os = "macos")]
133mod machine_id {
134 use std::error::Error;
143 use std::io;
144 #[cfg(test)]
145 use std::process::Command;
146
147 pub fn get_machine_id() -> Result<String, Box<dyn Error>> {
149 let mut uuid: [u8; 16] = [0; 16];
150 let wait = libc::timespec {
154 tv_sec: 0,
155 tv_nsec: 0,
156 };
157 let rc = unsafe { libc::gethostuuid(uuid.as_mut_ptr(), &wait) };
158 if rc != 0 {
159 return Err(Box::new(io::Error::last_os_error()));
160 }
161 Ok(format!(
162 "{:02X}{:02X}{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
163 uuid[0],
164 uuid[1],
165 uuid[2],
166 uuid[3],
167 uuid[4],
168 uuid[5],
169 uuid[6],
170 uuid[7],
171 uuid[8],
172 uuid[9],
173 uuid[10],
174 uuid[11],
175 uuid[12],
176 uuid[13],
177 uuid[14],
178 uuid[15],
179 ))
180 }
181
182 #[cfg(test)]
184 fn get_machine_id_legacy() -> Result<String, Box<dyn Error>> {
185 let output = Command::new("ioreg")
186 .args(["-rd1", "-c", "IOPlatformExpertDevice"])
187 .output()?;
188 let content = String::from_utf8_lossy(&output.stdout);
189 extract_id(&content)
190 }
191
192 #[cfg(test)]
193 fn extract_id(content: &str) -> Result<String, Box<dyn Error>> {
194 let lines = content.split('\n');
195 for line in lines {
196 if line.contains("IOPlatformUUID") {
197 let k: Vec<&str> = line.rsplitn(2, '=').collect();
198 let id = k[0].trim_matches(|c: char| c == '"' || c.is_whitespace());
199 return Ok(id.to_string());
200 }
201 }
202 Err(From::from(
203 "No matching IOPlatformUUID in `ioreg -rd1 -c IOPlatformExpertDevice` command.",
204 ))
205 }
206
207 #[test]
208 fn test_macos_get_matches_legacy_ioreg() {
209 let id = get_machine_id().unwrap();
210 let legacy_id = get_machine_id_legacy().unwrap();
211
212 assert_eq!(id, legacy_id);
213 }
214}
215
216#[cfg(target_os = "windows")]
217pub mod machine_id {
218 use std::error::Error;
219
220 use windows_registry::LOCAL_MACHINE;
221 use windows_sys::Win32::Foundation::GetLastError;
222 use windows_sys::Win32::System::Registry::{KEY_READ, KEY_WOW64_64KEY};
223 use windows_sys::Win32::System::Threading::{GetCurrentProcess, IsWow64Process};
224
225 fn machine_uid_is_wow64() -> Result<bool, Box<dyn Error>> {
226 unsafe {
227 let mut is_wow64: i32 = 0;
228 if IsWow64Process(GetCurrentProcess(), &mut is_wow64) == 0 {
229 return Err(From::from(format!("Failed to determine whether the specified process is running under WOW64 or an Intel64 of x64 processor: {}", GetLastError())));
230 }
231
232 Ok(is_wow64 == 1)
233 }
234 }
235
236 pub fn get_machine_id() -> Result<String, Box<dyn Error>> {
238 let flag = if machine_uid_is_wow64()? && cfg!(target_pointer_width = "32") {
239 KEY_READ | KEY_WOW64_64KEY
240 } else {
241 KEY_READ
242 };
243
244 let key = LOCAL_MACHINE
245 .options()
246 .access(flag)
247 .open("SOFTWARE\\Microsoft\\Cryptography")?;
248 let id = key.get_string("MachineGuid")?;
249
250 Ok(id.trim().to_string())
251 }
252}
253
254#[cfg(target_os = "illumos")]
255pub mod machine_id {
256 use std::error::Error;
257
258 pub fn get_machine_id() -> Result<String, Box<dyn Error>> {
260 Ok(format!("{:x}", unsafe { libc::gethostid() }))
261 }
262}
263
264pub use machine_id::get_machine_id as get;