platform_info/platform/
unix.rs1#![warn(unused_results)] use std::ffi::{OsStr, OsString};
23use std::fmt;
24use std::fmt::{Debug, Formatter};
25
26use crate::{PlatformInfoAPI, PlatformInfoError, UNameAPI};
27
28use unix_safe::{oss_from_cstr, utsname};
29
30#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct PlatformInfo {
34 pub utsname: UTSName, sysname: OsString,
39 nodename: OsString,
40 release: OsString,
41 version: OsString,
42 machine: OsString,
43 processor: OsString,
44 osname: OsString,
45}
46
47impl PlatformInfoAPI for PlatformInfo {
48 fn new() -> Result<Self, PlatformInfoError> {
50 let utsname = UTSName(utsname()?);
51 let machine = oss_from_cstr(&utsname.0.machine);
52 let processor = OsString::from(crate::lib_impl::map_processor(&machine.to_string_lossy()));
53 Ok(Self {
54 utsname,
55 sysname: oss_from_cstr(&utsname.0.sysname),
56 nodename: oss_from_cstr(&utsname.0.nodename),
57 release: oss_from_cstr(&utsname.0.release),
58 version: oss_from_cstr(&utsname.0.version),
59 machine,
60 processor,
61 osname: OsString::from(crate::lib_impl::HOST_OS_NAME),
62 })
63 }
64}
65
66impl UNameAPI for PlatformInfo {
67 fn sysname(&self) -> &OsStr {
68 &self.sysname
69 }
70
71 fn nodename(&self) -> &OsStr {
72 &self.nodename
73 }
74
75 fn release(&self) -> &OsStr {
76 &self.release
77 }
78
79 fn version(&self) -> &OsStr {
80 &self.version
81 }
82
83 fn machine(&self) -> &OsStr {
84 &self.machine
85 }
86
87 fn processor(&self) -> &OsStr {
88 &self.processor
89 }
90
91 fn osname(&self) -> &OsStr {
92 &self.osname
93 }
94}
95
96#[derive(Clone, Copy )]
115pub struct UTSName(libc::utsname);
116
117impl Debug for UTSName {
118 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
119 let mut debug_struct = &mut f.debug_struct("UTSName");
120 debug_struct = debug_struct
121 .field("sysname", &oss_from_cstr(&self.0.sysname))
122 .field("nodename", &oss_from_cstr(&self.0.nodename))
123 .field("release", &oss_from_cstr(&self.0.release))
124 .field("version", &oss_from_cstr(&self.0.version))
125 .field("machine", &oss_from_cstr(&self.0.machine));
126 #[cfg(not(any(
129 target_os = "aix",
130 target_os = "illumos",
131 target_os = "solaris",
132 target_os = "macos",
133 target_os = "ios",
134 target_os = "dragonfly",
135 target_os = "freebsd",
136 target_os = "openbsd",
137 target_os = "netbsd",
138 target_os = "haiku"
139 )))]
140 {
141 debug_struct = debug_struct.field("domainname", &oss_from_cstr(&self.0.domainname));
142 }
143 debug_struct.finish()
144 }
145}
146
147impl PartialEq for UTSName {
148 fn eq(&self, other: &Self) -> bool {
149 let mut equal = true; equal = equal
151 && (
152 self.0.sysname,
153 self.0.nodename,
154 self.0.release,
155 self.0.version,
156 self.0.machine,
157 ) == (
158 other.0.sysname,
159 other.0.nodename,
160 other.0.release,
161 other.0.version,
162 other.0.machine,
163 );
164 #[cfg(not(any(
167 target_os = "aix",
168 target_os = "illumos",
169 target_os = "solaris",
170 target_os = "macos",
171 target_os = "ios",
172 target_os = "dragonfly",
173 target_os = "freebsd",
174 target_os = "openbsd",
175 target_os = "netbsd",
176 target_os = "haiku"
177 )))]
178 {
179 equal = equal && (self.0.domainname == other.0.domainname);
180 }
181 equal
182 }
183}
184
185impl Eq for UTSName {}
186
187mod unix_safe {
191 use std::convert::TryFrom;
192 use std::ffi::{CStr, OsStr, OsString};
193 use std::io;
194 use std::mem::MaybeUninit;
195 use std::os::unix::ffi::OsStrExt;
196
197 pub fn oss_from_cstr(slice: &[libc::c_char]) -> OsString {
200 assert!(slice.len() < usize::try_from(isize::MAX).unwrap());
201 assert!(slice.iter().position(|&c| c == 0 ).unwrap() < slice.len());
202 OsString::from(OsStr::from_bytes(
203 unsafe { CStr::from_ptr(slice.as_ptr()) }.to_bytes(),
204 ))
205 }
206
207 pub fn utsname() -> Result<libc::utsname, std::io::Error> {
210 let mut uts = MaybeUninit::<libc::utsname>::uninit();
213 let result = unsafe { libc::uname(uts.as_mut_ptr()) };
214 if result != -1 {
215 Ok(unsafe { uts.assume_init() })
217 } else {
218 Err(io::Error::last_os_error())
219 }
220 }
221}
222#[test]
227fn test_osname() {
228 let info = PlatformInfo::new().unwrap();
229 let osname = info.osname().to_string_lossy();
230 assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME));
231}
232
233#[test]
234fn test_processor() {
235 let info = PlatformInfo::new().unwrap();
236 let processor = info.processor().to_string_lossy();
237
238 assert!(!processor.is_empty());
240
241 #[cfg(all(target_arch = "aarch64", target_os = "macos"))]
243 assert_eq!(processor, "arm", "macOS arm64 should map to 'arm'");
244
245 #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
246 assert_eq!(processor, "aarch64", "Linux aarch64 should pass through");
247
248 #[cfg(target_arch = "x86_64")]
249 assert_eq!(processor, "x86_64", "x86_64 should pass through");
250
251 #[cfg(target_arch = "x86")]
252 assert_eq!(processor, "i686", "x86 variants should normalize to i686");
253}
254
255#[test]
256fn structure_clone() {
257 let info = PlatformInfo::new().unwrap();
258 println!("{info:?}");
259 #[allow(clippy::redundant_clone)] let info_copy = info.clone();
261 assert_eq!(info_copy, info);
262}