// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. #[cfg(target_family = "windows")] use std::sync::Once; type LoadAvg = (f64, f64, f64); const DEFAULT_LOADAVG: LoadAvg = (0.0, 0.0, 0.0); pub fn loadavg() -> LoadAvg { #[cfg(any(target_os = "android", target_os = "linux"))] { use libc::SI_LOAD_SHIFT; let mut info = std::mem::MaybeUninit::uninit(); // SAFETY: `info` is a valid pointer to a `libc::sysinfo` struct. let res = unsafe { libc::sysinfo(info.as_mut_ptr()) }; if res == 0 { // SAFETY: `sysinfo` returns 0 on success, and `info` is initialized. let info = unsafe { info.assume_init() }; ( info.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64, info.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64, info.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64, ) } else { DEFAULT_LOADAVG } } #[cfg(any( target_vendor = "apple", target_os = "freebsd", target_os = "openbsd" ))] { let mut l: [f64; 3] = [0.; 3]; // SAFETY: `&mut l` is a valid pointer to an array of 3 doubles if unsafe { libc::getloadavg(&mut l as *mut f64, l.len() as _) } < 3 { DEFAULT_LOADAVG } else { (l[0], l[1], l[2]) } } #[cfg(target_os = "windows")] { DEFAULT_LOADAVG } } pub fn os_release() -> String { #[cfg(target_os = "linux")] { #[allow(clippy::disallowed_methods)] match std::fs::read_to_string("/proc/sys/kernel/osrelease") { Ok(mut s) => { s.pop(); // pop '\n' s } _ => String::from(""), } } #[cfg(target_os = "android")] { let mut info = std::mem::MaybeUninit::uninit(); // SAFETY: `info` is a valid pointer to a `libc::utsname` struct. let res = unsafe { libc::uname(info.as_mut_ptr()) }; if res != 0 { return String::from(""); } // SAFETY: `uname` returns 0 on success, and `info` is initialized. let mut info = unsafe { info.assume_init() }; let len = info.release.len(); info.release[len - 1] = 0; // SAFETY: `info.release` is a valid pointer and NUL-terminated. let c_str = unsafe { std::ffi::CStr::from_ptr(info.release.as_ptr()) }; c_str.to_string_lossy().into_owned() } #[cfg(any( target_vendor = "apple", target_os = "freebsd", target_os = "openbsd" ))] { let mut s = [0u8; 256]; let mut mib = [libc::CTL_KERN, libc::KERN_OSRELEASE]; // 256 is enough. let mut len = s.len(); // SAFETY: `sysctl` is thread-safe. // `s` is only accessed if sysctl() succeeds and agrees with the `len` set // by sysctl(). if unsafe { libc::sysctl( mib.as_mut_ptr(), mib.len() as _, s.as_mut_ptr() as _, &mut len, std::ptr::null_mut(), 0, ) } == -1 { return String::from("Unknown"); } // without the NUL terminator return String::from_utf8_lossy(&s[..len - 1]).to_string(); } #[cfg(target_family = "windows")] { use ntapi::ntrtl::RtlGetVersion; use winapi::shared::ntdef::NT_SUCCESS; use winapi::um::winnt::RTL_OSVERSIONINFOEXW; let mut version_info = std::mem::MaybeUninit::<RTL_OSVERSIONINFOEXW>::uninit(); // SAFETY: we need to initialize dwOSVersionInfoSize. unsafe { (*version_info.as_mut_ptr()).dwOSVersionInfoSize = std::mem::size_of::<RTL_OSVERSIONINFOEXW>() as u32; } // SAFETY: `version_info` is pointer to a valid `RTL_OSVERSIONINFOEXW` struct and // dwOSVersionInfoSize is set to the size of RTL_OSVERSIONINFOEXW. if !NT_SUCCESS(unsafe { RtlGetVersion(version_info.as_mut_ptr() as *mut _) }) { String::from("") } else { // SAFETY: we assume that RtlGetVersion() initializes the fields. let version_info = unsafe { version_info.assume_init() }; format!( "{}.{}.{}", version_info.dwMajorVersion, version_info.dwMinorVersion, version_info.dwBuildNumber ) } } } #[cfg(target_family = "windows")] static WINSOCKET_INIT: Once = Once::new(); pub fn hostname() -> String { #[cfg(target_family = "unix")] // SAFETY: `sysconf` returns a system constant. unsafe { let buf_size = libc::sysconf(libc::_SC_HOST_NAME_MAX) as usize; let mut buf = vec![0u8; buf_size + 1]; let len = buf.len(); if libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, len) < 0 { return String::from(""); } // ensure NUL termination buf[len - 1] = 0; std::ffi::CStr::from_ptr(buf.as_ptr() as *const libc::c_char) .to_string_lossy() .to_string() } #[cfg(target_family = "windows")] { use std::ffi::OsString; use std::mem; use std::os::windows::ffi::OsStringExt; use winapi::shared::minwindef::MAKEWORD; use winapi::um::winsock2::GetHostNameW; use winapi::um::winsock2::WSAStartup; let namelen = 256; let mut name: Vec<u16> = vec![0u16; namelen]; // Start winsock to make `GetHostNameW` work correctly // https://github.com/retep998/winapi-rs/issues/296 // SAFETY: winapi call WINSOCKET_INIT.call_once(|| unsafe { let mut data = mem::zeroed(); let wsa_startup_result = WSAStartup(MAKEWORD(2, 2), &mut data); if wsa_startup_result != 0 { panic!("Failed to start winsocket"); } }); let err = // SAFETY: length of wide string is 256 chars or less. // https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew unsafe { GetHostNameW(name.as_mut_ptr(), namelen as libc::c_int) }; if err == 0 { // TODO(@littledivy): Probably not the most efficient way. let len = name.iter().take_while(|&&c| c != 0).count(); OsString::from_wide(&name[..len]) .to_string_lossy() .into_owned() } else { String::from("") } } } #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] pub struct MemInfo { pub total: u64, pub free: u64, pub available: u64, pub buffers: u64, pub cached: u64, pub swap_total: u64, pub swap_free: u64, } pub fn mem_info() -> Option<MemInfo> { let mut mem_info = MemInfo { total: 0, free: 0, available: 0, buffers: 0, cached: 0, swap_total: 0, swap_free: 0, }; #[cfg(any(target_os = "android", target_os = "linux"))] { let mut info = std::mem::MaybeUninit::uninit(); // SAFETY: `info` is a valid pointer to a `libc::sysinfo` struct. let res = unsafe { libc::sysinfo(info.as_mut_ptr()) }; if res == 0 { // SAFETY: `sysinfo` initializes the struct. let info = unsafe { info.assume_init() }; let mem_unit = info.mem_unit as u64; mem_info.swap_total = info.totalswap * mem_unit; mem_info.swap_free = info.freeswap * mem_unit; mem_info.total = info.totalram * mem_unit; mem_info.free = info.freeram * mem_unit; mem_info.available = mem_info.free; mem_info.buffers = info.bufferram * mem_unit; } // Gets the available memory from /proc/meminfo in linux for compatibility #[allow(clippy::disallowed_methods)] if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") { let line = meminfo.lines().find(|l| l.starts_with("MemAvailable:")); if let Some(line) = line { let mem = line.split_whitespace().nth(1); let mem = mem.and_then(|v| v.parse::<u64>().ok()); mem_info.available = mem.unwrap_or(0) * 1024; } } } #[cfg(target_vendor = "apple")] { let mut mib: [i32; 2] = [0, 0]; mib[0] = libc::CTL_HW; mib[1] = libc::HW_MEMSIZE; // SAFETY: // - We assume that `mach_host_self` always returns a valid value. // - sysconf returns a system constant. unsafe { let mut size = std::mem::size_of::<u64>(); libc::sysctl( mib.as_mut_ptr(), mib.len() as _, &mut mem_info.total as *mut _ as *mut libc::c_void, &mut size, std::ptr::null_mut(), 0, ); let mut xs: libc::xsw_usage = std::mem::zeroed::<libc::xsw_usage>(); mib[0] = libc::CTL_VM; mib[1] = libc::VM_SWAPUSAGE; let mut size = std::mem::size_of::<libc::xsw_usage>(); libc::sysctl( mib.as_mut_ptr(), mib.len() as _, &mut xs as *mut _ as *mut libc::c_void, &mut size, std::ptr::null_mut(), 0, ); mem_info.swap_total = xs.xsu_total; mem_info.swap_free = xs.xsu_avail; let mut count: u32 = libc::HOST_VM_INFO64_COUNT as _; let mut stat = std::mem::zeroed::<libc::vm_statistics64>(); if libc::host_statistics64( // TODO(@littledivy): Put this in a once_cell. libc::mach_host_self(), libc::HOST_VM_INFO64, &mut stat as *mut libc::vm_statistics64 as *mut _, &mut count, ) == libc::KERN_SUCCESS { // TODO(@littledivy): Put this in a once_cell let page_size = libc::sysconf(libc::_SC_PAGESIZE) as u64; mem_info.available = (stat.free_count as u64 + stat.inactive_count as u64) * page_size / 1024; mem_info.free = (stat.free_count as u64 - stat.speculative_count as u64) * page_size / 1024; } } } #[cfg(target_family = "windows")] // SAFETY: // - `mem_status` is a valid pointer to a `libc::MEMORYSTATUSEX` struct. // - `dwLength` is set to the size of the struct. unsafe { use std::mem; use winapi::shared::minwindef; use winapi::um::psapi::GetPerformanceInfo; use winapi::um::psapi::PERFORMANCE_INFORMATION; use winapi::um::sysinfoapi; let mut mem_status = mem::MaybeUninit::<sysinfoapi::MEMORYSTATUSEX>::uninit(); let length = mem::size_of::<sysinfoapi::MEMORYSTATUSEX>() as minwindef::DWORD; (*mem_status.as_mut_ptr()).dwLength = length; let result = sysinfoapi::GlobalMemoryStatusEx(mem_status.as_mut_ptr()); if result != 0 { let stat = mem_status.assume_init(); mem_info.total = stat.ullTotalPhys; mem_info.available = 0; mem_info.free = stat.ullAvailPhys; mem_info.cached = 0; mem_info.buffers = 0; // `stat.ullTotalPageFile` is reliable only from GetPerformanceInfo() // // See https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex // and https://github.com/GuillaumeGomez/sysinfo/issues/534 let mut perf_info = mem::MaybeUninit::<PERFORMANCE_INFORMATION>::uninit(); let result = GetPerformanceInfo( perf_info.as_mut_ptr(), mem::size_of::<PERFORMANCE_INFORMATION>() as minwindef::DWORD, ); if result == minwindef::TRUE { let perf_info = perf_info.assume_init(); let swap_total = perf_info.PageSize * perf_info .CommitLimit .saturating_sub(perf_info.PhysicalTotal); let swap_free = perf_info.PageSize * perf_info .CommitLimit .saturating_sub(perf_info.PhysicalTotal) .saturating_sub(perf_info.PhysicalAvailable); mem_info.swap_total = (swap_total / 1000) as u64; mem_info.swap_free = (swap_free / 1000) as u64; } } } Some(mem_info) } pub fn os_uptime() -> u64 { let uptime: u64; #[cfg(any(target_os = "android", target_os = "linux"))] { let mut info = std::mem::MaybeUninit::uninit(); // SAFETY: `info` is a valid pointer to a `libc::sysinfo` struct. let res = unsafe { libc::sysinfo(info.as_mut_ptr()) }; uptime = if res == 0 { // SAFETY: `sysinfo` initializes the struct. let info = unsafe { info.assume_init() }; info.uptime as u64 } else { 0 } } #[cfg(any( target_vendor = "apple", target_os = "freebsd", target_os = "openbsd" ))] { use std::mem; use std::time::Duration; use std::time::SystemTime; let mut request = [libc::CTL_KERN, libc::KERN_BOOTTIME]; // SAFETY: `boottime` is only accessed if sysctl() succeeds // and agrees with the `size` set by sysctl(). let mut boottime: libc::timeval = unsafe { mem::zeroed() }; let mut size: libc::size_t = mem::size_of_val(&boottime) as libc::size_t; // SAFETY: `sysctl` is thread-safe. let res = unsafe { libc::sysctl( &mut request[0], 2, &mut boottime as *mut libc::timeval as *mut libc::c_void, &mut size, std::ptr::null_mut(), 0, ) }; uptime = if res == 0 { SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .map(|d| { (d - Duration::new( boottime.tv_sec as u64, boottime.tv_usec as u32 * 1000, )) .as_secs() }) .unwrap_or_default() } else { 0 } } #[cfg(target_family = "windows")] // SAFETY: windows API usage unsafe { // Windows is the only one that returns `uptime` in millisecond precision, // so we need to get the seconds out of it to be in sync with other envs. uptime = winapi::um::sysinfoapi::GetTickCount64() / 1000; } uptime }