Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add uname function and UnameInfo struct. #79

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 47 additions & 13 deletions lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ pub struct DiskInfo {
pub free: u64,
}

#[cfg(target_family = "unix")]
mod uname;
#[cfg(target_family = "unix")]
pub use uname::Info as UnameInfo;

#[cfg(not(target_family = "unix"))]
mod non_unix_uname;
#[cfg(not(target_family = "unix"))]
pub use non_unix_uname::Info as UnameInfo;

/// Error types
#[derive(Debug)]
pub enum Error {
Expand Down Expand Up @@ -377,6 +387,11 @@ extern "C" {
fn get_disk_info_bsd(di: &mut DiskInfo) -> i32;
}

/// Returns the result of the unix `uname` syscall or, on non-unix systems, a similar
/// data-structure.
pub fn uname() -> Result<UnameInfo, Error> {
UnameInfo::new()
}

/// Get operation system type.
///
Expand Down Expand Up @@ -445,19 +460,7 @@ pub fn os_release() -> Result<String, Error> {
}
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
{
let release: Option<String> = unsafe {
let mut name: libc::utsname = std::mem::zeroed();
if libc::uname(&mut name) < 0 {
None
} else {
let cstr = std::ffi::CStr::from_ptr(name.release.as_mut_ptr());
Some(cstr.to_string_lossy().to_string())
}
};
match release {
None => Err(Error::Unknown),
Some(release) => Ok(release),
}
Ok(uname()?.release()?.to_string())
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd")))]
{
Expand Down Expand Up @@ -934,4 +937,35 @@ mod test {
let os_release = linux_os_release().unwrap();
println!("linux_os_release(): {:?}", os_release.name)
}

#[test]
pub fn test_uname() {
let uname = uname().unwrap();
println!("uname:
sysname: {}
nodename: {}
release: {}
version: {}",
uname.sysname().unwrap(),
uname.nodename().unwrap(),
uname.release().unwrap(),
uname.version().unwrap(),
);
assert!(uname.release().unwrap() == uname.release().unwrap());
#[cfg(target_os = "Linux")]
assert!(uname.sysname().unwrap() == "Linux");
#[cfg(target_family = "unix")]
println!(" machine: {}",
uname.machine().unwrap(),
);
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox"
))]
println!(" domainname: {}",
uname.domainname().unwrap(),
);
}
}
48 changes: 48 additions & 0 deletions non_unix_uname.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use super::Error;

/// On non-unix systems, we emulate the uname data structure.
///
/// Fields can be accessed through methods.
///
/// Currently, this doesn't support `machine`.
pub struct Info {
sysname: String,
nodename: String,
release: String,
}

impl Info {
pub(crate) fn new() -> Result<Info, Error> {
let release = super::os_release()?;
Ok(Info {
sysname: super::os_type()?,
nodename: super::hostname()?,
release,
})
}

/// Kernel Name, for example "Linux".
pub fn sysname(&self) -> Result<&str, Error> {
Ok(&self.sysname)
}
/// Network node hostname, this is usually the same as the hostname.
pub fn nodename(&self) -> Result<&str, Error> {
Ok(&self.nodename)
}
/// Kernel release, for example "5.10.4-arch2-1".
pub fn release(&self) -> Result<&str, Error> {
Ok(&self.release)
}
/// Kernel version, for example "#1 SMP PREEMPT Fri, 01 Jan 2021 05:29:53 +0000".
///
/// On non-unix systems, this is the same as `self.release()`.
pub fn version(&self) -> Result<&str, Error> {
Ok(&self.release)
}
/// Machine hardware name, for example "x86_64".
///
/// Note that this isn't yet supported on non-unix systems.
pub fn machine(&self) -> Result<&str, Error> {
Err(Error::UnsupportedSystem)
}
}
26 changes: 26 additions & 0 deletions test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ fn main() {
let t = boottime().unwrap();
println!("boottime {} sec, {} usec", t.tv_sec, t.tv_usec);
}

let uname = sys_info::uname().unwrap();
println!("uname:
sysname: {}
nodename: {}
release: {}
version: {}",
uname.sysname().unwrap(),
uname.nodename().unwrap(),
uname.release().unwrap(),
uname.version().unwrap(),
);
#[cfg(target_family = "unix")]
println!(" machine: {}",
uname.machine().unwrap(),
);
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox"
))]
println!(" domainname: {}",
uname.domainname().unwrap(),
);

#[cfg(target_os = "linux")]
println!("/etc/os-release: {:?}", linux_os_release().unwrap());
}
Expand Down
75 changes: 75 additions & 0 deletions uname.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::ffi::CStr;
use std::io;

use libc::{uname, utsname};

use super::Error;

/// Result of `uname`, fields can be accessed through methods.
pub struct Info {
utsname: utsname,
}

macro_rules! info_methods {
(
$(
$(#[$meta:meta])*
$vis:vis fn $name:ident;
)*
) => {
$(
$(#[$meta])*
$vis fn $name(&self) -> Result<&str, Error> {
let bytes = &self.utsname.$name;
// Make sure the string is null-terminated so we don't overflow.
if !bytes.iter().any(|b| *b == 0) {
return Err(Error::Unknown);
}
unsafe { CStr::from_ptr(bytes.as_ptr()) }
.to_str()
.map_err(|_| Error::Unknown)
}
)*
};
}

impl Info {
pub(crate) fn new() -> Result<Info, Error> {
let mut info = Info {
utsname: unsafe { std::mem::zeroed() },
};
let ret = unsafe { uname(&mut info.utsname as *mut utsname) };
if ret != 0 {
return Err(io::Error::last_os_error().into());
}
Ok(info)
}

info_methods!(
/// Kernel Name, for example "Linux".
pub fn sysname;
/// Network node hostname, this is usually the same as the hostname.
pub fn nodename;
/// Kernel release, for example "5.10.4-arch2-1".
pub fn release;
/// Kernel version, for example "#1 SMP PREEMPT Fri, 01 Jan 2021 05:29:53 +0000".
pub fn version;
/// Machine hardware name, for example "x86_64".
///
/// Note that this isn't yet supported on non-unix systems.
pub fn machine;
);

#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "redox"
))]
info_methods!(
/// Domain name of the system.
///
/// This is only supported on linux, android, fuchsia and redox.
pub fn domainname;
);
}