mirror of
https://github.com/NotAShelf/microfetch.git
synced 2026-04-12 21:07:41 +00:00
Merge pull request #54 from NotAShelf/notashelf/push-ypvyyznuosqk
various: replace libc invocations with `std::env` and handwritten syscall wrappers
This commit is contained in:
commit
bf1b319971
9 changed files with 296 additions and 70 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -571,7 +571,6 @@ version = "0.4.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"hotpath",
|
"hotpath",
|
||||||
"libc",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hotpath = { optional = true, version = "0.13.0" }
|
hotpath = { optional = true, version = "0.13.0" }
|
||||||
libc = "0.2.183"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.8.1"
|
criterion = "0.8.1"
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ impl Colors {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static COLORS: LazyLock<Colors> = LazyLock::new(|| {
|
pub static COLORS: LazyLock<Colors> = LazyLock::new(|| {
|
||||||
const NO_COLOR: *const libc::c_char = c"NO_COLOR".as_ptr();
|
// Only presence matters; value is irrelevant per the NO_COLOR spec
|
||||||
let is_no_color = unsafe { !libc::getenv(NO_COLOR).is_null() };
|
let is_no_color = std::env::var_os("NO_COLOR").is_some();
|
||||||
Colors::new(is_no_color)
|
Colors::new(is_no_color)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,22 @@
|
||||||
use std::{ffi::CStr, fmt::Write};
|
use std::{ffi::OsStr, fmt::Write};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
pub fn get_desktop_info() -> String {
|
pub fn get_desktop_info() -> String {
|
||||||
// Retrieve the environment variables and handle Result types
|
let desktop_os = std::env::var_os("XDG_CURRENT_DESKTOP");
|
||||||
let desktop_str = unsafe {
|
let session_os = std::env::var_os("XDG_SESSION_TYPE");
|
||||||
let ptr = libc::getenv(c"XDG_CURRENT_DESKTOP".as_ptr());
|
|
||||||
if ptr.is_null() {
|
|
||||||
"Unknown"
|
|
||||||
} else {
|
|
||||||
let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown");
|
|
||||||
s.strip_prefix("none+").unwrap_or(s)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let backend_str = unsafe {
|
let desktop_raw = desktop_os
|
||||||
let ptr = libc::getenv(c"XDG_SESSION_TYPE".as_ptr());
|
.as_deref()
|
||||||
if ptr.is_null() {
|
.and_then(OsStr::to_str)
|
||||||
|
.unwrap_or("Unknown");
|
||||||
|
let desktop_str = desktop_raw.strip_prefix("none+").unwrap_or(desktop_raw);
|
||||||
|
|
||||||
|
let session_raw = session_os.as_deref().and_then(OsStr::to_str).unwrap_or("");
|
||||||
|
let backend_str = if session_raw.is_empty() {
|
||||||
"Unknown"
|
"Unknown"
|
||||||
} else {
|
} else {
|
||||||
let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown");
|
session_raw
|
||||||
if s.is_empty() { "Unknown" } else { s }
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pre-calculate capacity: desktop_len + " (" + backend_len + ")"
|
// Pre-calculate capacity: desktop_len + " (" + backend_len + ")"
|
||||||
|
|
|
||||||
28
src/lib.rs
28
src/lib.rs
|
|
@ -5,42 +5,44 @@ pub mod syscall;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod uptime;
|
pub mod uptime;
|
||||||
|
|
||||||
use std::mem::MaybeUninit;
|
use std::{ffi::CStr, mem::MaybeUninit};
|
||||||
|
|
||||||
/// Wrapper for `libc::utsname` with safe accessor methods
|
use crate::syscall::{UtsNameBuf, sys_uname};
|
||||||
pub struct UtsName(libc::utsname);
|
|
||||||
|
/// Wrapper for `utsname` with safe accessor methods
|
||||||
|
pub struct UtsName(UtsNameBuf);
|
||||||
|
|
||||||
impl UtsName {
|
impl UtsName {
|
||||||
/// Calls `uname` syscall and returns a `UtsName` wrapper
|
/// Calls `uname(2)` syscall and returns a `UtsName` wrapper
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the `uname` syscall fails
|
/// Returns an error if the `uname` syscall fails
|
||||||
pub fn uname() -> Result<Self, std::io::Error> {
|
pub fn uname() -> Result<Self, std::io::Error> {
|
||||||
let mut uts = MaybeUninit::uninit();
|
let mut uts = MaybeUninit::uninit();
|
||||||
if unsafe { libc::uname(uts.as_mut_ptr()) } != 0 {
|
if unsafe { sys_uname(uts.as_mut_ptr()) } != 0 {
|
||||||
return Err(std::io::Error::last_os_error());
|
return Err(std::io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
Ok(Self(unsafe { uts.assume_init() }))
|
Ok(Self(unsafe { uts.assume_init() }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn nodename(&self) -> &std::ffi::CStr {
|
pub const fn nodename(&self) -> &CStr {
|
||||||
unsafe { std::ffi::CStr::from_ptr(self.0.nodename.as_ptr()) }
|
unsafe { CStr::from_ptr(self.0.nodename.as_ptr().cast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn sysname(&self) -> &std::ffi::CStr {
|
pub const fn sysname(&self) -> &CStr {
|
||||||
unsafe { std::ffi::CStr::from_ptr(self.0.sysname.as_ptr()) }
|
unsafe { CStr::from_ptr(self.0.sysname.as_ptr().cast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn release(&self) -> &std::ffi::CStr {
|
pub const fn release(&self) -> &CStr {
|
||||||
unsafe { std::ffi::CStr::from_ptr(self.0.release.as_ptr()) }
|
unsafe { CStr::from_ptr(self.0.release.as_ptr().cast()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn machine(&self) -> &std::ffi::CStr {
|
pub const fn machine(&self) -> &CStr {
|
||||||
unsafe { std::ffi::CStr::from_ptr(self.0.machine.as_ptr()) }
|
unsafe { CStr::from_ptr(self.0.machine.as_ptr().cast()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -99,14 +99,19 @@ fn print_system_info(
|
||||||
{blue} ▟█▘ ▜█▖ {cyan}▝█▛ {cyan} {blue}Colors{reset} {colors}\n\n"
|
{blue} ▟█▘ ▜█▖ {cyan}▝█▛ {cyan} {blue}Colors{reset} {colors}\n\n"
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let len = cursor.position() as usize;
|
let len =
|
||||||
|
usize::try_from(cursor.position()).expect("cursor position fits usize");
|
||||||
// Direct syscall to avoid stdout buffering allocation
|
// Direct syscall to avoid stdout buffering allocation
|
||||||
let written = unsafe { libc::write(libc::STDOUT_FILENO, buf.as_ptr().cast(), len) };
|
let written = unsafe { syscall::sys_write(1, buf.as_ptr(), len) };
|
||||||
if written < 0 {
|
if written < 0 {
|
||||||
return Err(io::Error::last_os_error().into());
|
return Err(io::Error::last_os_error().into());
|
||||||
}
|
}
|
||||||
|
#[allow(clippy::cast_sign_loss)] // non-negative verified by the guard above
|
||||||
if written as usize != len {
|
if written as usize != len {
|
||||||
return Err(io::Error::new(io::ErrorKind::WriteZero, "partial write to stdout").into());
|
return Err(
|
||||||
|
io::Error::new(io::ErrorKind::WriteZero, "partial write to stdout")
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
200
src/syscall.rs
200
src/syscall.rs
|
|
@ -123,6 +123,63 @@ pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Direct syscall to write to a file descriptor
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Number of bytes written or -1 on error
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that:
|
||||||
|
///
|
||||||
|
/// - `buf` points to a valid readable buffer of at least `count` bytes
|
||||||
|
/// - `fd` is a valid open file descriptor
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
in("rax") 1i64, // SYS_write
|
||||||
|
in("rdi") fd,
|
||||||
|
in("rsi") buf,
|
||||||
|
in("rdx") count,
|
||||||
|
lateout("rax") ret,
|
||||||
|
lateout("rcx") _,
|
||||||
|
lateout("r11") _,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as isize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"svc #0",
|
||||||
|
in("x8") 64i64, // SYS_write
|
||||||
|
in("x0") fd,
|
||||||
|
in("x1") buf,
|
||||||
|
in("x2") count,
|
||||||
|
lateout("x0") ret,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as isize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
{
|
||||||
|
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Direct syscall to close a file descriptor
|
/// Direct syscall to close a file descriptor
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
@ -169,6 +226,149 @@ pub unsafe fn sys_close(fd: i32) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Raw buffer for the `uname(2)` syscall.
|
||||||
|
///
|
||||||
|
/// Linux ABI hasfive fields of `[i8; 65]`: sysname, nodename, release, version,
|
||||||
|
/// machine. The `domainname` field (GNU extension, `[i8; 65]`) follows but is
|
||||||
|
/// not used, nor any useful to us here.
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct UtsNameBuf {
|
||||||
|
pub sysname: [i8; 65],
|
||||||
|
pub nodename: [i8; 65],
|
||||||
|
pub release: [i8; 65],
|
||||||
|
pub version: [i8; 65],
|
||||||
|
pub machine: [i8; 65],
|
||||||
|
pub domainname: [i8; 65], // GNU extension, included for correct struct size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct `uname(2)` syscall
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// 0 on success, negative on error
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure `buf` points to a valid `UtsNameBuf`.
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
in("rax") 63i64, // SYS_uname
|
||||||
|
in("rdi") buf,
|
||||||
|
lateout("rax") ret,
|
||||||
|
lateout("rcx") _,
|
||||||
|
lateout("r11") _,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"svc #0",
|
||||||
|
in("x8") 160i64, // SYS_uname
|
||||||
|
in("x0") buf,
|
||||||
|
lateout("x0") ret,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
{
|
||||||
|
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Raw buffer for the `statfs(2)` syscall.
|
||||||
|
///
|
||||||
|
/// Linux ABI (`x86_64` and `aarch64`): the fields we use are at the same
|
||||||
|
/// offsets on both architectures. Only the fields needed for disk usage are
|
||||||
|
/// declared; the remainder of the 120-byte struct is covered by `_pad`.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct StatfsBuf {
|
||||||
|
pub f_type: i64,
|
||||||
|
pub f_bsize: i64,
|
||||||
|
pub f_blocks: u64,
|
||||||
|
pub f_bfree: u64,
|
||||||
|
pub f_bavail: u64,
|
||||||
|
pub f_files: u64,
|
||||||
|
pub f_ffree: u64,
|
||||||
|
pub f_fsid: [i32; 2],
|
||||||
|
pub f_namelen: i64,
|
||||||
|
pub f_frsize: i64,
|
||||||
|
pub f_flags: i64,
|
||||||
|
|
||||||
|
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
|
||||||
|
pub _pad: [i64; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct `statfs(2)` syscall
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// 0 on success, negative errno on error
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The caller must ensure that:
|
||||||
|
///
|
||||||
|
/// - `path` points to a valid null-terminated string
|
||||||
|
/// - `buf` points to a valid `StatfsBuf`
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
in("rax") 137i64, // SYS_statfs
|
||||||
|
in("rdi") path,
|
||||||
|
in("rsi") buf,
|
||||||
|
lateout("rax") ret,
|
||||||
|
lateout("rcx") _,
|
||||||
|
lateout("r11") _,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
unsafe {
|
||||||
|
let ret: i64;
|
||||||
|
std::arch::asm!(
|
||||||
|
"svc #0",
|
||||||
|
in("x8") 43i64, // SYS_statfs
|
||||||
|
in("x0") path,
|
||||||
|
in("x1") buf,
|
||||||
|
lateout("x0") ret,
|
||||||
|
options(nostack)
|
||||||
|
);
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
{
|
||||||
|
ret as i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
|
{
|
||||||
|
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Read entire file using direct syscalls. This avoids libc overhead and can be
|
/// Read entire file using direct syscalls. This avoids libc overhead and can be
|
||||||
/// significantly faster for small files.
|
/// significantly faster for small files.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
use std::{ffi::CStr, fmt::Write as _, io, mem::MaybeUninit};
|
use std::{ffi::OsStr, fmt::Write as _, io, mem::MaybeUninit};
|
||||||
|
|
||||||
use crate::{UtsName, colors::COLORS, syscall::read_file_fast};
|
use crate::{
|
||||||
|
UtsName,
|
||||||
|
colors::COLORS,
|
||||||
|
syscall::{StatfsBuf, read_file_fast, sys_statfs},
|
||||||
|
};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
||||||
let username = unsafe {
|
let username_os = std::env::var_os("USER");
|
||||||
let ptr = libc::getenv(c"USER".as_ptr());
|
let username = username_os
|
||||||
if ptr.is_null() {
|
.as_deref()
|
||||||
"unknown_user"
|
.and_then(OsStr::to_str)
|
||||||
} else {
|
.unwrap_or("unknown_user");
|
||||||
CStr::from_ptr(ptr).to_str().unwrap_or("unknown_user")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let hostname = utsname.nodename().to_str().unwrap_or("unknown_host");
|
let hostname = utsname.nodename().to_str().unwrap_or("unknown_host");
|
||||||
|
|
||||||
let capacity = COLORS.yellow.len()
|
let capacity = COLORS.yellow.len()
|
||||||
|
|
@ -38,16 +39,13 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
pub fn get_shell() -> String {
|
pub fn get_shell() -> String {
|
||||||
unsafe {
|
let shell_os = std::env::var_os("SHELL");
|
||||||
let ptr = libc::getenv(c"SHELL".as_ptr());
|
let shell = shell_os.as_deref().and_then(OsStr::to_str).unwrap_or("");
|
||||||
if ptr.is_null() {
|
let start = shell.rfind('/').map_or(0, |i| i + 1);
|
||||||
return "unknown_shell".into();
|
if shell.is_empty() {
|
||||||
}
|
"unknown_shell".into()
|
||||||
|
} else {
|
||||||
let bytes = CStr::from_ptr(ptr).to_bytes();
|
shell[start..].into()
|
||||||
let start = bytes.iter().rposition(|&b| b == b'/').map_or(0, |i| i + 1);
|
|
||||||
let name = std::str::from_utf8_unchecked(&bytes[start..]);
|
|
||||||
name.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,15 +57,16 @@ pub fn get_shell() -> String {
|
||||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
pub fn get_root_disk_usage() -> Result<String, io::Error> {
|
pub fn get_root_disk_usage() -> Result<String, io::Error> {
|
||||||
let mut vfs = MaybeUninit::uninit();
|
let mut vfs = MaybeUninit::<StatfsBuf>::uninit();
|
||||||
let path = b"/\0";
|
let path = b"/\0";
|
||||||
|
|
||||||
if unsafe { libc::statvfs(path.as_ptr().cast(), vfs.as_mut_ptr()) } != 0 {
|
if unsafe { sys_statfs(path.as_ptr(), vfs.as_mut_ptr()) } != 0 {
|
||||||
return Err(io::Error::last_os_error());
|
return Err(io::Error::last_os_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
let vfs = unsafe { vfs.assume_init() };
|
let vfs = unsafe { vfs.assume_init() };
|
||||||
let block_size = vfs.f_bsize;
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
let block_size = vfs.f_bsize as u64;
|
||||||
let total_blocks = vfs.f_blocks;
|
let total_blocks = vfs.f_blocks;
|
||||||
let available_blocks = vfs.f_bavail;
|
let available_blocks = vfs.f_bavail;
|
||||||
|
|
||||||
|
|
@ -158,12 +157,12 @@ pub fn get_memory_usage() -> Result<String, io::Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
let total_memory_gb = total_memory_kb as f64 / 1024.0 / 1024.0;
|
let total_gb = total_memory_kb as f64 / 1024.0 / 1024.0;
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
let available_memory_gb = available_memory_kb as f64 / 1024.0 / 1024.0;
|
let available_gb = available_memory_kb as f64 / 1024.0 / 1024.0;
|
||||||
let used_memory_gb = total_memory_gb - available_memory_gb;
|
let used_memory_gb = total_gb - available_gb;
|
||||||
|
|
||||||
Ok((used_memory_gb, total_memory_gb))
|
Ok((used_memory_gb, total_gb))
|
||||||
}
|
}
|
||||||
|
|
||||||
let (used_memory, total_memory) = parse_memory_info()?;
|
let (used_memory, total_memory) = parse_memory_info()?;
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,41 @@ fn itoa(mut n: u64, buf: &mut [u8]) -> &str {
|
||||||
unsafe { std::str::from_utf8_unchecked(&buf[i..]) }
|
unsafe { std::str::from_utf8_unchecked(&buf[i..]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct `sysinfo` syscall using inline assembly
|
/// Raw buffer for the `sysinfo(2)` syscall.
|
||||||
|
///
|
||||||
|
/// In the Linux ABI `uptime` is a `long` at offset 0. The remaining fields are
|
||||||
|
/// not needed, but are declared to give the struct its correct size (112 bytes
|
||||||
|
/// on 64-bit Linux).
|
||||||
|
///
|
||||||
|
/// The layout matches the kernel's `struct sysinfo` *exactly*:
|
||||||
|
/// `mem_unit` ends at offset 108, then 4 bytes of implicit padding to 112.
|
||||||
|
#[repr(C)]
|
||||||
|
struct SysInfo {
|
||||||
|
uptime: i64,
|
||||||
|
loads: [u64; 3],
|
||||||
|
totalram: u64,
|
||||||
|
freeram: u64,
|
||||||
|
sharedram: u64,
|
||||||
|
bufferram: u64,
|
||||||
|
totalswap: u64,
|
||||||
|
freeswap: u64,
|
||||||
|
procs: u16,
|
||||||
|
_pad: u16,
|
||||||
|
_pad2: u32, // alignment padding to reach 8-byte boundary for totalhigh
|
||||||
|
totalhigh: u64,
|
||||||
|
freehigh: u64,
|
||||||
|
mem_unit: u32,
|
||||||
|
// 4 bytes implicit trailing padding to reach 112 bytes total; no field
|
||||||
|
// needed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct `sysinfo(2)` syscall using inline assembly
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This function uses inline assembly to make a direct syscall.
|
|
||||||
/// The caller must ensure the sysinfo pointer is valid.
|
/// The caller must ensure the sysinfo pointer is valid.
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn sys_sysinfo(info: *mut libc::sysinfo) -> i64 {
|
unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
{
|
{
|
||||||
let ret: i64;
|
let ret: i64;
|
||||||
|
|
@ -59,7 +86,7 @@ unsafe fn sys_sysinfo(info: *mut libc::sysinfo) -> i64 {
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
||||||
{
|
{
|
||||||
unsafe { libc::sysinfo(info) as i64 }
|
compile_error!("Unsupported architecture for inline assembly syscalls");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue