summaryrefslogtreecommitdiff
path: root/runtime/ops/os
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-11-02 00:17:00 -0700
committerGitHub <noreply@github.com>2022-11-02 12:47:00 +0530
commitab7e80bde4513984046c093ed5eeb8c0640c4fe0 (patch)
treee1551b8f7c16bbc777b9ba6391238224fd550de2 /runtime/ops/os
parent5e4e324ceb657772f98bfae3c797e7417acc9837 (diff)
chore(runtime): remove dependency on sys-info crate (#16441)
Fixes #9862 `loadavg` | Target family | Syscall | Description | | ------------- | ------- | ----------- | | Linux | `sysinfo` | - | | Windows | - | Returns `DEFAULT_LOADAVG`. There is no concept of loadavg on Windows | | macOS, BSD | `getloadavg` | https://www.freebsd.org/cgi/man.cgi?query=getloadavg | `os_release` | Target family | Syscall | Description | | ------------- | ------- | ----------- | | Linux | `/proc/sys/kernel/osrelease` | - | | Windows | [`RtlGetVersion`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion) | dwMajorVersion . dwMinorVersion . dwBuildNumber | | macOS | `sysctl([CTL_KERN, KERN_OSRELEASE])` | - | `hostname` | Target family | Syscall | Description | | ------------- | ------- | ----------- | | Unix | `gethostname(sysconf(_SC_HOST_NAME_MAX))` | - | | Windows | `GetHostNameW` | - | `mem_info` | Target family | Syscall | Description | | ------------- | ------- | ----------- | | Linux | sysinfo | - | | Windows | `sysinfoapi::GlobalMemoryStatusEx` | - | | macOS | <br> <pre> sysctl([CTL_HW, HW_MEMSIZE]); <br> sysctl([CTL_VM, VM_SWAPUSAGE]); <br> host_statistics64(mach_host_self(), HOST_VM_INFO64) </pre> | - |
Diffstat (limited to 'runtime/ops/os')
-rw-r--r--runtime/ops/os/README.md32
-rw-r--r--runtime/ops/os/mod.rs307
-rw-r--r--runtime/ops/os/sys_info.rs284
3 files changed, 623 insertions, 0 deletions
diff --git a/runtime/ops/os/README.md b/runtime/ops/os/README.md
new file mode 100644
index 000000000..837bb7b3c
--- /dev/null
+++ b/runtime/ops/os/README.md
@@ -0,0 +1,32 @@
+## `os` ops
+
+`loadavg`
+
+| Target family | Syscall | Description |
+| ------------- | ------------ | -------------------------------------------------------------------- |
+| Linux | `sysinfo` | - |
+| Windows | - | Returns `DEFAULT_LOADAVG`. There is no concept of loadavg on Windows |
+| macOS, BSD | `getloadavg` | https://www.freebsd.org/cgi/man.cgi?query=getloadavg |
+
+`os_release`
+
+| Target family | Syscall | Description |
+| ------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
+| Linux | `/proc/sys/kernel/osrelease` | - |
+| Windows | [`RtlGetVersion`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion) | dwMajorVersion . dwMinorVersion . dwBuildNumber |
+| macOS | `sysctl([CTL_KERN, KERN_OSRELEASE])` | - |
+
+`hostname`
+
+| Target family | Syscall | Description |
+| ------------- | ----------------------------------------- | ----------- |
+| Unix | `gethostname(sysconf(_SC_HOST_NAME_MAX))` | - |
+| Windows | `GetHostNameW` | - |
+
+`mem_info`
+
+| Target family | Syscall | Description |
+| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
+| Linux | sysinfo | - |
+| Windows | `sysinfoapi::GlobalMemoryStatusEx` | - |
+| macOS | <br> <pre> sysctl([CTL_HW, HW_MEMSIZE]); <br> sysctl([CTL_VM, VM_SWAPUSAGE]); <br> host_statistics64(mach_host_self(), HOST_VM_INFO64) </pre> | - |
diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs
new file mode 100644
index 000000000..3b4645403
--- /dev/null
+++ b/runtime/ops/os/mod.rs
@@ -0,0 +1,307 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use super::utils::into_string;
+use crate::permissions::Permissions;
+use crate::worker::ExitCode;
+use deno_core::error::{type_error, AnyError};
+use deno_core::url::Url;
+use deno_core::Extension;
+use deno_core::OpState;
+use deno_core::{op, ExtensionBuilder};
+use deno_node::NODE_ENV_VAR_ALLOWLIST;
+use std::collections::HashMap;
+use std::env;
+
+mod sys_info;
+
+fn init_ops(builder: &mut ExtensionBuilder) -> &mut ExtensionBuilder {
+ builder.ops(vec![
+ op_env::decl(),
+ op_exec_path::decl(),
+ op_exit::decl(),
+ op_delete_env::decl(),
+ op_get_env::decl(),
+ op_gid::decl(),
+ op_hostname::decl(),
+ op_loadavg::decl(),
+ op_network_interfaces::decl(),
+ op_os_release::decl(),
+ op_set_env::decl(),
+ op_set_exit_code::decl(),
+ op_system_memory_info::decl(),
+ op_uid::decl(),
+ ])
+}
+
+pub fn init(exit_code: ExitCode) -> Extension {
+ let mut builder = Extension::builder();
+ init_ops(&mut builder)
+ .state(move |state| {
+ state.put::<ExitCode>(exit_code.clone());
+ Ok(())
+ })
+ .build()
+}
+
+pub fn init_for_worker() -> Extension {
+ let mut builder = Extension::builder();
+ init_ops(&mut builder)
+ .middleware(|op| match op.name {
+ "op_exit" => noop_op::decl(),
+ "op_set_exit_code" => noop_op::decl(),
+ _ => op,
+ })
+ .build()
+}
+
+#[op]
+fn noop_op() -> Result<(), AnyError> {
+ Ok(())
+}
+
+#[op]
+fn op_exec_path(state: &mut OpState) -> Result<String, AnyError> {
+ let current_exe = env::current_exe().unwrap();
+ state.borrow_mut::<Permissions>().read.check_blind(
+ &current_exe,
+ "exec_path",
+ "Deno.execPath()",
+ )?;
+ // Now apply URL parser to current exe to get fully resolved path, otherwise
+ // we might get `./` and `../` bits in `exec_path`
+ let exe_url = Url::from_file_path(current_exe).unwrap();
+ let path = exe_url.to_file_path().unwrap();
+
+ into_string(path.into_os_string())
+}
+
+#[op]
+fn op_set_env(
+ state: &mut OpState,
+ key: String,
+ value: String,
+) -> Result<(), AnyError> {
+ state.borrow_mut::<Permissions>().env.check(&key)?;
+ if key.is_empty() {
+ return Err(type_error("Key is an empty string."));
+ }
+ if key.contains(&['=', '\0'] as &[char]) {
+ return Err(type_error(format!(
+ "Key contains invalid characters: {:?}",
+ key
+ )));
+ }
+ if value.contains('\0') {
+ return Err(type_error(format!(
+ "Value contains invalid characters: {:?}",
+ value
+ )));
+ }
+ env::set_var(key, value);
+ Ok(())
+}
+
+#[op]
+fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
+ state.borrow_mut::<Permissions>().env.check_all()?;
+ Ok(env::vars().collect())
+}
+
+#[op]
+fn op_get_env(
+ state: &mut OpState,
+ key: String,
+) -> Result<Option<String>, AnyError> {
+ let skip_permission_check =
+ state.borrow::<crate::ops::UnstableChecker>().unstable
+ && NODE_ENV_VAR_ALLOWLIST.contains(&key);
+
+ if !skip_permission_check {
+ state.borrow_mut::<Permissions>().env.check(&key)?;
+ }
+
+ if key.is_empty() {
+ return Err(type_error("Key is an empty string."));
+ }
+
+ if key.contains(&['=', '\0'] as &[char]) {
+ return Err(type_error(format!(
+ "Key contains invalid characters: {:?}",
+ key
+ )));
+ }
+
+ let r = match env::var(key) {
+ Err(env::VarError::NotPresent) => None,
+ v => Some(v?),
+ };
+ Ok(r)
+}
+
+#[op]
+fn op_delete_env(state: &mut OpState, key: String) -> Result<(), AnyError> {
+ state.borrow_mut::<Permissions>().env.check(&key)?;
+ if key.is_empty() || key.contains(&['=', '\0'] as &[char]) {
+ return Err(type_error("Key contains invalid characters."));
+ }
+ env::remove_var(key);
+ Ok(())
+}
+
+#[op]
+fn op_set_exit_code(state: &mut OpState, code: i32) {
+ state.borrow_mut::<ExitCode>().set(code);
+}
+
+#[op]
+fn op_exit(state: &mut OpState) {
+ let code = state.borrow::<ExitCode>().get();
+ std::process::exit(code)
+}
+
+#[op]
+fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("loadavg", Some("Deno.loadavg()"))?;
+ Ok(sys_info::loadavg())
+}
+
+#[op]
+fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("hostname", Some("Deno.hostname()"))?;
+ Ok(sys_info::hostname())
+}
+
+#[op]
+fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("osRelease", Some("Deno.osRelease()"))?;
+ Ok(sys_info::os_release())
+}
+
+#[op]
+fn op_network_interfaces(
+ state: &mut OpState,
+) -> Result<Vec<NetworkInterface>, AnyError> {
+ super::check_unstable(state, "Deno.networkInterfaces");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("networkInterfaces", Some("Deno.networkInterfaces()"))?;
+ Ok(netif::up()?.map(NetworkInterface::from).collect())
+}
+
+#[derive(serde::Serialize)]
+struct NetworkInterface {
+ family: &'static str,
+ name: String,
+ address: String,
+ netmask: String,
+ scopeid: Option<u32>,
+ cidr: String,
+ mac: String,
+}
+
+impl From<netif::Interface> for NetworkInterface {
+ fn from(ifa: netif::Interface) -> Self {
+ let family = match ifa.address() {
+ std::net::IpAddr::V4(_) => "IPv4",
+ std::net::IpAddr::V6(_) => "IPv6",
+ };
+
+ let (address, range) = ifa.cidr();
+ let cidr = format!("{:?}/{}", address, range);
+
+ let name = ifa.name().to_owned();
+ let address = format!("{:?}", ifa.address());
+ let netmask = format!("{:?}", ifa.netmask());
+ let scopeid = ifa.scope_id();
+
+ let [b0, b1, b2, b3, b4, b5] = ifa.mac();
+ let mac = format!(
+ "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+ b0, b1, b2, b3, b4, b5
+ );
+
+ Self {
+ family,
+ name,
+ address,
+ netmask,
+ scopeid,
+ cidr,
+ mac,
+ }
+ }
+}
+
+#[op]
+fn op_system_memory_info(
+ state: &mut OpState,
+) -> Result<Option<sys_info::MemInfo>, AnyError> {
+ super::check_unstable(state, "Deno.systemMemoryInfo");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("systemMemoryInfo", Some("Deno.systemMemoryInfo()"))?;
+ Ok(sys_info::mem_info())
+}
+
+#[cfg(not(windows))]
+#[op]
+fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.gid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("gid", Some("Deno.gid()"))?;
+ // TODO(bartlomieju):
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ unsafe {
+ Ok(Some(libc::getgid()))
+ }
+}
+
+#[cfg(windows)]
+#[op]
+fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.gid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("gid", Some("Deno.gid()"))?;
+ Ok(None)
+}
+
+#[cfg(not(windows))]
+#[op]
+fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.uid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("uid", Some("Deno.uid()"))?;
+ // TODO(bartlomieju):
+ #[allow(clippy::undocumented_unsafe_blocks)]
+ unsafe {
+ Ok(Some(libc::getuid()))
+ }
+}
+
+#[cfg(windows)]
+#[op]
+fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+ super::check_unstable(state, "Deno.uid");
+ state
+ .borrow_mut::<Permissions>()
+ .sys
+ .check("uid", Some("Deno.uid()"))?;
+ Ok(None)
+}
diff --git a/runtime/ops/os/sys_info.rs b/runtime/ops/os/sys_info.rs
new file mode 100644
index 000000000..3e6cd4a51
--- /dev/null
+++ b/runtime/ops/os/sys_info.rs
@@ -0,0 +1,284 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+type LoadAvg = (f64, f64, f64);
+const DEFAULT_LOADAVG: LoadAvg = (0.0, 0.0, 0.0);
+
+pub fn loadavg() -> LoadAvg {
+ #[cfg(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")]
+ {
+ match std::fs::read_to_string("/proc/sys/kernel/osrelease") {
+ Ok(mut s) => {
+ s.pop(); // pop '\n'
+ s
+ }
+ _ => String::from(""),
+ }
+ }
+ #[cfg(target_vendor = "apple")]
+ {
+ 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
+ )
+ }
+ }
+}
+
+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::os::windows::ffi::OsStringExt;
+ use winapi::um::winsock2::GetHostNameW;
+
+ let namelen = 256;
+ let mut name: Vec<u16> = vec![0u16; namelen];
+ 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(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.buffers = info.bufferram * mem_unit;
+ }
+ }
+ #[cfg(any(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,
+ );
+ mem_info.total /= 1024;
+
+ 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 = unsafe { 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::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 / 1024;
+ mem_info.available = 0;
+ mem_info.free = stat.ullAvailPhys / 1024;
+ mem_info.cached = 0;
+ mem_info.buffers = 0;
+ mem_info.swap_total = (stat.ullTotalPageFile - stat.ullTotalPhys) / 1024;
+ mem_info.swap_free = (stat.ullAvailPageFile - stat.ullAvailPhys) / 1024;
+ if mem_info.swap_free > mem_info.swap_total {
+ mem_info.swap_free = mem_info.swap_total;
+ }
+ }
+ }
+
+ Some(mem_info)
+}