sysinfo-0.13.2/.cargo_vcs_info.json0000644000000001121364507430400127070ustar00{ "git": { "sha1": "cd15093afe417317cc8e9e33aa6fab5066c8c6a9" } } sysinfo-0.13.2/.github/FUNDING.yml010064400017500001750000000002121361711234300146610ustar0000000000000000# These are supported funding model platforms github: [GuillaumeGomez] patreon: GuillaumeGomez custom: ["https://paypal.me/imperioland"] sysinfo-0.13.2/.gitignore010066400017500001750000000014711353143751600135150ustar0000000000000000 # Created by https://www.gitignore.io/api/osx,rust ### OSX ### *.DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Rust ### # Generated by Cargo # will have compiled files and executables /target/ examples/target # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk rusty-tags.vi tags **.o simplesysinfo-0.13.2/.travis.yml010064400017500001750000000045251363221350000136220ustar0000000000000000language: rust matrix: include: - os: linux rust: stable dist: trusty - os: linux env: TARGET=armv7-unknown-linux-gnueabihf rust: stable dist: trusty - os: linux env: TARGET=arm-linux-androideabi rust: stable dist: trusty - os: linux env: TARGET=x86_64-apple-darwin rust: stable dist: trusty - os: linux env: TARGET=i686-unknown-linux-gnu rust: stable dist: trusty - os: linux env: TARGET=i686-unknown-linux-musl rust: stable dist: trusty - os: linux env: TARGET=wasm32-unknown-unknown rust: stable dist: trusty - os: linux rust: nightly dist: trusty - os: linux env: TARGET=armv7-unknown-linux-gnueabihf rust: nightly dist: trusty - os: linux env: TARGET=arm-linux-androideabi rust: nightly dist: trusty - os: linux env: TARGET=x86_64-apple-darwin rust: nightly dist: trusty - os: linux env: TARGET=i686-unknown-linux-gnu rust: nightly dist: trusty - os: linux env: TARGET=i686-unknown-linux-musl rust: nightly dist: trusty - os: osx rust: stable - os: osx rust: nightly script: - rustc --version - sysctl -a | grep mem - if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then (rustup component add clippy && cargo clippy) || touch clippy_install_failed; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cargo build --features debug; elif [[ -n "$TARGET" ]]; then rustup target add $TARGET;export EXTRA="--target=$TARGET"; fi - echo $EXTRA - if [[ -n "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo check $EXTRA; else RUST_BACKTRACE=1 cargo build; fi - if [[ "$TRAVIS_RUST_VERSION" == "nightly" && -z "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo bench; fi - if [[ -z "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo test; fi - cargo doc - cd examples - if [[ -z "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo build; fi - if [[ "$TRAVIS_RUST_VERSION" == "nightly" && -z "$EXTRA" && ! -f clippy_install_failed ]]; then cargo clippy $EXTRA || echo "clippy failed"; fi - cd .. - if [[ -z "$EXTRA" ]]; then make; fi - if [[ -z "$EXTRA" ]]; then LD_LIBRARY_PATH=./target/debug ./simple; fisysinfo-0.13.2/Cargo.toml0000644000000035121364507430400107140ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "sysinfo" version = "0.13.2" authors = ["Guillaume Gomez "] build = "build.rs" description = "Library to get system information such as processes, processors, disks, components and networks" readme = "README.md" categories = ["filesystem", "os", "api-bindings"] license = "MIT" repository = "https://github.com/GuillaumeGomez/sysinfo" [lib] name = "sysinfo" crate_type = ["rlib", "cdylib"] path = "src/sysinfo.rs" [dependencies.cfg-if] version = "0.1" [dependencies.doc-comment] version = "0.3" [dependencies.once_cell] version = "1.0" [dependencies.rayon] version = "^1.0" [features] c-interface = [] debug = ["libc/extra_traits"] [target."cfg(not(any(target_os = \"unknown\", target_arch = \"wasm32\")))".dependencies.libc] version = "0.2" [target."cfg(windows)".dependencies.ntapi] version = "0.3" [target."cfg(windows)".dependencies.winapi] version = "0.3" features = ["fileapi", "handleapi", "ifdef", "ioapiset", "minwindef", "pdh", "psapi", "synchapi", "sysinfoapi", "winbase", "winerror", "winioctl", "winnt", "oleauto", "wbemcli", "rpcdce", "combaseapi", "objidl", "powerbase", "netioapi", "lmcons", "lmaccess", "lmapibuf", "memoryapi", "shellapi"] [badges.appveyor] repository = "GuillaumeGomez/sysinfo" service = "github" [badges.travis-ci] repository = "GuillaumeGomez/sysinfo" sysinfo-0.13.2/Cargo.toml.orig010064400017500001750000000023041364506454600144130ustar0000000000000000[package] name = "sysinfo" version = "0.13.2" authors = ["Guillaume Gomez "] description = "Library to get system information such as processes, processors, disks, components and networks" repository = "https://github.com/GuillaumeGomez/sysinfo" license = "MIT" readme = "README.md" categories = ["filesystem", "os", "api-bindings"] build = "build.rs" [dependencies] cfg-if = "0.1" rayon = "^1.0" doc-comment = "0.3" once_cell = "1.0" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["fileapi", "handleapi", "ifdef", "ioapiset", "minwindef", "pdh", "psapi", "synchapi", "sysinfoapi", "winbase", "winerror", "winioctl", "winnt", "oleauto", "wbemcli", "rpcdce", "combaseapi", "objidl", "powerbase", "netioapi", "lmcons", "lmaccess", "lmapibuf", "memoryapi", "shellapi"] } ntapi = "0.3" [target.'cfg(not(any(target_os = "unknown", target_arch = "wasm32")))'.dependencies] libc = "0.2" [lib] name = "sysinfo" crate_type = ["rlib", "cdylib"] path = "src/sysinfo.rs" [features] c-interface = [] debug = ["libc/extra_traits"] [badges] travis-ci = { repository = "GuillaumeGomez/sysinfo" } appveyor = { repository = "GuillaumeGomez/sysinfo", service = "github" } sysinfo-0.13.2/LICENSE010066400017500001750000000020731353143751600125310ustar0000000000000000The MIT License (MIT) Copyright (c) 2015 Guillaume Gomez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. sysinfo-0.13.2/Makefile010066400017500001750000000014441353143751600131650ustar0000000000000000# # Sysinfo # # Copyright (c) 2017 Guillaume Gomez # # # Please note that this Makefile only generates the c example. # IDIR = ./src CC = gcc CFLAGS = -I$(IDIR) ODIR = examples/src LDIR = ./target/debug/ LDIR-RELEASE = ./target/release/ LIBS = -lsysinfo -lpthread _DEPS = sysinfo.h DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS)) _OBJ = simple.o OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) simple: $(OBJ) @echo "Compiling in debug mode" cargo build --features=c-interface gcc -o $@ $^ $(CFLAGS) -L$(LDIR) $(LIBS) release: $(OBJ) @echo "Compiling in release mode" cargo build --features=c-interface --release gcc -o simple $^ $(CFLAGS) -L$(LDIR-RELEASE) $(LIBS) $(ODIR)/%.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) .PHONY: simple clean: @echo "Cleaning mess" rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ sysinfo-0.13.2/README.md010064400017500001750000000161541364506453400130100ustar0000000000000000# sysinfo [![][img_travis-ci]][travis-ci] [![Build status](https://ci.appveyor.com/api/projects/status/nhep876b3legunwd/branch/master?svg=true)](https://ci.appveyor.com/project/GuillaumeGomez/sysinfo/branch/master) [![][img_crates]][crates] [![][img_doc]][doc] [img_travis-ci]: https://api.travis-ci.org/GuillaumeGomez/sysinfo.png?branch=master [img_crates]: https://img.shields.io/crates/v/sysinfo.svg [img_doc]: https://img.shields.io/badge/rust-documentation-blue.svg [travis-ci]: https://travis-ci.org/GuillaumeGomez/sysinfo [crates]: https://crates.io/crates/sysinfo [doc]: https://docs.rs/sysinfo/ A system handler to interact with processes. Supports the following platforms: * Linux * Raspberry Pi * Android * macOS * Windows It also compiles for Android but never been tested on it. ### Running on Raspberry Pi It'll be difficult to build on Raspberry Pi. A good way-around is to be build on Linux before sending it to your Raspberry Pi. First install the arm toolchain, for example on Ubuntu: `sudo apt-get install gcc-multilib-arm-linux-gnueabihf`. Then configure cargo to use the corresponding toolchain: ```bash cat << EOF > ~/.cargo/config [target.armv7-unknown-linux-gnueabihf] linker = "arm-linux-gnueabihf-gcc" EOF ``` Finally, cross compile: ```bash rustup target add armv7-unknown-linux-gnueabihf cargo build --target=armv7-unknown-linux-gnueabihf ``` ### Linux on Docker & Windows Subsystem for Linux (WSL) Virtual Linux systems, such as those run through Docker and Windows Subsystem for Linux (WSL), do not receive host hardware information via `/sys/class/hwmon` or `/sys/class/thermal`. As such, querying for components may return no results (or unexpected results) when using this library on virtual systems. ## Code example You have an example into the `examples` folder. Just run `cargo run` inside the `examples` folder to start it. Otherwise, here is a little code sample: ```rust use sysinfo::{NetworkExt, NetworksExt, ProcessExt, System, SystemExt}; let mut sys = System::new_all(); // We display the disks: println!("=> disk list:"); for disk in sys.get_disks() { println!("{:?}", disk); } // Network data: for (interface_name, data) in sys.get_networks() { println!("{}: {}/{} B", interface_name, data.get_received(), data.get_transmitted()); } // Components temperature: for component in sys.get_components() { println!("{:?}", component); } // Memory information: println!("total memory: {} KiB", sys.get_total_memory()); println!("used memory : {} KiB", sys.get_used_memory()); println!("total swap : {} KiB", sys.get_total_swap()); println!("used swap : {} KiB", sys.get_used_swap()); // Number of processors println!("NB processors: {}", sys.get_processors().len()); // To refresh all system information: sys.refresh_all(); // We show the processes and some of their information: for (pid, process) in sys.get_processes() { println!("[{}] {} {:?}", pid, process.name(), process.disk_usage()); } ``` ## C interface It's possible to use this crate directly from C. Take a look at the `Makefile` and at the `examples/src/simple.c` file. To build the C example, just run: ```bash > make > ./simple # If needed: > LD_LIBRARY_PATH=target/release/ ./simple ``` ### Benchmarks You can run the benchmarks locally with rust **nightly** by doing: ```bash > cargo bench ``` Here are the current results: **Linux**
```text test bench_new ... bench: 182,536 ns/iter (+/- 21,074) test bench_new_all ... bench: 19,911,714 ns/iter (+/- 1,612,109) test bench_refresh_all ... bench: 5,649,643 ns/iter (+/- 444,129) test bench_refresh_components ... bench: 25,293 ns/iter (+/- 1,748) test bench_refresh_components_list ... bench: 382,331 ns/iter (+/- 31,620) test bench_refresh_cpu ... bench: 13,633 ns/iter (+/- 1,135) test bench_refresh_disks ... bench: 2,509 ns/iter (+/- 75) test bench_refresh_disks_list ... bench: 51,488 ns/iter (+/- 5,470) test bench_refresh_memory ... bench: 12,941 ns/iter (+/- 3,023) test bench_refresh_networks ... bench: 256,506 ns/iter (+/- 37,196) test bench_refresh_networks_list ... bench: 266,751 ns/iter (+/- 54,535) test bench_refresh_process ... bench: 117,372 ns/iter (+/- 8,732) test bench_refresh_processes ... bench: 5,125,929 ns/iter (+/- 560,050) test bench_refresh_system ... bench: 52,526 ns/iter (+/- 6,786) test bench_refresh_users_list ... bench: 2,479,582 ns/iter (+/- 1,063,982) ```
**Windows**
```text test bench_new ... bench: 7,119,215 ns/iter (+/- 283,002) test bench_new_all ... bench: 27,364,010 ns/iter (+/- 1,353,879) test bench_refresh_all ... bench: 3,125,085 ns/iter (+/- 92,479) test bench_refresh_components ... bench: 1,239,478 ns/iter (+/- 45,790) test bench_refresh_components_list ... bench: 3,197,295 ns/iter (+/- 91,662) test bench_refresh_cpu ... bench: 24,973 ns/iter (+/- 1,844) test bench_refresh_disks ... bench: 52,321 ns/iter (+/- 1,533) test bench_refresh_disks_list ... bench: 114,756 ns/iter (+/- 3,900) test bench_refresh_memory ... bench: 581 ns/iter (+/- 25) test bench_refresh_networks ... bench: 35,231 ns/iter (+/- 2,210) test bench_refresh_networks_list ... bench: 661,170 ns/iter (+/- 56,636) test bench_refresh_process ... bench: 1,531 ns/iter (+/- 154) test bench_refresh_processes ... bench: 1,070,742 ns/iter (+/- 57,539) test bench_refresh_system ... bench: 1,303,291 ns/iter (+/- 44,538) test bench_refresh_users_list ... bench: 2,340,562 ns/iter (+/- 83,992) ```
**macOS**
```text test bench_new ... bench: 87,569 ns/iter (+/- 11,078) test bench_new_all ... bench: 21,445,081 ns/iter (+/- 523,973) test bench_refresh_all ... bench: 1,915,573 ns/iter (+/- 296,132) test bench_refresh_components ... bench: 293,904 ns/iter (+/- 63,492) test bench_refresh_components_list ... bench: 894,462 ns/iter (+/- 161,599) test bench_refresh_cpu ... bench: 8,636 ns/iter (+/- 1,244) test bench_refresh_disks ... bench: 937 ns/iter (+/- 97) test bench_refresh_disks_list ... bench: 25,116 ns/iter (+/- 990) test bench_refresh_memory ... bench: 2,172 ns/iter (+/- 67) test bench_refresh_networks ... bench: 183,552 ns/iter (+/- 2,253) test bench_refresh_networks_list ... bench: 183,623 ns/iter (+/- 11,183) test bench_refresh_process ... bench: 5,571 ns/iter (+/- 443) test bench_refresh_processes ... bench: 764,125 ns/iter (+/- 28,568) test bench_refresh_system ... bench: 333,610 ns/iter (+/- 53,204) test bench_refresh_users_list ... bench: 16,816,081 ns/iter (+/- 1,039,374) ```
## Donations If you appreciate my work and want to support me, you can do it here: [![Become a patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/GuillaumeGomez) sysinfo-0.13.2/appveyor.yml010064400017500001750000000011261361711234300141010ustar0000000000000000environment: matrix: - RUST: stable BITS: 32 - RUST: stable BITS: 64 install: - IF "%BITS%" == "32" SET ARCH=i686 - IF "%BITS%" == "64" SET ARCH=x86_64 - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe --default-host "%ARCH%-pc-windows-gnu" --default-toolchain %RUST% -y - SET PATH=C:\Users\appveyor\.cargo\bin;C:\msys64\mingw%BITS%\bin;%PATH%;C:\msys64\usr\bin - rustup component add clippy - rustc -Vv - cargo -Vv build_script: - cargo clippy - cargo build - cargo test - cargo doc - cd examples - cargo build test: false sysinfo-0.13.2/benches/basic.rs010064400017500001750000000052651364506453400145700ustar0000000000000000#![feature(test)] extern crate sysinfo; extern crate test; use sysinfo::get_current_pid; use sysinfo::SystemExt; #[bench] fn bench_new(b: &mut test::Bencher) { b.iter(|| { sysinfo::System::new(); }); } #[bench] fn bench_new_all(b: &mut test::Bencher) { b.iter(|| { sysinfo::System::new_all(); }); } #[bench] fn bench_refresh_all(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_all(); }); } #[bench] fn bench_refresh_system(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); s.refresh_system(); b.iter(move || { s.refresh_system(); }); } #[bench] fn bench_refresh_processes(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); s.refresh_processes(); // to load the whole processes list a first time. b.iter(move || { s.refresh_processes(); }); } #[bench] fn bench_refresh_process(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); s.refresh_all(); // to be sure it'll exist for at least as long as we run let pid = get_current_pid().expect("failed to get current pid"); b.iter(move || { s.refresh_process(pid, ); }); } #[bench] fn bench_refresh_disks(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_disks(); }); } #[bench] fn bench_refresh_disks_list(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); b.iter(move || { s.refresh_disks_list(); }); } #[bench] fn bench_refresh_networks(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_networks(); }); } #[bench] fn bench_refresh_networks_list(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); b.iter(move || { s.refresh_networks_list(); }); } #[bench] fn bench_refresh_memory(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); b.iter(move || { s.refresh_memory(); }); } #[bench] fn bench_refresh_cpu(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); b.iter(move || { s.refresh_cpu(); }); } #[bench] fn bench_refresh_components(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_components(); }); } #[bench] fn bench_refresh_components_list(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_components_list(); }); } #[bench] fn bench_refresh_users_list(b: &mut test::Bencher) { let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_users_list(); }); } sysinfo-0.13.2/build.rs010064400017500001750000000004231362722252500131620ustar0000000000000000fn main() { if std::env::var("TARGET").unwrap().contains("-apple") { println!("cargo:rustc-link-lib=framework=IOKit"); println!("cargo:rustc-link-lib=framework=CoreFoundation"); // println!("cargo:rustc-link-lib=framework=OpenDirectory"); } } sysinfo-0.13.2/src/c_interface.rs010064400017500001750000000343541364344617000151300ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use libc::{self, c_char, c_float, c_uint, c_void, pid_t, size_t}; use std::borrow::BorrowMut; use std::ffi::CString; use {NetworkExt, NetworksExt, Process, ProcessExt, ProcessorExt, System, SystemExt}; /// Equivalent of [`System`][crate::System] struct. pub type CSystem = *mut c_void; /// Equivalent of [`Process`][crate::Process] struct. pub type CProcess = *const c_void; /// C string returned from `CString::into_raw`. pub type RString = *const c_char; /// Callback used by [`get_processes`][crate::System#method.get_processes]. pub type ProcessLoop = extern "C" fn(pid: pid_t, process: CProcess, data: *mut c_void) -> bool; /// Equivalent of [`System::new()`][crate::System#method.new]. #[no_mangle] pub extern "C" fn sysinfo_init() -> CSystem { let system = Box::new(System::new()); Box::into_raw(system) as CSystem } /// Equivalent of `System::drop()`. Important in C to cleanup memory. #[no_mangle] pub extern "C" fn sysinfo_destroy(system: CSystem) { assert!(!system.is_null()); unsafe { Box::from_raw(system as *mut System); } } /// Equivalent of [`System::refresh_system()`][crate::System#method.refresh_system]. #[no_mangle] pub extern "C" fn sysinfo_refresh_system(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_system(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_memory()`][crate::System#method.refresh_memory]. #[no_mangle] pub extern "C" fn sysinfo_refresh_memory(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_memory(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_cpu()`][crate::System#method.refresh_cpu]. #[no_mangle] pub extern "C" fn sysinfo_refresh_cpu(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_cpu(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_components()`][crate::System#method.refresh_temperatures]. #[no_mangle] pub extern "C" fn sysinfo_refresh_components(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_components(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_all()`][crate::System#method.refresh_all]. #[no_mangle] pub extern "C" fn sysinfo_refresh_all(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_all(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_processes()`][crate::System#method.refresh_processes]. #[no_mangle] pub extern "C" fn sysinfo_refresh_processes(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_processes(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_process()`][crate::System#method.refresh_process]. #[cfg(target_os = "linux")] #[no_mangle] pub extern "C" fn sysinfo_refresh_process(system: CSystem, pid: pid_t) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_process(pid); } Box::into_raw(system); } /// Equivalent of [`System::refresh_disks()`][crate::System#method.refresh_disks]. #[no_mangle] pub extern "C" fn sysinfo_refresh_disks(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_disks(); } Box::into_raw(system); } /// Equivalent of [`System::refresh_disks_list()`][crate::System#method.refresh_disks_list]. #[no_mangle] pub extern "C" fn sysinfo_refresh_disks_list(system: CSystem) { assert!(!system.is_null()); let mut system: Box = unsafe { Box::from_raw(system as *mut System) }; { let system: &mut System = system.borrow_mut(); system.refresh_disks_list(); } Box::into_raw(system); } /// Equivalent of [`System::get_total_memory()`][crate::System#method.get_total_memory]. #[no_mangle] pub extern "C" fn sysinfo_get_total_memory(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_total_memory() as size_t; Box::into_raw(system); ret } /// Equivalent of [`System::get_free_memory()`][crate::System#method.get_free_memory]. #[no_mangle] pub extern "C" fn sysinfo_get_free_memory(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_free_memory() as size_t; Box::into_raw(system); ret } /// Equivalent of [`System::get_used_memory()`][crate::System#method.get_used_memory]. #[no_mangle] pub extern "C" fn sysinfo_get_used_memory(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_used_memory() as size_t; Box::into_raw(system); ret } /// Equivalent of [`System::get_total_swap()`][crate::System#method.get_total_swap]. #[no_mangle] pub extern "C" fn sysinfo_get_total_swap(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_total_swap() as size_t; Box::into_raw(system); ret } /// Equivalent of [`System::get_free_swap()`][crate::System#method.get_free_swap]. #[no_mangle] pub extern "C" fn sysinfo_get_free_swap(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_free_swap() as size_t; Box::into_raw(system); ret } /// Equivalent of [`System::get_used_swap()`][crate::System#method.get_used_swap]. #[no_mangle] pub extern "C" fn sysinfo_get_used_swap(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system.get_used_swap() as size_t; Box::into_raw(system); ret } /// Equivalent of /// `system::get_networks().iter().fold(0, |acc, (_, data)| acc + data.get_received() as size_t)`. #[no_mangle] pub extern "C" fn sysinfo_get_networks_received(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system .get_networks() .iter() .fold(0, |acc, (_, data)| acc + data.get_received() as size_t); Box::into_raw(system); ret } /// Equivalent of /// `system::get_networks().iter().fold(0, |acc, (_, data)| acc + data.get_transmitted() as size_t)`. #[no_mangle] pub extern "C" fn sysinfo_get_networks_transmitted(system: CSystem) -> size_t { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = system .get_networks() .iter() .fold(0, |acc, (_, data)| acc + data.get_transmitted() as size_t); Box::into_raw(system); ret } /// Equivalent of [`System::get_processors_usage()`][crate::System#method.get_processors_usage]. /// /// * `length` will contain the number of cpu usage added into `procs`. /// * `procs` will be allocated if it's null and will contain of cpu usage. #[no_mangle] pub extern "C" fn sysinfo_get_processors_usage( system: CSystem, length: *mut c_uint, procs: *mut *mut c_float, ) { assert!(!system.is_null()); if procs.is_null() || length.is_null() { return; } let system: Box = unsafe { Box::from_raw(system as *mut System) }; { let processors = system.get_processors(); unsafe { if (*procs).is_null() { (*procs) = libc::malloc(::std::mem::size_of::() * processors.len()) as *mut c_float; } for (pos, processor) in processors.iter().skip(1).enumerate() { (*(*procs).offset(pos as isize)) = processor.get_cpu_usage(); } *length = processors.len() as c_uint - 1; } } Box::into_raw(system); } /// Equivalent of [`System::get_processes()`][crate::System#method.get_processes]. Returns an /// array ended by a null pointer. Must be freed. /// /// # /!\ WARNING /!\ /// /// While having this method returned processes, you should *never* call any refresh method! #[no_mangle] pub extern "C" fn sysinfo_get_processes( system: CSystem, fn_pointer: Option, data: *mut c_void, ) -> size_t { assert!(!system.is_null()); if let Some(fn_pointer) = fn_pointer { let system: Box = unsafe { Box::from_raw(system as *mut System) }; let len = { let entries = system.get_processes(); for (pid, process) in entries { if !fn_pointer(*pid, process as *const Process as CProcess, data) { break; } } entries.len() as size_t }; Box::into_raw(system); len } else { 0 } } /// Equivalent of [`System::get_process()`][crate::System#method.get_process]. /// /// # /!\ WARNING /!\ /// /// While having this method returned process, you should *never* call any /// refresh method! #[no_mangle] pub extern "C" fn sysinfo_get_process_by_pid(system: CSystem, pid: pid_t) -> CProcess { assert!(!system.is_null()); let system: Box = unsafe { Box::from_raw(system as *mut System) }; let ret = if let Some(process) = system.get_process(pid) { process as *const Process as CProcess } else { ::std::ptr::null() }; Box::into_raw(system); ret } /// Equivalent of iterating over [`Process::tasks()`][crate::Process#method.tasks]. /// /// # /!\ WARNING /!\ /// /// While having this method processes, you should *never* call any refresh method! #[cfg(target_os = "linux")] #[no_mangle] pub extern "C" fn sysinfo_process_get_tasks( process: CProcess, fn_pointer: Option, data: *mut c_void, ) -> size_t { assert!(!process.is_null()); if let Some(fn_pointer) = fn_pointer { let process = process as *const Process; for (pid, process) in unsafe { (*process).tasks.iter() } { if !fn_pointer(*pid, process as *const Process as CProcess, data) { break; } } unsafe { (*process).tasks.len() as size_t } } else { 0 } } /// Equivalent of [`Process::pid()`][crate::Process#method.pid]. #[no_mangle] pub extern "C" fn sysinfo_process_get_pid(process: CProcess) -> pid_t { assert!(!process.is_null()); let process = process as *const Process; unsafe { (*process).pid() } } /// Equivalent of [`Process::parent()`][crate::Process#method.parent]. /// /// In case there is no known parent, it returns `0`. #[no_mangle] pub extern "C" fn sysinfo_process_get_parent_pid(process: CProcess) -> pid_t { assert!(!process.is_null()); let process = process as *const Process; unsafe { (*process).parent().unwrap_or_else(|| 0) } } /// Equivalent of [`Process::cpu_usage()`][crate::Process#method.cpu_usage]. #[no_mangle] pub extern "C" fn sysinfo_process_get_cpu_usage(process: CProcess) -> c_float { assert!(!process.is_null()); let process = process as *const Process; unsafe { (*process).cpu_usage() } } /// Equivalent of [`Process::memory()`][crate::Process#method.memory]. #[no_mangle] pub extern "C" fn sysinfo_process_get_memory(process: CProcess) -> size_t { assert!(!process.is_null()); let process = process as *const Process; unsafe { (*process).memory() as usize } } /// Equivalent of [`Process::virtual_memory()`][crate::Process#method.virtual_memory]. #[no_mangle] pub extern "C" fn sysinfo_process_get_virtual_memory(process: CProcess) -> size_t { assert!(!process.is_null()); let process = process as *const Process; unsafe { (*process).virtual_memory() as usize } } /// Equivalent of [`Process::exe()`][crate::Process#method.exe]. #[no_mangle] pub extern "C" fn sysinfo_process_get_executable_path(process: CProcess) -> RString { assert!(!process.is_null()); let process = process as *const Process; unsafe { if let Some(p) = (*process).exe().to_str() { if let Ok(c) = CString::new(p) { return c.into_raw() as _; } } ::std::ptr::null() } } /// Equivalent of [`Process::root()`][crate::Process#method.root]. #[no_mangle] pub extern "C" fn sysinfo_process_get_root_directory(process: CProcess) -> RString { assert!(!process.is_null()); let process = process as *const Process; unsafe { if let Some(p) = (*process).root().to_str() { if let Ok(c) = CString::new(p) { return c.into_raw() as _; } } ::std::ptr::null() } } /// Equivalent of [`Process::cwd()`][crate::Process#method.cwd]. #[no_mangle] pub extern "C" fn sysinfo_process_get_current_directory(process: CProcess) -> RString { assert!(!process.is_null()); let process = process as *const Process; unsafe { if let Some(p) = (*process).cwd().to_str() { if let Ok(c) = CString::new(p) { return c.into_raw() as _; } } ::std::ptr::null() } } /// Frees a C string created with `CString::into_raw()`. #[no_mangle] pub extern "C" fn sysinfo_rstring_free(s: RString) { if !s.is_null() { unsafe { let _ = CString::from_raw(s as usize as *mut i8); } } } sysinfo-0.13.2/src/common.rs010064400017500001750000000265341364506453400141610ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use NetworkData; use Networks; use NetworksExt; use UserExt; /// Trait to have a common fallback for the [`Pid`][crate::Pid] type. pub trait AsU32 { /// Allows to convert [`Pid`][crate::Pid] into [`u32`]. fn as_u32(&self) -> u32; } cfg_if! { if #[cfg(any(windows, target_os = "unknown", target_arch = "wasm32"))] { /// Process id. pub type Pid = usize; impl AsU32 for Pid { fn as_u32(&self) -> u32 { *self as u32 } } } else { use libc::pid_t; /// Process id. pub type Pid = pid_t; impl AsU32 for Pid { fn as_u32(&self) -> u32 { *self as u32 } } } } macro_rules! impl_get_set { ($name:ident, $with:ident, $without:ident) => { doc_comment! { concat!("Returns the value of the \"", stringify!($name), "\" refresh kind. ``` use sysinfo::RefreshKind; let r = RefreshKind::new(); assert_eq!(r.", stringify!($name), "(), false); let r = r.with_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), true); let r = r.without_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), false); ```"), pub fn $name(&self) -> bool { self.$name } } doc_comment! { concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`. ``` use sysinfo::RefreshKind; let r = RefreshKind::new(); assert_eq!(r.", stringify!($name), "(), false); let r = r.with_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), true); ```"), pub fn $with(mut self) -> RefreshKind { self.$name = true; self } } doc_comment! { concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`. ``` use sysinfo::RefreshKind; let r = RefreshKind::everything(); assert_eq!(r.", stringify!($name), "(), true); let r = r.without_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), false); ```"), pub fn $without(mut self) -> RefreshKind { self.$name = false; self } } }; } /// Used to determine what you want to refresh specifically on [`System`] type. /// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// /// // We want everything except disks. /// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list()); /// /// assert_eq!(system.get_disks().len(), 0); /// assert!(system.get_processes().len() > 0); /// ``` /// /// [`System`]: crate::System #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RefreshKind { networks: bool, networks_list: bool, processes: bool, disks_list: bool, disks: bool, memory: bool, cpu: bool, components: bool, components_list: bool, users_list: bool, } impl RefreshKind { /// Creates a new `RefreshKind` with every refresh set to `false`. /// /// ``` /// use sysinfo::RefreshKind; /// /// let r = RefreshKind::new(); /// /// assert_eq!(r.networks(), false); /// assert_eq!(r.networks_list(), false); /// assert_eq!(r.processes(), false); /// assert_eq!(r.disks_list(), false); /// assert_eq!(r.disks(), false); /// assert_eq!(r.memory(), false); /// assert_eq!(r.cpu(), false); /// assert_eq!(r.components(), false); /// assert_eq!(r.components_list(), false); /// assert_eq!(r.users_list(), false); /// ``` pub fn new() -> RefreshKind { RefreshKind { networks: false, networks_list: false, processes: false, disks: false, disks_list: false, memory: false, cpu: false, components: false, components_list: false, users_list: false, } } /// Creates a new `RefreshKind` with every refresh set to `true`. /// /// ``` /// use sysinfo::RefreshKind; /// /// let r = RefreshKind::everything(); /// /// assert_eq!(r.networks(), true); /// assert_eq!(r.networks_list(), true); /// assert_eq!(r.processes(), true); /// assert_eq!(r.disks_list(), true); /// assert_eq!(r.disks(), true); /// assert_eq!(r.memory(), true); /// assert_eq!(r.cpu(), true); /// assert_eq!(r.components(), true); /// assert_eq!(r.components_list(), true); /// assert_eq!(r.users_list(), true); /// ``` pub fn everything() -> RefreshKind { RefreshKind { networks: true, networks_list: true, processes: true, disks: true, disks_list: true, memory: true, cpu: true, components: true, components_list: true, users_list: true, } } impl_get_set!(networks, with_networks, without_networks); impl_get_set!(networks_list, with_networks_list, without_networks_list); impl_get_set!(processes, with_processes, without_processes); impl_get_set!(disks, with_disks, without_disks); impl_get_set!(disks_list, with_disks_list, without_disks_list); impl_get_set!(memory, with_memory, without_memory); impl_get_set!(cpu, with_cpu, without_cpu); impl_get_set!(components, with_components, without_components); impl_get_set!( components_list, with_components_list, without_components_list ); impl_get_set!(users_list, with_users_list, without_users_list); } /// Iterator over network interfaces. /// /// It is returned by [`Networks::iter`][crate::Networks#method.iter]. /// /// ```no_run /// use sysinfo::{System, SystemExt, NetworksExt}; /// /// let system = System::new_all(); /// let networks_iter = system.get_networks().iter(); /// ``` pub struct NetworksIter<'a> { inner: std::collections::hash_map::Iter<'a, String, NetworkData>, } impl<'a> NetworksIter<'a> { pub(crate) fn new(v: std::collections::hash_map::Iter<'a, String, NetworkData>) -> Self { NetworksIter { inner: v } } } impl<'a> Iterator for NetworksIter<'a> { type Item = (&'a String, &'a NetworkData); fn next(&mut self) -> Option { self.inner.next() } } impl<'a> IntoIterator for &'a Networks { type Item = (&'a String, &'a NetworkData); type IntoIter = NetworksIter<'a>; fn into_iter(self) -> Self::IntoIter { self.iter() } } /// Enum containing the different supported disks types. /// /// This type is returned by [`Disk::get_type`][crate::Disk#method.get_type]. /// /// ```no_run /// use sysinfo::{System, SystemExt, DiskExt}; /// /// let system = System::new_all(); /// for disk in system.get_disks() { /// println!("{:?}: {:?}", disk.get_name(), disk.get_type()); /// } /// ``` #[derive(Debug, PartialEq, Clone, Copy)] pub enum DiskType { /// HDD type. HDD, /// SSD type. SSD, /// Unknown type. Unknown(isize), } impl From for DiskType { fn from(t: isize) -> DiskType { match t { 0 => DiskType::HDD, 1 => DiskType::SSD, id => DiskType::Unknown(id), } } } /// An enum representing signal on UNIX-like systems. #[repr(C)] #[derive(Clone, PartialEq, PartialOrd, Debug, Copy)] pub enum Signal { /// Hangup detected on controlling terminal or death of controlling process. Hangup = 1, /// Interrupt from keyboard. Interrupt = 2, /// Quit from keyboard. Quit = 3, /// Illegal instruction. Illegal = 4, /// Trace/breakpoint trap. Trap = 5, /// Abort signal from C abort function. Abort = 6, // IOT trap. A synonym for SIGABRT. // IOT = 6, /// Bus error (bad memory access). Bus = 7, /// Floating point exception. FloatingPointException = 8, /// Kill signal. Kill = 9, /// User-defined signal 1. User1 = 10, /// Invalid memory reference. Segv = 11, /// User-defined signal 2. User2 = 12, /// Broken pipe: write to pipe with no readers. Pipe = 13, /// Timer signal from C alarm function. Alarm = 14, /// Termination signal. Term = 15, /// Stack fault on coprocessor (unused). Stklft = 16, /// Child stopped or terminated. Child = 17, /// Continue if stopped. Continue = 18, /// Stop process. Stop = 19, /// Stop typed at terminal. TSTP = 20, /// Terminal input for background process. TTIN = 21, /// Terminal output for background process. TTOU = 22, /// Urgent condition on socket. Urgent = 23, /// CPU time limit exceeded. XCPU = 24, /// File size limit exceeded. XFSZ = 25, /// Virtual alarm clock. VirtualAlarm = 26, /// Profiling time expired. Profiling = 27, /// Windows resize signal. Winch = 28, /// I/O now possible. IO = 29, // Pollable event (Sys V). Synonym for IO //Poll = 29, /// Power failure (System V). Power = 30, /// Bad argument to routine (SVr4). Sys = 31, } /// A struct representing system load average value. /// /// It is returned by [`SystemExt::get_load_average`][crate::SystemExt::get_load_average]. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// let load_avg = s.get_load_average(); /// println!( /// "one minute: {}%, five minutes: {}%, fifteen minutes: {}%", /// load_avg.one, /// load_avg.five, /// load_avg.fifteen, /// ); /// ``` #[repr(C)] #[derive(Default, Debug, Clone)] pub struct LoadAvg { /// Average load within one minute. pub one: f64, /// Average load within five minutes. pub five: f64, /// Average load within fifteen minutes. pub fifteen: f64, } /// Type containing user information. /// /// It is returned by [`SystemExt::get_users`][crate::SystemExt::get_users]. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("users: {:?}", s.get_users()); /// ``` #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct User { pub(crate) name: String, pub(crate) groups: Vec, } impl UserExt for User { fn get_name(&self) -> &str { &self.name } fn get_groups(&self) -> &[String] { &self.groups } } /// Type containing read and written bytes. /// /// It is returned by [`ProcessExt::disk_usage`][crate::ProcessExt::disk_usage]. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new_all(); /// for (pid, process) in s.get_processes() { /// let disk_usage = process.disk_usage(); /// println!("[{}] read bytes : new/total => {}/{} B", /// pid, /// disk_usage.read_bytes, /// disk_usage.total_read_bytes, /// ); /// println!("[{}] written bytes: new/total => {}/{} B", /// pid, /// disk_usage.written_bytes, /// disk_usage.total_written_bytes, /// ); /// } /// ``` #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)] pub struct DiskUsage { /// Total number of written bytes. pub total_written_bytes: u64, /// Number of written bytes since the last refresh. pub written_bytes: u64, /// Total number of read bytes. pub total_read_bytes: u64, /// Number of read bytes since the last refresh. pub read_bytes: u64, } sysinfo-0.13.2/src/debug.rs010064400017500001750000000111511364506453400137440ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // use Component; use ComponentExt; use Disk; use DiskExt; use NetworkData; use NetworkExt; use Networks; use NetworksExt; use Process; use ProcessExt; use Processor; use ProcessorExt; use System; use SystemExt; use std::fmt; impl fmt::Debug for Processor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Processor") .field("name", &self.get_name()) .field("CPU usage", &self.get_cpu_usage()) .finish() } } impl fmt::Debug for System { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("System") .field( "global CPU usage", &self.get_global_processor_info().get_cpu_usage(), ) .field("load average", &self.get_load_average()) .field("total memory", &self.get_total_memory()) .field("free memory", &self.get_free_memory()) .field("total swap", &self.get_total_swap()) .field("free swap", &self.get_free_swap()) .field("nb CPUs", &self.get_processors().len()) .field("nb network interfaces", &self.get_networks().iter().count()) .field("nb processes", &self.get_processes().len()) .field("nb disks", &self.get_disks().len()) .field("nb components", &self.get_components().len()) .finish() } } impl fmt::Debug for Disk { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!( fmt, "Disk({:?})[FS: {:?}][Type: {:?}] mounted on {:?}: {}/{} B", self.get_name(), self.get_file_system(), self.get_type(), self.get_mount_point(), self.get_available_space(), self.get_total_space() ) } } impl fmt::Debug for Process { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Process") .field("pid", &self.pid()) .field("parent", &self.parent()) .field("name", &self.name()) .field("environ", &self.environ()) .field("command", &self.cmd()) .field("executable path", &self.exe()) .field("current working directory", &self.cwd()) .field("memory usage", &self.memory()) .field("virtual memory usage", &self.virtual_memory()) .field("CPU usage", &self.cpu_usage()) .field("status", &self.status()) .field("root", &self.root()) .field("disk_usage", &self.disk_usage()) .finish() } } impl fmt::Debug for Component { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(critical) = self.get_critical() { write!( f, "{}: {}°C (max: {}°C / critical: {}°C)", self.get_label(), self.get_temperature(), self.get_max(), critical ) } else { write!( f, "{}: {}°C (max: {}°C)", self.get_label(), self.get_temperature(), self.get_max() ) } } } impl fmt::Debug for Networks { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Networks {{ {} }}", self.iter() .map(|x| format!("{:?}", x)) .collect::>() .join(", ") ) } } impl fmt::Debug for NetworkData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("NetworkData") .field("income", &self.get_received()) .field("total income", &self.get_total_received()) .field("outcome", &self.get_transmitted()) .field("total outcome", &self.get_total_transmitted()) .field("packets income", &self.get_packets_received()) .field("total packets income", &self.get_total_packets_received()) .field("packets outcome", &self.get_packets_transmitted()) .field( "total packets outcome", &self.get_total_packets_transmitted(), ) .field("errors income", &self.get_errors_on_received()) .field("total errors income", &self.get_total_errors_on_received()) .field("errors outcome", &self.get_errors_on_transmitted()) .field( "total errors outcome", &self.get_total_errors_on_transmitted(), ) .finish() } } sysinfo-0.13.2/src/linux/component.rs010064400017500001750000000154711364506453400160300ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use ComponentExt; use std::collections::HashMap; use std::ffi::OsStr; use std::fs::{metadata, read_dir, File}; use std::io::Read; use std::path::{Path, PathBuf}; /// More information can be found at [kernel.org][k]. /// /// Note: these may not be present on virtual Linux systems, such as **Docker** /// or **Windows Subsystem for Linux**. These hosts do not expose this information /// and therefore `Component` elements may be missing or not as expected. /// /// [k]: https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface pub struct Component { temperature: f32, max: f32, critical: Option, label: String, input_file: PathBuf, } fn get_file_line(file: &Path, capacity: usize) -> Option { let mut reader = String::with_capacity(capacity); if let Ok(mut f) = File::open(file) { if f.read_to_string(&mut reader).is_ok() { Some(reader) } else { None } } else { None } } fn is_file>(path: T) -> bool { metadata(path) .ok() .map(|m| m.is_file()) .unwrap_or_else(|| false) } fn append_files(components: &mut Vec, folder: &Path) { let mut matchings: HashMap> = HashMap::with_capacity(10); if let Ok(dir) = read_dir(folder) { for entry in dir { if let Ok(entry) = entry { let entry = entry.path(); if entry.is_dir() || !entry .file_name() .unwrap_or_else(|| OsStr::new("/")) .to_str() .unwrap_or("") .starts_with("temp") { continue; } if let Some(entry) = entry.file_name() { if let Some(entry) = entry.to_str() { let mut parts = entry.split('_'); if let Some(Some(id)) = parts.next().map(|s| s[4..].parse::().ok()) { matchings .entry(id) .or_insert_with(|| Vec::with_capacity(5)) .push( parts .next() .map(|s| format!("_{}", s)) .unwrap_or_else(String::new), ); } } } } } for (key, val) in &matchings { let mut found_input = None; let mut found_label = None; for (pos, v) in val.iter().enumerate() { match v.as_str() { // raspberry has empty string for temperature input "_input" | "" => { found_input = Some(pos); } "_label" => { found_label = Some(pos); } _ => {} } } if let (Some(_), Some(found_input)) = (found_label, found_input) { let mut p_label = folder.to_path_buf(); let mut p_input = folder.to_path_buf(); let mut p_crit = folder.to_path_buf(); let mut p_max = folder.to_path_buf(); p_label.push(&format!("temp{}_label", key)); p_input.push(&format!("temp{}{}", key, val[found_input])); p_max.push(&format!("temp{}_max", key)); p_crit.push(&format!("temp{}_crit", key)); if is_file(&p_input) { let label = get_file_line(p_label.as_path(), 10) .unwrap_or_else(|| format!("Component {}", key)) // needed for raspberry pi .replace("\n", ""); let max = if let Some(max) = get_file_line(p_max.as_path(), 10) { Some(max.replace("\n", "").parse::().unwrap_or(100_000f32) / 1000f32) } else { None }; let crit = if let Some(crit) = get_file_line(p_crit.as_path(), 10) { Some(crit.replace("\n", "").parse::().unwrap_or(100_000f32) / 1000f32) } else { None }; components.push(Component::new(label, p_input.as_path(), max, crit)); } } } } } impl Component { /// Creates a new component with the given information. pub(crate) fn new( label: String, input_path: &Path, max: Option, critical: Option, ) -> Component { let mut c = Component { temperature: 0f32, label, input_file: input_path.to_path_buf(), max: max.unwrap_or(0.0), critical, }; c.refresh(); c } } impl ComponentExt for Component { fn get_temperature(&self) -> f32 { self.temperature } fn get_max(&self) -> f32 { self.max } fn get_critical(&self) -> Option { self.critical } fn get_label(&self) -> &str { &self.label } fn refresh(&mut self) { if let Some(content) = get_file_line(self.input_file.as_path(), 10) { self.temperature = content .replace("\n", "") .parse::() .unwrap_or(100_000f32) / 1000f32; if self.temperature > self.max { self.max = self.temperature; } } } } pub fn get_components() -> Vec { if let Ok(dir) = read_dir(&Path::new("/sys/class/hwmon/")) { let mut components = Vec::with_capacity(10); for entry in dir { if let Ok(entry) = entry { let entry = entry.path(); if !entry.is_dir() || !entry .file_name() .unwrap_or_else(|| OsStr::new("/")) .to_str() .unwrap_or("") .starts_with("hwmon") { continue; } append_files(&mut components, &entry); } } components.sort_by(|c1, c2| c1.label.to_lowercase().cmp(&c2.label.to_lowercase())); components } else if is_file("/sys/class/thermal/thermal_zone0/temp") { // Specfic to raspberry pi. vec![Component::new( "CPU".to_owned(), Path::new("/sys/class/thermal/thermal_zone0/temp"), None, None, )] } else { Vec::new() } } sysinfo-0.13.2/src/linux/disk.rs010064400017500001750000000113641364506453400147550ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use super::system::get_all_data; use utils; use DiskExt; use DiskType; use libc::statvfs; use std::ffi::{OsStr, OsString}; use std::fs; use std::mem; use std::os::unix::ffi::OsStrExt; use std::path::{Path, PathBuf}; fn find_type_for_name(name: &OsStr) -> DiskType { /* The format of devices are as follows: - name_path is symbolic link in the case of /dev/mapper/ and /dev/root, and the target is corresponding device under /sys/block/ - In the case of /dev/sd, the format is /dev/sd[a-z][1-9], corresponding to /sys/block/sd[a-z] - In the case of /dev/nvme, the format is /dev/nvme[0-9]n[0-9]p[0-9], corresponding to /sys/block/nvme[0-9]n[0-9] - In the case of /dev/mmcblk, the format is /dev/mmcblk[0-9]p[0-9], corresponding to /sys/block/mmcblk[0-9] */ let name_path = name.to_str().unwrap_or_default(); let real_path = fs::canonicalize(name_path).unwrap_or(PathBuf::from(name_path)); let mut real_path = real_path.to_str().unwrap_or_default(); if name_path.starts_with("/dev/mapper/") { /* Recursively solve, for example /dev/dm-0 */ return find_type_for_name(OsStr::new(&real_path)); } else if name_path.starts_with("/dev/sd") { /* Turn "sda1" into "sda" */ real_path = real_path.trim_start_matches("/dev/"); real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9'); } else if name_path.starts_with("/dev/nvme") { /* Turn "nvme0n1p1" into "nvme0n1" */ real_path = real_path.trim_start_matches("/dev/"); real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9'); real_path = real_path.trim_end_matches(|c| c == 'p'); } else if name_path.starts_with("/dev/root") { /* Recursively solve, for example /dev/mmcblk0p1 */ return find_type_for_name(OsStr::new(&real_path)); } else if name_path.starts_with("/dev/mmcblk") { /* Turn "mmcblk0p1" into "mmcblk0" */ real_path = real_path.trim_start_matches("/dev/"); real_path = real_path.trim_end_matches(|c| c >= '0' && c <= '9'); real_path = real_path.trim_end_matches(|c| c == 'p'); } else { /* Default case: remove /dev/ and expects the name presents under /sys/block/ For example, /dev/dm-0 to dm-0 */ real_path = real_path.trim_start_matches("/dev/"); } let trimmed: &OsStr = OsStrExt::from_bytes(real_path.as_bytes()); let path = Path::new("/sys/block/") .to_owned() .join(trimmed) .join("queue/rotational"); // Normally, this file only contains '0' or '1' but just in case, we get 8 bytes... let rotational_int = get_all_data(path, 8).unwrap_or_default().trim().parse(); DiskType::from(rotational_int.unwrap_or(-1)) } macro_rules! cast { ($x:expr) => { u64::from($x) }; } pub fn new(name: &OsStr, mount_point: &Path, file_system: &[u8]) -> Disk { let mount_point_cpath = utils::to_cpath(mount_point); let type_ = find_type_for_name(name); let mut total = 0; let mut available = 0; unsafe { let mut stat: statvfs = mem::zeroed(); if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 { total = cast!(stat.f_bsize) * cast!(stat.f_blocks); available = cast!(stat.f_bsize) * cast!(stat.f_bavail); } } Disk { type_, name: name.to_owned(), file_system: file_system.to_owned(), mount_point: mount_point.to_owned(), total_space: cast!(total), available_space: cast!(available), } } /// Struct containing a disk information. pub struct Disk { type_: DiskType, name: OsString, file_system: Vec, mount_point: PathBuf, total_space: u64, available_space: u64, } impl DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ } fn get_name(&self) -> &OsStr { &self.name } fn get_file_system(&self) -> &[u8] { &self.file_system } fn get_mount_point(&self) -> &Path { &self.mount_point } fn get_total_space(&self) -> u64 { self.total_space } fn get_available_space(&self) -> u64 { self.available_space } fn refresh(&mut self) -> bool { unsafe { let mut stat: statvfs = mem::zeroed(); let mount_point_cpath = utils::to_cpath(&self.mount_point); if statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat) == 0 { let tmp = cast!(stat.f_bsize) * cast!(stat.f_bavail); self.available_space = cast!(tmp); true } else { false } } } } sysinfo-0.13.2/src/linux/mod.rs010064400017500001750000000006221362723011700145650ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // pub mod component; pub mod disk; pub mod network; pub mod process; pub mod processor; pub mod system; pub mod users; pub use self::component::Component; pub use self::disk::Disk; pub use self::network::{NetworkData, Networks}; pub use self::process::{Process, ProcessStatus}; pub use self::processor::Processor; pub use self::system::System; sysinfo-0.13.2/src/linux/network.rs010064400017500001750000000175111363516610000155030ustar0000000000000000// // Sysinfo // // Copyright (c) 2019 Guillaume Gomez // use std::fs::File; use std::io::Read; use std::path::Path; use std::collections::HashMap; use NetworkExt; use NetworksExt; use NetworksIter; /// Network interfaces. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// ``` pub struct Networks { interfaces: HashMap, } macro_rules! old_and_new { ($ty_:expr, $name:ident, $old:ident) => {{ $ty_.$old = $ty_.$name; $ty_.$name = $name; }}; ($ty_:expr, $name:ident, $old:ident, $path:expr) => {{ let _tmp = $path; $ty_.$old = $ty_.$name; $ty_.$name = _tmp; }}; } fn read>(parent: P, path: &str, data: &mut Vec) -> usize { if let Ok(mut f) = File::open(parent.as_ref().join(path)) { if let Ok(size) = f.read(data) { let mut i = 0; let mut ret = 0; while i < size && i < data.len() && data[i] >= b'0' && data[i] <= b'9' { ret *= 10; ret += (data[i] - b'0') as usize; i += 1; } return ret; } } 0 } impl Networks { pub(crate) fn new() -> Self { Networks { interfaces: HashMap::new(), } } } impl NetworksExt for Networks { fn iter<'a>(&'a self) -> NetworksIter<'a> { NetworksIter::new(self.interfaces.iter()) } fn refresh(&mut self) { let mut v = vec![0; 30]; for (interface_name, data) in self.interfaces.iter_mut() { data.update(interface_name, &mut v); } } fn refresh_networks_list(&mut self) { if let Ok(dir) = std::fs::read_dir("/sys/class/net/") { let mut data = vec![0; 30]; for entry in dir { if let Ok(entry) = entry { let parent = &entry.path().join("statistics"); let entry = match entry.file_name().into_string() { Ok(entry) => entry, Err(_) => continue, }; let rx_bytes = read(parent, "rx_bytes", &mut data); let tx_bytes = read(parent, "tx_bytes", &mut data); let rx_packets = read(parent, "rx_packets", &mut data); let tx_packets = read(parent, "tx_packets", &mut data); let rx_errors = read(parent, "rx_errors", &mut data); let tx_errors = read(parent, "tx_errors", &mut data); // let rx_compressed = read(parent, "rx_compressed", &mut data); // let tx_compressed = read(parent, "tx_compressed", &mut data); let interface = self.interfaces.entry(entry).or_insert_with(|| NetworkData { rx_bytes, old_rx_bytes: rx_bytes, tx_bytes, old_tx_bytes: tx_bytes, rx_packets, old_rx_packets: rx_packets, tx_packets, old_tx_packets: tx_packets, rx_errors, old_rx_errors: rx_errors, tx_errors, old_tx_errors: tx_errors, // rx_compressed, // old_rx_compressed: rx_compressed, // tx_compressed, // old_tx_compressed: tx_compressed, }); old_and_new!(interface, rx_bytes, old_rx_bytes); old_and_new!(interface, tx_bytes, old_tx_bytes); old_and_new!(interface, rx_packets, old_rx_packets); old_and_new!(interface, tx_packets, old_tx_packets); old_and_new!(interface, rx_errors, old_rx_errors); old_and_new!(interface, tx_errors, old_tx_errors); // old_and_new!(interface, rx_compressed, old_rx_compressed); // old_and_new!(interface, tx_compressed, old_tx_compressed); } } } } } /// Contains network information. pub struct NetworkData { /// Total number of bytes received over interface. rx_bytes: usize, old_rx_bytes: usize, /// Total number of bytes transmitted over interface. tx_bytes: usize, old_tx_bytes: usize, /// Total number of packets received. rx_packets: usize, old_rx_packets: usize, /// Total number of packets transmitted. tx_packets: usize, old_tx_packets: usize, /// Shows the total number of packets received with error. This includes /// too-long-frames errors, ring-buffer overflow errors, CRC errors, /// frame alignment errors, fifo overruns, and missed packets. rx_errors: usize, old_rx_errors: usize, /// similar to `rx_errors` tx_errors: usize, old_tx_errors: usize, // /// Indicates the number of compressed packets received by this // /// network device. This value might only be relevant for interfaces // /// that support packet compression (e.g: PPP). // rx_compressed: usize, // old_rx_compressed: usize, // /// Indicates the number of transmitted compressed packets. Note // /// this might only be relevant for devices that support // /// compression (e.g: PPP). // tx_compressed: usize, // old_tx_compressed: usize, } impl NetworkData { fn update(&mut self, path: &str, data: &mut Vec) { let path = &Path::new("/sys/class/net/").join(path).join("statistics"); old_and_new!(self, rx_bytes, old_rx_bytes, read(path, "rx_bytes", data)); old_and_new!(self, tx_bytes, old_tx_bytes, read(path, "tx_bytes", data)); old_and_new!( self, rx_packets, old_rx_packets, read(path, "rx_packets", data) ); old_and_new!( self, tx_packets, old_tx_packets, read(path, "tx_packets", data) ); old_and_new!( self, rx_errors, old_rx_errors, read(path, "rx_errors", data) ); old_and_new!( self, tx_errors, old_tx_errors, read(path, "tx_errors", data) ); // old_and_new!( // self, // rx_compressed, // old_rx_compressed, // read(path, "rx_compressed", data) // ); // old_and_new!( // self, // tx_compressed, // old_tx_compressed, // read(path, "tx_compressed", data) // ); } } impl NetworkExt for NetworkData { fn get_received(&self) -> u64 { self.rx_bytes as u64 - self.old_rx_bytes as u64 } fn get_total_received(&self) -> u64 { self.rx_bytes as u64 } fn get_transmitted(&self) -> u64 { self.tx_bytes as u64 - self.old_tx_bytes as u64 } fn get_total_transmitted(&self) -> u64 { self.tx_bytes as u64 } fn get_packets_received(&self) -> u64 { self.rx_packets as u64 - self.old_rx_packets as u64 } fn get_total_packets_received(&self) -> u64 { self.rx_packets as u64 } fn get_packets_transmitted(&self) -> u64 { self.tx_packets as u64 - self.old_tx_packets as u64 } fn get_total_packets_transmitted(&self) -> u64 { self.tx_packets as u64 } fn get_errors_on_received(&self) -> u64 { self.rx_errors as u64 - self.old_rx_errors as u64 } fn get_total_errors_on_received(&self) -> u64 { self.rx_errors as u64 } fn get_errors_on_transmitted(&self) -> u64 { self.tx_errors as u64 - self.old_tx_errors as u64 } fn get_total_errors_on_transmitted(&self) -> u64 { self.tx_errors as u64 } } sysinfo-0.13.2/src/linux/process.rs010064400017500001750000000166141364506453400155040ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use std::collections::HashMap; use std::fmt; use std::fs::File; use std::path::{Path, PathBuf}; use libc::{c_int, gid_t, kill, uid_t}; use DiskUsage; use Pid; use ProcessExt; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] pub enum ProcessStatus { /// Waiting in uninterruptible disk sleep. Idle, /// Running. Run, /// Sleeping in an interruptible waiting. Sleep, /// Stopped (on a signal) or (before Linux 2.6.33) trace stopped. Stop, /// Zombie. Zombie, /// Tracing stop (Linux 2.6.33 onward). Tracing, /// Dead. Dead, /// Wakekill (Linux 2.6.33 to 3.13 only). Wakekill, /// Waking (Linux 2.6.33 to 3.13 only). Waking, /// Parked (Linux 3.9 to 3.13 only). Parked, /// Unknown. Unknown(u32), } impl From for ProcessStatus { fn from(status: u32) -> ProcessStatus { match status { 1 => ProcessStatus::Idle, 2 => ProcessStatus::Run, 3 => ProcessStatus::Sleep, 4 => ProcessStatus::Stop, 5 => ProcessStatus::Zombie, x => ProcessStatus::Unknown(x), } } } impl From for ProcessStatus { fn from(status: char) -> ProcessStatus { match status { 'R' => ProcessStatus::Run, 'S' => ProcessStatus::Sleep, 'D' => ProcessStatus::Idle, 'Z' => ProcessStatus::Zombie, 'T' => ProcessStatus::Stop, 't' => ProcessStatus::Tracing, 'X' | 'x' => ProcessStatus::Dead, 'K' => ProcessStatus::Wakekill, 'W' => ProcessStatus::Waking, 'P' => ProcessStatus::Parked, x => ProcessStatus::Unknown(x as u32), } } } impl ProcessStatus { /// Used to display `ProcessStatus`. pub fn to_string(&self) -> &str { match *self { ProcessStatus::Idle => "Idle", ProcessStatus::Run => "Runnable", ProcessStatus::Sleep => "Sleeping", ProcessStatus::Stop => "Stopped", ProcessStatus::Zombie => "Zombie", ProcessStatus::Tracing => "Tracing", ProcessStatus::Dead => "Dead", ProcessStatus::Wakekill => "Wakekill", ProcessStatus::Waking => "Waking", ProcessStatus::Parked => "Parked", ProcessStatus::Unknown(_) => "Unknown", } } } impl fmt::Display for ProcessStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_string()) } } /// Struct containing a process' information. pub struct Process { pub(crate) name: String, pub(crate) cmd: Vec, pub(crate) exe: PathBuf, pub(crate) pid: Pid, parent: Option, pub(crate) environ: Vec, pub(crate) cwd: PathBuf, pub(crate) root: PathBuf, pub(crate) memory: u64, pub(crate) virtual_memory: u64, utime: u64, stime: u64, old_utime: u64, old_stime: u64, start_time: u64, updated: bool, cpu_usage: f32, /// User id of the process owner. pub uid: uid_t, /// Group id of the process owner. pub gid: gid_t, pub(crate) status: ProcessStatus, /// Tasks run by this process. pub tasks: HashMap, pub(crate) stat_file: Option, old_read_bytes: u64, old_written_bytes: u64, read_bytes: u64, written_bytes: u64, } impl ProcessExt for Process { fn new(pid: Pid, parent: Option, start_time: u64) -> Process { Process { name: String::with_capacity(20), pid, parent, cmd: Vec::with_capacity(2), environ: Vec::with_capacity(10), exe: PathBuf::new(), cwd: PathBuf::new(), root: PathBuf::new(), memory: 0, virtual_memory: 0, cpu_usage: 0., utime: 0, stime: 0, old_utime: 0, old_stime: 0, updated: true, start_time, uid: 0, gid: 0, status: ProcessStatus::Unknown(0), tasks: if pid == 0 { HashMap::with_capacity(1000) } else { HashMap::new() }, stat_file: None, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } fn kill(&self, signal: ::Signal) -> bool { unsafe { kill(self.pid, signal as c_int) == 0 } } fn name(&self) -> &str { &self.name } fn cmd(&self) -> &[String] { &self.cmd } fn exe(&self) -> &Path { self.exe.as_path() } fn pid(&self) -> Pid { self.pid } fn environ(&self) -> &[String] { &self.environ } fn cwd(&self) -> &Path { self.cwd.as_path() } fn root(&self) -> &Path { self.root.as_path() } fn memory(&self) -> u64 { self.memory } fn virtual_memory(&self) -> u64 { self.virtual_memory } fn parent(&self) -> Option { self.parent } /// Returns the status of the processus (idle, run, zombie, etc). `None` means that /// `sysinfo` doesn't have enough rights to get this information. fn status(&self) -> ProcessStatus { self.status } fn start_time(&self) -> u64 { self.start_time } fn cpu_usage(&self) -> f32 { self.cpu_usage } fn disk_usage(&self) -> DiskUsage { DiskUsage { written_bytes: self.written_bytes - self.old_written_bytes, total_written_bytes: self.written_bytes, read_bytes: self.read_bytes - self.old_read_bytes, total_read_bytes: self.read_bytes, } } } impl Drop for Process { fn drop(&mut self) { if self.stat_file.is_some() { if let Ok(ref mut x) = unsafe { ::linux::system::REMAINING_FILES.lock() } { **x += 1; } } } } pub fn compute_cpu_usage(p: &mut Process, nb_processors: u64, total_time: f32) { p.cpu_usage = ((p.utime - p.old_utime + p.stime - p.old_stime) * nb_processors * 100) as f32 / total_time; p.updated = false; } pub fn set_time(p: &mut Process, utime: u64, stime: u64) { p.old_utime = p.utime; p.old_stime = p.stime; p.utime = utime; p.stime = stime; p.updated = true; } pub fn has_been_updated(p: &Process) -> bool { p.updated } pub(crate) fn update_process_disk_activity(p: &mut Process, path: &Path) { let mut path = PathBuf::from(path); path.push("io"); let data = match super::system::get_all_data(&path, 16_384) { Ok(d) => d, Err(_) => return, }; let mut done = 0; for line in data.split("\n") { let mut parts = line.split(": "); match parts.next() { Some("read_bytes") => { p.old_read_bytes = p.read_bytes; p.read_bytes = parts.next().and_then(|x| x.parse::().ok()).unwrap_or(0); } Some("write_bytes") => { p.old_written_bytes = p.written_bytes; p.written_bytes = parts.next().and_then(|x| x.parse::().ok()).unwrap_or(0); } _ => continue, } done += 1; if done > 1 { // No need to continue the reading. break; } } } sysinfo-0.13.2/src/linux/processor.rs010064400017500001750000000154371361751223500160420ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // #![allow(clippy::too_many_arguments)] use std::fs::File; use std::io::Read; use ProcessorExt; /// Struct containing values to compute a CPU usage. #[derive(Clone, Copy)] pub struct CpuValues { user: u64, nice: u64, system: u64, idle: u64, iowait: u64, irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64, } impl CpuValues { /// Creates a new instance of `CpuValues` with everything set to `0`. pub fn new() -> CpuValues { CpuValues { user: 0, nice: 0, system: 0, idle: 0, iowait: 0, irq: 0, softirq: 0, steal: 0, guest: 0, guest_nice: 0, } } /// Creates a new instance of `CpuValues` with everything set to the corresponding argument. pub fn new_with_values( user: u64, nice: u64, system: u64, idle: u64, iowait: u64, irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64, ) -> CpuValues { CpuValues { user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, } } /*pub fn is_zero(&self) -> bool { self.user == 0 && self.nice == 0 && self.system == 0 && self.idle == 0 && self.iowait == 0 && self.irq == 0 && self.softirq == 0 && self.steal == 0 && self.guest == 0 && self.guest_nice == 0 }*/ /// Sets the given argument to the corresponding fields. pub fn set( &mut self, user: u64, nice: u64, system: u64, idle: u64, iowait: u64, irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64, ) { self.user = user; self.nice = nice; self.system = system; self.idle = idle; self.iowait = iowait; self.irq = irq; self.softirq = softirq; self.steal = steal; self.guest = guest; self.guest_nice = guest_nice; } /// Returns work time. pub fn work_time(&self) -> u64 { self.user + self.nice + self.system } /// Returns total time. pub fn total_time(&self) -> u64 { self.work_time() + self.idle + self.iowait + self.irq + self.softirq + self.steal + self.guest + self.guest_nice } } /// Struct containing a processor information. pub struct Processor { old_values: CpuValues, new_values: CpuValues, pub(crate) name: String, cpu_usage: f32, total_time: u64, old_total_time: u64, frequency: u64, pub(crate) vendor_id: String, pub(crate) brand: String, } impl Processor { pub(crate) fn new_with_values( name: &str, user: u64, nice: u64, system: u64, idle: u64, iowait: u64, irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64, frequency: u64, vendor_id: String, brand: String, ) -> Processor { Processor { name: name.to_owned(), old_values: CpuValues::new(), new_values: CpuValues::new_with_values( user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, ), cpu_usage: 0f32, total_time: 0, old_total_time: 0, frequency, vendor_id, brand, } } pub(crate) fn set( &mut self, user: u64, nice: u64, system: u64, idle: u64, iowait: u64, irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64, ) { fn min(a: u64, b: u64) -> f32 { (if a == b { 1 } else if a > b { a - b } else { b - a }) as f32 } //if !self.new_values.is_zero() { self.old_values = self.new_values; //} self.new_values.set( user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice, ); self.cpu_usage = min(self.new_values.work_time(), self.old_values.work_time()) / min(self.new_values.total_time(), self.old_values.total_time()) * 100.; self.old_total_time = self.old_values.total_time(); self.total_time = self.new_values.total_time(); } } impl ProcessorExt for Processor { fn get_cpu_usage(&self) -> f32 { self.cpu_usage } fn get_name(&self) -> &str { &self.name } /// Returns the CPU frequency in MHz. fn get_frequency(&self) -> u64 { self.frequency } fn get_vendor_id(&self) -> &str { &self.vendor_id } fn get_brand(&self) -> &str { &self.brand } } pub fn get_raw_times(p: &Processor) -> (u64, u64) { (p.new_values.total_time(), p.old_values.total_time()) } pub fn get_cpu_frequency() -> u64 { // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq let mut s = String::new(); if File::open("/proc/cpuinfo") .and_then(|mut f| f.read_to_string(&mut s)) .is_err() { return 0; } let find_cpu_mhz = s.split('\n').find(|line| { line.starts_with("cpu MHz\t") || line.starts_with("BogoMIPS") || line.starts_with("clock\t") || line.starts_with("bogomips per cpu") }); find_cpu_mhz .and_then(|line| line.split(':').last()) .and_then(|val| val.replace("MHz", "").trim().parse::().ok()) .map(|speed| speed as u64) .unwrap_or_default() } /// Returns the brand/vendor string for the first CPU (which should be the same for all CPUs). pub fn get_vendor_id_and_brand() -> (String, String) { let mut s = String::new(); if File::open("/proc/cpuinfo") .and_then(|mut f| f.read_to_string(&mut s)) .is_err() { return (String::new(), String::new()); } fn get_value(s: &str) -> String { s.split(':') .last() .map(|x| x.trim().to_owned()) .unwrap_or_default() } let mut vendor_id = None; let mut brand = None; for it in s.split('\n') { if it.starts_with("vendor_id\t") { vendor_id = Some(get_value(it)); } else if it.starts_with("model name\t") { brand = Some(get_value(it)); } else { continue; } if brand.is_some() && vendor_id.is_some() { break; } } (vendor_id.unwrap_or_default(), brand.unwrap_or_default()) } sysinfo-0.13.2/src/linux/system.rs010064400017500001750000000674471364506464500153670ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use sys::component::{self, Component}; use sys::disk; use sys::process::*; use sys::processor::*; use Disk; use LoadAvg; use Networks; use Pid; use User; use {ProcessExt, RefreshKind, SystemExt}; use libc::{self, gid_t, sysconf, uid_t, _SC_CLK_TCK, _SC_PAGESIZE}; use std::cell::UnsafeCell; use std::collections::HashMap; use std::fs::{self, read_link, File}; use std::io::{self, BufRead, BufReader, Read}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::SystemTime; use utils::realpath; use rayon::prelude::*; // This whole thing is to prevent having too much files open at once. It could be problematic // for processes using a lot of files and using sysinfo at the same time. pub(crate) static mut REMAINING_FILES: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| { #[cfg(target_os = "android")] { // The constant "RLIMIT_NOFILE" doesn't exist on Android so we have to return a value. // The default value seems to be 1024 so let's return 50% of it... Arc::new(Mutex::new(1024 / 2)) } #[cfg(not(target_os = "android"))] unsafe { let mut limits = libc::rlimit { rlim_cur: 0, rlim_max: 0, }; if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 { // Most linux system now defaults to 1024. return Arc::new(Mutex::new(1024 / 2)); } // We save the value in case the update fails. let current = limits.rlim_cur; // The set the soft limit to the hard one. limits.rlim_cur = limits.rlim_max; // In this part, we leave minimum 50% of the available file descriptors to the process // using sysinfo. Arc::new(Mutex::new( if libc::setrlimit(libc::RLIMIT_NOFILE, &limits) == 0 { limits.rlim_cur / 2 } else { current / 2 } as _, )) } }); pub(crate) fn get_max_nb_fds() -> isize { #[cfg(target_os = "android")] { // The constant "RLIMIT_NOFILE" doesn't exist on Android so we have to return a value. // The default value seems to be 1024... 1024 / 2 } #[cfg(not(target_os = "android"))] unsafe { let mut limits = libc::rlimit { rlim_cur: 0, rlim_max: 0, }; if libc::getrlimit(libc::RLIMIT_NOFILE, &mut limits) != 0 { // Most linux system now defaults to 1024. 1024 / 2 } else { limits.rlim_max as isize / 2 } } } macro_rules! to_str { ($e:expr) => { unsafe { ::std::str::from_utf8_unchecked($e) } }; } fn boot_time() -> u64 { if let Ok(f) = File::open("/proc/stat") { let buf = BufReader::new(f); let mut it = buf.split(b'\n'); while let Some(Ok(line)) = it.next() { if &line[..5] != b"btime" { continue; } return line .split(|x| *x == b' ') .filter(|s| !s.is_empty()) .skip(1) .next() .map(|v| to_u64(v)) .unwrap_or(0); } } // Either we didn't find "btime" or "/proc/stat" wasn't available for some reason... let mut up = libc::timespec { tv_sec: 0, tv_nsec: 0, }; if unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut up) } == 0 { up.tv_sec as u64 } else { sysinfo_debug!("clock_gettime failed: boot time cannot be retrieve..."); 0 } } /// Structs containing system's information. pub struct System { process_list: Process, mem_total: u64, mem_free: u64, swap_total: u64, swap_free: u64, global_processor: Processor, processors: Vec, page_size_kb: u64, components: Vec, disks: Vec, networks: Networks, uptime: u64, users: Vec, boot_time: u64, } impl System { fn clear_procs(&mut self) { if !self.processors.is_empty() { let (new, old) = get_raw_times(&self.global_processor); let total_time = (if old > new { 1 } else { new - old }) as f32; let mut to_delete = Vec::with_capacity(20); for (pid, proc_) in &mut self.process_list.tasks { if !has_been_updated(proc_) { to_delete.push(*pid); } else { compute_cpu_usage(proc_, self.processors.len() as u64, total_time); } } for pid in to_delete { self.process_list.tasks.remove(&pid); } } } fn refresh_processors(&mut self, limit: Option) { if let Ok(f) = File::open("/proc/stat") { let buf = BufReader::new(f); let mut i = 0; let first = self.processors.is_empty(); let mut it = buf.split(b'\n'); let mut count = 0; let frequency = if first { get_cpu_frequency() } else { 0 }; let (vendor_id, brand) = if first { get_vendor_id_and_brand() } else { (String::new(), String::new()) }; if let Some(Ok(line)) = it.next() { if &line[..3] != b"cpu" { return; } count += 1; let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); if first { self.global_processor.name = to_str!(parts.next().unwrap_or(&[])).to_owned(); } self.global_processor.set( parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), ); if let Some(limit) = limit { if count >= limit { return; } } } while let Some(Ok(line)) = it.next() { if &line[..3] != b"cpu" { break; } count += 1; let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); if first { self.processors.push(Processor::new_with_values( to_str!(parts.next().unwrap_or(&[])), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), frequency, vendor_id.clone(), brand.clone(), )); } else { parts.next(); // we don't want the name again self.processors[i].set( parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), parts.next().map(|v| to_u64(v)).unwrap_or(0), ); i += 1; } if let Some(limit) = limit { if count >= limit { break; } } } if first { self.global_processor.vendor_id = vendor_id; self.global_processor.brand = brand; } } } } impl SystemExt for System { fn new_with_specifics(refreshes: RefreshKind) -> System { let mut s = System { process_list: Process::new(0, None, 0), mem_total: 0, mem_free: 0, swap_total: 0, swap_free: 0, global_processor: Processor::new_with_values( "", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, String::new(), String::new(), ), processors: Vec::with_capacity(4), page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 / 1024 }, components: Vec::new(), disks: Vec::with_capacity(2), networks: Networks::new(), uptime: get_uptime(), users: Vec::new(), boot_time: boot_time(), }; if !refreshes.cpu() { s.refresh_processors(None); // We need the processors to be filled. } s.refresh_specifics(refreshes); s } fn refresh_components_list(&mut self) { self.components = component::get_components(); } fn refresh_memory(&mut self) { self.uptime = get_uptime(); if let Ok(data) = get_all_data("/proc/meminfo", 16_385) { for line in data.split('\n') { let field = match line.split(':').next() { Some("MemTotal") => &mut self.mem_total, Some("MemAvailable") | Some("MemFree") => &mut self.mem_free, Some("SwapTotal") => &mut self.swap_total, Some("SwapFree") => &mut self.swap_free, _ => continue, }; if let Some(val_str) = line.rsplit(' ').nth(1) { if let Ok(value) = u64::from_str(val_str) { *field = value; } } } } } fn refresh_cpu(&mut self) { self.uptime = get_uptime(); self.refresh_processors(None); } fn refresh_processes(&mut self) { self.uptime = get_uptime(); if refresh_procs( &mut self.process_list, "/proc", self.page_size_kb, 0, self.uptime, get_secs_since_epoch(), ) { self.clear_procs(); } } fn refresh_process(&mut self, pid: Pid) -> bool { self.uptime = get_uptime(); let found = match _get_process_data( &Path::new("/proc/").join(pid.to_string()), &mut self.process_list, self.page_size_kb, 0, self.uptime, get_secs_since_epoch(), ) { Ok(Some(p)) => { self.process_list.tasks.insert(p.pid(), p); false } Ok(_) => true, Err(_) => false, }; if found && !self.processors.is_empty() { self.refresh_processors(Some(1)); let (new, old) = get_raw_times(&self.global_processor); let total_time = (if old > new { 1 } else { new - old }) as f32; if let Some(p) = self.process_list.tasks.get_mut(&pid) { compute_cpu_usage(p, self.processors.len() as u64, total_time); } } found } fn refresh_disks_list(&mut self) { self.disks = get_all_disks(); } fn refresh_users_list(&mut self) { self.users = crate::linux::users::get_users_list(); } // COMMON PART // // Need to be moved into a "common" file to avoid duplication. fn get_processes(&self) -> &HashMap { &self.process_list.tasks } fn get_process(&self, pid: Pid) -> Option<&Process> { self.process_list.tasks.get(&pid) } fn get_networks(&self) -> &Networks { &self.networks } fn get_networks_mut(&mut self) -> &mut Networks { &mut self.networks } fn get_global_processor_info(&self) -> &Processor { &self.global_processor } fn get_processors(&self) -> &[Processor] { &self.processors } fn get_total_memory(&self) -> u64 { self.mem_total } fn get_free_memory(&self) -> u64 { self.mem_free } fn get_used_memory(&self) -> u64 { self.mem_total - self.mem_free } fn get_total_swap(&self) -> u64 { self.swap_total } fn get_free_swap(&self) -> u64 { self.swap_free } // need to be checked fn get_used_swap(&self) -> u64 { self.swap_total - self.swap_free } fn get_components(&self) -> &[Component] { &self.components } fn get_components_mut(&mut self) -> &mut [Component] { &mut self.components } fn get_disks(&self) -> &[Disk] { &self.disks } fn get_disks_mut(&mut self) -> &mut [Disk] { &mut self.disks } fn get_uptime(&self) -> u64 { self.uptime } fn get_boot_time(&self) -> u64 { self.boot_time } fn get_load_average(&self) -> LoadAvg { let mut s = String::new(); if File::open("/proc/loadavg") .and_then(|mut f| f.read_to_string(&mut s)) .is_err() { return LoadAvg::default(); } let loads = s .trim() .split(' ') .take(3) .map(|val| val.parse::().unwrap()) .collect::>(); LoadAvg { one: loads[0], five: loads[1], fifteen: loads[2], } } fn get_users(&self) -> &[User] { &self.users } } impl Default for System { fn default() -> System { System::new() } } fn to_u64(v: &[u8]) -> u64 { let mut x = 0; for c in v { x *= 10; x += u64::from(c - b'0'); } x } struct Wrap<'a, T>(UnsafeCell<&'a mut T>); impl<'a, T> Wrap<'a, T> { fn get(&self) -> &'a mut T { unsafe { *(self.0.get()) } } } unsafe impl<'a, T> Send for Wrap<'a, T> {} unsafe impl<'a, T> Sync for Wrap<'a, T> {} fn refresh_procs>( proc_list: &mut Process, path: P, page_size_kb: u64, pid: Pid, uptime: u64, now: u64, ) -> bool { if let Ok(d) = fs::read_dir(path.as_ref()) { let folders = d .filter_map(|entry| { if let Ok(entry) = entry { let entry = entry.path(); if entry.is_dir() { Some(entry) } else { None } } else { None } }) .collect::>(); if pid == 0 { let proc_list = Wrap(UnsafeCell::new(proc_list)); folders .par_iter() .filter_map(|e| { if let Ok(p) = _get_process_data( e.as_path(), proc_list.get(), page_size_kb, pid, uptime, now, ) { p } else { None } }) .collect::>() } else { folders .iter() .filter_map(|e| { if let Ok(p) = _get_process_data(e.as_path(), proc_list, page_size_kb, pid, uptime, now) { p } else { None } }) .collect::>() } .into_iter() .for_each(|e| { proc_list.tasks.insert(e.pid(), e); }); true } else { false } } fn update_time_and_memory( path: &Path, entry: &mut Process, parts: &[&str], page_size_kb: u64, parent_memory: u64, parent_virtual_memory: u64, pid: Pid, uptime: u64, now: u64, ) { { // rss entry.memory = u64::from_str(parts[23]).unwrap_or(0) * page_size_kb; if entry.memory >= parent_memory { entry.memory -= parent_memory; } // vsz entry.virtual_memory = u64::from_str(parts[22]).unwrap_or(0) * page_size_kb; if entry.virtual_memory >= parent_virtual_memory { entry.virtual_memory -= parent_virtual_memory; } set_time( entry, u64::from_str(parts[13]).unwrap_or(0), u64::from_str(parts[14]).unwrap_or(0), ); } refresh_procs( entry, path.join(Path::new("task")), page_size_kb, pid, uptime, now, ); } macro_rules! unwrap_or_return { ($data:expr) => {{ match $data { Some(x) => x, None => return Err(()), } }}; } fn _get_uid_and_gid(status_data: String) -> Option<(uid_t, gid_t)> { // We're only interested in the lines starting with Uid: and Gid: // here. From these lines, we're looking at the second entry to get // the effective u/gid. let f = |h: &str, n: &str| -> Option { if h.starts_with(n) { h.split_whitespace().nth(2).unwrap_or("0").parse().ok() } else { None } }; let mut uid = None; let mut gid = None; for line in status_data.lines() { if let Some(u) = f(line, "Uid:") { assert!(uid.is_none()); uid = Some(u); } else if let Some(g) = f(line, "Gid:") { assert!(gid.is_none()); gid = Some(g); } else { continue; } if uid.is_some() && gid.is_some() { break; } } match (uid, gid) { (Some(u), Some(g)) => Some((u, g)), _ => None, } } fn parse_stat_file(data: &str) -> Result, ()> { // The stat file is "interesting" to parse, because spaces cannot // be used as delimiters. The second field stores the command name // surrounded by parentheses. Unfortunately, whitespace and // parentheses are legal parts of the command, so parsing has to // proceed like this: The first field is delimited by the first // whitespace, the second field is everything until the last ')' // in the entire string. All other fields are delimited by // whitespace. let mut parts = Vec::with_capacity(52); let mut data_it = data.splitn(2, ' '); parts.push(unwrap_or_return!(data_it.next())); // The following loses the ) from the input, but that's ok because // we're not using it anyway. let mut data_it = unwrap_or_return!(data_it.next()).rsplitn(2, ')'); let data = unwrap_or_return!(data_it.next()); parts.push(unwrap_or_return!(data_it.next())); parts.extend(data.split_whitespace()); Ok(parts) } fn check_nb_open_files(f: File) -> Option { if let Ok(ref mut x) = unsafe { REMAINING_FILES.lock() } { if **x > 0 { **x -= 1; return Some(f); } } // Something bad happened... None } fn _get_process_data( path: &Path, proc_list: &mut Process, page_size_kb: u64, pid: Pid, uptime: u64, now: u64, ) -> Result, ()> { let nb = match path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) { Some(Ok(nb)) if nb != pid => nb, _ => return Err(()), }; let get_status = |p: &mut Process, part: &str| { p.status = part .chars() .next() .and_then(|c| Some(ProcessStatus::from(c))) .unwrap_or_else(|| ProcessStatus::Unknown(0)); }; let parent_memory = proc_list.memory; let parent_virtual_memory = proc_list.virtual_memory; if let Some(ref mut entry) = proc_list.tasks.get_mut(&nb) { let data = if let Some(ref mut f) = entry.stat_file { get_all_data_from_file(f, 1024).map_err(|_| ())? } else { let mut tmp = PathBuf::from(path); tmp.push("stat"); let mut file = ::std::fs::File::open(tmp).map_err(|_| ())?; let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?; entry.stat_file = check_nb_open_files(file); data }; let parts = parse_stat_file(&data)?; get_status(entry, parts[2]); update_time_and_memory( path, entry, &parts, page_size_kb, parent_memory, parent_virtual_memory, nb, uptime, now, ); update_process_disk_activity(entry, path); return Ok(None); } let mut tmp = PathBuf::from(path); tmp.push("stat"); let mut file = ::std::fs::File::open(&tmp).map_err(|_| ())?; let data = get_all_data_from_file(&mut file, 1024).map_err(|_| ())?; let stat_file = check_nb_open_files(file); let parts = parse_stat_file(&data)?; let parent_pid = if proc_list.pid != 0 { Some(proc_list.pid) } else { match Pid::from_str(parts[3]) { Ok(p) if p != 0 => Some(p), _ => None, } }; let clock_cycle = unsafe { sysconf(_SC_CLK_TCK) } as u64; let since_boot = u64::from_str(parts[21]).unwrap_or(0) / clock_cycle; let start_time = now .checked_sub(uptime.checked_sub(since_boot).unwrap_or_else(|| 0)) .unwrap_or_else(|| 0); let mut p = Process::new(nb, parent_pid, start_time); p.stat_file = stat_file; get_status(&mut p, parts[2]); tmp.pop(); tmp.push("status"); if let Ok(data) = get_all_data(&tmp, 16_385) { if let Some((uid, gid)) = _get_uid_and_gid(data) { p.uid = uid; p.gid = gid; } } if proc_list.pid != 0 { // If we're getting information for a child, no need to get those info since we // already have them... p.cmd = proc_list.cmd.clone(); p.name = proc_list.name.clone(); p.environ = proc_list.environ.clone(); p.exe = proc_list.exe.clone(); p.cwd = proc_list.cwd.clone(); p.root = proc_list.root.clone(); } else { tmp.pop(); tmp.push("cmdline"); p.cmd = copy_from_file(&tmp); p.name = p .cmd .get(0) .map(|x| x.split('/').last().unwrap_or_else(|| "").to_owned()) .unwrap_or_default(); tmp.pop(); tmp.push("environ"); p.environ = copy_from_file(&tmp); tmp.pop(); tmp.push("exe"); p.exe = read_link(tmp.to_str().unwrap_or_else(|| "")).unwrap_or_else(|_| PathBuf::new()); tmp.pop(); tmp.push("cwd"); p.cwd = realpath(&tmp); tmp.pop(); tmp.push("root"); p.root = realpath(&tmp); } update_time_and_memory( path, &mut p, &parts, page_size_kb, proc_list.memory, proc_list.virtual_memory, nb, uptime, now, ); update_process_disk_activity(&mut p, path); Ok(Some(p)) } fn copy_from_file(entry: &Path) -> Vec { match File::open(entry.to_str().unwrap_or("/")) { Ok(mut f) => { let mut data = vec![0; 16_384]; if let Ok(size) = f.read(&mut data) { data.truncate(size); let mut out = Vec::with_capacity(20); let mut start = 0; for (pos, x) in data.iter().enumerate() { if *x == 0 { if pos - start >= 1 { if let Ok(s) = ::std::str::from_utf8(&data[start..pos]) .map(|x| x.trim().to_owned()) { out.push(s); } } start = pos + 1; // to keeping prevent '\0' } } out } else { Vec::new() } } Err(_) => Vec::new(), } } fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result { use std::io::Seek; let mut buf = String::with_capacity(size); file.seek(::std::io::SeekFrom::Start(0))?; file.read_to_string(&mut buf)?; Ok(buf) } pub fn get_all_data>(file_path: P, size: usize) -> io::Result { let mut file = File::open(file_path.as_ref())?; get_all_data_from_file(&mut file, size) } fn get_all_disks() -> Vec { let content = get_all_data("/proc/mounts", 16_385).unwrap_or_default(); content .lines() .map(|line| { let line = line.trim_start(); // mounts format // http://man7.org/linux/man-pages/man5/fstab.5.html // fs_specfs_filefs_vfstypeother fields let mut fields = line.split_whitespace(); let fs_spec = fields.next().unwrap_or(""); let fs_file = fields.next().unwrap_or(""); let fs_vfstype = fields.next().unwrap_or(""); (fs_spec, fs_file, fs_vfstype) }) .filter(|(fs_spec, fs_file, fs_vfstype)| { // Check if fs_vfstype is one of our 'ignored' file systems. let filtered = match *fs_vfstype { "sysfs" | // pseudo file system for kernel objects "proc" | // another pseudo file system "tmpfs" | "cgroup" | "cgroup2" | "pstore" | // https://www.kernel.org/doc/Documentation/ABI/testing/pstore "squashfs" | // squashfs is a compressed read-only file system (for snaps) "rpc_pipefs" | // The pipefs pseudo file system service "iso9660" => true, // optical media _ => false, }; if filtered || fs_file.starts_with("/sys") || // check if fs_file is an 'ignored' mount point fs_file.starts_with("/proc") || fs_file.starts_with("/run") || fs_file.starts_with("/dev") || fs_spec.starts_with("sunrpc") { false } else { true } }) .map(|(fs_spec, fs_file, fs_vfstype)| { disk::new(fs_spec.as_ref(), Path::new(fs_file), fs_vfstype.as_bytes()) }) .collect() } fn get_uptime() -> u64 { let content = get_all_data("/proc/uptime", 50).unwrap_or_default(); u64::from_str_radix(content.split('.').next().unwrap_or_else(|| "0"), 10).unwrap_or_else(|_| 0) } fn get_secs_since_epoch() -> u64 { match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_secs(), _ => panic!("SystemTime before UNIX EPOCH!"), } } sysinfo-0.13.2/src/linux/users.rs010064400017500001750000000073431363020157200151530ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // use crate::User; use libc::{getgrgid, getgrouplist}; use std::fs::File; use std::io::Read; pub fn get_users_list() -> Vec { let mut s = String::new(); let mut ngroups = 100; let mut groups = vec![0; ngroups as usize]; let _ = File::open("/etc/passwd").and_then(|mut f| f.read_to_string(&mut s)); s.lines() .into_iter() .filter_map(|line| { let mut parts = line.split(':'); if let Some(username) = parts.next() { let mut parts = parts.skip(2); if let Some(group_id) = parts.next().and_then(|x| u32::from_str_radix(x, 10).ok()) { if let Some(command) = parts.last() { if command.is_empty() || command.ends_with("/false") || command.ends_with("/nologin") { // We don't want "fake" users so in case the user command is "bad", we // ignore this user. return None; } let mut c_user = username.as_bytes().to_vec(); c_user.push(0); loop { let mut current = ngroups; if unsafe { getgrouplist( c_user.as_ptr() as *const _, group_id, groups.as_mut_ptr(), &mut current, ) } == -1 { if current > ngroups { for _ in 0..current - ngroups { groups.push(0); } ngroups = current; continue; } // It really failed, let's move on... return None; } // Let's get all the group names! return Some(User { name: username.to_owned(), groups: groups[..current as usize] .iter() .filter_map(|id| { let g = unsafe { getgrgid(*id as _) }; if g.is_null() { return None; } let mut group_name = Vec::new(); let c_group_name = unsafe { (*g).gr_name }; let mut x = 0; loop { let c = unsafe { *c_group_name.offset(x) }; if c == 0 { break; } group_name.push(c as u8); x += 1; } String::from_utf8(group_name).ok() }) .collect(), }); } } } } None }) .collect() } sysinfo-0.13.2/src/mac/component.rs010064400017500001750000000132551361726603000154210ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use libc::{c_char, c_int, c_void}; use std::mem; use sys::ffi; use ComponentExt; pub(crate) const COMPONENTS_TEMPERATURE_IDS: &[(&str, &[i8])] = &[ ("CPU", &['T' as i8, 'C' as i8, '0' as i8, 'P' as i8]), // CPU "TC0P" ("GPU", &['T' as i8, 'G' as i8, '0' as i8, 'P' as i8]), // GPU "TG0P" ("Battery", &['T' as i8, 'B' as i8, '0' as i8, 'T' as i8]), // Battery "TB0T" ]; pub struct ComponentFFI { input_structure: ffi::KeyData_t, val: ffi::Val_t, } impl ComponentFFI { fn new(key: &[i8], con: ffi::io_connect_t) -> Option { unsafe { get_key_size(con, key) } .ok() .map(|(input_structure, val)| ComponentFFI { input_structure, val, }) } fn get_temperature(&self, con: ffi::io_connect_t) -> Option { get_temperature_inner(con, &self.input_structure, &self.val) } } /// Struct containing a component information (temperature and name for the moment). pub struct Component { temperature: f32, max: f32, critical: Option, label: String, ffi_part: ComponentFFI, connection: ffi::io_connect_t, } impl Component { /// Creates a new `Component` with the given information. pub(crate) fn new( label: String, max: Option, critical: Option, key: &[i8], connection: ffi::io_connect_t, ) -> Option { let ffi_part = ComponentFFI::new(key, connection)?; ffi_part .get_temperature(connection) .map(|temperature| Component { temperature, label, max: max.unwrap_or(0.0), critical, ffi_part, connection, }) } } impl ComponentExt for Component { fn get_temperature(&self) -> f32 { self.temperature } fn get_max(&self) -> f32 { self.max } fn get_critical(&self) -> Option { self.critical } fn get_label(&self) -> &str { &self.label } fn refresh(&mut self) { if let Some(temp) = self.ffi_part.get_temperature(self.connection) { self.temperature = temp; if self.temperature > self.max { self.max = self.temperature; } } } } unsafe fn perform_call( conn: ffi::io_connect_t, index: c_int, input_structure: *const ffi::KeyData_t, output_structure: *mut ffi::KeyData_t, ) -> i32 { let mut structure_output_size = mem::size_of::(); ffi::IOConnectCallStructMethod( conn, index as u32, input_structure, mem::size_of::(), output_structure, &mut structure_output_size, ) } // Adapted from https://github.com/lavoiesl/osx-cpu-temp/blob/master/smc.c#L28 #[inline] fn strtoul(s: &[i8]) -> u32 { ((s[0] as u32) << (3u32 << 3)) + ((s[1] as u32) << (2u32 << 3)) + ((s[2] as u32) << (1u32 << 3)) + ((s[3] as u32) << (0u32 << 3)) } #[inline] unsafe fn ultostr(s: *mut c_char, val: u32) { *s.offset(0) = ((val >> 24) % 128) as i8; *s.offset(1) = ((val >> 16) % 128) as i8; *s.offset(2) = ((val >> 8) % 128) as i8; *s.offset(3) = (val % 128) as i8; *s.offset(4) = 0; } unsafe fn get_key_size( con: ffi::io_connect_t, key: &[i8], ) -> Result<(ffi::KeyData_t, ffi::Val_t), i32> { let mut input_structure: ffi::KeyData_t = mem::zeroed::(); let mut output_structure: ffi::KeyData_t = mem::zeroed::(); let mut val: ffi::Val_t = mem::zeroed::(); input_structure.key = strtoul(key); input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO; let result = perform_call( con, ffi::KERNEL_INDEX_SMC, &input_structure, &mut output_structure, ); if result != ffi::KIO_RETURN_SUCCESS { return Err(result); } val.data_size = output_structure.key_info.data_size; ultostr( val.data_type.as_mut_ptr(), output_structure.key_info.data_type, ); input_structure.key_info.data_size = val.data_size; input_structure.data8 = ffi::SMC_CMD_READ_BYTES; Ok((input_structure, val)) } unsafe fn read_key( con: ffi::io_connect_t, input_structure: &ffi::KeyData_t, mut val: ffi::Val_t, ) -> Result { let mut output_structure: ffi::KeyData_t = mem::zeroed::(); match perform_call( con, ffi::KERNEL_INDEX_SMC, input_structure, &mut output_structure, ) { ffi::KIO_RETURN_SUCCESS => { libc::memcpy( val.bytes.as_mut_ptr() as *mut c_void, output_structure.bytes.as_mut_ptr() as *mut c_void, mem::size_of::<[u8; 32]>(), ); Ok(val) } result => Err(result), } } fn get_temperature_inner( con: ffi::io_connect_t, input_structure: &ffi::KeyData_t, original_val: &ffi::Val_t, ) -> Option { if let Ok(val) = unsafe { read_key(con, input_structure, (*original_val).clone()) } { if val.data_size > 0 && unsafe { libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) } == 0 { // convert sp78 value to temperature let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2); return Some(x as f32 / 64f32); } } None } pub(crate) fn get_temperature(con: ffi::io_connect_t, key: &[i8]) -> Option { let (input_structure, val) = unsafe { get_key_size(con, &key) }.ok()?; get_temperature_inner(con, &input_structure, &val) } sysinfo-0.13.2/src/mac/disk.rs010064400017500001750000000140611364506453400143530ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use utils; use DiskExt; use DiskType; use libc::{c_char, c_void, statfs}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fs; use std::mem; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStringExt; use std::path::{Path, PathBuf}; use std::ptr; use sys::ffi; /// Struct containing a disk information. pub struct Disk { type_: DiskType, name: OsString, file_system: Vec, mount_point: PathBuf, total_space: u64, available_space: u64, } impl DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ } fn get_name(&self) -> &OsStr { &self.name } fn get_file_system(&self) -> &[u8] { &self.file_system } fn get_mount_point(&self) -> &Path { &self.mount_point } fn get_total_space(&self) -> u64 { self.total_space } fn get_available_space(&self) -> u64 { self.available_space } fn refresh(&mut self) -> bool { unsafe { let mut stat: statfs = mem::zeroed(); let mount_point_cpath = utils::to_cpath(&self.mount_point); if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 { self.available_space = u64::from(stat.f_bsize) * stat.f_bavail; true } else { false } } } } static DISK_TYPES: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(get_disk_types); fn get_disk_types() -> HashMap { let mut master_port: ffi::mach_port_t = 0; let mut media_iterator: ffi::io_iterator_t = 0; let mut ret = HashMap::with_capacity(1); unsafe { ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port); let matching_dictionary = ffi::IOServiceMatching(b"IOMedia\0".as_ptr() as *const i8); let result = ffi::IOServiceGetMatchingServices( master_port, matching_dictionary, &mut media_iterator, ); if result != ffi::KERN_SUCCESS as i32 { sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result); return ret; } loop { let next_media = ffi::IOIteratorNext(media_iterator); if next_media == 0 { break; } let mut props = MaybeUninit::::uninit(); let result = ffi::IORegistryEntryCreateCFProperties( next_media, props.as_mut_ptr(), ffi::kCFAllocatorDefault, 0, ); let props = props.assume_init(); if result == ffi::KERN_SUCCESS as i32 && check_value(props, b"Whole\0") { let mut name: ffi::io_name_t = mem::zeroed(); if ffi::IORegistryEntryGetName(next_media, name.as_mut_ptr() as *mut c_char) == ffi::KERN_SUCCESS as i32 { ret.insert( make_name(&name), if check_value(props, b"RAID\0") { DiskType::Unknown(-1) } else { DiskType::SSD }, ); } ffi::CFRelease(props as *mut _); } ffi::IOObjectRelease(next_media); } ffi::IOObjectRelease(media_iterator); } ret } fn make_name(v: &[u8]) -> OsString { for (pos, x) in v.iter().enumerate() { if *x == 0 { return OsStringExt::from_vec(v[0..pos].to_vec()); } } OsStringExt::from_vec(v.to_vec()) } pub(crate) fn get_disks() -> Vec { match fs::read_dir("/Volumes") { Ok(d) => d .flat_map(|x| { if let Ok(ref entry) = x { let mount_point = utils::realpath(&entry.path()); if mount_point.as_os_str().is_empty() { None } else { let name = entry.path().file_name()?.to_owned(); let type_ = DISK_TYPES .get(&name) .cloned() .unwrap_or(DiskType::Unknown(-2)); Some(new_disk(name, &mount_point, type_)) } } else { None } }) .collect(), _ => Vec::new(), } } unsafe fn check_value(dict: ffi::CFMutableDictionaryRef, key: &[u8]) -> bool { let key = ffi::CFStringCreateWithCStringNoCopy( ptr::null_mut(), key.as_ptr() as *const c_char, ffi::kCFStringEncodingMacRoman, ffi::kCFAllocatorNull as *mut c_void, ); let ret = ffi::CFDictionaryContainsKey(dict as ffi::CFDictionaryRef, key as *const c_void) != 0 && *(ffi::CFDictionaryGetValue(dict as ffi::CFDictionaryRef, key as *const c_void) as *const ffi::Boolean) != 0; ffi::CFRelease(key as *const c_void); ret } fn new_disk(name: OsString, mount_point: &Path, type_: DiskType) -> Disk { let mount_point_cpath = utils::to_cpath(mount_point); let mut total_space = 0; let mut available_space = 0; let mut file_system = None; unsafe { let mut stat: statfs = mem::zeroed(); if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 { total_space = u64::from(stat.f_bsize) * stat.f_blocks; available_space = stat.f_bfree * stat.f_blocks; let mut vec = Vec::with_capacity(stat.f_fstypename.len()); for x in &stat.f_fstypename { if *x == 0 { break; } vec.push(*x as u8); } file_system = Some(vec); } } Disk { type_, name, file_system: file_system.unwrap_or_else(|| b"".to_vec()), mount_point: mount_point.to_owned(), total_space, available_space, } } sysinfo-0.13.2/src/mac/ffi.rs010064400017500001750000000352151364242346600141710ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use libc::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void, size_t}; extern "C" { #[no_mangle] pub static kCFAllocatorDefault: CFAllocatorRef; // #[no_mangle] // pub static kODSessionDefault: ODSessionRef; #[no_mangle] pub static kCFAllocatorNull: CFAllocatorRef; // from https://github.com/apple/ccs-pyosxframeworks/blob/ccbacc3408bd7583a7535bbaca4020bdfe94bd2f/osx/frameworks/_opendirectory_cffi.py // #[no_mangle] // pub static kODRecordTypeUsers: ODRecordType; pub fn proc_pidinfo( pid: c_int, flavor: c_int, arg: u64, buffer: *mut c_void, buffersize: c_int, ) -> c_int; pub fn proc_listallpids(buffer: *mut c_void, buffersize: c_int) -> c_int; //pub fn proc_listpids(kind: u32, x: u32, buffer: *mut c_void, buffersize: c_int) -> c_int; //pub fn proc_name(pid: c_int, buffer: *mut c_void, buffersize: u32) -> c_int; //pub fn proc_regionfilename(pid: c_int, address: u64, buffer: *mut c_void, // buffersize: u32) -> c_int; pub fn proc_pidpath(pid: c_int, buffer: *mut c_void, buffersize: u32) -> c_int; pub fn proc_pid_rusage(pid: c_int, flavor: c_int, buffer: *mut c_void) -> c_int; pub fn IOMasterPort(a: i32, b: *mut mach_port_t) -> i32; pub fn IOServiceMatching(a: *const c_char) -> *mut c_void; pub fn IOServiceGetMatchingServices( a: mach_port_t, b: *mut c_void, c: *mut io_iterator_t, ) -> i32; pub fn IOIteratorNext(iterator: io_iterator_t) -> io_object_t; pub fn IOObjectRelease(obj: io_object_t) -> i32; pub fn IOServiceOpen(device: io_object_t, a: u32, t: u32, x: *mut io_connect_t) -> i32; pub fn IOServiceClose(a: io_connect_t) -> i32; pub fn IOConnectCallStructMethod( connection: mach_port_t, selector: u32, inputStruct: *const KeyData_t, inputStructCnt: size_t, outputStruct: *mut KeyData_t, outputStructCnt: *mut size_t, ) -> i32; pub fn IORegistryEntryCreateCFProperties( entry: io_registry_entry_t, properties: *mut CFMutableDictionaryRef, allocator: CFAllocatorRef, options: IOOptionBits, ) -> kern_return_t; pub fn CFDictionaryContainsKey(d: CFDictionaryRef, key: *const c_void) -> Boolean; pub fn CFDictionaryGetValue(d: CFDictionaryRef, key: *const c_void) -> *const c_void; pub fn IORegistryEntryGetName(entry: io_registry_entry_t, name: *mut c_char) -> kern_return_t; pub fn CFRelease(cf: CFTypeRef); pub fn CFStringCreateWithCStringNoCopy( alloc: *mut c_void, cStr: *const c_char, encoding: CFStringEncoding, contentsDeallocator: *mut c_void, ) -> CFStringRef; // pub fn CFStringGetCharactersPtr(theString: CFStringRef) -> *mut u16; // pub fn CFStringGetLength(theString: CFStringRef) -> CFIndex; // pub fn CFStringGetCharacterAtIndex(theString: CFStringRef, idx: CFIndex) -> u16; // pub fn ODNodeCreateWithName( // allocator: CFAllocatorRef, // session: ODSessionRef, // nodeName: CFStringRef, // error: *mut CFErrorRef, // ) -> ODNodeRef; // pub fn ODQueryCopyResults( // query: ODQueryRef, // allowPartialResults: Boolean, // error: *mut CFErrorRef, // ) -> CFArrayRef; // pub fn ODQueryCreateWithNode( // allocator: CFAllocatorRef, // node: ODNodeRef, // recordTypeOrList: CFTypeRef, // attribute: ODAttributeType, // matchType: ODMatchType, // queryValueOrList: CFTypeRef, // returnAttributeOrList: CFTypeRef, // maxResults: CFIndex, // error: *mut CFErrorRef, // ) -> ODQueryRef; // pub fn CFArrayGetCount(theArray: CFArrayRef) -> CFIndex; // pub fn CFArrayGetValueAtIndex(theArray: CFArrayRef, idx: CFIndex) -> *const c_void; // pub fn ODRecordGetRecordName(record: ODRecordRef) -> CFStringRef; pub fn mach_absolute_time() -> u64; //pub fn task_for_pid(host: u32, pid: pid_t, task: *mut task_t) -> u32; pub fn mach_task_self() -> u32; pub fn mach_host_self() -> u32; //pub fn task_info(host_info: u32, t: u32, c: *mut c_void, x: *mut u32) -> u32; pub fn host_statistics64( host_info: u32, x: u32, y: *mut c_void, z: *const u32, ) -> kern_return_t; pub fn host_processor_info( host_info: u32, t: u32, num_cpu_u: *mut u32, cpu_info: *mut *mut i32, num_cpu_info: *mut u32, ) -> kern_return_t; //pub fn host_statistics(host_priv: u32, flavor: u32, host_info: *mut c_void, // host_count: *const u32) -> u32; pub fn vm_deallocate(target_task: u32, address: *mut i32, size: u32) -> kern_return_t; pub fn sysctlbyname( name: *const c_char, oldp: *mut c_void, oldlenp: *mut usize, newp: *mut c_void, newlen: usize, ) -> kern_return_t; pub fn getloadavg(loads: *const f64, size: c_int); // pub fn proc_pidpath(pid: i32, buf: *mut i8, bufsize: u32) -> i32; // pub fn proc_name(pid: i32, buf: *mut i8, bufsize: u32) -> i32; } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 macro_rules! cfg_if { ($( if #[cfg($($meta:meta),*)] { $($it:item)* } ) else * else { $($it2:item)* }) => { __cfg_if_items! { () ; $( ( ($($meta),*) ($($it)*) ), )* ( () ($($it2)*) ), } } } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 macro_rules! __cfg_if_items { (($($not:meta,)*) ; ) => {}; (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { __cfg_if_apply! { cfg(all(not(any($($not),*)), $($m,)*)), $($it)* } __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } } } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 macro_rules! __cfg_if_apply { ($m:meta, $($it:item)*) => { $(#[$m] $it)* } } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 cfg_if! { if #[cfg(any(target_arch = "arm", target_arch = "x86"))] { pub type timeval32 = ::libc::timeval; } else { use libc::timeval32; } } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct if_data64 { pub ifi_type: c_uchar, pub ifi_typelen: c_uchar, pub ifi_physical: c_uchar, pub ifi_addrlen: c_uchar, pub ifi_hdrlen: c_uchar, pub ifi_recvquota: c_uchar, pub ifi_xmitquota: c_uchar, pub ifi_unused1: c_uchar, pub ifi_mtu: u32, pub ifi_metric: u32, pub ifi_baudrate: u64, pub ifi_ipackets: u64, pub ifi_ierrors: u64, pub ifi_opackets: u64, pub ifi_oerrors: u64, pub ifi_collisions: u64, pub ifi_ibytes: u64, pub ifi_obytes: u64, pub ifi_imcasts: u64, pub ifi_omcasts: u64, pub ifi_iqdrops: u64, pub ifi_noproto: u64, pub ifi_recvtiming: u32, pub ifi_xmittiming: u32, pub ifi_lastchange: timeval32, } // TODO: waiting for https://github.com/rust-lang/libc/pull/678 #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct if_msghdr2 { pub ifm_msglen: c_ushort, pub ifm_version: c_uchar, pub ifm_type: c_uchar, pub ifm_addrs: c_int, pub ifm_flags: c_int, pub ifm_index: c_ushort, pub ifm_snd_len: c_int, pub ifm_snd_maxlen: c_int, pub ifm_snd_drops: c_int, pub ifm_timer: c_int, pub ifm_data: if_data64, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __CFAllocator { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __CFDictionary { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __CFString { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __ODNode { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __ODSession { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __CFError { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __CFArray { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __ODRecord { __private: c_void, } #[cfg_attr(feature = "debug", derive(Debug))] #[repr(C)] pub struct __ODQuery { __private: c_void, } pub type CFAllocatorRef = *const __CFAllocator; pub type CFMutableDictionaryRef = *mut __CFDictionary; pub type CFDictionaryRef = *const __CFDictionary; #[allow(non_camel_case_types)] pub type io_name_t = [u8; 128]; #[allow(non_camel_case_types)] pub type io_registry_entry_t = io_object_t; pub type CFTypeRef = *const c_void; pub type CFStringRef = *const __CFString; // pub type ODNodeRef = *const __ODNode; // pub type ODSessionRef = *const __ODSession; // pub type CFErrorRef = *const __CFError; // pub type CFArrayRef = *const __CFArray; // pub type ODRecordRef = *const __ODRecord; // pub type ODQueryRef = *const __ODQuery; //#[allow(non_camel_case_types)] //pub type policy_t = i32; #[allow(non_camel_case_types)] //pub type integer_t = i32; //#[allow(non_camel_case_types)] //pub type time_t = i64; //#[allow(non_camel_case_types)] //pub type suseconds_t = i32; //#[allow(non_camel_case_types)] //pub type mach_vm_size_t = u64; //#[allow(non_camel_case_types)] //pub type task_t = u32; //#[allow(non_camel_case_types)] //pub type pid_t = i32; #[allow(non_camel_case_types)] pub type natural_t = u32; #[allow(non_camel_case_types)] pub type mach_port_t = u32; #[allow(non_camel_case_types)] pub type io_object_t = mach_port_t; #[allow(non_camel_case_types)] pub type io_iterator_t = io_object_t; #[allow(non_camel_case_types)] pub type io_connect_t = io_object_t; #[allow(non_camel_case_types)] pub type boolean_t = c_uint; #[allow(non_camel_case_types)] pub type kern_return_t = c_int; pub type Boolean = c_uchar; pub type IOOptionBits = u32; pub type CFStringEncoding = u32; // pub type ODRecordType = CFStringRef; // pub type ODAttributeType = CFStringRef; // pub type ODMatchType = u32; // pub type CFIndex = c_long; /*#[repr(C)] pub struct task_thread_times_info { pub user_time: time_value, pub system_time: time_value, }*/ /*#[repr(C)] pub struct task_basic_info_64 { pub suspend_count: integer_t, pub virtual_size: mach_vm_size_t, pub resident_size: mach_vm_size_t, pub user_time: time_value_t, pub system_time: time_value_t, pub policy: policy_t, }*/ #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct vm_statistics64 { pub free_count: natural_t, pub active_count: natural_t, pub inactive_count: natural_t, pub wire_count: natural_t, pub zero_fill_count: u64, pub reactivations: u64, pub pageins: u64, pub pageouts: u64, pub faults: u64, pub cow_faults: u64, pub lookups: u64, pub hits: u64, pub purges: u64, pub purgeable_count: natural_t, pub speculative_count: natural_t, pub decompressions: u64, pub compressions: u64, pub swapins: u64, pub swapouts: u64, pub compressor_page_count: natural_t, pub throttled_count: natural_t, pub external_page_count: natural_t, pub internal_page_count: natural_t, pub total_uncompressed_pages_in_compressor: u64, } #[cfg_attr(feature = "debug", derive(Eq, Hash, PartialEq))] #[derive(Clone)] #[repr(C)] pub struct Val_t { pub key: [i8; 5], pub data_size: u32, pub data_type: [i8; 5], // UInt32Char_t pub bytes: [i8; 32], // SMCBytes_t } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct KeyData_vers_t { pub major: u8, pub minor: u8, pub build: u8, pub reserved: [u8; 1], pub release: u16, } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct KeyData_pLimitData_t { pub version: u16, pub length: u16, pub cpu_plimit: u32, pub gpu_plimit: u32, pub mem_plimit: u32, } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct KeyData_keyInfo_t { pub data_size: u32, pub data_type: u32, pub data_attributes: u8, } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct KeyData_t { pub key: u32, pub vers: KeyData_vers_t, pub p_limit_data: KeyData_pLimitData_t, pub key_info: KeyData_keyInfo_t, pub result: u8, pub status: u8, pub data8: u8, pub data32: u32, pub bytes: [i8; 32], // SMCBytes_t } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] #[repr(C)] pub struct xsw_usage { pub xsu_total: u64, pub xsu_avail: u64, pub xsu_used: u64, pub xsu_pagesize: u32, pub xsu_encrypted: boolean_t, } //https://github.com/andrewdavidmackenzie/libproc-rs/blob/master/src/libproc/pid_rusage.rs #[derive(Debug, Default)] #[repr(C)] pub struct RUsageInfoV2 { pub ri_uuid: [u8; 16], pub ri_user_time: u64, pub ri_system_time: u64, pub ri_pkg_idle_wkups: u64, pub ri_interrupt_wkups: u64, pub ri_pageins: u64, pub ri_wired_size: u64, pub ri_resident_size: u64, pub ri_phys_footprint: u64, pub ri_proc_start_abstime: u64, pub ri_proc_exit_abstime: u64, pub ri_child_user_time: u64, pub ri_child_system_time: u64, pub ri_child_pkg_idle_wkups: u64, pub ri_child_interrupt_wkups: u64, pub ri_child_pageins: u64, pub ri_child_elapsed_abstime: u64, pub ri_diskio_bytesread: u64, pub ri_diskio_byteswritten: u64, } //pub const HOST_CPU_LOAD_INFO_COUNT: usize = 4; //pub const HOST_CPU_LOAD_INFO: u32 = 3; pub const KERN_SUCCESS: kern_return_t = 0; pub const HW_NCPU: u32 = 3; pub const CTL_HW: u32 = 6; pub const CTL_VM: u32 = 2; pub const VM_SWAPUSAGE: u32 = 5; pub const PROCESSOR_CPU_LOAD_INFO: u32 = 2; pub const CPU_STATE_USER: u32 = 0; pub const CPU_STATE_SYSTEM: u32 = 1; pub const CPU_STATE_IDLE: u32 = 2; pub const CPU_STATE_NICE: u32 = 3; pub const CPU_STATE_MAX: usize = 4; pub const HW_MEMSIZE: u32 = 24; //pub const PROC_ALL_PIDS: c_uint = 1; pub const PROC_PIDTBSDINFO: c_int = 3; //pub const TASK_THREAD_TIMES_INFO: u32 = 3; //pub const TASK_THREAD_TIMES_INFO_COUNT: u32 = 4; //pub const TASK_BASIC_INFO_64: u32 = 5; //pub const TASK_BASIC_INFO_64_COUNT: u32 = 10; pub const HOST_VM_INFO64: u32 = 4; pub const HOST_VM_INFO64_COUNT: u32 = 38; pub const MACH_PORT_NULL: i32 = 0; pub const KERNEL_INDEX_SMC: i32 = 2; pub const SMC_CMD_READ_KEYINFO: u8 = 9; pub const SMC_CMD_READ_BYTES: u8 = 5; pub const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; pub const KIO_RETURN_SUCCESS: i32 = 0; #[allow(non_upper_case_globals)] pub const kCFStringEncodingMacRoman: CFStringEncoding = 0; sysinfo-0.13.2/src/mac/mod.rs010064400017500001750000000006331362722252500141740ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // pub mod component; pub mod disk; mod ffi; pub mod network; pub mod process; pub mod processor; pub mod system; pub mod users; pub use self::component::Component; pub use self::disk::Disk; pub use self::network::{NetworkData, Networks}; pub use self::process::{Process, ProcessStatus}; pub use self::processor::Processor; pub use self::system::System; sysinfo-0.13.2/src/mac/network.rs010064400017500001750000000150271363516610000151040ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use libc::{self, c_char, CTL_NET, NET_RT_IFLIST2, PF_ROUTE, RTM_IFINFO2}; use std::collections::HashMap; use std::ptr::null_mut; use sys::ffi; use NetworkExt; use NetworksExt; use NetworksIter; macro_rules! old_and_new { ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{ $ty_.$old = $ty_.$name; $ty_.$name = $new_val; }}; } /// Network interfaces. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// ``` pub struct Networks { interfaces: HashMap, } impl Networks { pub(crate) fn new() -> Self { Networks { interfaces: HashMap::new(), } } #[allow(clippy::cast_ptr_alignment)] fn update_networks(&mut self) { let mib = &mut [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0]; let mut len = 0; if unsafe { libc::sysctl(mib.as_mut_ptr(), 6, null_mut(), &mut len, null_mut(), 0) } < 0 { // TODO: might be nice to put an error in here... return; } let mut buf = Vec::with_capacity(len); unsafe { buf.set_len(len); if libc::sysctl( mib.as_mut_ptr(), 6, buf.as_mut_ptr(), &mut len, null_mut(), 0, ) < 0 { // TODO: might be nice to put an error in here... return; } } let buf = buf.as_ptr() as *const c_char; let lim = unsafe { buf.add(len) }; let mut next = buf; while next < lim { unsafe { let ifm = next as *const libc::if_msghdr; next = next.offset((*ifm).ifm_msglen as isize); if (*ifm).ifm_type == RTM_IFINFO2 as u8 { // The interface (line description) name stored at ifname will be returned in // the default coded character set identifier (CCSID) currently in effect for // the job. If this is not a single byte CCSID, then storage greater than // IFNAMSIZ (16) bytes may be needed. 22 bytes is large enough for all CCSIDs. let mut name = vec![0u8; libc::IFNAMSIZ + 6]; let if2m: *const ffi::if_msghdr2 = ifm as *const ffi::if_msghdr2; let pname = libc::if_indextoname((*if2m).ifm_index as _, name.as_mut_ptr() as _); if pname.is_null() { continue; } name.set_len(libc::strlen(pname)); let name = String::from_utf8_unchecked(name); let interface = self.interfaces.entry(name).or_insert_with(|| NetworkData { current_in: (*if2m).ifm_data.ifi_ibytes, old_in: 0, current_out: (*if2m).ifm_data.ifi_obytes, old_out: 0, packets_in: (*if2m).ifm_data.ifi_ipackets, old_packets_in: 0, packets_out: (*if2m).ifm_data.ifi_opackets, old_packets_out: 0, errors_in: (*if2m).ifm_data.ifi_ierrors, old_errors_in: 0, errors_out: (*if2m).ifm_data.ifi_oerrors, old_errors_out: 0, updated: true, }); old_and_new!(interface, current_out, old_out, (*if2m).ifm_data.ifi_obytes); old_and_new!(interface, current_in, old_in, (*if2m).ifm_data.ifi_ibytes); old_and_new!( interface, packets_in, old_packets_in, (*if2m).ifm_data.ifi_ipackets ); old_and_new!( interface, packets_out, old_packets_out, (*if2m).ifm_data.ifi_opackets ); old_and_new!( interface, errors_in, old_errors_in, (*if2m).ifm_data.ifi_ierrors ); old_and_new!( interface, errors_out, old_errors_out, (*if2m).ifm_data.ifi_oerrors ); interface.updated = true; } } } } } impl NetworksExt for Networks { fn iter<'a>(&'a self) -> NetworksIter<'a> { NetworksIter::new(self.interfaces.iter()) } fn refresh_networks_list(&mut self) { for (_, data) in self.interfaces.iter_mut() { data.updated = false; } self.update_networks(); self.interfaces.retain(|_, data| data.updated); } fn refresh(&mut self) { self.update_networks(); } } /// Contains network information. #[derive(PartialEq, Eq)] pub struct NetworkData { current_in: u64, old_in: u64, current_out: u64, old_out: u64, packets_in: u64, old_packets_in: u64, packets_out: u64, old_packets_out: u64, errors_in: u64, old_errors_in: u64, errors_out: u64, old_errors_out: u64, updated: bool, } impl NetworkExt for NetworkData { fn get_received(&self) -> u64 { self.current_in - self.old_in } fn get_total_received(&self) -> u64 { self.current_in } fn get_transmitted(&self) -> u64 { self.current_out - self.old_out } fn get_total_transmitted(&self) -> u64 { self.current_out } fn get_packets_received(&self) -> u64 { self.packets_in - self.old_packets_in } fn get_total_packets_received(&self) -> u64 { self.packets_in } fn get_packets_transmitted(&self) -> u64 { self.packets_out - self.old_packets_out } fn get_total_packets_transmitted(&self) -> u64 { self.packets_out } fn get_errors_on_received(&self) -> u64 { self.errors_in - self.old_errors_in } fn get_total_errors_on_received(&self) -> u64 { self.errors_in } fn get_errors_on_transmitted(&self) -> u64 { self.errors_out - self.old_errors_out } fn get_total_errors_on_transmitted(&self) -> u64 { self.errors_out } } sysinfo-0.13.2/src/mac/process.rs010064400017500001750000000454201364506453400151020ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use std::borrow::Borrow; use std::ffi::OsStr; use std::fmt; use std::mem::{self, MaybeUninit}; use std::ops::Deref; use std::path::{Path, PathBuf}; use libc::{c_int, c_void, gid_t, kill, size_t, uid_t}; use DiskUsage; use Pid; use ProcessExt; use sys::ffi; use sys::system::Wrap; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] pub enum ProcessStatus { /// Process being created by fork. Idle, /// Currently runnable. Run, /// Sleeping on an address. Sleep, /// Process debugging or suspension. Stop, /// Awaiting collection by parent. Zombie, /// Unknown. Unknown(u32), } impl From for ProcessStatus { fn from(status: u32) -> ProcessStatus { match status { 1 => ProcessStatus::Idle, 2 => ProcessStatus::Run, 3 => ProcessStatus::Sleep, 4 => ProcessStatus::Stop, 5 => ProcessStatus::Zombie, x => ProcessStatus::Unknown(x), } } } impl ProcessStatus { /// Used to display `ProcessStatus`. pub fn to_string(&self) -> &str { match *self { ProcessStatus::Idle => "Idle", ProcessStatus::Run => "Runnable", ProcessStatus::Sleep => "Sleeping", ProcessStatus::Stop => "Stopped", ProcessStatus::Zombie => "Zombie", ProcessStatus::Unknown(_) => "Unknown", } } } impl fmt::Display for ProcessStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_string()) } } /// Enum describing the different status of a thread. #[derive(Clone, Debug)] pub enum ThreadStatus { /// Thread is running normally. Running, /// Thread is stopped. Stopped, /// Thread is waiting normally. Waiting, /// Thread is in an uninterruptible wait Uninterruptible, /// Thread is halted at a clean point. Halted, /// Unknown. Unknown(i32), } impl From for ThreadStatus { fn from(status: i32) -> ThreadStatus { match status { 1 => ThreadStatus::Running, 2 => ThreadStatus::Stopped, 3 => ThreadStatus::Waiting, 4 => ThreadStatus::Uninterruptible, 5 => ThreadStatus::Halted, x => ThreadStatus::Unknown(x), } } } impl ThreadStatus { /// Used to display `ThreadStatus`. pub fn to_string(&self) -> &str { match *self { ThreadStatus::Running => "Running", ThreadStatus::Stopped => "Stopped", ThreadStatus::Waiting => "Waiting", ThreadStatus::Uninterruptible => "Uninterruptible", ThreadStatus::Halted => "Halted", ThreadStatus::Unknown(_) => "Unknown", } } } impl fmt::Display for ThreadStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_string()) } } /// Struct containing a process' information. #[derive(Clone)] pub struct Process { pub(crate) name: String, pub(crate) cmd: Vec, pub(crate) exe: PathBuf, pid: Pid, parent: Option, pub(crate) environ: Vec, cwd: PathBuf, pub(crate) root: PathBuf, pub(crate) memory: u64, pub(crate) virtual_memory: u64, utime: u64, stime: u64, old_utime: u64, old_stime: u64, start_time: u64, updated: bool, cpu_usage: f32, /// User id of the process owner. pub uid: uid_t, /// Group id of the process owner. pub gid: gid_t, pub(crate) process_status: ProcessStatus, /// Status of process (running, stopped, waiting, etc). `None` means `sysinfo` doesn't have /// enough rights to get this information. /// /// This is very likely this one that you want instead of `process_status`. pub status: Option, pub(crate) old_read_bytes: u64, pub(crate) old_written_bytes: u64, pub(crate) read_bytes: u64, pub(crate) written_bytes: u64, } impl Process { pub(crate) fn new_empty(pid: Pid, exe: PathBuf, name: String) -> Process { Process { name, pid, parent: None, cmd: Vec::new(), environ: Vec::new(), exe, cwd: PathBuf::new(), root: PathBuf::new(), memory: 0, virtual_memory: 0, cpu_usage: 0., utime: 0, stime: 0, old_utime: 0, old_stime: 0, updated: true, start_time: 0, uid: 0, gid: 0, process_status: ProcessStatus::Unknown(0), status: None, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } pub(crate) fn new_with( pid: Pid, parent: Option, start_time: u64, exe: PathBuf, name: String, cmd: Vec, environ: Vec, root: PathBuf, ) -> Process { Process { name, pid, parent, cmd, environ, exe, cwd: PathBuf::new(), root, memory: 0, virtual_memory: 0, cpu_usage: 0., utime: 0, stime: 0, old_utime: 0, old_stime: 0, updated: true, start_time, uid: 0, gid: 0, process_status: ProcessStatus::Unknown(0), status: None, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } } impl ProcessExt for Process { fn new(pid: Pid, parent: Option, start_time: u64) -> Process { Process { name: String::new(), pid, parent, cmd: Vec::new(), environ: Vec::new(), exe: PathBuf::new(), cwd: PathBuf::new(), root: PathBuf::new(), memory: 0, virtual_memory: 0, cpu_usage: 0., utime: 0, stime: 0, old_utime: 0, old_stime: 0, updated: true, start_time, uid: 0, gid: 0, process_status: ProcessStatus::Unknown(0), status: None, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } fn kill(&self, signal: ::Signal) -> bool { unsafe { kill(self.pid, signal as c_int) == 0 } } fn name(&self) -> &str { &self.name } fn cmd(&self) -> &[String] { &self.cmd } fn exe(&self) -> &Path { self.exe.as_path() } fn pid(&self) -> Pid { self.pid } fn environ(&self) -> &[String] { &self.environ } fn cwd(&self) -> &Path { self.cwd.as_path() } fn root(&self) -> &Path { self.root.as_path() } fn memory(&self) -> u64 { self.memory } fn virtual_memory(&self) -> u64 { self.virtual_memory } fn parent(&self) -> Option { self.parent } fn status(&self) -> ProcessStatus { self.process_status } fn start_time(&self) -> u64 { self.start_time } fn cpu_usage(&self) -> f32 { self.cpu_usage } fn disk_usage(&self) -> DiskUsage { DiskUsage { read_bytes: self.read_bytes - self.old_read_bytes, total_read_bytes: self.read_bytes, written_bytes: self.written_bytes - self.old_written_bytes, total_written_bytes: self.written_bytes, } } } pub(crate) fn compute_cpu_usage(p: &mut Process, time: u64, task_time: u64) { let system_time_delta = task_time - p.old_utime; let time_delta = time - p.old_stime; p.old_utime = task_time; p.old_stime = time; p.cpu_usage = if time_delta == 0 { 0f32 } else { (system_time_delta as f64 * 100f64 / time_delta as f64) as f32 }; p.updated = true; } /*pub fn set_time(p: &mut Process, utime: u64, stime: u64) { p.old_utime = p.utime; p.old_stime = p.stime; p.utime = utime; p.stime = stime; p.updated = true; }*/ pub(crate) fn has_been_updated(p: &mut Process) -> bool { let old = p.updated; p.updated = false; old } pub(crate) fn force_update(p: &mut Process) { p.updated = true; } unsafe fn get_task_info(pid: Pid) -> libc::proc_taskinfo { let mut task_info = mem::zeroed::(); // If it doesn't work, we just don't have memory information for this process // so it's "fine". ffi::proc_pidinfo( pid, libc::PROC_PIDTASKINFO, 0, &mut task_info as *mut libc::proc_taskinfo as *mut c_void, mem::size_of::() as _, ); task_info } pub(crate) fn update_process( wrap: &Wrap, pid: Pid, mut size: size_t, ) -> Result, ()> { let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0]; let mut proc_args = Vec::with_capacity(size as usize); unsafe { if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) { if p.memory == 0 { // We don't have access to this process' information. force_update(p); return Ok(None); } let task_info = get_task_info(pid); let mut thread_info = mem::zeroed::(); let (user_time, system_time, thread_status) = if ffi::proc_pidinfo( pid, libc::PROC_PIDTHREADINFO, 0, &mut thread_info as *mut libc::proc_threadinfo as *mut c_void, mem::size_of::() as _, ) != 0 { ( thread_info.pth_user_time, thread_info.pth_system_time, Some(ThreadStatus::from(thread_info.pth_run_state)), ) } else { (0, 0, None) }; p.status = thread_status; let task_time = user_time + system_time + task_info.pti_total_user + task_info.pti_total_system; let time = ffi::mach_absolute_time(); compute_cpu_usage(p, time, task_time); p.memory = task_info.pti_resident_size >> 10; // divide by 1024 p.virtual_memory = task_info.pti_virtual_size >> 10; // divide by 1024 update_proc_disk_activity(p); return Ok(None); } let mut info = mem::zeroed::(); if ffi::proc_pidinfo( pid, ffi::PROC_PIDTBSDINFO, 0, &mut info as *mut _ as *mut _, mem::size_of::() as _, ) != mem::size_of::() as _ { let mut buffer: Vec = Vec::with_capacity(ffi::PROC_PIDPATHINFO_MAXSIZE as _); match ffi::proc_pidpath( pid, buffer.as_mut_ptr() as *mut _, ffi::PROC_PIDPATHINFO_MAXSIZE, ) { x if x > 0 => { buffer.set_len(x as _); let tmp = String::from_utf8_unchecked(buffer); let exe = PathBuf::from(tmp); let name = exe .file_name() .unwrap_or_else(|| OsStr::new("")) .to_str() .unwrap_or_else(|| "") .to_owned(); return Ok(Some(Process::new_empty(pid, exe, name))); } _ => {} } return Err(()); } let parent = match info.pbi_ppid as i32 { 0 => None, p => Some(p), }; let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr(); mib[0] = libc::CTL_KERN; mib[1] = libc::KERN_PROCARGS2; mib[2] = pid as c_int; /* * /---------------\ 0x00000000 * | ::::::::::::: | * |---------------| <-- Beginning of data returned by sysctl() is here. * | argc | * |---------------| * | exec_path | * |---------------| * | 0 | * |---------------| * | arg[0] | * |---------------| * | 0 | * |---------------| * | arg[n] | * |---------------| * | 0 | * |---------------| * | env[0] | * |---------------| * | 0 | * |---------------| * | env[n] | * |---------------| * | ::::::::::::: | * |---------------| <-- Top of stack. * : : * : : * \---------------/ 0xffffffff */ if libc::sysctl( mib.as_mut_ptr(), 3, ptr as *mut c_void, &mut size, ::std::ptr::null_mut(), 0, ) == -1 { return Err(()); // not enough rights I assume? } let mut n_args: c_int = 0; libc::memcpy( (&mut n_args) as *mut c_int as *mut c_void, ptr as *const c_void, mem::size_of::(), ); let mut cp = ptr.add(mem::size_of::()); let mut start = cp; let mut p = if cp < ptr.add(size) { while cp < ptr.add(size) && *cp != 0 { cp = cp.offset(1); } let exe = Path::new(get_unchecked_str(cp, start).as_str()).to_path_buf(); let name = exe .file_name() .unwrap_or_else(|| OsStr::new("")) .to_str() .unwrap_or_else(|| "") .to_owned(); while cp < ptr.add(size) && *cp == 0 { cp = cp.offset(1); } start = cp; let mut c = 0; let mut cmd = Vec::with_capacity(n_args as usize); while c < n_args && cp < ptr.add(size) { if *cp == 0 { c += 1; cmd.push(get_unchecked_str(cp, start)); start = cp.offset(1); } cp = cp.offset(1); } #[inline] fn do_nothing(_: &str, _: &mut PathBuf, _: &mut bool) {} #[inline] fn do_something(env: &str, root: &mut PathBuf, check: &mut bool) { if *check && env.starts_with("PATH=") { *check = false; *root = Path::new(&env[6..]).to_path_buf(); } } #[inline] unsafe fn get_environ( ptr: *mut u8, mut cp: *mut u8, size: size_t, mut root: PathBuf, callback: F, ) -> (Vec, PathBuf) { let mut environ = Vec::with_capacity(10); let mut start = cp; let mut check = true; while cp < ptr.add(size) { if *cp == 0 { if cp == start { break; } let e = get_unchecked_str(cp, start); callback(&e, &mut root, &mut check); environ.push(e); start = cp.offset(1); } cp = cp.offset(1); } (environ, root) } let (environ, root) = if exe.is_absolute() { if let Some(parent) = exe.parent() { get_environ(ptr, cp, size, parent.to_path_buf(), do_nothing) } else { get_environ(ptr, cp, size, PathBuf::new(), do_something) } } else { get_environ(ptr, cp, size, PathBuf::new(), do_something) }; Process::new_with( pid, parent, info.pbi_start_tvsec, exe, name, parse_command_line(&cmd), environ, root, ) } else { Process::new(pid, parent, info.pbi_start_tvsec) }; let task_info = get_task_info(pid); p.memory = task_info.pti_resident_size >> 10; // divide by 1024 p.virtual_memory = task_info.pti_virtual_size >> 10; // divide by 1024 p.uid = info.pbi_uid; p.gid = info.pbi_gid; p.process_status = ProcessStatus::from(info.pbi_status); update_proc_disk_activity(&mut p); Ok(Some(p)) } } fn update_proc_disk_activity(p: &mut Process) { p.old_read_bytes = p.read_bytes; p.old_written_bytes = p.written_bytes; let mut pidrusage: ffi::RUsageInfoV2 = unsafe { MaybeUninit::uninit().assume_init() }; let retval = unsafe { ffi::proc_pid_rusage(p.pid() as c_int, 2, &mut pidrusage as *mut _ as _) }; if retval < 0 { sysinfo_debug!("proc_pid_rusage failed: {:?}", retval); } else { p.read_bytes = pidrusage.ri_diskio_bytesread; p.written_bytes = pidrusage.ri_diskio_byteswritten; } } pub(crate) fn get_proc_list() -> Option> { let count = unsafe { ffi::proc_listallpids(::std::ptr::null_mut(), 0) }; if count < 1 { return None; } let mut pids: Vec = Vec::with_capacity(count as usize); unsafe { pids.set_len(count as usize); } let count = count * mem::size_of::() as i32; let x = unsafe { ffi::proc_listallpids(pids.as_mut_ptr() as *mut c_void, count) }; if x < 1 || x as usize >= pids.len() { None } else { unsafe { pids.set_len(x as usize); } Some(pids) } } unsafe fn get_unchecked_str(cp: *mut u8, start: *mut u8) -> String { let len = cp as usize - start as usize; let part = Vec::from_raw_parts(start, len, len); let tmp = String::from_utf8_unchecked(part.clone()); mem::forget(part); tmp } fn parse_command_line + Borrow>(cmd: &[T]) -> Vec { let mut x = 0; let mut command = Vec::with_capacity(cmd.len()); while x < cmd.len() { let mut y = x; if cmd[y].starts_with('\'') || cmd[y].starts_with('"') { let c = if cmd[y].starts_with('\'') { '\'' } else { '"' }; while y < cmd.len() && !cmd[y].ends_with(c) { y += 1; } command.push(cmd[x..y].join(" ")); x = y; } else { command.push(cmd[x].to_owned()); } x += 1; } command } sysinfo-0.13.2/src/mac/processor.rs010064400017500001750000000140321361751223500154310ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use libc::{c_char, c_void}; use std::mem; use std::ops::Deref; use std::sync::Arc; use sys::ffi; use sys::system::get_sys_value; use ProcessorExt; pub struct UnsafePtr(*mut T); unsafe impl Send for UnsafePtr {} unsafe impl Sync for UnsafePtr {} impl Deref for UnsafePtr { type Target = *mut T; fn deref(&self) -> &*mut T { &self.0 } } pub struct ProcessorData { pub cpu_info: UnsafePtr, pub num_cpu_info: u32, } impl ProcessorData { pub fn new(cpu_info: *mut i32, num_cpu_info: u32) -> ProcessorData { ProcessorData { cpu_info: UnsafePtr(cpu_info), num_cpu_info, } } } impl Drop for ProcessorData { fn drop(&mut self) { if !self.cpu_info.0.is_null() { let prev_cpu_info_size = ::std::mem::size_of::() as u32 * self.num_cpu_info; unsafe { ffi::vm_deallocate(ffi::mach_task_self(), self.cpu_info.0, prev_cpu_info_size); } self.cpu_info.0 = ::std::ptr::null_mut(); } } } /// Struct containing a processor information. pub struct Processor { name: String, cpu_usage: f32, processor_data: Arc, frequency: u64, vendor_id: String, brand: String, } impl Processor { pub(crate) fn new( name: String, processor_data: Arc, frequency: u64, vendor_id: String, brand: String, ) -> Processor { Processor { name, cpu_usage: 0f32, processor_data, frequency, vendor_id, brand, } } pub(crate) fn set_cpu_usage(&mut self, cpu_usage: f32) { self.cpu_usage = cpu_usage; } pub(crate) fn update(&mut self, cpu_usage: f32, processor_data: Arc) { self.cpu_usage = cpu_usage; self.processor_data = processor_data; } pub(crate) fn get_data(&self) -> Arc { Arc::clone(&self.processor_data) } } impl ProcessorExt for Processor { fn get_cpu_usage(&self) -> f32 { self.cpu_usage } fn get_name(&self) -> &str { &self.name } /// Returns the processor frequency in MHz. fn get_frequency(&self) -> u64 { self.frequency } fn get_vendor_id(&self) -> &str { &self.vendor_id } fn get_brand(&self) -> &str { &self.brand } } pub fn get_cpu_frequency() -> u64 { let mut speed: u64 = 0; let mut len = std::mem::size_of::(); unsafe { ffi::sysctlbyname( b"hw.cpufrequency\0".as_ptr() as *const c_char, &mut speed as *mut _ as _, &mut len, std::ptr::null_mut(), 0, ); } speed / 1_000_000 } pub fn init_processors(port: ffi::mach_port_t) -> (Processor, Vec) { let mut num_cpu = 0; let mut processors = Vec::new(); let mut pourcent = 0f32; let mut mib = [0, 0]; let (vendor_id, brand) = get_vendor_id_and_brand(); let frequency = get_cpu_frequency(); unsafe { if !get_sys_value( ffi::CTL_HW, ffi::HW_NCPU, mem::size_of::(), &mut num_cpu as *mut usize as *mut c_void, &mut mib, ) { num_cpu = 1; } let mut num_cpu_u = 0u32; let mut cpu_info: *mut i32 = ::std::ptr::null_mut(); let mut num_cpu_info = 0u32; if ffi::host_processor_info( port, ffi::PROCESSOR_CPU_LOAD_INFO, &mut num_cpu_u as *mut u32, &mut cpu_info as *mut *mut i32, &mut num_cpu_info as *mut u32, ) == ffi::KERN_SUCCESS { let proc_data = Arc::new(ProcessorData::new(cpu_info, num_cpu_info)); for i in 0..num_cpu { let mut p = Processor::new( format!("{}", i + 1), Arc::clone(&proc_data), frequency, vendor_id.clone(), brand.clone(), ); let in_use = *cpu_info .offset((ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize) + *cpu_info .offset((ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize) + *cpu_info .offset((ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize); let total = in_use + *cpu_info .offset((ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize); p.set_cpu_usage(in_use as f32 / total as f32 * 100.); pourcent += p.get_cpu_usage(); processors.push(p); } } } let mut global_processor = Processor::new( "0".to_owned(), Arc::new(ProcessorData::new(::std::ptr::null_mut(), 0)), frequency, vendor_id, brand, ); global_processor.set_cpu_usage(pourcent / processors.len() as f32); (global_processor, processors) } fn get_sysctl_str(s: &[u8]) -> String { let mut len = 0; unsafe { ffi::sysctlbyname( s.as_ptr() as *const c_char, std::ptr::null_mut(), &mut len, std::ptr::null_mut(), 0, ); } if len < 1 { return String::new(); } let mut buf = Vec::with_capacity(len); unsafe { ffi::sysctlbyname( s.as_ptr() as *const c_char, buf.as_mut_ptr() as _, &mut len, std::ptr::null_mut(), 0, ); } if len > 0 { unsafe { buf.set_len(len); } String::from_utf8(buf).unwrap_or_else(|_| String::new()) } else { String::new() } } pub fn get_vendor_id_and_brand() -> (String, String) { ( get_sysctl_str(b"machdep.cpu.brand_string\0"), get_sysctl_str(b"machdep.cpu.vendor\0"), ) } sysinfo-0.13.2/src/mac/system.rs010064400017500001750000000332271364506453400147520ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use sys::component::Component; use sys::disk::Disk; use sys::ffi; use sys::network::Networks; use sys::process::*; use sys::processor::*; use {LoadAvg, Pid, ProcessExt, ProcessorExt, RefreshKind, SystemExt, User}; use std::cell::UnsafeCell; use std::collections::HashMap; use std::mem; use std::sync::Arc; use libc::{self, c_int, c_void, size_t, sysconf, _SC_PAGESIZE}; use rayon::prelude::*; /// Structs containing system's information. pub struct System { process_list: HashMap, mem_total: u64, mem_free: u64, swap_total: u64, swap_free: u64, global_processor: Processor, processors: Vec, page_size_kb: u64, components: Vec, connection: Option, disks: Vec, networks: Networks, port: ffi::mach_port_t, users: Vec, boot_time: u64, } impl Drop for System { fn drop(&mut self) { if let Some(conn) = self.connection { unsafe { ffi::IOServiceClose(conn); } } } } pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap>); unsafe impl<'a> Send for Wrap<'a> {} unsafe impl<'a> Sync for Wrap<'a> {} impl System { fn clear_procs(&mut self) { let mut to_delete = Vec::new(); for (pid, mut proc_) in &mut self.process_list { if !has_been_updated(&mut proc_) { to_delete.push(*pid); } } for pid in to_delete { self.process_list.remove(&pid); } } } fn boot_time() -> u64 { let mut boot_time = libc::timeval { tv_sec: 0, tv_usec: 0, }; let mut len = ::std::mem::size_of::(); let mut mib: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME]; if unsafe { libc::sysctl( mib.as_mut_ptr(), 2, &mut boot_time as *mut libc::timeval as *mut _, &mut len, ::std::ptr::null_mut(), 0, ) } < 0 { 0 } else { boot_time.tv_sec as _ } } impl SystemExt for System { fn new_with_specifics(refreshes: RefreshKind) -> System { let port = unsafe { ffi::mach_host_self() }; let (global_processor, processors) = init_processors(port); let mut s = System { process_list: HashMap::with_capacity(200), mem_total: 0, mem_free: 0, swap_total: 0, swap_free: 0, global_processor, processors, page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 >> 10 }, // divide by 1024 components: Vec::with_capacity(2), connection: get_io_service_connection(), disks: Vec::with_capacity(1), networks: Networks::new(), port, users: Vec::new(), boot_time: boot_time(), }; s.refresh_specifics(refreshes); s } fn refresh_memory(&mut self) { let mut mib = [0, 0]; unsafe { // get system values // get swap info let mut xs: ffi::xsw_usage = mem::zeroed::(); if get_sys_value( ffi::CTL_VM, ffi::VM_SWAPUSAGE, mem::size_of::(), &mut xs as *mut ffi::xsw_usage as *mut c_void, &mut mib, ) { self.swap_total = xs.xsu_total >> 10; // divide by 1024 self.swap_free = xs.xsu_avail >> 10; // divide by 1024 } // get ram info if self.mem_total < 1 { get_sys_value( ffi::CTL_HW, ffi::HW_MEMSIZE, mem::size_of::(), &mut self.mem_total as *mut u64 as *mut c_void, &mut mib, ); self.mem_total >>= 10; // divide by 1024 } let count: u32 = ffi::HOST_VM_INFO64_COUNT; let mut stat = mem::zeroed::(); if ffi::host_statistics64( self.port, ffi::HOST_VM_INFO64, &mut stat as *mut ffi::vm_statistics64 as *mut c_void, &count, ) == ffi::KERN_SUCCESS { // From the apple documentation: // // /* // * NB: speculative pages are already accounted for in "free_count", // * so "speculative_count" is the number of "free" pages that are // * used to hold data that was read speculatively from disk but // * haven't actually been used by anyone so far. // */ // self.mem_free = u64::from(stat.free_count) * self.page_size_kb; self.mem_free = self.mem_total - (u64::from(stat.active_count) + u64::from(stat.inactive_count) + u64::from(stat.wire_count) + u64::from(stat.speculative_count) - u64::from(stat.purgeable_count)) * self.page_size_kb; } } } fn refresh_components_list(&mut self) { if let Some(con) = self.connection { self.components.clear(); // getting CPU critical temperature let critical_temp = crate::mac::component::get_temperature( con, &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0], ); for (id, v) in crate::mac::component::COMPONENTS_TEMPERATURE_IDS.iter() { if let Some(c) = Component::new((*id).to_owned(), None, critical_temp, v, con) { self.components.push(c); } } } } fn refresh_cpu(&mut self) { // get processor values let mut num_cpu_u = 0u32; let mut cpu_info: *mut i32 = ::std::ptr::null_mut(); let mut num_cpu_info = 0u32; let mut pourcent = 0f32; unsafe { if ffi::host_processor_info( self.port, ffi::PROCESSOR_CPU_LOAD_INFO, &mut num_cpu_u as *mut u32, &mut cpu_info as *mut *mut i32, &mut num_cpu_info as *mut u32, ) == ffi::KERN_SUCCESS { let proc_data = Arc::new(ProcessorData::new(cpu_info, num_cpu_info)); for (i, proc_) in self.processors.iter_mut().enumerate() { let old_proc_data = &*proc_.get_data(); let in_use = (*cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize, ) - *old_proc_data.cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_USER as isize, )) + (*cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize, ) - *old_proc_data.cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_SYSTEM as isize, )) + (*cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize, ) - *old_proc_data.cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_NICE as isize, )); let total = in_use + (*cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize, ) - *old_proc_data.cpu_info.offset( (ffi::CPU_STATE_MAX * i) as isize + ffi::CPU_STATE_IDLE as isize, )); proc_.update(in_use as f32 / total as f32 * 100., Arc::clone(&proc_data)); pourcent += proc_.get_cpu_usage(); } } } self.global_processor .set_cpu_usage(pourcent / self.processors.len() as f32); } fn refresh_processes(&mut self) { let count = unsafe { ffi::proc_listallpids(::std::ptr::null_mut(), 0) }; if count < 1 { return; } if let Some(pids) = get_proc_list() { let arg_max = get_arg_max(); let entries: Vec = { let wrap = &Wrap(UnsafeCell::new(&mut self.process_list)); pids.par_iter() .flat_map(|pid| match update_process(wrap, *pid, arg_max as size_t) { Ok(x) => x, Err(_) => None, }) .collect() }; entries.into_iter().for_each(|entry| { self.process_list.insert(entry.pid(), entry); }); self.clear_procs(); } } fn refresh_process(&mut self, pid: Pid) -> bool { let arg_max = get_arg_max(); match { let wrap = Wrap(UnsafeCell::new(&mut self.process_list)); update_process(&wrap, pid, arg_max as size_t) } { Ok(Some(p)) => { self.process_list.insert(p.pid(), p); true } Ok(_) => true, Err(_) => false, } } fn refresh_disks_list(&mut self) { self.disks = crate::mac::disk::get_disks(); } fn refresh_users_list(&mut self) { self.users = crate::mac::users::get_users_list(); } // COMMON PART // // Need to be moved into a "common" file to avoid duplication. fn get_processes(&self) -> &HashMap { &self.process_list } fn get_process(&self, pid: Pid) -> Option<&Process> { self.process_list.get(&pid) } fn get_global_processor_info(&self) -> &Processor { &self.global_processor } fn get_processors(&self) -> &[Processor] { &self.processors } fn get_networks(&self) -> &Networks { &self.networks } fn get_networks_mut(&mut self) -> &mut Networks { &mut self.networks } fn get_total_memory(&self) -> u64 { self.mem_total } fn get_free_memory(&self) -> u64 { self.mem_free } fn get_used_memory(&self) -> u64 { self.mem_total - self.mem_free } fn get_total_swap(&self) -> u64 { self.swap_total } fn get_free_swap(&self) -> u64 { self.swap_free } // need to be checked fn get_used_swap(&self) -> u64 { self.swap_total - self.swap_free } fn get_components(&self) -> &[Component] { &self.components } fn get_components_mut(&mut self) -> &mut [Component] { &mut self.components } fn get_disks(&self) -> &[Disk] { &self.disks } fn get_disks_mut(&mut self) -> &mut [Disk] { &mut self.disks } fn get_uptime(&self) -> u64 { let csec = unsafe { libc::time(::std::ptr::null_mut()) }; unsafe { libc::difftime(csec, self.boot_time as _) as u64 } } fn get_load_average(&self) -> LoadAvg { let loads = vec![0f64; 3]; unsafe { ffi::getloadavg(loads.as_ptr() as *const f64, 3); } LoadAvg { one: loads[0], five: loads[1], fifteen: loads[2], } } fn get_users(&self) -> &[User] { &self.users } fn get_boot_time(&self) -> u64 { self.boot_time } } impl Default for System { fn default() -> System { System::new() } } // code from https://github.com/Chris911/iStats fn get_io_service_connection() -> Option { let mut master_port: ffi::mach_port_t = 0; let mut iterator: ffi::io_iterator_t = 0; unsafe { ffi::IOMasterPort(ffi::MACH_PORT_NULL, &mut master_port); let matching_dictionary = ffi::IOServiceMatching(b"AppleSMC\0".as_ptr() as *const i8); let result = ffi::IOServiceGetMatchingServices(master_port, matching_dictionary, &mut iterator); if result != ffi::KIO_RETURN_SUCCESS { sysinfo_debug!("Error: IOServiceGetMatchingServices() = {}", result); return None; } let device = ffi::IOIteratorNext(iterator); ffi::IOObjectRelease(iterator); if device == 0 { sysinfo_debug!("Error: no SMC found"); return None; } let mut conn = 0; let result = ffi::IOServiceOpen(device, ffi::mach_task_self(), 0, &mut conn); ffi::IOObjectRelease(device); if result != ffi::KIO_RETURN_SUCCESS { sysinfo_debug!("Error: IOServiceOpen() = {}", result); return None; } Some(conn) } } fn get_arg_max() -> usize { let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0]; let mut arg_max = 0i32; let mut size = mem::size_of::(); unsafe { if libc::sysctl( mib.as_mut_ptr(), 2, (&mut arg_max) as *mut i32 as *mut c_void, &mut size, ::std::ptr::null_mut(), 0, ) == -1 { 4096 // We default to this value } else { arg_max as usize } } } pub(crate) unsafe fn get_sys_value( high: u32, low: u32, mut len: usize, value: *mut libc::c_void, mib: &mut [i32; 2], ) -> bool { mib[0] = high as i32; mib[1] = low as i32; libc::sysctl( mib.as_mut_ptr(), 2, value, &mut len as *mut usize, ::std::ptr::null_mut(), 0, ) == 0 } sysinfo-0.13.2/src/mac/users.rs010064400017500001750000000123241363020457200145520ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // use crate::User; use libc::{c_char, endpwent, getgrgid, getgrouplist, getpwent, gid_t, setpwent, strlen}; fn cstr_to_rust(c: *const c_char) -> Option { let mut s = Vec::new(); let mut i = 0; loop { let value = unsafe { *c.offset(i) } as u8; if value == 0 { break; } s.push(value); i += 1; } String::from_utf8(s).ok() } fn get_user_groups(name: *const c_char, group_id: gid_t) -> Vec { let mut add = 0; loop { let mut nb_groups = 256 + add; let mut groups = Vec::with_capacity(nb_groups as _); if unsafe { getgrouplist(name, group_id as _, groups.as_mut_ptr(), &mut nb_groups) } == -1 { add += 100; continue; } unsafe { groups.set_len(nb_groups as _); } return groups .into_iter() .filter_map(|g| { let group = unsafe { getgrgid(g as _) }; if group.is_null() { return None; } cstr_to_rust(unsafe { (*group).gr_name }) }) .collect(); } } fn endswith(s1: *const c_char, s2: &[u8]) -> bool { if s1.is_null() { return false; } let mut len = unsafe { strlen(s1) } as isize - 1; let mut i = s2.len() as isize - 1; while len >= 0 && i >= 0 && unsafe { *s1.offset(len) } == s2[i as usize] as _ { i -= 1; len -= 1; } i == -1 } fn users_list(filter: F) -> Vec where F: Fn(*const c_char) -> bool, { let mut users = Vec::new(); unsafe { setpwent() }; loop { let pw = unsafe { getpwent() }; if pw.is_null() { break; } if !filter(unsafe { (*pw).pw_shell }) { // This is not a "real" user. continue; } let groups = get_user_groups(unsafe { (*pw).pw_name }, unsafe { (*pw).pw_gid }); if let Some(name) = cstr_to_rust(unsafe { (*pw).pw_name }) { users.push(User { name, groups }); } } unsafe { endpwent() }; users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap()); users.dedup_by(|a, b| a.name == b.name); users } pub fn get_users_list() -> Vec { users_list(|shell| !endswith(shell, b"/false") && !endswith(shell, b"/uucico")) } // This was the OSX-based solution. It provides enough information, but what a mess! // pub fn get_users_list() -> Vec { // let mut users = Vec::new(); // let node_name = b"/Local/Default\0"; // unsafe { // let node_name = ffi::CFStringCreateWithCStringNoCopy( // ::std::ptr::null_mut(), // node_name.as_ptr() as *const c_char, // ffi::kCFStringEncodingMacRoman, // ffi::kCFAllocatorNull as *mut c_void, // ); // let node_ref = ffi::ODNodeCreateWithName( // ffi::kCFAllocatorDefault, // ffi::kODSessionDefault, // node_name, // ::std::ptr::null_mut(), // ); // let query = ffi::ODQueryCreateWithNode( // ffi::kCFAllocatorDefault, // node_ref, // ffi::kODRecordTypeUsers as _, // kODRecordTypeGroups // ::std::ptr::null(), // 0, // ::std::ptr::null(), // ::std::ptr::null(), // 0, // ::std::ptr::null_mut(), // ); // if query.is_null() { // return users; // } // let results = ffi::ODQueryCopyResults( // query, // false as _, // ::std::ptr::null_mut(), // ); // let len = ffi::CFArrayGetCount(results); // for i in 0..len { // let name = match get_user_name(ffi::CFArrayGetValueAtIndex(results, i)) { // Some(n) => n, // None => continue, // }; // let groups = get_user_groups(&name); // users.push(User { name }); // } // ffi::CFRelease(results as *const c_void); // ffi::CFRelease(query as *const c_void); // ffi::CFRelease(node_ref as *const c_void); // ffi::CFRelease(node_name as *const c_void); // } // users.sort_unstable_by(|x, y| x.name.partial_cmp(&y.name).unwrap()); // return users; // } // fn get_user_name(result: *const c_void) -> Option { // let user_name = ffi::ODRecordGetRecordName(result as _); // let ptr = ffi::CFStringGetCharactersPtr(user_name); // String::from_utf16(&if ptr.is_null() { // let len = ffi::CFStringGetLength(user_name); // It returns the len in UTF-16 code pairs. // if len == 0 { // continue; // } // let mut v = Vec::with_capacity(len as _); // for x in 0..len { // v.push(ffi::CFStringGetCharacterAtIndex(user_name, x)); // } // v // } else { // let mut v: Vec = Vec::new(); // let mut x = 0; // loop { // let letter = *ptr.offset(x); // if letter == 0 { // break; // } // v.push(letter); // x += 1; // } // v // }.ok() // } sysinfo-0.13.2/src/sysinfo.h010066400017500001750000000037361353143751600141650ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // #pragma once #include #include typedef void* CSystem; typedef const void* CProcess; typedef const char* RString; CSystem *sysinfo_init(); void sysinfo_destroy(CSystem system); void sysinfo_refresh_system(CSystem system); void sysinfo_refresh_all(CSystem system); void sysinfo_refresh_processes(CSystem system); #ifdef __linux__ void sysinfo_refresh_process(CSystem system, pid_t pid); #endif void sysinfo_refresh_disks(CSystem system); void sysinfo_refresh_disk_list(CSystem system); size_t sysinfo_get_total_memory(CSystem system); size_t sysinfo_get_free_memory(CSystem system); size_t sysinfo_get_used_memory(CSystem system); size_t sysinfo_get_total_swap(CSystem system); size_t sysinfo_get_free_swap(CSystem system); size_t sysinfo_get_used_swap(CSystem system); size_t sysinfo_get_network_income(CSystem system); size_t sysinfo_get_network_outcome(CSystem system); void sysinfo_get_processors_usage(CSystem system, unsigned int *length, float **procs); size_t sysinfo_get_processes(CSystem system, bool (*fn_pointer)(pid_t, CProcess, void*), void *data); #ifdef __linux__ size_t sysinfo_process_get_tasks(CProcess process, bool (*fn_pointer)(pid_t, CProcess, void*), void *data); #endif CProcess sysinfo_get_process_by_pid(CSystem system, pid_t pid); pid_t sysinfo_process_get_pid(CProcess process); pid_t sysinfo_process_get_parent_pid(CProcess process); float sysinfo_process_get_cpu_usage(CProcess process); size_t sysinfo_process_get_memory(CProcess process); RString sysinfo_process_get_executable_path(CProcess process); RString sysinfo_process_get_root_directory(CProcess process); RString sysinfo_process_get_current_directory(CProcess process); void sysinfo_rstring_free(RString str); sysinfo-0.13.2/src/sysinfo.rs010064400017500001750000000131201364344617000143440ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // //! `sysinfo` is a crate used to get a system's information. //! //! Before any attempt to read the different structs' information, you need to update them to //! get up-to-date information. //! //! # Examples //! //! ``` //! use sysinfo::{ProcessExt, SystemExt}; //! //! let mut system = sysinfo::System::new_all(); //! //! // First we update all information of our system struct. //! system.refresh_all(); //! //! // Now let's print every process' id and name: //! for (pid, proc_) in system.get_processes() { //! println!("{}:{} => status: {:?}", pid, proc_.name(), proc_.status()); //! } //! //! // Then let's print the temperature of the different components: //! for component in system.get_components() { //! println!("{:?}", component); //! } //! //! // And then all disks' information: //! for disk in system.get_disks() { //! println!("{:?}", disk); //! } //! //! // And finally the RAM and SWAP information: //! println!("total memory: {} KiB", system.get_total_memory()); //! println!("used memory : {} KiB", system.get_used_memory()); //! println!("total swap : {} KiB", system.get_total_swap()); //! println!("used swap : {} KiB", system.get_used_swap()); //! ``` #![crate_name = "sysinfo"] #![crate_type = "lib"] #![crate_type = "rlib"] #![deny(missing_docs)] #![deny(intra_doc_link_resolution_failure)] //#![deny(warnings)] #![allow(unknown_lints)] #[macro_use] extern crate cfg_if; #[cfg(not(any(target_os = "unknown", target_arch = "wasm32")))] extern crate libc; extern crate rayon; #[macro_use] extern crate doc_comment; #[cfg(doctest)] doctest!("../README.md"); #[cfg(feature = "debug")] #[doc(hidden)] macro_rules! sysinfo_debug { ($($x:tt)*) => {{ eprintln!($($x)*); }} } #[cfg(not(feature = "debug"))] #[doc(hidden)] macro_rules! sysinfo_debug { ($($x:tt)*) => {{}}; } cfg_if! { if #[cfg(target_os = "macos")] { mod mac; use mac as sys; #[cfg(test)] const MIN_USERS: usize = 1; } else if #[cfg(windows)] { mod windows; use windows as sys; extern crate winapi; extern crate ntapi; #[cfg(test)] const MIN_USERS: usize = 1; } else if #[cfg(unix)] { mod linux; use linux as sys; #[cfg(test)] const MIN_USERS: usize = 1; } else { mod unknown; use unknown as sys; #[cfg(test)] const MIN_USERS: usize = 0; } } pub use common::{ AsU32, DiskType, DiskUsage, LoadAvg, NetworksIter, Pid, RefreshKind, Signal, User, }; pub use sys::{Component, Disk, NetworkData, Networks, Process, ProcessStatus, Processor, System}; pub use traits::{ ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, ProcessorExt, SystemExt, UserExt, }; #[cfg(feature = "c-interface")] pub use c_interface::*; pub use utils::get_current_pid; #[cfg(feature = "c-interface")] mod c_interface; mod common; mod debug; mod system; mod traits; mod utils; /// This function is only used on linux targets, on the other platforms it does nothing and returns /// `false`. /// /// On linux, to improve performance, we keep a `/proc` file open for each process we index with /// a maximum number of files open equivalent to half of the system limit. /// /// The problem is that some users might need all the available file descriptors so we need to /// allow them to change this limit. /// /// Note that if you set a limit bigger than the system limit, the system limit will be set. /// /// Returns `true` if the new value has been set. /// /// ```no_run /// use sysinfo::{System, SystemExt, set_open_files_limit}; /// /// // We call the function before any call to the processes update. /// if !set_open_files_limit(10) { /// // It'll always return false on non-linux targets. /// eprintln!("failed to update the open files limit..."); /// } /// let s = System::new_all(); /// ``` pub fn set_open_files_limit(mut _new_limit: isize) -> bool { #[cfg(all(not(target_os = "macos"), unix))] { if _new_limit < 0 { _new_limit = 0; } let max = sys::system::get_max_nb_fds(); if _new_limit > max { _new_limit = max; } if let Ok(ref mut x) = unsafe { sys::system::REMAINING_FILES.lock() } { // If files are already open, to be sure that the number won't be bigger when those // files are closed, we subtract the current number of opened files to the new limit. let diff = max - **x; **x = _new_limit - diff; true } else { false } } #[cfg(any(not(unix), target_os = "macos"))] { false } } #[cfg(test)] mod test { use super::*; #[test] fn check_memory_usage() { let mut s = ::System::new(); s.refresh_all(); assert_eq!( s.get_processes() .iter() .all(|(_, proc_)| proc_.memory() == 0), false ); } #[test] fn check_users() { let mut s = ::System::new(); assert!(s.get_users().is_empty()); s.refresh_users_list(); assert!(s.get_users().len() >= MIN_USERS); let mut s = ::System::new(); assert!(s.get_users().is_empty()); s.refresh_all(); assert!(s.get_users().is_empty()); let s = ::System::new_all(); assert!(s.get_users().len() >= MIN_USERS); } } // Used to check that System is Send and Sync. #[cfg(doctest)] doc_comment!( " ``` fn is_send() {} is_send::(); ``` ``` fn is_sync() {} is_sync::(); ```" ); sysinfo-0.13.2/src/system.rs010064400017500001750000000040341364344617000142020ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // // Once https://github.com/rust-lang/rfcs/blob/master/text/1422-pub-restricted.md // feature gets stabilized, we can move common parts in here. #[cfg(test)] mod tests { use utils; use {ProcessExt, System, SystemExt}; #[test] fn test_refresh_system() { let mut sys = System::new(); sys.refresh_system(); assert!(sys.get_total_memory() != 0); assert!(sys.get_free_memory() != 0); assert!(sys.get_total_memory() >= sys.get_free_memory()); assert!(sys.get_total_swap() >= sys.get_free_swap()); } #[test] fn test_refresh_process() { let mut sys = System::new(); assert!( sys.get_processes().is_empty(), "no process should be listed!" ); sys.refresh_processes(); assert!( sys.refresh_process(utils::get_current_pid().expect("failed to get current pid")), "process not listed", ); } #[test] fn test_get_process() { let mut sys = System::new(); sys.refresh_processes(); let p = sys .get_process(utils::get_current_pid().expect("failed to get current pid")) .expect("didn't find process"); assert!(p.memory() > 0); } #[test] fn check_if_send_and_sync() { trait Foo { fn foo(&self) {} } impl Foo for T where T: Send {} trait Bar { fn bar(&self) {} } impl Bar for T where T: Sync {} let mut sys = System::new(); sys.refresh_processes(); let p = sys .get_process(utils::get_current_pid().expect("failed to get current pid")) .expect("didn't find process"); p.foo(); // If this doesn't compile, it'll simply mean that the Process type // doesn't implement the Send trait. p.bar(); // If this doesn't compile, it'll simply mean that the Process type // doesn't implement the Sync trait. } } sysinfo-0.13.2/src/traits.rs010064400017500001750000001036131364506453400141710ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use sys::{Component, Disk, Networks, Process, Processor}; use DiskType; use DiskUsage; use LoadAvg; use NetworksIter; use Pid; use ProcessStatus; use RefreshKind; use User; use std::collections::HashMap; use std::ffi::OsStr; use std::fmt::Debug; use std::path::Path; /// Contains all the methods of the [`Disk`][crate::Disk] struct. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{:?}: {:?}", disk.get_name(), disk.get_type()); /// } /// ``` pub trait DiskExt: Debug { /// Returns the disk type. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{:?}", disk.get_type()); /// } /// ``` fn get_type(&self) -> DiskType; /// Returns the disk name. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{:?}", disk.get_name()); /// } /// ``` fn get_name(&self) -> &OsStr; /// Returns the file system used on this disk (so for example: `EXT4`, `NTFS`, etc...). /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{:?}", disk.get_file_system()); /// } /// ``` fn get_file_system(&self) -> &[u8]; /// Returns the mount point of the disk (`/` for example). /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{:?}", disk.get_mount_point()); /// } /// ``` fn get_mount_point(&self) -> &Path; /// Returns the total disk size, in bytes. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{}", disk.get_total_space()); /// } /// ``` fn get_total_space(&self) -> u64; /// Returns the available disk size, in bytes. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new(); /// for disk in s.get_disks() { /// println!("{}", disk.get_available_space()); /// } /// ``` fn get_available_space(&self) -> u64; /// Updates the disk' information. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// for disk in s.get_disks_mut() { /// disk.refresh(); /// } /// ``` fn refresh(&mut self) -> bool; } /// Contains all the methods of the [`Process`][crate::Process] struct. pub trait ProcessExt: Debug { /// Creates a new process only containing the given information. /// /// On windows, the `start_time` argument is ignored. #[doc(hidden)] fn new(pid: Pid, parent: Option, start_time: u64) -> Self; /// Sends the given `signal` to the process. /// /// ```no_run /// use sysinfo::{ProcessExt, Signal, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// process.kill(Signal::Kill); /// } /// ``` fn kill(&self, signal: ::Signal) -> bool; /// Returns the name of the process. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.name()); /// } /// ``` fn name(&self) -> &str; /// Returns the command line. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{:?}", process.cmd()); /// } /// ``` fn cmd(&self) -> &[String]; /// Returns the path to the process. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.exe().display()); /// } /// ``` fn exe(&self) -> &Path; /// Returns the pid of the process. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.pid()); /// } /// ``` fn pid(&self) -> Pid; /// Returns the environment of the process. /// /// Always empty on Windows, except for current process. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{:?}", process.environ()); /// } /// ``` fn environ(&self) -> &[String]; /// Returns the current working directory. /// /// Always empty on Windows. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.cwd().display()); /// } /// ``` fn cwd(&self) -> &Path; /// Returns the path of the root directory. /// /// Always empty on Windows. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.root().display()); /// } /// ``` fn root(&self) -> &Path; /// Returns the memory usage (in KiB). /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{} KiB", process.memory()); /// } /// ``` fn memory(&self) -> u64; /// Returns the virtual memory usage (in KiB). /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{} KiB", process.virtual_memory()); /// } /// ``` fn virtual_memory(&self) -> u64; /// Returns the parent pid. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{:?}", process.parent()); /// } /// ``` fn parent(&self) -> Option; /// Returns the status of the processus. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{:?}", process.status()); /// } /// ``` fn status(&self) -> ProcessStatus; /// Returns the time of process launch (in seconds). /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("Running since {} seconds", process.start_time()); /// } /// ``` fn start_time(&self) -> u64; /// Returns the total CPU usage (in %). /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// println!("{}%", process.cpu_usage()); /// } /// ``` fn cpu_usage(&self) -> f32; /// Returns number of bytes read and written to disk. /// /// /!\\ On Windows, this method actually returns **ALL** I/O read and written bytes. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new(); /// if let Some(process) = s.get_process(1337) { /// let disk_usage = process.disk_usage(); /// println!("read bytes : new/total => {}/{}", /// disk_usage.read_bytes, /// disk_usage.total_read_bytes, /// ); /// println!("written bytes: new/total => {}/{}", /// disk_usage.written_bytes, /// disk_usage.total_written_bytes, /// ); /// } /// ``` fn disk_usage(&self) -> DiskUsage; } /// Contains all the methods of the [`Processor`][crate::Processor] struct. pub trait ProcessorExt: Debug { /// Returns this processor's usage. /// /// Note: You'll need to refresh it at least twice (diff between the first and the second is /// how CPU usage is computed) at first if you want to have a non-zero value. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}%", processor.get_cpu_usage()); /// } /// ``` fn get_cpu_usage(&self) -> f32; /// Returns this processor's name. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}", processor.get_name()); /// } /// ``` fn get_name(&self) -> &str; /// Returns the processor's vendor id. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}", processor.get_vendor_id()); /// } /// ``` fn get_vendor_id(&self) -> &str; /// Returns the processor's brand. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}", processor.get_brand()); /// } /// ``` fn get_brand(&self) -> &str; /// Returns the processor's frequency. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}", processor.get_frequency()); /// } /// ``` fn get_frequency(&self) -> u64; } /// Contains all the methods of the [`System`][crate::System] type. pub trait SystemExt: Sized + Debug + Default { /// Creates a new [`System`] instance with nothing loaded except the processors list. If you /// want to load components, network interfaces or the disks, you'll have to use the /// `refresh_*_list` methods. [`SystemExt::refresh_networks_list`] for example. /// /// Use the [`refresh_all`] method to update its internal information (or any of the `refresh_` /// method). /// /// [`System`]: crate::System /// [`refresh_all`]: #method.refresh_all /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new(); /// ``` fn new() -> Self { Self::new_with_specifics(RefreshKind::new()) } /// Creates a new [`System`] instance with everything loaded. /// /// It is an equivalent of [`SystemExt::new_with_specifics`]`(`[`RefreshKind::everything`]`())`. /// /// [`System`]: crate::System /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// ``` fn new_all() -> Self { Self::new_with_specifics(RefreshKind::everything()) } /// Creates a new [`System`] instance and refresh the data corresponding to the /// given [`RefreshKind`]. /// /// [`System`]: crate::System /// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// /// // We want everything except disks. /// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list()); /// /// assert_eq!(system.get_disks().len(), 0); /// assert!(system.get_processes().len() > 0); /// /// // If you want the disks list afterwards, just call the corresponding /// // "refresh_disks_list": /// system.refresh_disks_list(); /// let disks = system.get_disks(); /// ``` fn new_with_specifics(refreshes: RefreshKind) -> Self; /// Refreshes according to the given [`RefreshKind`]. It calls the corresponding /// "refresh_" methods. /// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// /// let mut s = System::new_all(); /// /// // Let's just update networks and processes: /// s.refresh_specifics(RefreshKind::new().with_networks().with_processes()); /// ``` fn refresh_specifics(&mut self, refreshes: RefreshKind) { if refreshes.memory() { self.refresh_memory(); } if refreshes.cpu() { self.refresh_cpu(); } if refreshes.components_list() { self.refresh_components_list(); } else if refreshes.components() { self.refresh_components(); } if refreshes.networks_list() { self.refresh_networks_list(); } else if refreshes.networks() { self.refresh_networks(); } if refreshes.processes() { self.refresh_processes(); } if refreshes.disks_list() { self.refresh_disks_list(); } else if refreshes.disks() { self.refresh_disks(); } if refreshes.users_list() { self.refresh_users_list(); } } /// Refreshes system information (RAM, swap, CPU usage and components' temperature). /// /// If you want some more specific refreshes, you might be interested into looking at /// [`refresh_memory`], [`refresh_cpu`] and [`refresh_components`]. /// /// [`refresh_memory`]: SystemExt::refresh_memory /// [`refresh_cpu`]: SystemExt::refresh_memory /// [`refresh_components`]: SystemExt::refresh_components /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_system(); /// ``` fn refresh_system(&mut self) { self.refresh_memory(); self.refresh_cpu(); self.refresh_components(); } /// Refreshes RAM and SWAP usage. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_memory(); /// ``` fn refresh_memory(&mut self); /// Refreshes CPU usage. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_cpu(); /// ``` fn refresh_cpu(&mut self); /// Refreshes components' temperature. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_components(); /// ``` fn refresh_components(&mut self) { for component in self.get_components_mut() { component.refresh(); } } /// Refreshes components list. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new(); /// s.refresh_components_list(); /// ``` fn refresh_components_list(&mut self); /// Gets all processes and updates their information. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_processes(); /// ``` fn refresh_processes(&mut self); /// Refreshes *only* the process corresponding to `pid`. Returns `false` if the process doesn't /// exist. If it isn't listed yet, it'll be added. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_process(1337); /// ``` fn refresh_process(&mut self, pid: Pid) -> bool; /// Refreshes the listed disks' information. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_disks(); /// ``` fn refresh_disks(&mut self) { for disk in self.get_disks_mut() { disk.refresh(); } } /// The disk list will be emptied then completely recomputed. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_disks_list(); /// ``` fn refresh_disks_list(&mut self); /// Refreshes users list. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_users_list(); /// ``` fn refresh_users_list(&mut self); /// Refreshes networks data. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_networks(); /// ``` /// /// It is a shortcut for: /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// let networks = s.get_networks_mut(); /// networks.refresh(); /// ``` fn refresh_networks(&mut self) { self.get_networks_mut().refresh(); } /// The network list will be updated: removing not existing anymore interfaces and adding new /// ones. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_networks_list(); /// ``` /// /// This is a shortcut for: /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// let networks = s.get_networks_mut(); /// networks.refresh_networks_list(); /// ``` fn refresh_networks_list(&mut self) { self.get_networks_mut().refresh_networks_list(); } /// Refreshes all system, processes, disks and network interfaces information. /// /// Please note that it doesn't recompute disks list, components list, network interfaces /// list nor users list. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let mut s = System::new_all(); /// s.refresh_all(); /// ``` fn refresh_all(&mut self) { self.refresh_system(); self.refresh_processes(); self.refresh_disks(); self.refresh_networks(); } /// Returns the process list. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new_all(); /// for (pid, process) in s.get_processes() { /// println!("{} {}", pid, process.name()); /// } /// ``` fn get_processes(&self) -> &HashMap; /// Returns the process corresponding to the given pid or `None` if no such process exists. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new_all(); /// if let Some(process) = s.get_process(1337) { /// println!("{}", process.name()); /// } /// ``` fn get_process(&self, pid: Pid) -> Option<&Process>; /// Returns a list of process containing the given `name`. /// /// ```no_run /// use sysinfo::{ProcessExt, System, SystemExt}; /// /// let s = System::new_all(); /// for process in s.get_process_by_name("htop") { /// println!("{} {}", process.pid(), process.name()); /// } /// ``` fn get_process_by_name(&self, name: &str) -> Vec<&Process> { let mut ret = vec![]; for val in self.get_processes().values() { if val.name().contains(name) { ret.push(val); } } ret } /// Returns "global" processors information (aka the addition of all the processors). /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// println!("{}%", s.get_global_processor_info().get_cpu_usage()); /// ``` fn get_global_processor_info(&self) -> &Processor; /// Returns the list of the processors. /// /// ```no_run /// use sysinfo::{ProcessorExt, System, SystemExt}; /// /// let s = System::new(); /// for processor in s.get_processors() { /// println!("{}%", processor.get_cpu_usage()); /// } /// ``` fn get_processors(&self) -> &[Processor]; /// Returns the RAM size in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_total_memory()); /// ``` fn get_total_memory(&self) -> u64; /// Returns the amount of free RAM in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_free_memory()); /// ``` fn get_free_memory(&self) -> u64; /// Returns the amound of used RAM in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_used_memory()); /// ``` fn get_used_memory(&self) -> u64; /// Returns the SWAP size in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_total_swap()); /// ``` fn get_total_swap(&self) -> u64; /// Returns the amount of free SWAP in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_free_swap()); /// ``` fn get_free_swap(&self) -> u64; /// Returns the amount of used SWAP in KiB. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("{} KiB", s.get_used_swap()); /// ``` fn get_used_swap(&self) -> u64; /// Returns the components list. /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let s = System::new_all(); /// for component in s.get_components() { /// println!("{}: {}°C", component.get_label(), component.get_temperature()); /// } /// ``` fn get_components(&self) -> &[Component]; /// Returns a mutable components list. /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// for component in s.get_components_mut() { /// component.refresh(); /// } /// ``` fn get_components_mut(&mut self) -> &mut [Component]; /// Returns the disks list. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let s = System::new_all(); /// for disk in s.get_disks() { /// println!("{:?}", disk.get_name()); /// } /// ``` fn get_disks(&self) -> &[Disk]; /// Returns the users list. /// /// ```no_run /// use sysinfo::{System, SystemExt, UserExt}; /// /// let mut s = System::new_all(); /// for user in s.get_users() { /// println!("{} is in {} groups", user.get_name(), user.get_groups().len()); /// } /// ``` fn get_users(&self) -> &[User]; /// Returns the disks list. /// /// ```no_run /// use sysinfo::{DiskExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// for disk in s.get_disks_mut() { /// disk.refresh(); /// } /// ``` fn get_disks_mut(&mut self) -> &mut [Disk]; /// Returns the network interfaces object. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, data) in networks { /// println!("[{}] in: {}, out: {}", interface_name, data.get_received(), data.get_transmitted()); /// } /// ``` fn get_networks(&self) -> &Networks; /// Returns a mutable access to network interfaces. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// let networks = s.get_networks_mut(); /// networks.refresh_networks_list(); /// ``` fn get_networks_mut(&mut self) -> &mut Networks; /// Returns system uptime (in seconds). /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// println!("System running since {} seconds", s.get_uptime()); /// ``` fn get_uptime(&self) -> u64; /// Returns the time (in seconds) when the system booted since UNIX epoch. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new(); /// println!("System booted at {} seconds", s.get_boot_time()); /// ``` fn get_boot_time(&self) -> u64; /// Returns the system load average value. /// /// ```no_run /// use sysinfo::{System, SystemExt}; /// /// let s = System::new_all(); /// let load_avg = s.get_load_average(); /// println!( /// "one minute: {}%, five minutes: {}%, fifteen minutes: {}%", /// load_avg.one, /// load_avg.five, /// load_avg.fifteen, /// ); /// ``` fn get_load_average(&self) -> LoadAvg; } /// Getting volume of received and transmitted data. pub trait NetworkExt: Debug { /// Returns the number of received bytes since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {} B", network.get_received()); /// } /// ``` fn get_received(&self) -> u64; /// Returns the total number of received bytes. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {} B", network.get_total_received()); /// } /// ``` fn get_total_received(&self) -> u64; /// Returns the number of transmitted bytes since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {} B", network.get_transmitted()); /// } /// ``` fn get_transmitted(&self) -> u64; /// Returns the total number of transmitted bytes. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {} B", network.get_total_transmitted()); /// } /// ``` fn get_total_transmitted(&self) -> u64; /// Returns the number of incoming packets since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {}", network.get_packets_received()); /// } /// ``` fn get_packets_received(&self) -> u64; /// Returns the total number of incoming packets. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {}", network.get_total_packets_received()); /// } /// ``` fn get_total_packets_received(&self) -> u64; /// Returns the number of outcoming packets since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {}", network.get_packets_transmitted()); /// } /// ``` fn get_packets_transmitted(&self) -> u64; /// Returns the total number of outcoming packets. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {}", network.get_total_packets_transmitted()); /// } /// ``` fn get_total_packets_transmitted(&self) -> u64; /// Returns the number of incoming errors since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {}", network.get_errors_on_received()); /// } /// ``` fn get_errors_on_received(&self) -> u64; /// Returns the total number of incoming errors. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {}", network.get_total_errors_on_received()); /// } /// ``` fn get_total_errors_on_received(&self) -> u64; /// Returns the number of outcoming errors since the last refresh. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {}", network.get_errors_on_transmitted()); /// } /// ``` fn get_errors_on_transmitted(&self) -> u64; /// Returns the total number of outcoming errors. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("out: {}", network.get_total_errors_on_transmitted()); /// } /// ``` fn get_total_errors_on_transmitted(&self) -> u64; } /// Interacting with network interfaces. pub trait NetworksExt: Debug { /// Returns an iterator over the network interfaces. /// /// ```no_run /// use sysinfo::{NetworkExt, NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// for (interface_name, network) in networks { /// println!("in: {} B", network.get_received()); /// } /// ``` fn iter(&self) -> NetworksIter; /// Refreshes the network interfaces list. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// let networks = s.get_networks_mut(); /// networks.refresh_networks_list(); /// ``` fn refresh_networks_list(&mut self); /// Refreshes the network interfaces' content. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// let networks = s.get_networks_mut(); /// networks.refresh(); /// ``` fn refresh(&mut self); } /// Getting a component temperature information. pub trait ComponentExt: Debug { /// Returns the temperature of the component (in celsius degree). /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let s = System::new_all(); /// for component in s.get_components() { /// println!("{}°C", component.get_temperature()); /// } /// ``` fn get_temperature(&self) -> f32; /// Returns the maximum temperature of the component (in celsius degree). /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let s = System::new_all(); /// for component in s.get_components() { /// println!("{}°C", component.get_max()); /// } /// ``` fn get_max(&self) -> f32; /// Returns the highest temperature before the component halts (in celsius degree). /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let s = System::new_all(); /// for component in s.get_components() { /// println!("{:?}°C", component.get_critical()); /// } /// ``` fn get_critical(&self) -> Option; /// Returns the label of the component. /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let s = System::new_all(); /// for component in s.get_components() { /// println!("{}", component.get_label()); /// } /// ``` fn get_label(&self) -> &str; /// Refreshes component. /// /// ```no_run /// use sysinfo::{ComponentExt, System, SystemExt}; /// /// let mut s = System::new_all(); /// for component in s.get_components_mut() { /// component.refresh(); /// } /// ``` fn refresh(&mut self); } /// Getting information for a user. /// /// It is returned from [`SystemExt::get_users`]. /// /// ```no_run /// use sysinfo::{System, SystemExt, UserExt}; /// /// let mut s = System::new_all(); /// for user in s.get_users() { /// println!("{} is in {} groups", user.get_name(), user.get_groups().len()); /// } /// ``` pub trait UserExt: Debug { /// Returns the name of the user. /// /// ```no_run /// use sysinfo::{System, SystemExt, UserExt}; /// /// let mut s = System::new_all(); /// for user in s.get_users() { /// println!("{}", user.get_name()); /// } /// ``` fn get_name(&self) -> &str; /// Returns the groups of the user. /// /// ```no_run /// use sysinfo::{System, SystemExt, UserExt}; /// /// let mut s = System::new_all(); /// for user in s.get_users() { /// println!("{} is in {:?}", user.get_name(), user.get_groups()); /// } /// ``` fn get_groups(&self) -> &[String]; } sysinfo-0.13.2/src/unknown/component.rs010064400017500001750000000006631363516610000163540ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use ComponentExt; /// Dummy struct representing a component. pub struct Component {} impl ComponentExt for Component { fn get_temperature(&self) -> f32 { 0.0 } fn get_max(&self) -> f32 { 0.0 } fn get_critical(&self) -> Option { None } fn get_label(&self) -> &str { "" } fn refresh(&mut self) {} } sysinfo-0.13.2/src/unknown/disk.rs010064400017500001750000000012131361711234300152740ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use DiskExt; use DiskType; use std::ffi::OsStr; use std::path::Path; /// Struct containing a disk information. pub struct Disk {} impl DiskExt for Disk { fn get_type(&self) -> DiskType { unreachable!() } fn get_name(&self) -> &OsStr { unreachable!() } fn get_file_system(&self) -> &[u8] { &[] } fn get_mount_point(&self) -> &Path { Path::new("") } fn get_total_space(&self) -> u64 { 0 } fn get_available_space(&self) -> u64 { 0 } fn refresh(&mut self) -> bool { true } } sysinfo-0.13.2/src/unknown/mod.rs010064400017500001750000000006031361711234300151230ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // pub mod component; pub mod disk; pub mod network; pub mod process; pub mod processor; pub mod system; pub use self::component::Component; pub use self::disk::Disk; pub use self::network::{NetworkData, Networks}; pub use self::process::{Process, ProcessStatus}; pub use self::processor::Processor; pub use self::system::System; sysinfo-0.13.2/src/unknown/network.rs010064400017500001750000000030641363516610000160410ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use std::collections::HashMap; use NetworkExt; use NetworksExt; use NetworksIter; /// Network interfaces. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// ``` pub struct Networks { interfaces: HashMap, } impl Networks { pub(crate) fn new() -> Networks { Networks { interfaces: HashMap::new(), } } } impl NetworksExt for Networks { fn iter<'a>(&'a self) -> NetworksIter<'a> { NetworksIter::new(self.interfaces.iter()) } fn refresh_networks_list(&mut self) {} fn refresh(&mut self) {} } /// Contains network information. pub struct NetworkData; impl NetworkExt for NetworkData { fn get_received(&self) -> u64 { 0 } fn get_total_received(&self) -> u64 { 0 } fn get_transmitted(&self) -> u64 { 0 } fn get_total_transmitted(&self) -> u64 { 0 } fn get_packets_received(&self) -> u64 { 0 } fn get_total_packets_received(&self) -> u64 { 0 } fn get_packets_transmitted(&self) -> u64 { 0 } fn get_total_packets_transmitted(&self) -> u64 { 0 } fn get_errors_on_received(&self) -> u64 { 0 } fn get_total_errors_on_received(&self) -> u64 { 0 } fn get_errors_on_transmitted(&self) -> u64 { 0 } fn get_total_errors_on_transmitted(&self) -> u64 { 0 } } sysinfo-0.13.2/src/unknown/process.rs010064400017500001750000000025321364506453400160360ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use std::path::Path; use DiskUsage; use Pid; use ProcessExt; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] pub struct ProcessStatus; /// Struct containing a process' information. #[derive(Clone)] pub struct Process { pid: Pid, parent: Option, } impl ProcessExt for Process { fn new(pid: Pid, parent: Option, _start_time: u64) -> Process { Process { pid, parent } } fn kill(&self, _signal: ::Signal) -> bool { false } fn name(&self) -> &str { "" } fn cmd(&self) -> &[String] { &[] } fn exe(&self) -> &Path { &Path::new("") } fn pid(&self) -> Pid { self.pid } fn environ(&self) -> &[String] { &[] } fn cwd(&self) -> &Path { &Path::new("") } fn root(&self) -> &Path { &Path::new("") } fn memory(&self) -> u64 { 0 } fn virtual_memory(&self) -> u64 { 0 } fn parent(&self) -> Option { self.parent } fn status(&self) -> ProcessStatus { ProcessStatus } fn start_time(&self) -> u64 { 0 } fn cpu_usage(&self) -> f32 { 0.0 } fn disk_usage(&self) -> DiskUsage { DiskUsage::default() } } sysinfo-0.13.2/src/unknown/processor.rs010064400017500001750000000010331363221350000163530ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use ProcessorExt; /// Dummy struct that represents a processor. pub struct Processor {} impl Processor { pub(crate) fn new() -> Processor { Processor {} } } impl ProcessorExt for Processor { fn get_cpu_usage(&self) -> f32 { 0.0 } fn get_name(&self) -> &str { "" } fn get_frequency(&self) -> u64 { 0 } fn get_vendor_id(&self) -> &str { "" } fn get_brand(&self) -> &str { "" } } sysinfo-0.13.2/src/unknown/system.rs010064400017500001750000000050311364344617000156770ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // use sys::component::Component; use sys::process::*; use sys::processor::*; use sys::Disk; use sys::Networks; use LoadAvg; use Pid; use User; use {RefreshKind, SystemExt}; use std::collections::HashMap; /// Structs containing system's information. pub struct System { processes_list: HashMap, networks: Networks, global_processor: Processor, } impl SystemExt for System { fn new_with_specifics(_: RefreshKind) -> System { System { processes_list: Default::default(), networks: Networks::new(), global_processor: Processor::new(), } } fn refresh_memory(&mut self) {} fn refresh_cpu(&mut self) {} fn refresh_components_list(&mut self) {} fn refresh_processes(&mut self) {} fn refresh_process(&mut self, _pid: Pid) -> bool { false } fn refresh_disks_list(&mut self) {} fn refresh_users_list(&mut self) {} // COMMON PART // // Need to be moved into a "common" file to avoid duplication. fn get_processes(&self) -> &HashMap { &self.processes_list } fn get_process(&self, _pid: Pid) -> Option<&Process> { None } fn get_networks(&self) -> &Networks { &self.networks } fn get_networks_mut(&mut self) -> &mut Networks { &mut self.networks } fn get_global_processor_info(&self) -> &Processor { &self.global_processor } fn get_processors(&self) -> &[Processor] { &[] } fn get_total_memory(&self) -> u64 { 0 } fn get_free_memory(&self) -> u64 { 0 } fn get_used_memory(&self) -> u64 { 0 } fn get_total_swap(&self) -> u64 { 0 } fn get_free_swap(&self) -> u64 { 0 } fn get_used_swap(&self) -> u64 { 0 } fn get_components(&self) -> &[Component] { &[] } fn get_components_mut(&mut self) -> &mut [Component] { &mut [] } fn get_disks(&self) -> &[Disk] { &[] } fn get_disks_mut(&mut self) -> &mut [Disk] { &mut [] } fn get_uptime(&self) -> u64 { 0 } fn get_boot_time(&self) -> u64 { 0 } fn get_load_average(&self) -> LoadAvg { LoadAvg { one: 0., five: 0., fifteen: 0., } } fn get_users(&self) -> &[User] { &[] } } impl Default for System { fn default() -> System { System::new() } } sysinfo-0.13.2/src/utils.rs010064400017500001750000000064011363443502400140120ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] use libc::{c_char, lstat, stat, S_IFLNK, S_IFMT}; #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] use std::ffi::OsStr; #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] use std::fs; #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] use std::os::unix::ffi::OsStrExt; #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] use std::path::{Path, PathBuf}; use Pid; #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] pub fn realpath(original: &Path) -> PathBuf { use std::mem::MaybeUninit; fn and(x: u32, y: u32) -> u32 { x & y } if let Some(original_str) = original.to_str() { let ori = Path::new(original_str); // Right now lstat on windows doesn't work quite well if cfg!(windows) { return PathBuf::from(ori); } let result = PathBuf::from(original); let mut result_s = result.to_str().unwrap_or("").as_bytes().to_vec(); result_s.push(0); let mut buf = MaybeUninit::::uninit(); let res = unsafe { lstat(result_s.as_ptr() as *const c_char, buf.as_mut_ptr()) }; let buf = unsafe { buf.assume_init() }; if res < 0 || and(buf.st_mode.into(), S_IFMT.into()) != S_IFLNK.into() { PathBuf::new() } else { match fs::read_link(&result) { Ok(f) => f, Err(_) => PathBuf::new(), } } } else { PathBuf::new() } } /* convert a path to a NUL-terminated Vec suitable for use with C functions */ #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] pub fn to_cpath(path: &Path) -> Vec { let path_os: &OsStr = path.as_ref(); let mut cpath = path_os.as_bytes().to_vec(); cpath.push(0); cpath } /// Returns the pid for the current process. /// /// `Err` is returned in case the platform isn't supported. /// /// ```no_run /// use sysinfo::get_current_pid; /// /// match get_current_pid() { /// Ok(pid) => { /// println!("current pid: {}", pid); /// } /// Err(e) => { /// eprintln!("failed to get current pid: {}", e); /// } /// } /// ``` pub fn get_current_pid() -> Result { cfg_if! { if #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] { fn inner() -> Result { unsafe { Ok(::libc::getpid()) } } } else if #[cfg(target_os = "windows")] { fn inner() -> Result { use winapi::um::processthreadsapi::GetCurrentProcessId; unsafe { Ok(GetCurrentProcessId() as Pid) } } } else if #[cfg(target_os = "unknown")] { fn inner() -> Result { Err("Unavailable on this platform") } } else { fn inner() -> Result { Err("Unknown platform") } } } inner() } sysinfo-0.13.2/src/windows/component.rs010064400017500001750000000256141363516610000163520ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use std::ptr::null_mut; use winapi::shared::rpcdce::{ RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, }; use winapi::shared::winerror::{FAILED, SUCCEEDED}; use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER; use winapi::um::combaseapi::{ CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize, }; use winapi::um::oaidl::VARIANT; use winapi::um::objidl::EOAC_NONE; use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear}; use winapi::um::wbemcli::{ CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator, IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY, }; use ComponentExt; /// Struct containing a component information (temperature and name for the moment). /// /// Please note that on Windows, you need to have Administrator priviledges to get this /// information. pub struct Component { temperature: f32, max: f32, critical: Option, label: String, connection: Option, } impl Component { /// Creates a new `Component` with the given information. fn new() -> Option { match Connection::new() .and_then(|x| x.initialize_security()) .and_then(|x| x.create_instance()) .and_then(|x| x.connect_server()) .and_then(|x| x.set_proxy_blanket()) .and_then(|x| x.exec_query()) { Some(mut c) => match c.get_temperature(true) { Some((temperature, critical)) => Some(Component { temperature, label: "Computer".to_owned(), max: temperature, critical, connection: Some(c), }), None => None, }, None => None, } } } impl ComponentExt for Component { fn get_temperature(&self) -> f32 { self.temperature } fn get_max(&self) -> f32 { self.max } fn get_critical(&self) -> Option { self.critical } fn get_label(&self) -> &str { &self.label } fn refresh(&mut self) { if self.connection.is_none() { self.connection = Connection::new() .and_then(|x| x.initialize_security()) .and_then(|x| x.create_instance()) .and_then(|x| x.connect_server()) .and_then(|x| x.set_proxy_blanket()); } self.connection = if let Some(x) = self.connection.take() { x.exec_query() } else { None }; if let Some(ref mut connection) = self.connection { if let Some((temperature, _)) = connection.get_temperature(false) { self.temperature = temperature; if self.temperature > self.max { self.max = self.temperature; } } } } } pub fn get_components() -> Vec { match Component::new() { Some(c) => vec![c], None => Vec::new(), } } struct Instance(*mut IWbemLocator); impl Drop for Instance { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } struct ServerConnection(*mut IWbemServices); impl Drop for ServerConnection { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } struct Enumerator(*mut IEnumWbemClassObject); impl Drop for Enumerator { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } macro_rules! bstr { ($($x:expr),*) => {{ let x: &[u16] = &[$($x as u16),*, 0]; SysAllocString(x.as_ptr()) }} } struct Connection { instance: Option, server_connection: Option, enumerator: Option, } unsafe impl Send for Connection {} unsafe impl Sync for Connection {} impl Connection { fn new() -> Option { // "Funnily", this function returns ok, false or "this function has already been called". // So whatever, let's just ignore whatever it might return then! unsafe { CoInitializeEx(null_mut(), 0) }; Some(Connection { instance: None, server_connection: None, enumerator: None, }) } fn initialize_security(self) -> Option { if FAILED(unsafe { CoInitializeSecurity( null_mut(), -1, null_mut(), null_mut(), RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, null_mut(), EOAC_NONE, null_mut(), ) }) { None } else { Some(self) } } fn create_instance(mut self) -> Option { let mut p_loc = null_mut(); if FAILED(unsafe { CoCreateInstance( &CLSID_WbemLocator as *const _, null_mut(), CLSCTX_INPROC_SERVER, &IID_IWbemLocator as *const _, &mut p_loc as *mut _ as *mut _, ) }) { None } else { self.instance = Some(Instance(p_loc)); Some(self) } } fn connect_server(mut self) -> Option { let mut p_svc = null_mut(); if let Some(ref instance) = self.instance { unsafe { // "root\WMI" let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I'); let res = (*instance.0).ConnectServer( s, null_mut(), null_mut(), null_mut(), 0, null_mut(), null_mut(), &mut p_svc as *mut _, ); SysFreeString(s); if FAILED(res) { return None; } } } else { return None; } self.server_connection = Some(ServerConnection(p_svc)); Some(self) } fn set_proxy_blanket(self) -> Option { if let Some(ref server_connection) = self.server_connection { unsafe { if FAILED(CoSetProxyBlanket( server_connection.0 as *mut _, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, null_mut(), RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, null_mut(), EOAC_NONE, )) { return None; } } } else { return None; } Some(self) } fn exec_query(mut self) -> Option { let mut p_enumerator = null_mut(); if let Some(ref server_connection) = self.server_connection { unsafe { // "WQL" let s = bstr!('W', 'Q', 'L'); // query kind // "SELECT * FROM MSAcpi_ThermalZoneTemperature" let query = bstr!( 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S', 'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e' ); let hres = (*server_connection.0).ExecQuery( s, query, (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _, null_mut(), &mut p_enumerator as *mut _, ); SysFreeString(s); SysFreeString(query); if FAILED(hres) { return None; } } } else { return None; } self.enumerator = Some(Enumerator(p_enumerator)); Some(self) } fn get_temperature(&mut self, get_critical: bool) -> Option<(f32, Option)> { let p_enum = match self.enumerator.take() { Some(x) => x, None => { return None; } }; let mut p_obj: *mut IWbemClassObject = null_mut(); let mut nb_returned = 0; unsafe { use winapi::um::wbemcli::WBEM_INFINITE; (*p_enum.0).Next( WBEM_INFINITE as _, // Time out 1, // One object &mut p_obj as *mut _, &mut nb_returned, ); }; if nb_returned == 0 { return None; // not enough rights I suppose... } unsafe { (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _); let mut p_val: VARIANT = ::std::mem::MaybeUninit::uninit().assume_init(); // "CurrentTemperature" let temp = bstr!( 'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e' ); let res = (*p_obj).Get(temp, 0, &mut p_val, null_mut(), null_mut()); SysFreeString(temp); VariantClear(&mut p_val as *mut _ as *mut _); let temp = if SUCCEEDED(res) { // temperature is given in tenth of degrees Kelvin (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15 } else { (*p_obj).Release(); return None; }; let mut critical = None; if get_critical { // "CriticalPoint" let crit = bstr!( 'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n', 't' ); let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut()); SysFreeString(crit); VariantClear(&mut p_val as *mut _ as *mut _); if SUCCEEDED(res) { // temperature is given in tenth of degrees Kelvin critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15); } } (*p_obj).Release(); Some((temp, critical)) } } } impl Drop for Connection { fn drop(&mut self) { // Those three calls are here to enforce that they get dropped in the good order. self.enumerator.take(); self.server_connection.take(); self.instance.take(); unsafe { CoUninitialize(); } } } sysinfo-0.13.2/src/windows/disk.rs010064400017500001750000000036571363015362500153100ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use std::ffi::{OsStr, OsString}; use std::path::Path; use DiskExt; use DiskType; use winapi::um::fileapi::GetDiskFreeSpaceExW; use winapi::um::winnt::ULARGE_INTEGER; pub fn new_disk( name: &OsStr, mount_point: &[u16], file_system: &[u8], type_: DiskType, total_space: u64, ) -> Disk { let mut d = Disk { type_: type_, name: name.to_owned(), file_system: file_system.to_vec(), mount_point: mount_point.to_vec(), s_mount_point: String::from_utf16_lossy(&mount_point[..mount_point.len() - 1]), total_space: total_space, available_space: 0, }; d.refresh(); d } /// Struct containing a disk information. pub struct Disk { type_: DiskType, name: OsString, file_system: Vec, mount_point: Vec, s_mount_point: String, total_space: u64, available_space: u64, } impl DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ } fn get_name(&self) -> &OsStr { &self.name } fn get_file_system(&self) -> &[u8] { &self.file_system } fn get_mount_point(&self) -> &Path { &Path::new(&self.s_mount_point) } fn get_total_space(&self) -> u64 { self.total_space } fn get_available_space(&self) -> u64 { self.available_space } fn refresh(&mut self) -> bool { if self.total_space != 0 { unsafe { let mut tmp: ULARGE_INTEGER = ::std::mem::zeroed(); if GetDiskFreeSpaceExW( self.mount_point.as_ptr(), ::std::ptr::null_mut(), ::std::ptr::null_mut(), &mut tmp, ) != 0 { self.available_space = *tmp.QuadPart(); return true; } } } false } } sysinfo-0.13.2/src/windows/ffi.rs010064400017500001750000000160651363015362500151170ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // // TO BE REMOVED ONCE https://github.com/retep998/winapi-rs/pull/802 IS MERGED!!! #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(dead_code)] use winapi::shared::basetsd::ULONG64; use winapi::shared::guiddef::GUID; use winapi::shared::ifdef::{NET_IFINDEX, NET_LUID}; use winapi::shared::minwindef::BYTE; use winapi::shared::netioapi::NETIOAPI_API; use winapi::shared::ntdef::{PVOID, UCHAR, ULONG, WCHAR}; use winapi::{ENUM, STRUCT}; const ANY_SIZE: usize = 1; pub const IF_MAX_STRING_SIZE: usize = 256; pub const IF_MAX_PHYS_ADDRESS_LENGTH: usize = 32; pub type NET_IF_NETWORK_GUID = GUID; pub type PMIB_IF_TABLE2 = *mut MIB_IF_TABLE2; pub type PMIB_IF_ROW2 = *mut MIB_IF_ROW2; macro_rules! BITFIELD { ($base:ident $field:ident: $fieldtype:ty [ $($thing:ident $set_thing:ident[$r:expr],)+ ]) => { impl $base {$( #[inline] pub fn $thing(&self) -> $fieldtype { let size = ::std::mem::size_of::<$fieldtype>() * 8; self.$field << (size - $r.end) >> (size - $r.end + $r.start) } #[inline] pub fn $set_thing(&mut self, val: $fieldtype) { let mask = ((1 << ($r.end - $r.start)) - 1) << $r.start; self.$field &= !mask; self.$field |= (val << $r.start) & mask; } )+} } } STRUCT! {struct MIB_IF_TABLE2 { NumEntries: ULONG, Table: [MIB_IF_ROW2; ANY_SIZE], }} ENUM! {enum NDIS_MEDIUM { NdisMedium802_3 = 0, NdisMedium802_5 = 1, NdisMediumFddi = 2, NdisMediumWan = 3, NdisMediumLocalTalk = 4, NdisMediumDix = 5, // defined for convenience, not a real medium NdisMediumArcnetRaw = 6, NdisMediumArcnet878_2 = 7, NdisMediumAtm = 8, NdisMediumWirelessWan = 9, NdisMediumIrda = 10, NdisMediumBpc = 11, NdisMediumCoWan = 12, NdisMedium1394 = 13, NdisMediumInfiniBand = 14, NdisMediumTunnel = 15, NdisMediumNative802_11 = 16, NdisMediumLoopback = 17, NdisMediumWiMAX = 18, NdisMediumIP = 19, NdisMediumMax = 20, // Not a real medium, defined as an upper-bound }} ENUM! {enum TUNNEL_TYPE { TUNNEL_TYPE_NONE = 0, TUNNEL_TYPE_OTHER = 1, TUNNEL_TYPE_DIRECT = 2, TUNNEL_TYPE_6TO4 = 11, TUNNEL_TYPE_ISATAP = 13, TUNNEL_TYPE_TEREDO = 14, TUNNEL_TYPE_IPHTTPS = 15, }} ENUM! {enum NDIS_PHYSICAL_MEDIUM { NdisPhysicalMediumUnspecified = 0, NdisPhysicalMediumWirelessLan = 1, NdisPhysicalMediumCableModem = 2, NdisPhysicalMediumPhoneLine = 3, NdisPhysicalMediumPowerLine = 4, NdisPhysicalMediumDSL = 5, // includes ADSL and UADSL (G.Lite) NdisPhysicalMediumFibreChannel = 6, NdisPhysicalMedium1394 = 7, NdisPhysicalMediumWirelessWan = 8, NdisPhysicalMediumNative802_11 = 9, NdisPhysicalMediumBluetooth = 10, NdisPhysicalMediumInfiniband = 11, NdisPhysicalMediumWiMax = 12, NdisPhysicalMediumUWB = 13, NdisPhysicalMedium802_3 = 14, NdisPhysicalMedium802_5 = 15, NdisPhysicalMediumIrda = 16, NdisPhysicalMediumWiredWAN = 17, NdisPhysicalMediumWiredCoWan = 18, NdisPhysicalMediumOther = 19, NdisPhysicalMediumMax = 20, // Not a real physical type, defined as an upper-bound }} ENUM! {enum NET_IF_ACCESS_TYPE { NET_IF_ACCESS_LOOPBACK = 1, NET_IF_ACCESS_BROADCAST = 2, NET_IF_ACCESS_POINT_TO_POINT = 3, NET_IF_ACCESS_POINT_TO_MULTI_POINT = 4, NET_IF_ACCESS_MAXIMUM = 5, }} ENUM! {enum NET_IF_DIRECTION_TYPE { NET_IF_DIRECTION_SENDRECEIVE = 0, NET_IF_DIRECTION_SENDONLY = 1, NET_IF_DIRECTION_RECEIVEONLY = 2, NET_IF_DIRECTION_MAXIMUM = 3, }} ENUM! {enum IF_OPER_STATUS { IfOperStatusUp = 1, IfOperStatusDown = 2, IfOperStatusTesting = 3, IfOperStatusUnknown = 4, IfOperStatusDormant = 5, IfOperStatusNotPresent = 6, IfOperStatusLowerLayerDown = 7, }} ENUM! {enum NET_IF_ADMIN_STATUS { NET_IF_ADMIN_STATUS_UP = 1, NET_IF_ADMIN_STATUS_DOWN = 2, NET_IF_ADMIN_STATUS_TESTING = 3, }} ENUM! {enum NET_IF_MEDIA_CONNECT_STATE { MediaConnectStateUnknown = 0, MediaConnectStateConnected = 1, MediaConnectStateDisconnected = 2, }} ENUM! {enum NET_IF_CONNECTION_TYPE { NET_IF_CONNECTION_DEDICATED = 1, NET_IF_CONNECTION_PASSIVE = 2, NET_IF_CONNECTION_DEMAND = 3, NET_IF_CONNECTION_MAXIMUM = 4, }} STRUCT! {struct MIB_IF_ROW2_InterfaceAndOperStatusFlags { bitfield: BYTE, }} BITFIELD! {MIB_IF_ROW2_InterfaceAndOperStatusFlags bitfield: BYTE [ HardwareInterface set_HardwareInterface[0..1], FilterInterface set_FilterInterface[1..2], ConnectorPresent set_ConnectorPresent[2..3], NotAuthenticated set_NotAuthenticated[3..4], NotMediaConnected set_NotMediaConnected[4..5], Paused set_Paused[5..6], LowPower set_LowPower[6..7], EndPointInterface set_EndPointInterface[7..8], ]} STRUCT! {struct MIB_IF_ROW2 { InterfaceLuid: NET_LUID, InterfaceIndex: NET_IFINDEX, InterfaceGuid: GUID, Alias: [WCHAR; IF_MAX_STRING_SIZE + 1], Description: [WCHAR; IF_MAX_STRING_SIZE + 1], PhysicalAddressLength: ULONG, PhysicalAddress: [UCHAR; IF_MAX_PHYS_ADDRESS_LENGTH], PermanentPhysicalAddress: [UCHAR; IF_MAX_PHYS_ADDRESS_LENGTH], Mtu: ULONG, Type: ULONG, // Interface Type. TunnelType: TUNNEL_TYPE, // Tunnel Type, if Type = IF_TUNNEL. MediaType: NDIS_MEDIUM, PhysicalMediumType: NDIS_PHYSICAL_MEDIUM, AccessType: NET_IF_ACCESS_TYPE, DirectionType: NET_IF_DIRECTION_TYPE, InterfaceAndOperStatusFlags: MIB_IF_ROW2_InterfaceAndOperStatusFlags, OperStatus: IF_OPER_STATUS, AdminStatus: NET_IF_ADMIN_STATUS, MediaConnectState: NET_IF_MEDIA_CONNECT_STATE, NetworkGuid: NET_IF_NETWORK_GUID, ConnectionType: NET_IF_CONNECTION_TYPE, TransmitLinkSpeed: ULONG64, ReceiveLinkSpeed: ULONG64, InOctets: ULONG64, InUcastPkts: ULONG64, InNUcastPkts: ULONG64, InDiscards: ULONG64, InErrors: ULONG64, InUnknownProtos: ULONG64, InUcastOctets: ULONG64, InMulticastOctets: ULONG64, InBroadcastOctets: ULONG64, OutOctets: ULONG64, OutUcastPkts: ULONG64, OutNUcastPkts: ULONG64, OutDiscards: ULONG64, OutErrors: ULONG64, OutUcastOctets: ULONG64, OutMulticastOctets: ULONG64, OutBroadcastOctets: ULONG64, OutQLen: ULONG64, }} // To be removed once https://github.com/retep998/winapi-rs/pull/872 is merged use winapi::shared::lmcons::NET_API_STATUS; use winapi::shared::minwindef::{DWORD, LPBYTE, LPDWORD}; use winapi::um::winnt::LPCWSTR; extern "system" { pub fn GetIfTable2(Table: *mut PMIB_IF_TABLE2) -> NETIOAPI_API; pub fn GetIfEntry2(Row: PMIB_IF_ROW2) -> NETIOAPI_API; pub fn FreeMibTable(Memory: PVOID); // To be removed once https://github.com/retep998/winapi-rs/pull/872 is merged pub fn NetUserGetLocalGroups( servername: LPCWSTR, username: LPCWSTR, level: DWORD, flags: DWORD, bufptr: *mut LPBYTE, prefmaxlen: DWORD, entriesread: LPDWORD, totalentries: LPDWORD, ) -> NET_API_STATUS; } sysinfo-0.13.2/src/windows/macros.rs010064400017500001750000000005351357223510000156240ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // /// Allows to cast only when needed. #[macro_export] macro_rules! auto_cast { ($t:expr, $cast:ty) => {{ #[cfg(target_pointer_width = "32")] { $t as $cast } #[cfg(not(target_pointer_width = "32"))] { $t } }}; } sysinfo-0.13.2/src/windows/mod.rs010064400017500001750000000006441363015362500151260ustar0000000000000000// // Sysinfo // // Copyright (c) 2015 Guillaume Gomez // mod component; mod disk; #[macro_use] mod macros; mod network; mod process; mod processor; mod system; mod tools; mod users; mod ffi; pub use self::component::Component; pub use self::disk::Disk; pub use self::network::{NetworkData, Networks}; pub use self::process::{Process, ProcessStatus}; pub use self::processor::Processor; pub use self::system::System; sysinfo-0.13.2/src/windows/network.rs010064400017500001750000000170661363516610000160430ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use std::collections::{HashMap, HashSet}; use windows::ffi::{self, MIB_IF_ROW2, PMIB_IF_TABLE2}; use NetworkExt; use NetworksExt; use NetworksIter; use winapi::shared::ifdef::NET_LUID; use winapi::shared::winerror::NO_ERROR; macro_rules! old_and_new { ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{ $ty_.$old = $ty_.$name; $ty_.$name = $new_val; }}; } /// Network interfaces. /// /// ```no_run /// use sysinfo::{NetworksExt, System, SystemExt}; /// /// let s = System::new_all(); /// let networks = s.get_networks(); /// ``` pub struct Networks { interfaces: HashMap, } impl Networks { pub(crate) fn new() -> Networks { Networks { interfaces: HashMap::new(), } } } impl NetworksExt for Networks { fn iter<'a>(&'a self) -> NetworksIter<'a> { NetworksIter::new(self.interfaces.iter()) } fn refresh_networks_list(&mut self) { let mut table: PMIB_IF_TABLE2 = ::std::ptr::null_mut(); if unsafe { ffi::GetIfTable2(&mut table) } != NO_ERROR { return; } let mut to_be_removed = HashSet::with_capacity(self.interfaces.len()); for key in self.interfaces.keys() { to_be_removed.insert(key.clone()); } // In here, this is tricky: we have to filter out the software interfaces to only keep // the hardware ones. To do so, we first check the connection potential speed (if 0, not // interesting), then we check its state: if not open, not interesting either. And finally, // we count the members of a same group: if there is more than 1, then it's software level. let mut groups = HashMap::new(); let mut indexes = Vec::new(); let ptr = unsafe { (*table).Table.as_ptr() }; for i in 0..unsafe { *table }.NumEntries { let ptr = unsafe { &*ptr.offset(i as _) }; if (ptr.TransmitLinkSpeed == 0 && ptr.ReceiveLinkSpeed == 0) || ptr.MediaConnectState == ffi::MediaConnectStateDisconnected || ptr.PhysicalAddressLength == 0 { continue; } let id = vec![ ptr.InterfaceGuid.Data2, ptr.InterfaceGuid.Data3, ptr.InterfaceGuid.Data4[0] as _, ptr.InterfaceGuid.Data4[1] as _, ptr.InterfaceGuid.Data4[2] as _, ptr.InterfaceGuid.Data4[3] as _, ptr.InterfaceGuid.Data4[4] as _, ptr.InterfaceGuid.Data4[5] as _, ptr.InterfaceGuid.Data4[6] as _, ptr.InterfaceGuid.Data4[7] as _, ]; let entry = groups.entry(id.clone()).or_insert(0); *entry += 1; if *entry > 1 { continue; } indexes.push((i, id)); } for (i, id) in indexes { let ptr = unsafe { &*ptr.offset(i as _) }; if *groups.get(&id).unwrap_or(&0) > 1 { continue; } let mut pos = 0; for x in ptr.Alias.iter() { if *x == 0 { break; } pos += 1; } let interface_name = match String::from_utf16(&ptr.Alias[..pos]) { Ok(s) => s, _ => continue, }; to_be_removed.remove(&interface_name); let mut interface = self.interfaces .entry(interface_name) .or_insert_with(|| NetworkData { id: ptr.InterfaceLuid, current_out: ptr.OutOctets, old_out: 0, current_in: ptr.InOctets, old_in: 0, packets_in: ptr.InUcastPkts + ptr.InNUcastPkts, old_packets_in: 0, packets_out: ptr.OutUcastPkts + ptr.OutNUcastPkts, old_packets_out: 0, errors_in: ptr.InErrors, old_errors_in: 0, errors_out: ptr.OutErrors, old_errors_out: 0, }); old_and_new!(interface, current_out, old_out, ptr.OutOctets); old_and_new!(interface, current_in, old_in, ptr.InOctets); old_and_new!( interface, packets_in, old_packets_in, ptr.InUcastPkts + ptr.InNUcastPkts ); old_and_new!( interface, packets_out, old_packets_out, ptr.OutUcastPkts + ptr.OutNUcastPkts ); old_and_new!(interface, errors_in, old_errors_in, ptr.InErrors); old_and_new!(interface, errors_out, old_errors_out, ptr.OutErrors); } unsafe { ffi::FreeMibTable(table as _); } for key in to_be_removed { self.interfaces.remove(&key); } } fn refresh(&mut self) { let mut entry: MIB_IF_ROW2 = unsafe { ::std::mem::MaybeUninit::uninit().assume_init() }; for (_, interface) in self.interfaces.iter_mut() { entry.InterfaceLuid = interface.id; entry.InterfaceIndex = 0; // to prevent the function to pick this one as index if unsafe { ffi::GetIfEntry2(&mut entry) } != NO_ERROR { continue; } old_and_new!(interface, current_out, old_out, entry.OutOctets); old_and_new!(interface, current_in, old_in, entry.InOctets); old_and_new!( interface, packets_in, old_packets_in, entry.InUcastPkts + entry.InNUcastPkts ); old_and_new!( interface, packets_out, old_packets_out, entry.OutUcastPkts + entry.OutNUcastPkts ); old_and_new!(interface, errors_in, old_errors_in, entry.InErrors); old_and_new!(interface, errors_out, old_errors_out, entry.OutErrors); } } } /// Contains network information. pub struct NetworkData { id: NET_LUID, current_out: u64, old_out: u64, current_in: u64, old_in: u64, packets_in: u64, old_packets_in: u64, packets_out: u64, old_packets_out: u64, errors_in: u64, old_errors_in: u64, errors_out: u64, old_errors_out: u64, } impl NetworkExt for NetworkData { fn get_received(&self) -> u64 { self.current_in - self.old_in } fn get_total_received(&self) -> u64 { self.current_in } fn get_transmitted(&self) -> u64 { self.current_out - self.old_out } fn get_total_transmitted(&self) -> u64 { self.current_out } fn get_packets_received(&self) -> u64 { self.packets_in - self.old_packets_in } fn get_total_packets_received(&self) -> u64 { self.packets_in } fn get_packets_transmitted(&self) -> u64 { self.packets_out - self.old_packets_out } fn get_total_packets_transmitted(&self) -> u64 { self.packets_out } fn get_errors_on_received(&self) -> u64 { self.errors_in - self.old_errors_in } fn get_total_errors_on_received(&self) -> u64 { self.errors_in } fn get_errors_on_transmitted(&self) -> u64 { self.errors_out - self.old_errors_out } fn get_total_errors_on_transmitted(&self) -> u64 { self.errors_out } } sysinfo-0.13.2/src/windows/process.rs010064400017500001750000000510421364506453400160310ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use std::fmt::{self, Debug}; use std::mem::{size_of, zeroed, MaybeUninit}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process; use std::ptr::null_mut; use std::str; use libc::{c_void, memcpy}; use DiskUsage; use Pid; use ProcessExt; use ntapi::ntpsapi::{ NtQueryInformationProcess, ProcessBasicInformation, PROCESS_BASIC_INFORMATION, }; use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, MAX_PATH, TRUE}; use winapi::um::handleapi::CloseHandle; use winapi::um::processthreadsapi::{GetProcessTimes, OpenProcess}; use winapi::um::psapi::{ EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo, LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX, }; use winapi::um::sysinfoapi::GetSystemTimeAsFileTime; use winapi::um::winbase::GetProcessIoCounters; use winapi::um::winnt::{IO_COUNTERS, HANDLE, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, ULARGE_INTEGER}; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] pub enum ProcessStatus { /// Currently runnable. Run, } impl ProcessStatus { /// Used to display `ProcessStatus`. pub fn to_string(&self) -> &str { match *self { ProcessStatus::Run => "Runnable", } } } impl fmt::Display for ProcessStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.to_string()) } } fn get_process_handler(pid: Pid) -> Option { if pid == 0 { return None; } let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; let process_handler = unsafe { OpenProcess(options, FALSE, pid as DWORD) }; if process_handler.is_null() { None } else { Some(process_handler) } } #[derive(Clone)] struct PtrWrapper(T); impl Deref for PtrWrapper { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } unsafe impl Send for PtrWrapper {} unsafe impl Sync for PtrWrapper {} /// Struct containing a process' information. pub struct Process { name: String, cmd: Vec, exe: PathBuf, pid: Pid, environ: Vec, cwd: PathBuf, root: PathBuf, pub(crate) memory: u64, pub(crate) virtual_memory: u64, parent: Option, status: ProcessStatus, handle: PtrWrapper, old_cpu: u64, old_sys_cpu: u64, old_user_cpu: u64, start_time: u64, cpu_usage: f32, pub(crate) updated: bool, old_read_bytes: u64, old_written_bytes: u64, read_bytes: u64, written_bytes: u64, } unsafe fn get_process_name(process_handler: HANDLE, h_mod: *mut c_void) -> String { let mut process_name = [0u16; MAX_PATH + 1]; GetModuleBaseNameW( process_handler, h_mod as _, process_name.as_mut_ptr(), MAX_PATH as DWORD + 1, ); let mut pos = 0; for x in process_name.iter() { if *x == 0 { break; } pos += 1; } String::from_utf16_lossy(&process_name[..pos]) } unsafe fn get_h_mod(process_handler: HANDLE, h_mod: &mut *mut c_void) -> bool { let mut cb_needed = 0; EnumProcessModulesEx( process_handler, h_mod as *mut *mut c_void as _, size_of::() as DWORD, &mut cb_needed, LIST_MODULES_ALL, ) != 0 } unsafe fn get_exe(process_handler: HANDLE, h_mod: *mut c_void) -> PathBuf { let mut exe_buf = [0u16; MAX_PATH + 1]; GetModuleFileNameExW( process_handler, h_mod as _, exe_buf.as_mut_ptr(), MAX_PATH as DWORD + 1, ); let mut pos = 0; for x in exe_buf.iter() { if *x == 0 { break; } pos += 1; } PathBuf::from(String::from_utf16_lossy(&exe_buf[..pos])) } impl Process { #[allow(clippy::uninit_assumed_init)] pub(crate) fn new_from_pid(pid: Pid) -> Option { let process_handler = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid as _) }; if process_handler.is_null() { return None; } let mut info: PROCESS_BASIC_INFORMATION = unsafe { MaybeUninit::uninit().assume_init() }; if unsafe { NtQueryInformationProcess( process_handler, ProcessBasicInformation, &mut info as *mut _ as *mut _, size_of::() as _, null_mut(), ) } != 0 { unsafe { CloseHandle(process_handler) }; return None; } Some(Process::new_with_handle( pid, if info.InheritedFromUniqueProcessId as usize != 0 { Some(info.InheritedFromUniqueProcessId as usize) } else { None }, process_handler, )) } pub(crate) fn new_full( pid: Pid, parent: Option, memory: u64, virtual_memory: u64, name: String, ) -> Process { if let Some(handle) = get_process_handler(pid) { let mut h_mod = null_mut(); unsafe { get_h_mod(handle, &mut h_mod) }; let environ = unsafe { get_proc_env(handle, pid as u32, &name) }; let exe = unsafe { get_exe(handle, h_mod) }; let mut root = exe.clone(); root.pop(); Process { handle: PtrWrapper(handle), name, pid, parent, cmd: get_cmd_line(handle), environ, exe, cwd: PathBuf::new(), root, status: ProcessStatus::Run, memory, virtual_memory, cpu_usage: 0., old_cpu: 0, old_sys_cpu: 0, old_user_cpu: 0, start_time: unsafe { get_start_time(handle) }, updated: true, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } else { Process { handle: PtrWrapper(null_mut()), name, pid, parent, cmd: Vec::new(), environ: Vec::new(), exe: get_executable_path(pid), cwd: PathBuf::new(), root: PathBuf::new(), status: ProcessStatus::Run, memory, virtual_memory, cpu_usage: 0., old_cpu: 0, old_sys_cpu: 0, old_user_cpu: 0, start_time: 0, updated: true, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } } fn new_with_handle(pid: Pid, parent: Option, process_handler: HANDLE) -> Process { let mut h_mod = null_mut(); unsafe { let name = if get_h_mod(process_handler, &mut h_mod) { get_process_name(process_handler, h_mod) } else { String::new() }; let environ = get_proc_env(process_handler, pid as u32, &name); let exe = get_exe(process_handler, h_mod); let mut root = exe.clone(); root.pop(); Process { handle: PtrWrapper(process_handler), name, pid, parent, cmd: get_cmd_line(process_handler), environ, exe, cwd: PathBuf::new(), root, status: ProcessStatus::Run, memory: 0, virtual_memory: 0, cpu_usage: 0., old_cpu: 0, old_sys_cpu: 0, old_user_cpu: 0, start_time: get_start_time(process_handler), updated: true, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } } } // TODO: it's possible to get environment variables like it's done in // https://github.com/processhacker/processhacker // // They have a very nice function called PhGetProcessEnvironment. Just very complicated as it // seems... impl ProcessExt for Process { fn new(pid: Pid, parent: Option, _: u64) -> Process { if let Some(process_handler) = get_process_handler(pid) { Process::new_with_handle(pid, parent, process_handler) } else { Process { handle: PtrWrapper(null_mut()), name: String::new(), pid, parent, cmd: Vec::new(), environ: Vec::new(), exe: get_executable_path(pid), cwd: PathBuf::new(), root: PathBuf::new(), status: ProcessStatus::Run, memory: 0, virtual_memory: 0, cpu_usage: 0., old_cpu: 0, old_sys_cpu: 0, old_user_cpu: 0, start_time: 0, updated: true, old_read_bytes: 0, old_written_bytes: 0, read_bytes: 0, written_bytes: 0, } } } fn kill(&self, _signal: ::Signal) -> bool { let mut kill = process::Command::new("taskkill.exe"); kill.arg("/PID").arg(self.pid().to_string()).arg("/F"); match kill.output() { Ok(o) => o.status.success(), Err(_) => false, } } fn name(&self) -> &str { &self.name } fn cmd(&self) -> &[String] { &self.cmd } fn exe(&self) -> &Path { self.exe.as_path() } fn pid(&self) -> Pid { self.pid } fn environ(&self) -> &[String] { &self.environ } fn cwd(&self) -> &Path { self.cwd.as_path() } fn root(&self) -> &Path { self.root.as_path() } fn memory(&self) -> u64 { self.memory } fn virtual_memory(&self) -> u64 { self.virtual_memory } fn parent(&self) -> Option { self.parent } fn status(&self) -> ProcessStatus { self.status } fn start_time(&self) -> u64 { self.start_time } fn cpu_usage(&self) -> f32 { self.cpu_usage } fn disk_usage(&self) -> DiskUsage { DiskUsage { written_bytes: self.written_bytes - self.old_written_bytes, total_written_bytes: self.written_bytes, read_bytes: self.read_bytes - self.old_read_bytes, total_read_bytes: self.read_bytes, } } } impl Drop for Process { fn drop(&mut self) { unsafe { if self.handle.is_null() { return; } CloseHandle(*self.handle); } } } unsafe fn get_start_time(handle: HANDLE) -> u64 { let mut fstart: FILETIME = zeroed(); let mut x = zeroed(); GetProcessTimes( handle, &mut fstart as *mut FILETIME, &mut x as *mut FILETIME, &mut x as *mut FILETIME, &mut x as *mut FILETIME, ); let tmp = (fstart.dwHighDateTime as u64) << 32 | (fstart.dwLowDateTime as u64); tmp / 10_000_000 - 11_644_473_600 } fn get_cmd_line(handle: HANDLE) -> Vec { use ntapi::ntpebteb::{PEB, PPEB}; use ntapi::ntrtl::{PRTL_USER_PROCESS_PARAMETERS, RTL_USER_PROCESS_PARAMETERS}; use winapi::shared::basetsd::SIZE_T; use winapi::um::memoryapi::ReadProcessMemory; unsafe { let mut pinfo = MaybeUninit::::uninit(); if NtQueryInformationProcess( handle, 0, // ProcessBasicInformation pinfo.as_mut_ptr() as *mut _, size_of::() as u32, null_mut(), ) != 0 { return Vec::new(); } let pinfo = pinfo.assume_init(); let ppeb: PPEB = pinfo.PebBaseAddress; let mut peb_copy = MaybeUninit::::uninit(); if ReadProcessMemory( handle, ppeb as *mut _, peb_copy.as_mut_ptr() as *mut _, size_of::() as SIZE_T, ::std::ptr::null_mut(), ) != TRUE { return Vec::new(); } let peb_copy = peb_copy.assume_init(); let proc_param = peb_copy.ProcessParameters; let mut rtl_proc_param_copy = MaybeUninit::::uninit(); if ReadProcessMemory( handle, proc_param as *mut PRTL_USER_PROCESS_PARAMETERS as *mut _, rtl_proc_param_copy.as_mut_ptr() as *mut _, size_of::() as SIZE_T, ::std::ptr::null_mut(), ) != TRUE { return Vec::new(); } let rtl_proc_param_copy = rtl_proc_param_copy.assume_init(); let len = rtl_proc_param_copy.CommandLine.Length as usize; let len = len / 2; // For len symbols + '\0' let mut buffer_copy: Vec = Vec::with_capacity(len + 1); buffer_copy.set_len(len); if ReadProcessMemory( handle, rtl_proc_param_copy.CommandLine.Buffer as *mut _, buffer_copy.as_mut_ptr() as *mut _, len * 2 as SIZE_T, ::std::ptr::null_mut(), ) != TRUE { return Vec::new(); } buffer_copy.push(0); // Get argc and argv from command line let mut argc = MaybeUninit::::uninit(); let argv_p = winapi::um::shellapi::CommandLineToArgvW(buffer_copy.as_ptr(), argc.as_mut_ptr()); if argv_p.is_null() { return Vec::new(); } let argc = argc.assume_init(); let argv = std::slice::from_raw_parts(argv_p, argc as usize); let mut res = Vec::new(); for arg in argv { let len = libc::wcslen(*arg); let str_slice = std::slice::from_raw_parts(*arg, len); res.push(String::from_utf16_lossy(str_slice)); } winapi::um::winbase::LocalFree(argv_p as *mut _); res } } unsafe fn get_proc_env(_handle: HANDLE, _pid: u32, _name: &str) -> Vec { let ret = Vec::new(); /* println!("current pid: {}", kernel32::GetCurrentProcessId()); if kernel32::GetCurrentProcessId() == pid { println!("current proc!"); for (key, value) in env::vars() { ret.push(format!("{}={}", key, value)); } return ret; } println!("1"); let snapshot_handle = kernel32::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if !snapshot_handle.is_null() { println!("2"); let mut target_thread: THREADENTRY32 = zeroed(); target_thread.dwSize = size_of::() as DWORD; if kernel32::Thread32First(snapshot_handle, &mut target_thread) == TRUE { println!("3"); loop { if target_thread.th32OwnerProcessID == pid { println!("4"); let thread_handle = kernel32::OpenThread(THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT, FALSE, target_thread.th32ThreadID); if !thread_handle.is_null() { println!("5 -> {}", pid); if kernel32::SuspendThread(thread_handle) != DWORD::max_value() { println!("6"); let mut context = zeroed(); if kernel32::GetThreadContext(thread_handle, &mut context) != 0 { println!("7 --> {:?}", context); let mut x = vec![0u8; 10]; if kernel32::ReadProcessMemory(handle, context.MxCsr as usize as *mut winapi::c_void, x.as_mut_ptr() as *mut winapi::c_void, x.len() as u64, null_mut()) != 0 { for y in x { print!("{}", y as char); } println!(""); } else { println!("failure... {:?}", kernel32::GetLastError()); } } else { println!("-> {:?}", kernel32::GetLastError()); } kernel32::ResumeThread(thread_handle); } kernel32::CloseHandle(thread_handle); } break; } if kernel32::Thread32Next(snapshot_handle, &mut target_thread) != TRUE { break; } } } kernel32::CloseHandle(snapshot_handle); }*/ ret } pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf { /*let where_req = format!("ProcessId={}", pid); if let Some(ret) = run_wmi(&["process", "where", &where_req, "get", "ExecutablePath"]) { for line in ret.lines() { if line.is_empty() || line == "ExecutablePath" { continue } return line.to_owned(); } }*/ PathBuf::new() } pub(crate) fn get_system_computation_time() -> ULARGE_INTEGER { unsafe { let mut now: ULARGE_INTEGER = ::std::mem::zeroed(); let mut ftime: FILETIME = zeroed(); GetSystemTimeAsFileTime(&mut ftime); memcpy( &mut now as *mut ULARGE_INTEGER as *mut c_void, &mut ftime as *mut FILETIME as *mut c_void, size_of::(), ); now } } pub(crate) fn compute_cpu_usage(p: &mut Process, nb_processors: u64, now: ULARGE_INTEGER) { unsafe { let mut sys: ULARGE_INTEGER = ::std::mem::zeroed(); let mut user: ULARGE_INTEGER = ::std::mem::zeroed(); let mut ftime: FILETIME = zeroed(); let mut fsys: FILETIME = zeroed(); let mut fuser: FILETIME = zeroed(); GetProcessTimes( *p.handle, &mut ftime as *mut FILETIME, &mut ftime as *mut FILETIME, &mut fsys as *mut FILETIME, &mut fuser as *mut FILETIME, ); memcpy( &mut sys as *mut ULARGE_INTEGER as *mut c_void, &mut fsys as *mut FILETIME as *mut c_void, size_of::(), ); memcpy( &mut user as *mut ULARGE_INTEGER as *mut c_void, &mut fuser as *mut FILETIME as *mut c_void, size_of::(), ); p.cpu_usage = ((*sys.QuadPart() - p.old_sys_cpu) as f32 + (*user.QuadPart() - p.old_user_cpu) as f32) / (*now.QuadPart() - p.old_cpu) as f32 / nb_processors as f32 * 100.; p.old_cpu = *now.QuadPart(); p.old_user_cpu = *user.QuadPart(); p.old_sys_cpu = *sys.QuadPart(); } } pub fn get_handle(p: &Process) -> HANDLE { *p.handle } pub fn update_disk_usage(p: &mut Process) { let mut counters = MaybeUninit::::uninit(); let ret = unsafe { GetProcessIoCounters(*p.handle, counters.as_mut_ptr()) }; if ret == 0 { sysinfo_debug!("GetProcessIoCounters call failed on process {}", p.pid()); } else { let counters = unsafe { counters.assume_init() }; p.old_read_bytes = p.read_bytes; p.old_written_bytes = p.written_bytes; p.read_bytes = counters.ReadTransferCount; p.written_bytes = counters.WriteTransferCount; } } pub fn update_memory(p: &mut Process) { unsafe { let mut pmc: PROCESS_MEMORY_COUNTERS_EX = zeroed(); if GetProcessMemoryInfo( *p.handle, &mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void as *mut PROCESS_MEMORY_COUNTERS, size_of::() as DWORD, ) != 0 { p.memory = (pmc.WorkingSetSize as u64) >> 10u64; // / 1024; p.virtual_memory = (pmc.PrivateUsage as u64) >> 10u64; // / 1024; } } } sysinfo-0.13.2/src/windows/processor.rs010064400017500001750000000277411364506453400164030ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // use std::collections::HashMap; use std::mem; use std::ops::DerefMut; use std::ptr::null_mut; use std::sync::Mutex; use windows::tools::KeyHandler; use LoadAvg; use ProcessorExt; use ntapi::ntpoapi::PROCESSOR_POWER_INFORMATION; use winapi::shared::minwindef::FALSE; use winapi::shared::winerror::ERROR_SUCCESS; use winapi::um::handleapi::CloseHandle; use winapi::um::pdh::{ PdhAddCounterW, PdhAddEnglishCounterA, PdhCloseQuery, PdhCollectQueryData, PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter, PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY, }; use winapi::um::powerbase::CallNtPowerInformation; use winapi::um::synchapi::CreateEventA; use winapi::um::sysinfoapi::SYSTEM_INFO; use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE}; use winapi::um::winnt::{ProcessorInformation, BOOLEAN, HANDLE, PVOID, WT_EXECUTEDEFAULT}; // This formula comes from linux's include/linux/sched/loadavg.h // https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23 const LOADAVG_FACTOR_1F: f64 = 0.9200444146293232478931553241; const LOADAVG_FACTOR_5F: f64 = 0.6592406302004437462547604110; const LOADAVG_FACTOR_15F: f64 = 0.2865047968601901003248854266; // The time interval in seconds between taking load counts, same as Linux const SAMPLING_INTERVAL: usize = 5; // maybe use a read/write lock instead? static LOAD_AVG: once_cell::sync::Lazy>> = once_cell::sync::Lazy::new(|| unsafe { init_load_avg() }); pub(crate) fn get_load_average() -> LoadAvg { if let Ok(avg) = LOAD_AVG.lock() { if let Some(avg) = &*avg { return avg.clone(); } } return LoadAvg::default(); } unsafe extern "system" fn load_avg_callback(counter: PVOID, _: BOOLEAN) { let mut display_value: PDH_FMT_COUNTERVALUE = mem::MaybeUninit::uninit().assume_init(); if PdhGetFormattedCounterValue(counter as _, PDH_FMT_DOUBLE, null_mut(), &mut display_value) != ERROR_SUCCESS as _ { return; } if let Ok(mut avg) = LOAD_AVG.lock() { if let Some(avg) = avg.deref_mut() { let current_load = display_value.u.doubleValue(); avg.one = avg.one * LOADAVG_FACTOR_1F + current_load * (1.0 - LOADAVG_FACTOR_1F); avg.five = avg.five * LOADAVG_FACTOR_5F + current_load * (1.0 - LOADAVG_FACTOR_5F); avg.fifteen = avg.fifteen * LOADAVG_FACTOR_15F + current_load * (1.0 - LOADAVG_FACTOR_15F); } } } unsafe fn init_load_avg() -> Mutex> { // You can see the original implementation here: https://github.com/giampaolo/psutil let mut query = null_mut(); if PdhOpenQueryA(null_mut(), 0, &mut query) != ERROR_SUCCESS as _ { return Mutex::new(None); } let mut counter: PDH_HCOUNTER = mem::zeroed(); if PdhAddEnglishCounterA( query, b"\\System\\Processor Queue Length\0".as_ptr() as _, 0, &mut counter, ) != ERROR_SUCCESS as _ { PdhCloseQuery(query); return Mutex::new(None); } let event = CreateEventA(null_mut(), FALSE, FALSE, b"LoadUpdateEvent\0".as_ptr() as _); if event.is_null() { PdhCloseQuery(query); return Mutex::new(None); } if PdhCollectQueryDataEx(query, SAMPLING_INTERVAL as _, event) != ERROR_SUCCESS as _ { PdhCloseQuery(query); return Mutex::new(None); } let mut wait_handle = null_mut(); if RegisterWaitForSingleObject( &mut wait_handle, event, Some(load_avg_callback), counter as _, INFINITE, WT_EXECUTEDEFAULT, ) == 0 { PdhRemoveCounter(counter); PdhCloseQuery(query); Mutex::new(None) } else { Mutex::new(Some(LoadAvg::default())) } } struct InternalQuery { query: PDH_HQUERY, event: HANDLE, data: HashMap, } unsafe impl Send for InternalQuery {} unsafe impl Sync for InternalQuery {} impl Drop for InternalQuery { fn drop(&mut self) { unsafe { for (_, counter) in self.data.iter() { PdhRemoveCounter(*counter); } if !self.event.is_null() { CloseHandle(self.event); } if !self.query.is_null() { PdhCloseQuery(self.query); } } } } pub struct Query { internal: InternalQuery, } impl Query { pub fn new() -> Option { let mut query = null_mut(); unsafe { if PdhOpenQueryA(null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 { let q = InternalQuery { query, event: null_mut(), data: HashMap::new(), }; Some(Query { internal: q }) } else { None } } } pub fn get(&self, name: &String) -> Option { if let Some(ref counter) = self.internal.data.get(name) { unsafe { let mut display_value: PDH_FMT_COUNTERVALUE = mem::MaybeUninit::uninit().assume_init(); let counter: PDH_HCOUNTER = **counter; let ret = PdhGetFormattedCounterValue( counter, PDH_FMT_DOUBLE, null_mut(), &mut display_value, ) as u32; return if ret == ERROR_SUCCESS as _ { let data = *display_value.u.doubleValue(); Some(data as f32) } else { Some(0.) }; } } None } pub fn add_counter(&mut self, name: &String, getter: Vec) -> bool { if self.internal.data.contains_key(name) { return false; } unsafe { let mut counter: PDH_HCOUNTER = ::std::mem::zeroed(); let ret = PdhAddCounterW(self.internal.query, getter.as_ptr(), 0, &mut counter); if ret == ERROR_SUCCESS as _ { self.internal.data.insert(name.clone(), counter); } else { sysinfo_debug!("failed to add counter '{}': {:x}...", name, ret); return false; } } true } pub fn refresh(&self) { unsafe { if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ { sysinfo_debug!("failed to refresh CPU data"); } } } } /// Struct containing a processor information. pub struct Processor { name: String, cpu_usage: f32, key_used: Option, vendor_id: String, brand: String, frequency: u64, } impl ProcessorExt for Processor { fn get_cpu_usage(&self) -> f32 { self.cpu_usage } fn get_name(&self) -> &str { &self.name } fn get_frequency(&self) -> u64 { self.frequency } fn get_vendor_id(&self) -> &str { &self.vendor_id } fn get_brand(&self) -> &str { &self.brand } } impl Processor { pub(crate) fn new_with_values( name: &str, vendor_id: String, brand: String, frequency: u64, ) -> Processor { Processor { name: name.to_owned(), cpu_usage: 0f32, key_used: None, vendor_id, brand, frequency, } } pub(crate) fn set_cpu_usage(&mut self, value: f32) { self.cpu_usage = value; } } fn get_vendor_id_not_great(info: &SYSTEM_INFO) -> String { use winapi::um::winnt; // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info match unsafe { info.u.s() }.wProcessorArchitecture { winnt::PROCESSOR_ARCHITECTURE_INTEL => "Intel x86", winnt::PROCESSOR_ARCHITECTURE_MIPS => "MIPS", winnt::PROCESSOR_ARCHITECTURE_ALPHA => "RISC Alpha", winnt::PROCESSOR_ARCHITECTURE_PPC => "PPC", winnt::PROCESSOR_ARCHITECTURE_SHX => "SHX", winnt::PROCESSOR_ARCHITECTURE_ARM => "ARM", winnt::PROCESSOR_ARCHITECTURE_IA64 => "Intel Itanium-based x64", winnt::PROCESSOR_ARCHITECTURE_ALPHA64 => "RISC Alpha x64", winnt::PROCESSOR_ARCHITECTURE_MSIL => "MSIL", winnt::PROCESSOR_ARCHITECTURE_AMD64 => "(Intel or AMD) x64", winnt::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => "Intel Itanium-based x86", winnt::PROCESSOR_ARCHITECTURE_NEUTRAL => "unknown", winnt::PROCESSOR_ARCHITECTURE_ARM64 => "ARM x64", winnt::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => "ARM", winnt::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 => "Intel Itanium-based x86", _ => "unknown", } .to_owned() } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) { #[cfg(target_arch = "x86")] use std::arch::x86::__cpuid; #[cfg(target_arch = "x86_64")] use std::arch::x86_64::__cpuid; fn add_u32(v: &mut Vec, i: u32) { let i = &i as *const u32 as *const u8; unsafe { v.push(*i); v.push(*i.offset(1)); v.push(*i.offset(2)); v.push(*i.offset(3)); } } // First, we try to get the complete name. let res = unsafe { __cpuid(0x80000000) }; let n_ex_ids = res.eax; let brand = if n_ex_ids >= 0x80000004 { let mut extdata = Vec::with_capacity(5); for i in 0x80000000..=n_ex_ids { extdata.push(unsafe { __cpuid(i) }); } let mut out = Vec::with_capacity(4 * 4 * 3); // 4 * u32 * nb_entries for i in 2..5 { add_u32(&mut out, extdata[i].eax); add_u32(&mut out, extdata[i].ebx); add_u32(&mut out, extdata[i].ecx); add_u32(&mut out, extdata[i].edx); } let mut pos = 0; for e in out.iter() { if *e == 0 { break; } pos += 1; } match ::std::str::from_utf8(&out[..pos]) { Ok(s) => s.to_owned(), _ => String::new(), } } else { String::new() }; // Failed to get full name, let's retry for the short version! let res = unsafe { __cpuid(0) }; let mut x = Vec::with_capacity(16); // 3 * u32 add_u32(&mut x, res.ebx); add_u32(&mut x, res.edx); add_u32(&mut x, res.ecx); let mut pos = 0; for e in x.iter() { if *e == 0 { break; } pos += 1; } let vendor_id = match ::std::str::from_utf8(&x[..pos]) { Ok(s) => s.to_owned(), Err(_) => get_vendor_id_not_great(info), }; (vendor_id, brand) } #[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))] pub fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) { (get_vendor_id_not_great(info), String::new()) } pub fn get_key_used(p: &mut Processor) -> &mut Option { &mut p.key_used } // From https://stackoverflow.com/a/43813138: // // If your PC has 64 or fewer logical processors installed, the above code will work fine. However, // if your PC has more than 64 logical processors installed, use GetActiveProcessorCount() or // GetLogicalProcessorInformation() to determine the total number of logical processors installed. pub fn get_frequencies(nb_processors: usize) -> Vec { let size = nb_processors * mem::size_of::(); let mut infos: Vec = Vec::with_capacity(nb_processors); if unsafe { CallNtPowerInformation( ProcessorInformation, null_mut(), 0, infos.as_mut_ptr() as _, size as _, ) } == 0 { unsafe { infos.set_len(nb_processors); } // infos.Number infos .into_iter() .map(|i| i.CurrentMhz as u64) .collect::>() } else { vec![0; nb_processors] } } sysinfo-0.13.2/src/windows/system.rs010064400017500001750000000334161364506453400157040ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use sys::component::{self, Component}; use sys::disk::Disk; use sys::processor::*; use sys::users::get_users; use std::cell::UnsafeCell; use std::collections::HashMap; use std::mem::{size_of, zeroed}; use std::time::SystemTime; use LoadAvg; use Networks; use Pid; use ProcessExt; use RefreshKind; use SystemExt; use User; use windows::process::{ compute_cpu_usage, get_handle, get_system_computation_time, update_disk_usage, update_memory, Process, }; use windows::tools::*; use ntapi::ntexapi::{ NtQuerySystemInformation, SystemProcessInformation, SYSTEM_PROCESS_INFORMATION, }; use winapi::shared::minwindef::FALSE; use winapi::shared::ntdef::{PVOID, ULONG}; use winapi::shared::ntstatus::STATUS_INFO_LENGTH_MISMATCH; use winapi::um::minwinbase::STILL_ACTIVE; use winapi::um::processthreadsapi::GetExitCodeProcess; use winapi::um::sysinfoapi::{GetTickCount64, GlobalMemoryStatusEx, MEMORYSTATUSEX}; use winapi::um::winnt::HANDLE; use rayon::prelude::*; /// Struct containing the system's information. pub struct System { process_list: HashMap, mem_total: u64, mem_free: u64, swap_total: u64, swap_free: u64, global_processor: Processor, processors: Vec, components: Vec, disks: Vec, query: Option, networks: Networks, boot_time: u64, users: Vec, } // Useful for parallel iterations. struct Wrap(T); unsafe impl Send for Wrap {} unsafe impl Sync for Wrap {} unsafe fn boot_time() -> u64 { match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(n) => n.as_secs() - GetTickCount64() / 1000, Err(_e) => { sysinfo_debug!("Failed to compute boot time: {:?}", _e); 0 } } } impl SystemExt for System { #[allow(non_snake_case)] fn new_with_specifics(refreshes: RefreshKind) -> System { let (processors, vendor_id, brand) = init_processors(); let mut s = System { process_list: HashMap::with_capacity(500), mem_total: 0, mem_free: 0, swap_total: 0, swap_free: 0, global_processor: Processor::new_with_values("Total CPU", vendor_id, brand, 0), processors, components: Vec::new(), disks: Vec::with_capacity(2), query: Query::new(), networks: Networks::new(), boot_time: unsafe { boot_time() }, users: Vec::new(), }; // TODO: in case a translation fails, it might be nice to log it somewhere... if let Some(ref mut query) = s.query { let x = unsafe { load_symbols() }; if let Some(processor_trans) = get_translation(&"Processor".to_owned(), &x) { // let idle_time_trans = get_translation(&"% Idle Time".to_owned(), &x); let proc_time_trans = get_translation(&"% Processor Time".to_owned(), &x); if let Some(ref proc_time_trans) = proc_time_trans { add_counter( format!("\\{}(_Total)\\{}", processor_trans, proc_time_trans), query, get_key_used(&mut s.global_processor), "tot_0".to_owned(), ); } for (pos, proc_) in s.processors.iter_mut().enumerate() { if let Some(ref proc_time_trans) = proc_time_trans { add_counter( format!("\\{}({})\\{}", processor_trans, pos, proc_time_trans), query, get_key_used(proc_), format!("{}_0", pos), ); } } } else { sysinfo_debug!("failed to get `Processor` translation"); } } s.refresh_specifics(refreshes); s } fn refresh_cpu(&mut self) { if let Some(ref mut query) = self.query { query.refresh(); let mut used_time = None; if let &mut Some(ref key_used) = get_key_used(&mut self.global_processor) { used_time = Some( query .get(&key_used.unique_id) .expect("global_key_idle disappeared"), ); } if let Some(used_time) = used_time { self.global_processor.set_cpu_usage(used_time); } for p in self.processors.iter_mut() { let mut used_time = None; if let &mut Some(ref key_used) = get_key_used(p) { used_time = Some( query .get(&key_used.unique_id) .expect("key_used disappeared"), ); } if let Some(used_time) = used_time { p.set_cpu_usage(used_time); } } } } fn refresh_memory(&mut self) { unsafe { let mut mem_info: MEMORYSTATUSEX = zeroed(); mem_info.dwLength = size_of::() as u32; GlobalMemoryStatusEx(&mut mem_info); self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64); self.mem_free = auto_cast!(mem_info.ullAvailPhys, u64); //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64); //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64); } } fn refresh_components_list(&mut self) { self.components = component::get_components(); } fn refresh_process(&mut self, pid: Pid) -> bool { if self.process_list.contains_key(&pid) { if refresh_existing_process(self, pid) == false { self.process_list.remove(&pid); return false; } true } else if let Some(mut p) = Process::new_from_pid(pid) { let system_time = get_system_computation_time(); compute_cpu_usage(&mut p, self.processors.len() as u64, system_time); update_disk_usage(&mut p); self.process_list.insert(pid, p); true } else { false } } #[allow(clippy::cast_ptr_alignment)] fn refresh_processes(&mut self) { // Windows 10 notebook requires at least 512KiB of memory to make it in one go let mut buffer_size: usize = 512 * 1024; loop { let mut process_information: Vec = Vec::with_capacity(buffer_size); let mut cb_needed = 0; let ntstatus = unsafe { process_information.set_len(buffer_size); NtQuerySystemInformation( SystemProcessInformation, process_information.as_mut_ptr() as PVOID, buffer_size as ULONG, &mut cb_needed, ) }; if ntstatus != STATUS_INFO_LENGTH_MISMATCH { if ntstatus < 0 { sysinfo_debug!( "Couldn't get process infos: NtQuerySystemInformation returned {}", ntstatus ); } // Parse the data block to get process information let mut process_ids = Vec::with_capacity(500); let mut process_information_offset = 0; loop { let p = unsafe { process_information .as_ptr() .offset(process_information_offset) as *const SYSTEM_PROCESS_INFORMATION }; let pi = unsafe { &*p }; process_ids.push(Wrap(p)); if pi.NextEntryOffset == 0 { break; } process_information_offset += pi.NextEntryOffset as isize; } let nb_processors = self.processors.len() as u64; let process_list = Wrap(UnsafeCell::new(&mut self.process_list)); let system_time = get_system_computation_time(); // TODO: instead of using parallel iterator only here, would be better to be able // to run it over `process_information` directly! let processes = process_ids .into_par_iter() .filter_map(|pi| unsafe { let pi = *pi.0; let pid = pi.UniqueProcessId as usize; if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) { proc_.memory = (pi.WorkingSetSize as u64) >> 10u64; proc_.virtual_memory = (pi.VirtualSize as u64) >> 10u64; compute_cpu_usage(proc_, nb_processors, system_time); update_disk_usage(proc_); proc_.updated = true; return None; } let name = get_process_name(&pi, pid); let mut p = Process::new_full( pid, if pi.InheritedFromUniqueProcessId as usize != 0 { Some(pi.InheritedFromUniqueProcessId as usize) } else { None }, (pi.WorkingSetSize as u64) >> 10u64, (pi.VirtualSize as u64) >> 10u64, name, ); compute_cpu_usage(&mut p, nb_processors, system_time); update_disk_usage(&mut p); Some(p) }) .collect::>(); self.process_list.retain(|_, v| { let x = v.updated; v.updated = false; x }); for p in processes.into_iter() { self.process_list.insert(p.pid(), p); } break; } // GetNewBufferSize if cb_needed == 0 { buffer_size *= 2; continue; } // allocating a few more kilo bytes just in case there are some new process // kicked in since new call to NtQuerySystemInformation buffer_size = (cb_needed + (1024 * 10)) as usize; } } fn refresh_disks_list(&mut self) { self.disks = unsafe { get_disks() }; } fn refresh_users_list(&mut self) { self.users = unsafe { get_users() }; } fn get_processes(&self) -> &HashMap { &self.process_list } fn get_process(&self, pid: Pid) -> Option<&Process> { self.process_list.get(&(pid as usize)) } fn get_global_processor_info(&self) -> &Processor { &self.global_processor } fn get_processors(&self) -> &[Processor] { &self.processors } fn get_total_memory(&self) -> u64 { self.mem_total >> 10 } fn get_free_memory(&self) -> u64 { self.mem_free >> 10 } fn get_used_memory(&self) -> u64 { (self.mem_total - self.mem_free) >> 10 } fn get_total_swap(&self) -> u64 { self.swap_total >> 10 } fn get_free_swap(&self) -> u64 { self.swap_free >> 10 } fn get_used_swap(&self) -> u64 { (self.swap_total - self.swap_free) >> 10 } fn get_components(&self) -> &[Component] { &self.components } fn get_components_mut(&mut self) -> &mut [Component] { &mut self.components } fn get_disks(&self) -> &[Disk] { &self.disks } fn get_disks_mut(&mut self) -> &mut [Disk] { &mut self.disks } fn get_users(&self) -> &[User] { &self.users } fn get_networks(&self) -> &Networks { &self.networks } fn get_networks_mut(&mut self) -> &mut Networks { &mut self.networks } fn get_uptime(&self) -> u64 { unsafe { GetTickCount64() / 1000 } } fn get_boot_time(&self) -> u64 { self.boot_time } fn get_load_average(&self) -> LoadAvg { get_load_average() } } impl Default for System { fn default() -> System { System::new() } } fn is_proc_running(handle: HANDLE) -> bool { let mut exit_code = 0; let ret = unsafe { GetExitCodeProcess(handle, &mut exit_code) }; !(ret == FALSE || exit_code != STILL_ACTIVE) } fn refresh_existing_process(s: &mut System, pid: Pid) -> bool { if let Some(ref mut entry) = s.process_list.get_mut(&(pid as usize)) { if !is_proc_running(get_handle(entry)) { return false; } update_memory(entry); update_disk_usage(entry); compute_cpu_usage( entry, s.processors.len() as u64, get_system_computation_time(), ); true } else { false } } pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: usize) -> String { let name = &process.ImageName; if name.Buffer.is_null() { match process_id { 0 => "Idle".to_owned(), 4 => "System".to_owned(), _ => format!(" Process {}", process_id), } } else { let slice = unsafe { std::slice::from_raw_parts( name.Buffer, //The length is in bytes, not the length of string name.Length as usize / std::mem::size_of::(), ) }; String::from_utf16_lossy(slice) } } sysinfo-0.13.2/src/windows/tools.rs010064400017500001750000000237211363042323300155030ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // use windows::processor::{self, Processor, Query}; use sys::disk::{new_disk, Disk}; use DiskType; use std::collections::HashMap; use std::ffi::OsStr; use std::mem::{size_of, zeroed}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use winapi::ctypes::c_void; use winapi::shared::minwindef::{BYTE, DWORD, MAX_PATH, TRUE}; use winapi::um::fileapi::{ CreateFileW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW, OPEN_EXISTING, }; use winapi::um::handleapi::CloseHandle; use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::ioapiset::DeviceIoControl; use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO}; use winapi::um::winbase::DRIVE_FIXED; use winapi::um::winioctl::{ DISK_GEOMETRY, IOCTL_DISK_GET_DRIVE_GEOMETRY, IOCTL_STORAGE_QUERY_PROPERTY, }; use winapi::um::winnt::{BOOLEAN, FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE}; pub struct KeyHandler { pub unique_id: String, pub win_key: Vec, } impl KeyHandler { pub fn new(unique_id: String, win_key: Vec) -> KeyHandler { KeyHandler { unique_id: unique_id, win_key: win_key, } } } pub fn init_processors() -> (Vec, String, String) { unsafe { let mut sys_info: SYSTEM_INFO = zeroed(); GetSystemInfo(&mut sys_info); let (vendor_id, brand) = processor::get_vendor_id_and_brand(&sys_info); let frequencies = processor::get_frequencies(sys_info.dwNumberOfProcessors as usize); let mut ret = Vec::with_capacity(sys_info.dwNumberOfProcessors as usize + 1); for nb in 0..sys_info.dwNumberOfProcessors { ret.push(Processor::new_with_values( &format!("CPU {}", nb + 1), vendor_id.clone(), brand.clone(), frequencies[nb as usize], )); } (ret, vendor_id, brand) } } pub unsafe fn open_drive(drive_name: &[u16], open_rights: DWORD) -> HANDLE { CreateFileW( drive_name.as_ptr(), open_rights, FILE_SHARE_READ | FILE_SHARE_WRITE, ::std::ptr::null_mut(), OPEN_EXISTING, 0, ::std::ptr::null_mut(), ) } pub unsafe fn get_drive_size(handle: HANDLE) -> u64 { let mut pdg: DISK_GEOMETRY = ::std::mem::zeroed(); let mut junk = 0; let result = DeviceIoControl( handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, ::std::ptr::null_mut(), 0, &mut pdg as *mut DISK_GEOMETRY as *mut c_void, size_of::() as DWORD, &mut junk, ::std::ptr::null_mut(), ); if result == TRUE { *pdg.Cylinders.QuadPart() as u64 * pdg.TracksPerCylinder as u64 * pdg.SectorsPerTrack as u64 * pdg.BytesPerSector as u64 } else { 0 } } pub unsafe fn get_disks() -> Vec { let drives = GetLogicalDrives(); if drives == 0 { return Vec::new(); } (0..size_of::() * 8) .into_par_iter() .filter_map(|x| { if (drives >> x) & 1 == 0 { return None; } let mount_point = [b'A' as u16 + x as u16, b':' as u16, b'\\' as u16, 0]; if GetDriveTypeW(mount_point.as_ptr()) != DRIVE_FIXED { return None; } let mut name = [0u16; MAX_PATH + 1]; let mut file_system = [0u16; 32]; if GetVolumeInformationW( mount_point.as_ptr(), name.as_mut_ptr(), name.len() as DWORD, ::std::ptr::null_mut(), ::std::ptr::null_mut(), ::std::ptr::null_mut(), file_system.as_mut_ptr(), file_system.len() as DWORD, ) == 0 { return None; } let mut pos = 0; for x in name.iter() { if *x == 0 { break; } pos += 1; } let name = String::from_utf16_lossy(&name[..pos]); let name = OsStr::new(&name); pos = 0; for x in file_system.iter() { if *x == 0 { break; } pos += 1; } let file_system: Vec = file_system[..pos].iter().map(|x| *x as u8).collect(); let drive_name = [ b'\\' as u16, b'\\' as u16, b'.' as u16, b'\\' as u16, b'A' as u16 + x as u16, b':' as u16, 0, ]; let handle = open_drive(&drive_name, 0); if handle == INVALID_HANDLE_VALUE { CloseHandle(handle); return Some(new_disk( name, &mount_point, &file_system, DiskType::Unknown(-1), 0, )); } let disk_size = get_drive_size(handle); /*let mut spq_trim: ffi::STORAGE_PROPERTY_QUERY = ::std::mem::zeroed(); spq_trim.PropertyId = ffi::StorageDeviceTrimProperty; spq_trim.QueryType = ffi::PropertyStandardQuery; let mut dtd: ffi::DEVICE_TRIM_DESCRIPTOR = ::std::mem::zeroed();*/ #[allow(non_snake_case)] #[repr(C)] struct STORAGE_PROPERTY_QUERY { PropertyId: i32, QueryType: i32, AdditionalParameters: [BYTE; 1], } #[allow(non_snake_case)] #[repr(C)] struct DEVICE_TRIM_DESCRIPTOR { Version: DWORD, Size: DWORD, TrimEnabled: BOOLEAN, } let mut spq_trim = STORAGE_PROPERTY_QUERY { PropertyId: 8i32, QueryType: 0i32, AdditionalParameters: [0], }; let mut dtd: DEVICE_TRIM_DESCRIPTOR = ::std::mem::zeroed(); let mut dw_size = 0; if DeviceIoControl( handle, IOCTL_STORAGE_QUERY_PROPERTY, &mut spq_trim as *mut STORAGE_PROPERTY_QUERY as *mut c_void, size_of::() as DWORD, &mut dtd as *mut DEVICE_TRIM_DESCRIPTOR as *mut c_void, size_of::() as DWORD, &mut dw_size, ::std::ptr::null_mut(), ) == 0 || dw_size != size_of::() as DWORD { CloseHandle(handle); return Some(new_disk( name, &mount_point, &file_system, DiskType::Unknown(-1), disk_size, )); } let is_ssd = dtd.TrimEnabled != 0; CloseHandle(handle); Some(new_disk( name, &mount_point, &file_system, if is_ssd { DiskType::SSD } else { DiskType::HDD }, disk_size, )) }) .collect::>() } #[allow(non_snake_case)] pub unsafe fn load_symbols() -> HashMap { use winapi::um::winreg::{RegQueryValueExA, HKEY_PERFORMANCE_DATA}; let mut cbCounters = 0; let mut dwType = 0; let mut ret = HashMap::new(); let _dwStatus = RegQueryValueExA( HKEY_PERFORMANCE_DATA, b"Counter 009\0".as_ptr() as *const _, ::std::ptr::null_mut(), &mut dwType as *mut i32 as *mut _, ::std::ptr::null_mut(), &mut cbCounters as *mut i32 as *mut _, ); let mut lpmszCounters = Vec::with_capacity(cbCounters as usize); lpmszCounters.set_len(cbCounters as usize); let _dwStatus = RegQueryValueExA( HKEY_PERFORMANCE_DATA, b"Counter 009\0".as_ptr() as *const _, ::std::ptr::null_mut(), &mut dwType as *mut i32 as *mut _, lpmszCounters.as_mut_ptr(), &mut cbCounters as *mut i32 as *mut _, ); for (pos, s) in lpmszCounters .split(|x| *x == 0) .filter(|x| !x.is_empty()) .collect::>() .chunks(2) .filter(|&x| x.len() == 2) .filter_map( |x| match (std::str::from_utf8(x[0]), String::from_utf8(x[1].to_vec())) { (Ok(n), Ok(s)) => { if let Ok(n) = u32::from_str_radix(n, 10) { Some((n, s)) } else { None } } _ => None, }, ) { ret.insert(s, pos as u32); } ret } pub fn get_translation(s: &String, map: &HashMap) -> Option { use winapi::um::pdh::PdhLookupPerfNameByIndexW; if let Some(index) = map.get(s) { let mut size: usize = 0; unsafe { let _res = PdhLookupPerfNameByIndexW( ::std::ptr::null(), *index, ::std::ptr::null_mut(), &mut size as *mut usize as *mut _, ); if size == 0 { return Some(String::new()); } else { let mut v = Vec::with_capacity(size); v.set_len(size); let _res = PdhLookupPerfNameByIndexW( ::std::ptr::null(), *index, v.as_mut_ptr() as *mut _, &mut size as *mut usize as *mut _, ); return Some(String::from_utf16(&v[..size - 1]).expect("invalid utf16")); } } } None } pub fn add_counter( s: String, query: &mut Query, keys: &mut Option, counter_name: String, ) { let mut full = s.encode_utf16().collect::>(); full.push(0); if query.add_counter(&counter_name, full.clone()) { *keys = Some(KeyHandler::new(counter_name, full)); } } sysinfo-0.13.2/src/windows/users.rs010064400017500001750000000060101364506453400155070ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // use User; use winapi::shared::lmcons::MAX_PREFERRED_LENGTH; use winapi::shared::winerror::{ERROR_MORE_DATA, ERROR_SUCCESS}; use winapi::um::lmaccess::NetQueryDisplayInformation; use winapi::um::lmaccess::{ LG_INCLUDE_INDIRECT, LPLOCALGROUP_USERS_INFO_0, PNET_DISPLAY_USER, UF_NORMAL_ACCOUNT, }; use winapi::um::lmapibuf::NetApiBufferFree; use winapi::um::winnt::LPWSTR; use sys::ffi::NetUserGetLocalGroups; unsafe fn to_str(p: LPWSTR) -> String { let mut i = 0; let mut s = Vec::new(); loop { let c = *p.offset(i); if c == 0 { break; } s.push(c); i += 1; } String::from_utf16(&s).unwrap_or_else(|_e| { sysinfo_debug!("Failed to convert to UTF-16 string: {}", _e); String::new() }) } unsafe fn get_groups_for_user(username: LPWSTR) -> Vec { let mut buf: LPLOCALGROUP_USERS_INFO_0 = ::std::ptr::null_mut(); let mut nb_entries = 0; let mut total_entries = 0; let mut groups; let status = NetUserGetLocalGroups( [0u16].as_ptr(), username, 0, LG_INCLUDE_INDIRECT, &mut buf as *mut _ as _, MAX_PREFERRED_LENGTH, &mut nb_entries, &mut total_entries, ); if status == 0 { // NERR_Success groups = Vec::with_capacity(nb_entries as _); if !buf.is_null() { for i in 0..nb_entries { let tmp = buf.offset(i as _); if tmp.is_null() { break; } groups.push(to_str((*tmp).lgrui0_name)); } } } else { groups = Vec::new(); sysinfo_debug!("NetUserGetLocalGroups failed with ret code {}", status); } if !buf.is_null() { NetApiBufferFree(buf as *mut _); } groups } pub unsafe fn get_users() -> Vec { let mut users = Vec::new(); let mut i = 0; let mut buf: PNET_DISPLAY_USER = ::std::ptr::null_mut(); let mut nb_entries = 0; let mut res = ERROR_MORE_DATA; while res == ERROR_MORE_DATA { res = NetQueryDisplayInformation( [0u16].as_ptr(), 1, i, 100, MAX_PREFERRED_LENGTH, &mut nb_entries, &mut buf as *mut _ as _, ); if res == ERROR_SUCCESS || res == ERROR_MORE_DATA { for it in 0..nb_entries { let buf = buf.offset(it as _); if (*buf).usri1_flags & UF_NORMAL_ACCOUNT != 0 { let groups = get_groups_for_user((*buf).usri1_name); users.push(User { name: to_str((*buf).usri1_name), groups, }); } i = (*buf).usri1_next_index; } NetApiBufferFree(buf as *mut _); } else { sysinfo_debug!("NetQueryDisplayInformation failed with ret code {}", res); break; } } users } sysinfo-0.13.2/tests/disk_list.rs010064400017500001750000000003311364506453400152140ustar0000000000000000// // Sysinfo // // Copyright (c) 2017 Guillaume Gomez // extern crate sysinfo; #[test] fn test_disks() { use sysinfo::SystemExt; let s = sysinfo::System::new_all(); assert!(s.get_disks().len() > 0); } sysinfo-0.13.2/tests/network.rs010064400017500001750000000007031364337607700147310ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // // This test is used to ensure that the processors are loaded whatever the method // used to initialize `System`. extern crate sysinfo; #[test] fn test_processor() { use sysinfo::{NetworksExt, SystemExt}; let s = sysinfo::System::new(); assert_eq!(s.get_networks().iter().count(), 0); let s = sysinfo::System::new_all(); assert!(s.get_networks().iter().count() > 0); } sysinfo-0.13.2/tests/process.rs010064400017500001750000000054471364506453400147220ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // extern crate sysinfo; use sysinfo::ProcessExt; use sysinfo::SystemExt; #[test] fn test_process() { let mut s = sysinfo::System::new(); assert_eq!(s.get_processes().len(), 0); s.refresh_processes(); assert!(s.get_processes().len() != 0); #[cfg(not(windows))] assert!(s .get_processes() .values() .any(|p| p.exe().to_str().unwrap_or_else(|| "").len() != 0)); } #[test] fn test_process_refresh() { let mut s = sysinfo::System::new(); assert_eq!(s.get_processes().len(), 0); s.refresh_process(sysinfo::get_current_pid().expect("failed to get current pid")); assert_eq!( s.get_process(sysinfo::get_current_pid().expect("failed to get current pid")) .is_some(), true ); } #[test] #[cfg(windows)] fn test_get_cmd_line() { let p = std::process::Command::new("timeout") .arg("/t") .arg("3") .spawn() .unwrap(); let mut s = sysinfo::System::new(); assert!(s.get_processes().len() == 0); s.refresh_processes(); assert!(s.get_processes().len() > 0); if let Some(process) = s.get_process(p.id() as sysinfo::Pid) { assert_eq!(process.cmd(), &["timeout", "/t", "3"]); } else { // We're very likely on a "linux-like" shell so let's try some unix command... unix_like_cmd(); } } #[test] #[cfg(not(windows))] fn test_get_cmd_line() { unix_like_cmd(); } fn unix_like_cmd() { let p = std::process::Command::new("sleep") .arg("3") .spawn() .unwrap(); let mut s = sysinfo::System::new(); assert!(s.get_processes().len() == 0); s.refresh_processes(); assert!(s.get_processes().len() > 0); let process = s.get_process(p.id() as sysinfo::Pid).unwrap(); assert_eq!(process.cmd(), &["sleep", "3"]); } #[test] fn test_process_disk_usage() { use std::fs; use std::fs::File; use std::io::prelude::*; use sysinfo::{get_current_pid, ProcessExt, SystemExt}; { let mut file = File::create("test.txt").unwrap(); file.write_all(b"This is a test file\nwith test data.\n") .unwrap(); } fs::remove_file("test.txt").ok(); let mut system = sysinfo::System::new(); assert!(system.get_processes().len() == 0); system.refresh_processes(); assert!(system.get_processes().len() > 0); let p = system .get_process(get_current_pid().expect("Failed retrieving current pid.")) .expect("failed to get process"); assert!( p.disk_usage().total_written_bytes > 0, "found {} total written bytes...", p.disk_usage().total_written_bytes ); assert!( p.disk_usage().written_bytes > 0, "found {} written bytes...", p.disk_usage().written_bytes ); } sysinfo-0.13.2/tests/processor.rs010064400017500001750000000006441361731102400152410ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // // This test is used to ensure that the processors are loaded whatever the method // used to initialize `System`. extern crate sysinfo; #[test] fn test_processor() { use sysinfo::SystemExt; let s = sysinfo::System::new(); assert!(s.get_processors().len() > 0); let s = sysinfo::System::new_all(); assert!(s.get_processors().len() > 0); } sysinfo-0.13.2/tests/send_sync.rs010064400017500001750000000003611363516610000152060ustar0000000000000000// // Sysinfo // // Copyright (c) 2020 Guillaume Gomez // extern crate sysinfo; #[test] fn test_send_sync() { fn is_send() {} fn is_sync() {} is_send::(); is_sync::(); } sysinfo-0.13.2/tests/uptime.rs010064400017500001750000000003531364344617000145340ustar0000000000000000// // Sysinfo // // Copyright (c) 2018 Guillaume Gomez // extern crate sysinfo; #[test] fn test_uptime() { use sysinfo::SystemExt; let mut s = sysinfo::System::new(); s.refresh_all(); assert!(s.get_uptime() != 0); }