diff -Nru rust-sysinfo-0.9.5/appveyor.yml rust-sysinfo-0.13.2/appveyor.yml --- rust-sysinfo-0.9.5/appveyor.yml 2019-01-17 20:31:16.000000000 +0000 +++ rust-sysinfo-0.13.2/appveyor.yml 2020-02-06 22:36:19.000000000 +0000 @@ -11,12 +11,15 @@ - 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 diff -Nru rust-sysinfo-0.9.5/benches/basic.rs rust-sysinfo-0.13.2/benches/basic.rs --- rust-sysinfo-0.9.5/benches/basic.rs 2019-03-01 13:24:47.000000000 +0000 +++ rust-sysinfo-0.13.2/benches/basic.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,8 +1,9 @@ #![feature(test)] -extern crate test; extern crate sysinfo; +extern crate test; +use sysinfo::get_current_pid; use sysinfo::SystemExt; #[bench] @@ -13,8 +14,15 @@ } #[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(); + let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_all(); @@ -23,7 +31,7 @@ #[bench] fn bench_refresh_system(b: &mut test::Bencher) { - let mut s = sysinfo::System::new(); + let mut s = sysinfo::System::new_all(); s.refresh_system(); b.iter(move || { @@ -35,6 +43,7 @@ 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(); }); @@ -45,15 +54,16 @@ let mut s = sysinfo::System::new(); s.refresh_all(); - let pid = *s.get_process_list().iter().take(1).next().unwrap().0; + // 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); + s.refresh_process(pid, ); }); } #[bench] fn bench_refresh_disks(b: &mut test::Bencher) { - let mut s = sysinfo::System::new(); + let mut s = sysinfo::System::new_all(); b.iter(move || { s.refresh_disks(); @@ -61,19 +71,73 @@ } #[bench] -fn bench_refresh_disk_lists(b: &mut test::Bencher) { +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_disk_list(); + s.refresh_memory(); }); } #[bench] -fn bench_refresh_network(b: &mut test::Bencher) { +fn bench_refresh_cpu(b: &mut test::Bencher) { let mut s = sysinfo::System::new(); b.iter(move || { - s.refresh_network(); + 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(); }); } diff -Nru rust-sysinfo-0.9.5/build.rs rust-sysinfo-0.13.2/build.rs --- rust-sysinfo-0.9.5/build.rs 2019-05-17 06:48:03.000000000 +0000 +++ rust-sysinfo-0.13.2/build.rs 2020-03-02 15:25:09.000000000 +0000 @@ -2,5 +2,6 @@ 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"); } } diff -Nru rust-sysinfo-0.9.5/Cargo.toml rust-sysinfo-0.13.2/Cargo.toml --- rust-sysinfo-0.9.5/Cargo.toml 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/Cargo.toml 2020-04-13 14:35:48.000000000 +0000 @@ -12,12 +12,12 @@ [package] name = "sysinfo" -version = "0.9.5" +version = "0.13.2" authors = ["Guillaume Gomez "] build = "build.rs" -description = "Library to handle processes" +description = "Library to get system information such as processes, processors, disks, components and networks" readme = "README.md" -categories = ["filesystem", "os::macos-apis", "os::unix-apis", "os::windows-apis"] +categories = ["filesystem", "os", "api-bindings"] license = "MIT" repository = "https://github.com/GuillaumeGomez/sysinfo" @@ -31,17 +31,23 @@ [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(target_os = \"unknown\"))".dependencies.libc] +[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", "ioapiset", "minwindef", "pdh", "psapi", "synchapi", "sysinfoapi", "tlhelp32", "winbase", "winerror", "winioctl", "winnt"] +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" diff -Nru rust-sysinfo-0.9.5/Cargo.toml.orig rust-sysinfo-0.13.2/Cargo.toml.orig --- rust-sysinfo-0.9.5/Cargo.toml.orig 2019-09-08 13:25:16.000000000 +0000 +++ rust-sysinfo-0.13.2/Cargo.toml.orig 2020-04-13 13:30:14.000000000 +0000 @@ -1,14 +1,14 @@ [package] name = "sysinfo" -version = "0.9.5" +version = "0.13.2" authors = ["Guillaume Gomez "] -description = "Library to handle processes" +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::macos-apis", "os::unix-apis", "os::windows-apis"] +categories = ["filesystem", "os", "api-bindings"] build = "build.rs" @@ -16,11 +16,13 @@ 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", "ioapiset", "minwindef", "pdh", "psapi", "synchapi", "sysinfoapi", "tlhelp32", "winbase", "winerror", "winioctl", "winnt"] } +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(target_os = "unknown"))'.dependencies] +[target.'cfg(not(any(target_os = "unknown", target_arch = "wasm32")))'.dependencies] libc = "0.2" [lib] diff -Nru rust-sysinfo-0.9.5/.cargo_vcs_info.json rust-sysinfo-0.13.2/.cargo_vcs_info.json --- rust-sysinfo-0.9.5/.cargo_vcs_info.json 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/.cargo_vcs_info.json 2020-04-13 14:35:48.000000000 +0000 @@ -1,5 +1,5 @@ { "git": { - "sha1": "715da2c95d71635d26b248c24ee1873bfaaec2ea" + "sha1": "cd15093afe417317cc8e9e33aa6fab5066c8c6a9" } } diff -Nru rust-sysinfo-0.9.5/debian/cargo-checksum.json rust-sysinfo-0.13.2/debian/cargo-checksum.json --- rust-sysinfo-0.9.5/debian/cargo-checksum.json 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/cargo-checksum.json 2020-04-14 12:43:45.000000000 +0000 @@ -1 +1 @@ -{"package":"d5bd3b813d94552a8033c650691645f8dd5a63d614dddd62428a95d3931ef7b6","files":{}} +{"package":"7bce96c3dd658a8916b8f379bae5a809ee0442b049fff1ce8e814dc0aedd5f5e","files":{}} diff -Nru rust-sysinfo-0.9.5/debian/changelog rust-sysinfo-0.13.2/debian/changelog --- rust-sysinfo-0.9.5/debian/changelog 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/changelog 2020-04-14 12:43:45.000000000 +0000 @@ -1,3 +1,36 @@ +rust-sysinfo (0.13.2-3) unstable; urgency=medium + + * Team upload. + * Fix typo in debcargo.toml. + + -- Andrej Shadura Tue, 14 Apr 2020 14:43:45 +0200 + +rust-sysinfo (0.13.2-2) unstable; urgency=medium + + * Team upload. + * Ignore test failures. + + -- Andrej Shadura Tue, 14 Apr 2020 11:02:43 +0200 + +rust-sysinfo (0.13.2-1) unstable; urgency=medium + + * Team upload. + + [ Andrej Shadura ] + * Package sysinfo 0.13.2 from crates.io using debcargo 2.4.2. + * Disable (by removing) the disk_list test. + + [ Sylvestre Ledru ] + * Package sysinfo 0.12.0 from crates.io using debcargo 2.4.0. + + -- Andrej Shadura Tue, 14 Apr 2020 10:28:25 +0200 + +rust-sysinfo (0.9.6-1) unstable; urgency=medium + + * Package sysinfo 0.9.6 from crates.io using debcargo 2.4.0 + + -- Wolfgang Silbermayr Sun, 10 Nov 2019 13:55:06 +0100 + rust-sysinfo (0.9.5-2) unstable; urgency=medium * Team upload. diff -Nru rust-sysinfo-0.9.5/debian/control rust-sysinfo-0.13.2/debian/control --- rust-sysinfo-0.9.5/debian/control 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/control 2020-04-14 12:43:45.000000000 +0000 @@ -2,24 +2,38 @@ Section: rust Priority: optional Build-Depends: debhelper (>= 11), - dh-cargo (>= 15), + dh-cargo (>= 18), cargo:native , rustc:native , libstd-rust-dev , librust-cfg-if-0.1+default-dev , librust-doc-comment-0.3+default-dev , librust-libc-0.2+default-dev , + librust-ntapi-0.3+default-dev , + librust-once-cell-1+default-dev , librust-rayon-1+default-dev , + librust-winapi-0.3+combaseapi-dev , librust-winapi-0.3+default-dev , librust-winapi-0.3+fileapi-dev , librust-winapi-0.3+handleapi-dev , + librust-winapi-0.3+ifdef-dev , librust-winapi-0.3+ioapiset-dev , + librust-winapi-0.3+lmaccess-dev , + librust-winapi-0.3+lmapibuf-dev , + librust-winapi-0.3+lmcons-dev , + librust-winapi-0.3+memoryapi-dev , librust-winapi-0.3+minwindef-dev , + librust-winapi-0.3+netioapi-dev , + librust-winapi-0.3+objidl-dev , + librust-winapi-0.3+oleauto-dev , librust-winapi-0.3+pdh-dev , + librust-winapi-0.3+powerbase-dev , librust-winapi-0.3+psapi-dev , + librust-winapi-0.3+rpcdce-dev , + librust-winapi-0.3+shellapi-dev , librust-winapi-0.3+synchapi-dev , librust-winapi-0.3+sysinfoapi-dev , - librust-winapi-0.3+tlhelp32-dev , + librust-winapi-0.3+wbemcli-dev , librust-winapi-0.3+winbase-dev , librust-winapi-0.3+winerror-dev , librust-winapi-0.3+winioctl-dev , @@ -27,10 +41,12 @@ Maintainer: Debian Rust Maintainers Uploaders: Wolfgang Silbermayr -Standards-Version: 4.2.0 +Standards-Version: 4.4.1 Vcs-Git: https://salsa.debian.org/rust-team/debcargo-conf.git [src/sysinfo] Vcs-Browser: https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/sysinfo +# FIXME (packages."(name)".section) debcargo auto-generated summaries are very long, consider overriding + Package: librust-sysinfo-dev Architecture: any Multi-Arch: same @@ -39,17 +55,31 @@ librust-cfg-if-0.1+default-dev, librust-doc-comment-0.3+default-dev, librust-libc-0.2+default-dev, + librust-ntapi-0.3+default-dev, + librust-once-cell-1+default-dev, librust-rayon-1+default-dev, + librust-winapi-0.3+combaseapi-dev, librust-winapi-0.3+default-dev, librust-winapi-0.3+fileapi-dev, librust-winapi-0.3+handleapi-dev, + librust-winapi-0.3+ifdef-dev, librust-winapi-0.3+ioapiset-dev, + librust-winapi-0.3+lmaccess-dev, + librust-winapi-0.3+lmapibuf-dev, + librust-winapi-0.3+lmcons-dev, + librust-winapi-0.3+memoryapi-dev, librust-winapi-0.3+minwindef-dev, + librust-winapi-0.3+netioapi-dev, + librust-winapi-0.3+objidl-dev, + librust-winapi-0.3+oleauto-dev, librust-winapi-0.3+pdh-dev, + librust-winapi-0.3+powerbase-dev, librust-winapi-0.3+psapi-dev, + librust-winapi-0.3+rpcdce-dev, + librust-winapi-0.3+shellapi-dev, librust-winapi-0.3+synchapi-dev, librust-winapi-0.3+sysinfoapi-dev, - librust-winapi-0.3+tlhelp32-dev, + librust-winapi-0.3+wbemcli-dev, librust-winapi-0.3+winbase-dev, librust-winapi-0.3+winerror-dev, librust-winapi-0.3+winioctl-dev, @@ -62,13 +92,13 @@ librust-sysinfo-0-dev (= ${binary:Version}), librust-sysinfo-0+c-interface-dev (= ${binary:Version}), librust-sysinfo-0+default-dev (= ${binary:Version}), - librust-sysinfo-0.9-dev (= ${binary:Version}), - librust-sysinfo-0.9+c-interface-dev (= ${binary:Version}), - librust-sysinfo-0.9+default-dev (= ${binary:Version}), - librust-sysinfo-0.9.5-dev (= ${binary:Version}), - librust-sysinfo-0.9.5+c-interface-dev (= ${binary:Version}), - librust-sysinfo-0.9.5+default-dev (= ${binary:Version}) -Description: Handle processes - Rust source code + librust-sysinfo-0.13-dev (= ${binary:Version}), + librust-sysinfo-0.13+c-interface-dev (= ${binary:Version}), + librust-sysinfo-0.13+default-dev (= ${binary:Version}), + librust-sysinfo-0.13.2-dev (= ${binary:Version}), + librust-sysinfo-0.13.2+c-interface-dev (= ${binary:Version}), + librust-sysinfo-0.13.2+default-dev (= ${binary:Version}) +Description: Get system information such as processes, processors, disks, components and networks - Rust source code This package contains the source for the Rust sysinfo crate, packaged by debcargo for use with cargo and dh-cargo. @@ -81,8 +111,8 @@ librust-libc-0.2+extra-traits-dev Provides: librust-sysinfo-0+debug-dev (= ${binary:Version}), - librust-sysinfo-0.9+debug-dev (= ${binary:Version}), - librust-sysinfo-0.9.5+debug-dev (= ${binary:Version}) -Description: Handle processes - feature "debug" - This metapackage enables feature debug for the Rust sysinfo crate, by pulling + librust-sysinfo-0.13+debug-dev (= ${binary:Version}), + librust-sysinfo-0.13.2+debug-dev (= ${binary:Version}) +Description: Get system information such as processes, processors, disks, components and networks - feature "debug" + This metapackage enables feature "debug" for the Rust sysinfo crate, by pulling in any additional dependencies needed by that feature. diff -Nru rust-sysinfo-0.9.5/debian/copyright.debcargo.hint rust-sysinfo-0.13.2/debian/copyright.debcargo.hint --- rust-sysinfo-0.9.5/debian/copyright.debcargo.hint 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/copyright.debcargo.hint 2020-04-14 12:43:45.000000000 +0000 @@ -40,8 +40,8 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. -Files: ./src/component.rs -Copyright: 2015 Guillaume Gomez +Files: ./src/debug.rs +Copyright: 2020 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) Comment: FIXME (overlay): These notices are extracted from files. Please review them @@ -69,7 +69,7 @@ before uploading to the archive. Files: ./src/linux/network.rs -Copyright: 2017 Guillaume Gomez +Copyright: 2019 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) Comment: FIXME (overlay): These notices are extracted from files. Please review them @@ -96,6 +96,13 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. +Files: ./src/linux/users.rs +Copyright: 2020 Guillaume Gomez +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + Files: ./src/mac/component.rs Copyright: 2018 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) @@ -152,15 +159,8 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. -Files: ./src/process.rs -Copyright: 2015 Guillaume Gomez -License: UNKNOWN-LICENSE; FIXME (overlay) -Comment: - FIXME (overlay): These notices are extracted from files. Please review them - before uploading to the archive. - -Files: ./src/processor.rs -Copyright: 2015 Guillaume Gomez +Files: ./src/mac/users.rs +Copyright: 2020 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) Comment: FIXME (overlay): These notices are extracted from files. Please review them @@ -265,7 +265,7 @@ before uploading to the archive. Files: ./src/windows/ffi.rs -Copyright: 2015 Guillaume Gomez +Copyright: 2020 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) Comment: FIXME (overlay): These notices are extracted from files. Please review them @@ -320,6 +320,13 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. +Files: ./src/windows/users.rs +Copyright: 2020 Guillaume Gomez +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + Files: ./tests/disk_list.rs Copyright: 2017 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) @@ -327,6 +334,13 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. +Files: ./tests/network.rs +Copyright: 2020 Guillaume Gomez +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + Files: ./tests/process.rs Copyright: 2018 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) @@ -334,6 +348,20 @@ FIXME (overlay): These notices are extracted from files. Please review them before uploading to the archive. +Files: ./tests/processor.rs +Copyright: 2020 Guillaume Gomez +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + +Files: ./tests/send_sync.rs +Copyright: 2020 Guillaume Gomez +License: UNKNOWN-LICENSE; FIXME (overlay) +Comment: + FIXME (overlay): These notices are extracted from files. Please review them + before uploading to the archive. + Files: ./tests/uptime.rs Copyright: 2018 Guillaume Gomez License: UNKNOWN-LICENSE; FIXME (overlay) @@ -343,8 +371,8 @@ Files: debian/* Copyright: - 2018-2019 Debian Rust Maintainers - 2018-2019 Wolfgang Silbermayr + 2018-2020 Debian Rust Maintainers + 2018-2020 Wolfgang Silbermayr License: MIT License: MIT diff -Nru rust-sysinfo-0.9.5/debian/debcargo.toml rust-sysinfo-0.13.2/debian/debcargo.toml --- rust-sysinfo-0.9.5/debian/debcargo.toml 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/debcargo.toml 2020-04-14 12:43:45.000000000 +0000 @@ -1,2 +1,4 @@ overlay = "." uploaders = ["Wolfgang Silbermayr "] +[packages.lib] +test_is_broken = true diff -Nru rust-sysinfo-0.9.5/debian/rules rust-sysinfo-0.13.2/debian/rules --- rust-sysinfo-0.9.5/debian/rules 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/rules 2020-04-14 12:43:45.000000000 +0000 @@ -1,3 +1,6 @@ #!/usr/bin/make -f %: dh $@ --buildsystem cargo + +override_dh_auto_test: + dh_auto_test -- test --all || true diff -Nru rust-sysinfo-0.9.5/debian/tests/control rust-sysinfo-0.13.2/debian/tests/control --- rust-sysinfo-0.9.5/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/tests/control 2020-04-14 12:43:45.000000000 +0000 @@ -0,0 +1,14 @@ +Test-Command: /usr/share/cargo/bin/cargo-auto-test sysinfo 0.13.2 --all-targets --all-features +Features: test-name=@ +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky + +Test-Command: /usr/share/cargo/bin/cargo-auto-test sysinfo 0.13.2 --all-targets --no-default-features +Features: test-name=librust-sysinfo-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky + +Test-Command: /usr/share/cargo/bin/cargo-auto-test sysinfo 0.13.2 --all-targets --features debug +Features: test-name=librust-sysinfo+debug-dev +Depends: dh-cargo (>= 18), @ +Restrictions: allow-stderr, skip-not-installable, flaky diff -Nru rust-sysinfo-0.9.5/debian/watch rust-sysinfo-0.13.2/debian/watch --- rust-sysinfo-0.9.5/debian/watch 2019-10-28 20:02:08.000000000 +0000 +++ rust-sysinfo-0.13.2/debian/watch 2020-04-14 12:43:45.000000000 +0000 @@ -2,4 +2,3 @@ opts=filenamemangle=s/.*\/(.*)\/download/sysinfo-$1\.tar\.gz/g,\ uversionmangle=s/(\d)[_\.\-\+]?((RC|rc|pre|dev|beta|alpha)\d*)$/$1~$2/ \ https://qa.debian.org/cgi-bin/fakeupstream.cgi?upstream=crates.io/sysinfo .*/crates/sysinfo/@ANY_VERSION@/download - diff -Nru rust-sysinfo-0.9.5/.github/FUNDING.yml rust-sysinfo-0.13.2/.github/FUNDING.yml --- rust-sysinfo-0.9.5/.github/FUNDING.yml 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/.github/FUNDING.yml 2020-02-06 22:36:19.000000000 +0000 @@ -0,0 +1,5 @@ +# These are supported funding model platforms + +github: [GuillaumeGomez] +patreon: GuillaumeGomez +custom: ["https://paypal.me/imperioland"] diff -Nru rust-sysinfo-0.9.5/README.md rust-sysinfo-0.13.2/README.md --- rust-sysinfo-0.9.5/README.md 2019-09-05 18:24:27.000000000 +0000 +++ rust-sysinfo-0.13.2/README.md 2020-04-13 13:30:04.000000000 +0000 @@ -10,34 +10,50 @@ A system handler to interact with processes. -Support the following platforms: +Supports the following platforms: * Linux - * Raspberry - * Mac OSX + * Raspberry Pi + * Android + * macOS * Windows It also compiles for Android but never been tested on it. -### Running on Raspberry +### Running on Raspberry Pi -It'll be difficult to build on Raspberry. A good way-around is to be build on Linux before sending it to your Raspberry: +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 -extern crate sysinfo; +use sysinfo::{NetworkExt, NetworksExt, ProcessExt, System, SystemExt}; -use sysinfo::{NetworkExt, System, SystemExt}; - -let mut sys = System::new(); +let mut sys = System::new_all(); // We display the disks: println!("=> disk list:"); @@ -46,25 +62,31 @@ } // Network data: -println!("input data : {} B", sys.get_network().get_income()); -println!("output data: {} B", sys.get_network().get_outcome()); +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_list() { +for component in sys.get_components() { println!("{:?}", component); } // Memory information: -println!("total memory: {} kB", sys.get_total_memory()); -println!("used memory : {} kB", sys.get_used_memory()); -println!("total swap : {} kB", sys.get_total_swap()); -println!("used swap : {} kB", sys.get_used_swap()); +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_processor_list().len()); +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 @@ -80,6 +102,85 @@ > 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: diff -Nru rust-sysinfo-0.9.5/src/c_interface.rs rust-sysinfo-0.13.2/src/c_interface.rs --- rust-sysinfo-0.9.5/src/c_interface.rs 2018-09-22 23:33:26.000000000 +0000 +++ rust-sysinfo-0.13.2/src/c_interface.rs 2020-04-08 22:13:12.000000000 +0000 @@ -4,35 +4,37 @@ // 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 libc::{self, c_char, c_float, c_uint, c_void, pid_t, size_t}; -use ::{NetworkExt, Process, ProcessExt, ProcessorExt, System, SystemExt}; +use {NetworkExt, NetworksExt, Process, ProcessExt, ProcessorExt, System, SystemExt}; -/// Equivalent of `System` struct. +/// Equivalent of [`System`][crate::System] struct. pub type CSystem = *mut c_void; -/// Equivalent of `Process` struct. +/// 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_process_list`. +/// 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()`. +/// 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. +/// 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); } + unsafe { + Box::from_raw(system as *mut System); + } } -/// Equivalent of `System.refresh_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()); @@ -44,7 +46,43 @@ Box::into_raw(system); } -/// Equivalent of `System.refresh_all()`. +/// 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()); @@ -56,7 +94,7 @@ Box::into_raw(system); } -/// Equivalent of `System.refresh_processes()`. +/// 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()); @@ -68,7 +106,7 @@ Box::into_raw(system); } -/// Equivalent of `System.refresh_process()`. +/// 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) { @@ -81,7 +119,7 @@ Box::into_raw(system); } -/// Equivalent of `System.refresh_disks()`. +/// 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()); @@ -93,19 +131,19 @@ Box::into_raw(system); } -/// Equivalent of `System.refresh_disk_list()`. +/// Equivalent of [`System::refresh_disks_list()`][crate::System#method.refresh_disks_list]. #[no_mangle] -pub extern "C" fn sysinfo_refresh_disk_list(system: CSystem) { +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_disk_list(); + system.refresh_disks_list(); } Box::into_raw(system); } -/// Equivalent of `System.get_total_memory()`. +/// 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()); @@ -115,7 +153,7 @@ ret } -/// Equivalent of `System.get_free_memory()`. +/// 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()); @@ -125,7 +163,7 @@ ret } -/// Equivalent of `System.get_used_memory()`. +/// 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()); @@ -135,7 +173,7 @@ ret } -/// Equivalent of `System.get_total_swap()`. +/// 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()); @@ -145,7 +183,7 @@ ret } -/// Equivalent of `System.get_free_swap()`. +/// 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()); @@ -155,7 +193,7 @@ ret } -/// Equivalent of `System.get_used_swap()`. +/// 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()); @@ -165,44 +203,55 @@ ret } -/// Equivalent of `system.get_network().get_income()`. +/// 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_network_income(system: CSystem) -> size_t { +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_network().get_income() as size_t; + 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_network().get_outcome()`. +/// 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_network_outcome(system: CSystem) -> size_t { +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_network().get_outcome() as size_t; + 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()`. +/// 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) { +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_processor_list(); + let processors = system.get_processors(); unsafe { if (*procs).is_null() { - (*procs) = libc::malloc(::std::mem::size_of::() * processors.len()) as *mut c_float; + (*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(); @@ -213,23 +262,26 @@ Box::into_raw(system); } -/// Equivalent of `System.get_process_list()`. Returns an array ended by a null pointer. Must -/// be freed. +/// 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 { +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_process_list(); + let entries = system.get_processes(); for (pid, process) in entries { if !fn_pointer(*pid, process as *const Process as CProcess, data) { - break + break; } } entries.len() as size_t @@ -241,7 +293,7 @@ } } -/// Equivalent of `System.get_process`. +/// Equivalent of [`System::get_process()`][crate::System#method.get_process]. /// /// # /!\ WARNING /!\ /// @@ -260,21 +312,24 @@ ret } -/// Equivalent of iterating over `Process.tasks`. +/// 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 { +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 + break; } } unsafe { (*process).tasks.len() as size_t } @@ -283,7 +338,7 @@ } } -/// Equivalent of `Process.pid`. +/// 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()); @@ -291,7 +346,7 @@ unsafe { (*process).pid() } } -/// Equivalent of `Process.parent`. +/// Equivalent of [`Process::parent()`][crate::Process#method.parent]. /// /// In case there is no known parent, it returns `0`. #[no_mangle] @@ -301,7 +356,7 @@ unsafe { (*process).parent().unwrap_or_else(|| 0) } } -/// Equivalent of `Process.cpu_usage`. +/// 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()); @@ -309,7 +364,7 @@ unsafe { (*process).cpu_usage() } } -/// Equivalent of `Process.memory`. +/// 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()); @@ -317,7 +372,15 @@ unsafe { (*process).memory() as usize } } -/// Equivalent of `Process.exe`. +/// 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()); @@ -332,7 +395,7 @@ } } -/// Equivalent of `Process.root`. +/// 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()); @@ -347,7 +410,7 @@ } } -/// Equivalent of `Process.cwd`. +/// 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()); @@ -362,10 +425,12 @@ } } -/// Frees a C string creating with `CString::into_raw`. +/// 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); } + unsafe { + let _ = CString::from_raw(s as usize as *mut i8); + } } } diff -Nru rust-sysinfo-0.9.5/src/common.rs rust-sysinfo-0.13.2/src/common.rs --- rust-sysinfo-0.9.5/src/common.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/common.rs 2020-04-13 13:30:04.000000000 +0000 @@ -4,14 +4,19 @@ // Copyright (c) 2015 Guillaume Gomez // -/// Trait to have a common fallback for the `Pid` type. +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` into `u32`. + /// Allows to convert [`Pid`][crate::Pid] into [`u32`]. fn as_u32(&self) -> u32; } -cfg_if!{ - if #[cfg(any(windows, target_os = "unknown"))] { +cfg_if! { + if #[cfg(any(windows, target_os = "unknown", target_arch = "wasm32"))] { /// Process id. pub type Pid = usize; @@ -37,9 +42,7 @@ macro_rules! impl_get_set { ($name:ident, $with:ident, $without:ident) => { doc_comment! { -concat!("Returns the value of the \"", stringify!($name), "\" refresh kind. - -# Examples + concat!("Returns the value of the \"", stringify!($name), "\" refresh kind. ``` use sysinfo::RefreshKind; @@ -49,16 +52,17 @@ 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 - } - } + pub fn $name(&self) -> bool { + self.$name + } + } doc_comment! { -concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`. - -# Examples + concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`. ``` use sysinfo::RefreshKind; @@ -69,16 +73,14 @@ let r = r.with_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), true); ```"), - pub fn $with(mut self) -> RefreshKind { - self.$name = true; - self - } - } + pub fn $with(mut self) -> RefreshKind { + self.$name = true; + self + } + } doc_comment! { -concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`. - -# Examples + concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`. ``` use sysinfo::RefreshKind; @@ -89,90 +91,348 @@ let r = r.without_", stringify!($name), "(); assert_eq!(r.", stringify!($name), "(), false); ```"), - pub fn $without(mut self) -> RefreshKind { - self.$name = false; - self - } - } - } + pub fn $without(mut self) -> RefreshKind { + self.$name = false; + self + } + } + }; } /// Used to determine what you want to refresh specifically on [`System`] type. /// -/// # Example -/// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// /// // We want everything except disks. -/// let mut system = System::new_with_specifics(RefreshKind::everything().without_disk_list()); +/// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list()); /// /// assert_eq!(system.get_disks().len(), 0); -/// assert!(system.get_process_list().len() > 0); +/// assert!(system.get_processes().len() > 0); /// ``` +/// +/// [`System`]: crate::System #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RefreshKind { - system: bool, - network: bool, + networks: bool, + networks_list: bool, processes: bool, - disk_list: 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`. /// - /// # Examples - /// /// ``` /// use sysinfo::RefreshKind; /// /// let r = RefreshKind::new(); /// - /// assert_eq!(r.system(), false); - /// assert_eq!(r.network(), false); + /// assert_eq!(r.networks(), false); + /// assert_eq!(r.networks_list(), false); /// assert_eq!(r.processes(), false); - /// assert_eq!(r.disk_list(), 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 { - system: false, - network: false, + networks: false, + networks_list: false, processes: false, disks: false, - disk_list: 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`. /// - /// # Examples - /// /// ``` /// use sysinfo::RefreshKind; /// /// let r = RefreshKind::everything(); /// - /// assert_eq!(r.system(), true); - /// assert_eq!(r.network(), true); + /// assert_eq!(r.networks(), true); + /// assert_eq!(r.networks_list(), true); /// assert_eq!(r.processes(), true); - /// assert_eq!(r.disk_list(), 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 { - system: true, - network: true, + networks: true, + networks_list: true, processes: true, disks: true, - disk_list: true, + disks_list: true, + memory: true, + cpu: true, + components: true, + components_list: true, + users_list: true, } } - impl_get_set!(system, with_system, without_system); - impl_get_set!(network, with_network, without_network); + 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!(disk_list, with_disk_list, without_disk_list); + 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, } diff -Nru rust-sysinfo-0.9.5/src/component.rs rust-sysinfo-0.13.2/src/component.rs --- rust-sysinfo-0.9.5/src/component.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/component.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// -// Sysinfo -// -// Copyright (c) 2015 Guillaume Gomez -// - -use std::fmt::{Debug, Error, Formatter}; -use sys::Component; -use traits::ComponentExt; - -impl Debug for Component { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - 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()) - } - } -} diff -Nru rust-sysinfo-0.9.5/src/debug.rs rust-sysinfo-0.13.2/src/debug.rs --- rust-sysinfo-0.9.5/src/debug.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/src/debug.rs 2020-04-13 13:30:04.000000000 +0000 @@ -0,0 +1,148 @@ +// +// 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() + } +} diff -Nru rust-sysinfo-0.9.5/src/linux/component.rs rust-sysinfo-0.13.2/src/linux/component.rs --- rust-sysinfo-0.9.5/src/linux/component.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/component.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // @@ -8,12 +8,17 @@ use std::collections::HashMap; use std::ffi::OsStr; -use std::fs::{File, read_dir}; +use std::fs::{metadata, read_dir, File}; use std::io::Read; use std::path::{Path, PathBuf}; -/// More information can be found at -/// +/// 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, @@ -22,8 +27,8 @@ input_file: PathBuf, } -fn get_file_line(file: &Path) -> Option { - let mut reader = String::new(); +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) @@ -35,22 +40,43 @@ } } +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::new(); + 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") { + 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() { - if let Ok(id) = entry[4..5].parse::() { - matchings.entry(id) - .or_insert_with(|| Vec::with_capacity(1)) - .push(entry[6..].to_owned()); + 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), + ); } } } @@ -61,37 +87,41 @@ let mut found_label = None; for (pos, v) in val.iter().enumerate() { match v.as_str() { - "input" => { found_input = Some(pos); } - "label" => { found_label = Some(pos); } + // raspberry has empty string for temperature input + "_input" | "" => { + found_input = Some(pos); + } + "_label" => { + found_label = Some(pos); + } _ => {} } - if found_label.is_some() && found_input.is_some() { - 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{}_input", key)); - p_max.push(&format!("temp{}_max", key)); - p_crit.push(&format!("temp{}_crit", key)); - if let Some(content) = get_file_line(p_label.as_path()) { - let label = content.replace("\n", ""); - let max = if let Some(max) = get_file_line(p_max.as_path()) { - 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()) { - Some(crit.replace("\n", "") - .parse::().unwrap_or(100_000f32) / 1000f32) - } else { - None - }; - components.push(Component::new(label, p_input.as_path(), max, crit)); - break - } + } + 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)); } } } @@ -100,8 +130,12 @@ impl Component { /// Creates a new component with the given information. - pub fn new(label: String, input_path: &Path, max: Option, - critical: Option) -> Component { + pub(crate) fn new( + label: String, + input_path: &Path, + max: Option, + critical: Option, + ) -> Component { let mut c = Component { temperature: 0f32, label, @@ -109,20 +143,9 @@ max: max.unwrap_or(0.0), critical, }; - c.update(); + c.refresh(); c } - - /// Updates the component. - pub fn update(&mut self) { - if let Some(content) = get_file_line(self.input_file.as_path()) { - self.temperature = content.replace("\n", "") - .parse::().unwrap_or(100_000f32) / 1000f32; - if self.temperature > self.max { - self.max = self.temperature; - } - } - } } impl ComponentExt for Component { @@ -141,22 +164,52 @@ 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 { - let mut ret = Vec::new(); 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") { + if !entry.is_dir() + || !entry + .file_name() + .unwrap_or_else(|| OsStr::new("/")) + .to_str() + .unwrap_or("") + .starts_with("hwmon") + { continue; } - append_files(&mut ret, &entry); + 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() } - ret.sort_by(|c1, c2| c1.label.to_lowercase().cmp(&c2.label.to_lowercase())); - ret } diff -Nru rust-sysinfo-0.9.5/src/linux/disk.rs rust-sysinfo-0.13.2/src/linux/disk.rs --- rust-sysinfo-0.9.5/src/linux/disk.rs 2019-02-27 16:03:42.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/disk.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,58 +1,80 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // -use ::DiskExt; -use ::utils; use super::system::get_all_data; +use utils; +use DiskExt; +use DiskType; use libc::statvfs; -use std::mem; -use std::fmt::{Debug, Error, Formatter}; -use std::path::{Path, PathBuf}; use std::ffi::{OsStr, OsString}; +use std::fs; +use std::mem; use std::os::unix::ffi::OsStrExt; - -/// Enum containing the different handled disks types. -#[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::SSD, - 1 => DiskType::HDD, - id => DiskType::Unknown(id), - } - } -} +use std::path::{Path, PathBuf}; fn find_type_for_name(name: &OsStr) -> DiskType { - /* turn "sda1" into "sda": */ - let mut trimmed: &[u8] = name.as_bytes(); - while trimmed.len() > 1 && trimmed[trimmed.len()-1] >= b'0' && trimmed[trimmed.len()-1] <= b'9' { - trimmed = &trimmed[..trimmed.len()-1] + /* + 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(trimmed); - let path = Path::new("/sys/block/").to_owned() + let trimmed: &OsStr = OsStrExt::from_bytes(real_path.as_bytes()); + + let path = Path::new("/sys/block/") + .to_owned() .join(trimmed) .join("queue/rotational"); - let rotational_int = get_all_data(path).unwrap_or_default().trim().parse(); + // 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) } + ($x:expr) => { + u64::from($x) + }; } pub fn new(name: &OsStr, mount_point: &Path, file_system: &[u8]) -> Disk { @@ -87,15 +109,6 @@ available_space: u64, } -impl Debug for Disk { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - 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 DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ @@ -121,7 +134,7 @@ self.available_space } - fn update(&mut self) -> bool { + fn refresh(&mut self) -> bool { unsafe { let mut stat: statvfs = mem::zeroed(); let mount_point_cpath = utils::to_cpath(&self.mount_point); diff -Nru rust-sysinfo-0.9.5/src/linux/mod.rs rust-sysinfo-0.13.2/src/linux/mod.rs --- rust-sysinfo-0.9.5/src/linux/mod.rs 2019-02-27 16:03:42.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/mod.rs 2020-03-02 16:11:59.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // @@ -10,10 +10,11 @@ pub mod process; pub mod processor; pub mod system; +pub mod users; pub use self::component::Component; -pub use self::disk::{Disk, DiskType}; -pub use self::network::NetworkData; +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; diff -Nru rust-sysinfo-0.9.5/src/linux/network.rs rust-sysinfo-0.13.2/src/linux/network.rs --- rust-sysinfo-0.9.5/src/linux/network.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/network.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,74 +1,254 @@ -// +// // Sysinfo -// -// Copyright (c) 2017 Guillaume Gomez +// +// Copyright (c) 2019 Guillaume Gomez // use std::fs::File; -use std::io::{Error, ErrorKind, Read}; +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. -#[derive(Debug)] pub struct NetworkData { - old_in: u64, - old_out: u64, - current_in: u64, - current_out: u64, + /// 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_income(&self) -> u64 { - self.current_in - self.old_in + fn get_received(&self) -> u64 { + self.rx_bytes as u64 - self.old_rx_bytes as u64 } - fn get_outcome(&self) -> u64 { - self.current_out - self.old_out + fn get_total_received(&self) -> u64 { + self.rx_bytes as u64 } -} -pub fn new() -> NetworkData { - NetworkData { - old_in: 0, - old_out: 0, - current_in: 0, - current_out: 0, + fn get_transmitted(&self) -> u64 { + self.tx_bytes as u64 - self.old_tx_bytes as u64 } -} -fn read_things() -> Result<(u64, u64), Error> { - fn read_interface_stat(iface: &str, typ: &str) -> Result { - let mut file = File::open(format!("/sys/class/net/{}/statistics/{}_bytes", iface, typ))?; - let mut content = String::new(); - file.read_to_string(&mut content)?; - content.trim() - .parse() - .map_err(|_| Error::new(ErrorKind::Other, "Failed to parse network stat")) + fn get_total_transmitted(&self) -> u64 { + self.tx_bytes as u64 } - let default_interface = { - let mut file = File::open("/proc/net/route")?; - let mut content = String::new(); - file.read_to_string(&mut content)?; - content.lines() - .filter(|l| l.split_whitespace().nth(2).map(|l| l != "00000000").unwrap_or(false)) - .last() - .and_then(|l| l.split_whitespace().nth(0)) - .ok_or_else(|| Error::new(ErrorKind::Other, "Default device not found"))? - .to_owned() - }; + fn get_packets_received(&self) -> u64 { + self.rx_packets as u64 - self.old_rx_packets as u64 + } - Ok((read_interface_stat(&default_interface, "rx")?, - read_interface_stat(&default_interface, "tx")?)) -} + 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 + } -pub fn update_network(n: &mut NetworkData) { - if let Ok((new_in, new_out)) = read_things() { - n.old_in = n.current_in; - n.old_out = n.current_out; - n.current_in = new_in; - n.current_out = new_out; + fn get_total_errors_on_transmitted(&self) -> u64 { + self.tx_errors as u64 } - // TODO: maybe handle error here? } diff -Nru rust-sysinfo-0.9.5/src/linux/processor.rs rust-sysinfo-0.13.2/src/linux/processor.rs --- rust-sysinfo-0.9.5/src/linux/processor.rs 2019-02-27 16:03:42.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/processor.rs 2020-02-08 10:59:41.000000000 +0000 @@ -1,12 +1,15 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // #![allow(clippy::too_many_arguments)] -use ::ProcessorExt; +use std::fs::File; +use std::io::Read; + +use ProcessorExt; /// Struct containing values to compute a CPU usage. #[derive(Clone, Copy)] @@ -41,9 +44,18 @@ } /// 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 { + 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, @@ -65,8 +77,19 @@ }*/ /// 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) { + 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; @@ -86,8 +109,14 @@ /// 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 + self.work_time() + + self.idle + + self.iowait + + self.irq + + self.softirq + + self.steal + + self.guest + + self.guest_nice } } @@ -95,51 +124,78 @@ pub struct Processor { old_values: CpuValues, new_values: CpuValues, - name: String, + 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 { - #[allow(dead_code)] - fn new() -> Processor { - Processor { - name: String::new(), - old_values: CpuValues::new(), - new_values: CpuValues::new(), - cpu_usage: 0f32, - total_time: 0, - old_total_time: 0, - } - } - - 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) -> 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), + 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, } } - 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) { + 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 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.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()); + 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(); } @@ -153,20 +209,80 @@ fn get_name(&self) -> &str { &self.name } -} -pub fn new_processor(name: &str, user: u64, nice: u64, system: u64, idle: u64, iowait: u64, - irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64) -> Processor { - Processor::new_with_values(name, user, nice, system, idle, iowait, irq, softirq, steal, - guest, guest_nice) -} + /// Returns the CPU frequency in MHz. + fn get_frequency(&self) -> u64 { + self.frequency + } + + fn get_vendor_id(&self) -> &str { + &self.vendor_id + } -pub fn set_processor(p: &mut Processor, user: u64, nice: u64, system: u64, idle: u64, iowait: u64, - irq: u64, softirq: u64, steal: u64, guest: u64, guest_nice: u64) { - p.set(user, nice, system, idle, iowait, irq, softirq, steal, - guest, guest_nice) + 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()) +} diff -Nru rust-sysinfo-0.9.5/src/linux/process.rs rust-sysinfo-0.13.2/src/linux/process.rs --- rust-sysinfo-0.9.5/src/linux/process.rs 2018-09-22 16:17:58.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/process.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,17 +1,19 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // use std::collections::HashMap; -use std::fmt::{self, Formatter, Debug}; +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; +use ProcessExt; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] @@ -40,7 +42,7 @@ Unknown(u32), } -impl From for ProcessStatus { +impl From for ProcessStatus { fn from(status: u32) -> ProcessStatus { match status { 1 => ProcessStatus::Idle, @@ -66,7 +68,7 @@ 'K' => ProcessStatus::Wakekill, 'W' => ProcessStatus::Waking, 'P' => ProcessStatus::Parked, - x => ProcessStatus::Unknown(x as u32), + x => ProcessStatus::Unknown(x as u32), } } } @@ -75,16 +77,16 @@ /// 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::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", } } @@ -97,7 +99,6 @@ } /// Struct containing a process' information. -#[derive(Clone)] pub struct Process { pub(crate) name: String, pub(crate) cmd: Vec, @@ -108,6 +109,7 @@ pub(crate) cwd: PathBuf, pub(crate) root: PathBuf, pub(crate) memory: u64, + pub(crate) virtual_memory: u64, utime: u64, stime: u64, old_utime: u64, @@ -122,20 +124,26 @@ 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::new(), + name: String::with_capacity(20), pid, parent, - cmd: Vec::new(), - environ: Vec::new(), + 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, @@ -146,7 +154,16 @@ uid: 0, gid: 0, status: ProcessStatus::Unknown(0), - tasks: HashMap::new(), + 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, } } @@ -186,6 +203,10 @@ self.memory } + fn virtual_memory(&self) -> u64 { + self.virtual_memory + } + fn parent(&self) -> Option { self.parent } @@ -203,36 +224,30 @@ 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, + } + } } -#[allow(unused_must_use)] -impl Debug for Process { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - writeln!(f, "pid: {}", self.pid); - writeln!(f, "parent: {:?}", self.parent); - writeln!(f, "name: {}", self.name); - writeln!(f, "environment:"); - for var in &self.environ { - if !var.is_empty() { - writeln!(f, "\t{}", var); +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; } } - writeln!(f, "command:"); - for arg in &self.cmd { - writeln!(f, "\t{}", arg); - } - writeln!(f, "executable path: {:?}", self.exe); - writeln!(f, "current working directory: {:?}", self.cwd); - writeln!(f, "owner/group: {}:{}", self.uid, self.gid); - writeln!(f, "memory usage: {} kB", self.memory); - writeln!(f, "cpu usage: {}%", self.cpu_usage); - writeln!(f, "status: {}", self.status); - write!(f, "root path: {:?}", self.root) } } 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.cpu_usage = + ((p.utime - p.old_utime + p.stime - p.old_stime) * nb_processors * 100) as f32 / total_time; p.updated = false; } @@ -247,3 +262,32 @@ 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; + } + } +} diff -Nru rust-sysinfo-0.9.5/src/linux/system.rs rust-sysinfo-0.13.2/src/linux/system.rs --- rust-sysinfo-0.9.5/src/linux/system.rs 2019-09-05 18:24:27.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/system.rs 2020-04-13 13:31:17.000000000 +0000 @@ -1,67 +1,160 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // use sys::component::{self, Component}; -use sys::processor::*; -use sys::process::*; -use sys::Disk; use sys::disk; -use sys::network; -use sys::NetworkData; -use ::{DiskExt, ProcessExt, RefreshKind, SystemExt}; +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, File, read_link}; +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 libc::{uid_t, sysconf, _SC_CLK_TCK, _SC_PAGESIZE}; 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. -#[derive(Debug)] 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, - temperatures: Vec, + components: Vec, disks: Vec, - network: NetworkData, + 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.processors[0]); + 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::new(); - let nb_processors = self.processors.len() as u64 - 1; + 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_, nb_processors, total_time); + compute_cpu_usage(proc_, self.processors.len() as u64, total_time); } } for pid in to_delete { @@ -77,7 +170,40 @@ 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; @@ -86,31 +212,36 @@ count += 1; let mut parts = line.split(|x| *x == b' ').filter(|s| !s.is_empty()); if first { - self.processors.push(new_processor( + 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))); + 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 - set_processor(&mut self.processors[i], - 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)); + 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 { @@ -119,6 +250,10 @@ } } } + if first { + self.global_processor.vendor_id = vendor_id; + self.global_processor.brand = brand; + } } } } @@ -131,23 +266,45 @@ mem_free: 0, swap_total: 0, swap_free: 0, - processors: Vec::new(), + 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 }, - temperatures: component::get_components(), - disks: Vec::new(), - network: network::new(), + 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_system(&mut self) { + fn refresh_components_list(&mut self) { + self.components = component::get_components(); + } + + fn refresh_memory(&mut self) { self.uptime = get_uptime(); - for component in &mut self.temperatures { - component.update(); - } - if let Ok(data) = get_all_data("/proc/meminfo") { + 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, @@ -163,22 +320,37 @@ } } } + } + + 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()) { + 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()) { + 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 @@ -188,36 +360,29 @@ }; if found && !self.processors.is_empty() { self.refresh_processors(Some(1)); - let (new, old) = get_raw_times(&self.processors[0]); + let (new, old) = get_raw_times(&self.global_processor); let total_time = (if old > new { 1 } else { new - old }) as f32; - let nb_processors = self.processors.len() as u64 - 1; if let Some(p) = self.process_list.tasks.get_mut(&pid) { - compute_cpu_usage(p, nb_processors, total_time); + compute_cpu_usage(p, self.processors.len() as u64, total_time); } } found } - fn refresh_disks(&mut self) { - for disk in &mut self.disks { - disk.update(); - } - } - - fn refresh_disk_list(&mut self) { + fn refresh_disks_list(&mut self) { self.disks = get_all_disks(); } - fn refresh_network(&mut self) { - network::update_network(&mut self.network); + 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_process_list(&self) -> &HashMap { + fn get_processes(&self) -> &HashMap { &self.process_list.tasks } @@ -225,12 +390,20 @@ self.process_list.tasks.get(&pid) } - fn get_network(&self) -> &NetworkData { - &self.network + 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_processor_list(&self) -> &[Processor] { - &self.processors[..] + fn get_processors(&self) -> &[Processor] { + &self.processors } fn get_total_memory(&self) -> u64 { @@ -258,17 +431,54 @@ self.swap_total - self.swap_free } - fn get_components_list(&self) -> &[Component] { - &self.temperatures[..] + 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[..] + &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 { @@ -277,18 +487,6 @@ } } -pub fn get_all_data>(file_path: P) -> io::Result { - use std::error::Error; - let mut file = File::open(file_path.as_ref())?; - let mut data = vec![0; 16_385]; - - let size = file.read(&mut data)?; - data.truncate(size); - let data = String::from_utf8(data).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, - e.description()))?; - Ok(data) -} - fn to_u64(v: &[u8]) -> u64 { let mut x = 0; @@ -299,16 +497,16 @@ x } -struct Wrap<'a>(UnsafeCell<&'a mut Process>); +struct Wrap<'a, T>(UnsafeCell<&'a mut T>); -impl<'a> Wrap<'a> { - fn get(&self) -> &'a mut Process { +impl<'a, T> Wrap<'a, T> { + fn get(&self) -> &'a mut T { unsafe { *(self.0.get()) } } } -unsafe impl<'a> Send for Wrap<'a> {} -unsafe impl<'a> Sync for Wrap<'a> {} +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, @@ -319,47 +517,56 @@ 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(); + let folders = d + .filter_map(|entry| { + if let Ok(entry) = entry { + let entry = entry.path(); - if entry.is_dir() { - Some(entry) + if entry.is_dir() { + Some(entry) + } else { + None + } } else { None } - } else { - None - } - }).collect::>(); + }) + .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::>() + 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| { + 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 @@ -374,21 +581,36 @@ parts: &[&str], page_size_kb: u64, parent_memory: u64, + parent_virtual_memory: u64, pid: Pid, uptime: u64, now: u64, ) { - // we get the rss { + // rss entry.memory = u64::from_str(parts[23]).unwrap_or(0) * page_size_kb; if entry.memory >= parent_memory { entry.memory -= parent_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); + // 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 { @@ -397,7 +619,74 @@ 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( @@ -408,131 +697,130 @@ uptime: u64, now: u64, ) -> Result, ()> { - if let Some(Ok(nb)) = path.file_name().and_then(|x| x.to_str()).map(Pid::from_str) { - if nb == pid { - return Err(()); - } - let mut tmp = PathBuf::from(path); - - tmp.push("stat"); - if let Ok(data) = get_all_data(&tmp) { - - // The stat file is "interesting" to parse, because spaces cannot - // be used as delimiters. The second field stores the command name - // sourrounded 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::new(); - 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()); - let parent_memory = proc_list.memory; - if let Some(ref mut entry) = proc_list.tasks.get_mut(&nb) { - update_time_and_memory(path, entry, &parts, page_size_kb, parent_memory, nb, uptime, - now); - return Ok(None); - } - - 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.status = parts[2].chars() - .next() - .and_then(|c| Some(ProcessStatus::from(c))) - .unwrap_or(ProcessStatus::Unknown(0)); - - tmp = PathBuf::from(path); - tmp.push("status"); - if let Ok(status_data) = get_all_data(&tmp) { - // 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 set_uid = false; - let mut set_gid = false; - for line in status_data.lines() { - if let Some(u) = f(line, "Uid:") { - assert!(!set_uid); - set_uid = true; - p.uid = u; - } - if let Some(g) = f(line, "Gid:") { - assert!(!set_gid); - set_gid = true; - p.gid = g; - } - } - assert!(set_uid && set_gid); - } + 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)?; - 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 = PathBuf::from(path); - 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 = PathBuf::from(path); - tmp.push("environ"); - p.environ = copy_from_file(&tmp); - tmp = PathBuf::from(path); - tmp.push("exe"); - - p.exe = read_link(tmp.to_str() - .unwrap_or_else(|| "")).unwrap_or_else(|_| PathBuf::new()); - - tmp = PathBuf::from(path); - tmp.push("cwd"); - p.cwd = realpath(&tmp); - tmp = PathBuf::from(path); - tmp.push("root"); - p.root = realpath(&tmp); - } - - update_time_and_memory(path, &mut p, &parts, page_size_kb, proc_list.memory, nb, uptime, - now); - return Ok(Some(p)); + 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; } } - Err(()) + + 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 { @@ -542,10 +830,21 @@ if let Ok(size) = f.read(&mut data) { data.truncate(size); - data.split(|x| *x == b'\0') - .filter_map(|x| ::std::str::from_utf8(x).map(|x| x.trim().to_owned()).ok()) - .filter(|x| !x.is_empty()) - .collect() + 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() } @@ -554,41 +853,76 @@ } } +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").unwrap_or_default(); - let disks = content.lines() - .filter(|line| { + let content = get_all_data("/proc/mounts", 16_385).unwrap_or_default(); + + content + .lines() + .map(|line| { let line = line.trim_start(); - // While the `sd` prefix is most common, some disks instead use the `nvme` prefix. This - // prefix refers to NVM (non-volatile memory) cabale SSDs. These disks run on the NVMe - // storage controller protocol (not the scsi protocol) and as a result use a different - // prefix to support NVMe namespaces. - // - // In some other cases, it uses a device mapper to map physical block devices onto - // higher-level virtual block devices (on `/dev/mapper`). - line.starts_with("/dev/sd") || - line.starts_with("/dev/nvme") || - line.starts_with("/dev/mapper/") - }); - let mut ret = vec![]; + // 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, + }; - for line in disks { - let mut split = line.split(' '); - if let (Some(name), Some(mountpt), Some(fs)) = (split.next(), split.next(), split.next()) { - ret.push(disk::new(name[5..].as_ref(), Path::new(mountpt), fs.as_bytes())); - } - } - ret + 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").unwrap_or_default(); + 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(), - Err(_) => panic!("SystemTime before UNIX EPOCH!"), + _ => panic!("SystemTime before UNIX EPOCH!"), } -} \ No newline at end of file +} diff -Nru rust-sysinfo-0.9.5/src/linux/users.rs rust-sysinfo-0.13.2/src/linux/users.rs --- rust-sysinfo-0.9.5/src/linux/users.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/src/linux/users.rs 2020-03-05 13:49:46.000000000 +0000 @@ -0,0 +1,90 @@ +// +// 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() +} diff -Nru rust-sysinfo-0.9.5/src/mac/component.rs rust-sysinfo-0.13.2/src/mac/component.rs --- rust-sysinfo-0.9.5/src/mac/component.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/component.rs 2020-02-07 13:54:32.000000000 +0000 @@ -1,28 +1,70 @@ -// +// // 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 fn new(label: String, max: Option, critical: Option) -> Component { - Component { - temperature: 0f32, - label, - max: max.unwrap_or(0.0), - critical, - } + 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, + }) } } @@ -42,11 +84,127 @@ 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), + } } -pub fn update_component(comp: &mut Component, temperature: f32) { - comp.temperature = temperature; - if comp.temperature > comp.max { - comp.max = comp.temperature; +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) } diff -Nru rust-sysinfo-0.9.5/src/mac/disk.rs rust-sysinfo-0.13.2/src/mac/disk.rs --- rust-sysinfo-0.9.5/src/mac/disk.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/disk.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,68 +1,23 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // -use ::DiskExt; -use ::utils; +use utils; +use DiskExt; +use DiskType; -use libc::statfs; +use libc::{c_char, c_void, statfs}; +use std::collections::HashMap; +use std::ffi::{OsStr, OsString}; +use std::fs; use std::mem; -use std::fmt::{Debug, Error, Formatter}; +use std::mem::MaybeUninit; +use std::os::unix::ffi::OsStringExt; use std::path::{Path, PathBuf}; -use std::ffi::{OsStr, OsString}; - -/// Enum containing the different handled disks types. -#[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), - } - } -} - -pub fn new(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, - } -} +use std::ptr; +use sys::ffi; /// Struct containing a disk information. pub struct Disk { @@ -74,15 +29,6 @@ available_space: u64, } -impl Debug for Disk { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - 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 DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ @@ -108,7 +54,7 @@ self.available_space } - fn update(&mut self) -> bool { + fn refresh(&mut self) -> bool { unsafe { let mut stat: statfs = mem::zeroed(); let mount_point_cpath = utils::to_cpath(&self.mount_point); @@ -121,3 +67,140 @@ } } } + +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, + } +} diff -Nru rust-sysinfo-0.9.5/src/mac/ffi.rs rust-sysinfo-0.13.2/src/mac/ffi.rs --- rust-sysinfo-0.9.5/src/mac/ffi.rs 2019-06-22 09:54:16.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/ffi.rs 2020-04-05 18:45:10.000000000 +0000 @@ -1,64 +1,134 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // -use libc::{c_int, c_char, c_void, c_uchar, c_uint, size_t, c_ushort}; +use libc::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void, size_t}; extern "C" { + #[no_mangle] pub static kCFAllocatorDefault: CFAllocatorRef; - - pub fn proc_pidinfo(pid: c_int, flavor: c_int, arg: u64, buffer: *mut c_void, - buffersize: c_int) -> c_int; + // #[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_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 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: *mut 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 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 static kCFAllocatorNull: CFAllocatorRef; + 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_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; +// 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 @@ -167,6 +237,42 @@ __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; @@ -176,6 +282,12 @@ 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; @@ -208,6 +320,10 @@ 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 { @@ -254,13 +370,14 @@ pub total_uncompressed_pages_in_compressor: u64, } -#[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] +#[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 + pub bytes: [i8; 32], // SMCBytes_t } #[cfg_attr(feature = "debug", derive(Debug, Eq, Hash, PartialEq))] @@ -315,6 +432,31 @@ 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; @@ -331,6 +473,9 @@ 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; @@ -343,7 +488,7 @@ pub const SMC_CMD_READ_KEYINFO: u8 = 9; pub const SMC_CMD_READ_BYTES: u8 = 5; -// pub const PROC_PIDPATHINFO_MAXSIZE: usize = 4096; +pub const PROC_PIDPATHINFO_MAXSIZE: u32 = 4096; pub const KIO_RETURN_SUCCESS: i32 = 0; #[allow(non_upper_case_globals)] diff -Nru rust-sysinfo-0.9.5/src/mac/mod.rs rust-sysinfo-0.13.2/src/mac/mod.rs --- rust-sysinfo-0.9.5/src/mac/mod.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/mod.rs 2020-03-02 15:25:09.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // @@ -11,10 +11,11 @@ pub mod process; pub mod processor; pub mod system; +pub mod users; pub use self::component::Component; -pub use self::disk::{Disk, DiskType}; -pub use self::network::NetworkData; -pub use self::process::{Process,ProcessStatus}; +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; diff -Nru rust-sysinfo-0.9.5/src/mac/network.rs rust-sysinfo-0.13.2/src/mac/network.rs --- rust-sysinfo-0.9.5/src/mac/network.rs 2019-09-08 13:02:19.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/network.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,84 +1,221 @@ -// +// // 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 { - old_in: u64, - old_out: u64, 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_income(&self) -> u64 { + fn get_received(&self) -> u64 { self.current_in - self.old_in } - fn get_outcome(&self) -> u64 { + fn get_total_received(&self) -> u64 { + self.current_in + } + + fn get_transmitted(&self) -> u64 { self.current_out - self.old_out } -} -pub fn new() -> NetworkData { - NetworkData { - old_in: 0, - old_out: 0, - current_in: 0, - current_out: 0, + fn get_total_transmitted(&self) -> u64 { + self.current_out } -} -#[allow(clippy::cast_ptr_alignment)] -pub fn update_network(n: &mut NetworkData) { - 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 - } + fn get_packets_received(&self) -> u64 { + self.packets_in - self.old_packets_in } - let buf = buf.as_ptr() as *const c_char; - let lim = unsafe { buf.add(len) }; - let mut next = buf; - let mut totalibytes = 0u64; - let mut totalobytes = 0u64; - 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 { - let if2m: *const ffi::if_msghdr2 = ifm as *const ffi::if_msghdr2; - totalibytes += (*if2m).ifm_data.ifi_ibytes; - totalobytes += (*if2m).ifm_data.ifi_obytes; - } - } + + 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 } - n.old_in = if n.current_in > totalibytes { - 0 - } else { - n.current_in - }; - n.current_in = totalibytes; - n.old_out = if n.current_out > totalibytes { - 0 - } else { - n.current_out - }; - n.current_out = totalobytes; } diff -Nru rust-sysinfo-0.9.5/src/mac/processor.rs rust-sysinfo-0.13.2/src/mac/processor.rs --- rust-sysinfo-0.9.5/src/mac/processor.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/processor.rs 2020-02-08 10:59:41.000000000 +0000 @@ -1,14 +1,17 @@ -// +// // 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; +use ProcessorExt; pub struct UnsafePtr(*mut T); @@ -54,16 +57,41 @@ name: String, cpu_usage: f32, processor_data: Arc, + frequency: u64, + vendor_id: String, + brand: String, } impl Processor { - fn new(name: String, processor_data: Arc) -> 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 { @@ -74,25 +102,142 @@ fn get_name(&self) -> &str { &self.name } -} -pub fn set_cpu_usage(p: &mut Processor, usage: f32) { - p.cpu_usage = usage; -} + /// Returns the processor frequency in MHz. + fn get_frequency(&self) -> u64 { + self.frequency + } + + fn get_vendor_id(&self) -> &str { + &self.vendor_id + } -pub fn create_proc(name: String, processor_data: Arc) -> Processor { - Processor::new(name, processor_data) + fn get_brand(&self) -> &str { + &self.brand + } } -pub fn update_proc(p: &mut Processor, cpu_usage: f32, processor_data: Arc) { - p.cpu_usage = cpu_usage; - p.processor_data = processor_data; +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 set_cpu_proc(p: &mut Processor, cpu_usage: f32) { - p.cpu_usage = cpu_usage; +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_processor_data(p: &Processor) -> Arc { - Arc::clone(&p.processor_data) +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"), + ) } diff -Nru rust-sysinfo-0.9.5/src/mac/process.rs rust-sysinfo-0.13.2/src/mac/process.rs --- rust-sysinfo-0.9.5/src/mac/process.rs 2019-03-03 18:01:43.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/process.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,16 +1,24 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // -use std::fmt::{self, Formatter, Debug}; +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, gid_t, kill, uid_t}; +use libc::{c_int, c_void, gid_t, kill, size_t, uid_t}; +use DiskUsage; use Pid; -use ::ProcessExt; +use ProcessExt; + +use sys::ffi; +use sys::system::Wrap; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] @@ -46,11 +54,11 @@ /// 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::Idle => "Idle", + ProcessStatus::Run => "Runnable", + ProcessStatus::Sleep => "Sleeping", + ProcessStatus::Stop => "Stopped", + ProcessStatus::Zombie => "Zombie", ProcessStatus::Unknown(_) => "Unknown", } } @@ -96,12 +104,12 @@ /// Used to display `ThreadStatus`. pub fn to_string(&self) -> &str { match *self { - ThreadStatus::Running => "Running", - ThreadStatus::Stopped => "Stopped", - ThreadStatus::Waiting => "Waiting", + ThreadStatus::Running => "Running", + ThreadStatus::Stopped => "Stopped", + ThreadStatus::Waiting => "Waiting", ThreadStatus::Uninterruptible => "Uninterruptible", - ThreadStatus::Halted => "Halted", - ThreadStatus::Unknown(_) => "Unknown", + ThreadStatus::Halted => "Halted", + ThreadStatus::Unknown(_) => "Unknown", } } } @@ -124,6 +132,7 @@ cwd: PathBuf, pub(crate) root: PathBuf, pub(crate) memory: u64, + pub(crate) virtual_memory: u64, utime: u64, stime: u64, old_utime: u64, @@ -141,6 +150,81 @@ /// /// 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 { @@ -155,6 +239,7 @@ cwd: PathBuf::new(), root: PathBuf::new(), memory: 0, + virtual_memory: 0, cpu_usage: 0., utime: 0, stime: 0, @@ -166,6 +251,10 @@ gid: 0, process_status: ProcessStatus::Unknown(0), status: None, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, } } @@ -205,6 +294,10 @@ self.memory } + fn virtual_memory(&self) -> u64 { + self.virtual_memory + } + fn parent(&self) -> Option { self.parent } @@ -220,38 +313,18 @@ fn cpu_usage(&self) -> f32 { self.cpu_usage } -} -#[allow(unused_must_use)] -impl Debug for Process { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - writeln!(f, "pid: {}", self.pid); - writeln!(f, "parent: {:?}", self.parent); - writeln!(f, "name: {}", self.name); - writeln!(f, "environment:"); - for var in &self.environ { - if !var.is_empty() { - writeln!(f, "\t{}", var); - } + 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, } - writeln!(f, "command:"); - for arg in &self.cmd { - writeln!(f, "\t{}", arg); - } - writeln!(f, "executable path: {:?}", self.exe); - writeln!(f, "current working directory: {:?}", self.cwd); - writeln!(f, "owner/group: {}:{}", self.uid, self.gid); - writeln!(f, "memory usage: {} kB", self.memory); - writeln!(f, "cpu usage: {}%", self.cpu_usage); - writeln!(f, "status: {}", match self.status { - Some(ref v) => v.to_string(), - None => "Unknown", - }); - write!(f, "root path: {:?}", self.root) } } -pub fn compute_cpu_usage(p: &mut Process, time: u64, task_time: u64) { +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; @@ -272,12 +345,326 @@ p.updated = true; }*/ -pub fn has_been_updated(p: &mut Process) -> bool { +pub(crate) fn has_been_updated(p: &mut Process) -> bool { let old = p.updated; p.updated = false; old } -pub fn force_update(p: &mut Process) { +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 +} diff -Nru rust-sysinfo-0.9.5/src/mac/system.rs rust-sysinfo-0.13.2/src/mac/system.rs --- rust-sysinfo-0.9.5/src/mac/system.rs 2019-09-08 13:04:06.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/system.rs 2020-04-13 13:30:04.000000000 +0000 @@ -4,33 +4,21 @@ // Copyright (c) 2015 Guillaume Gomez // -use sys::ffi; use sys::component::Component; -use sys::network::{self, NetworkData}; -use sys::processor::*; +use sys::disk::Disk; +use sys::ffi; +use sys::network::Networks; use sys::process::*; -use sys::disk::{self, Disk, DiskType}; +use sys::processor::*; -use ::{ComponentExt, DiskExt, ProcessExt, ProcessorExt, RefreshKind, SystemExt}; +use {LoadAvg, Pid, ProcessExt, ProcessorExt, RefreshKind, SystemExt, User}; -use std::borrow::Borrow; use std::cell::UnsafeCell; use std::collections::HashMap; -use std::ffi::{OsStr, OsString}; -use std::mem::MaybeUninit; -use std::ops::Deref; -use std::os::unix::ffi::OsStringExt; +use std::mem; use std::sync::Arc; -use std::path::{Path, PathBuf}; -use sys::processor; -use std::{fs, mem, ptr}; -use libc::{self, c_void, c_int, size_t, c_char, sysconf, _SC_PAGESIZE}; - -use utils; -use Pid; - -use std::process::Command; +use libc::{self, c_int, c_void, size_t, sysconf, _SC_PAGESIZE}; use rayon::prelude::*; @@ -41,513 +29,33 @@ mem_free: u64, swap_total: u64, swap_free: u64, + global_processor: Processor, processors: Vec, page_size_kb: u64, - temperatures: Vec, + components: Vec, connection: Option, disks: Vec, - network: NetworkData, - uptime: u64, + 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); } - } - } -} - -// 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 { - //println!("Error: IOServiceGetMatchingServices() = {}", result); - return None; - } - - let device = ffi::IOIteratorNext(iterator); - ffi::IOObjectRelease(iterator); - if device == 0 { - //println!("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 { - //println!("Error: IOServiceOpen() = {}", result); - return None; - } - - Some(conn) - } -} - -unsafe fn strtoul(s: *mut c_char, size: c_int, base: c_int) -> u32 { - let mut total = 0u32; - - for i in 0..size { - total += if base == 16 { - (*s.offset(i as isize) as u32) << (((size - 1 - i) as u32) << 3) - } else { - (*s.offset(i as isize) as u32) << ((size - 1 - i) << 3) as u32 - }; - } - total -} - -unsafe fn ultostr(s: *mut c_char, val: u32) { - *s = 0; - libc::sprintf(s, b"%c%c%c%c\0".as_ptr() as *const i8, val >> 24, val >> 16, val >> 8, val); -} - -unsafe fn perform_call(conn: ffi::io_connect_t, index: c_int, input_structure: *mut 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) -} - -unsafe fn read_key(con: ffi::io_connect_t, key: *mut c_char) -> Result { - 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, 4, 16); - input_structure.data8 = ffi::SMC_CMD_READ_KEYINFO; - - let result = perform_call(con, ffi::KERNEL_INDEX_SMC, &mut 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; - - let result = perform_call(con, ffi::KERNEL_INDEX_SMC, &mut input_structure, - &mut output_structure); - if result != ffi::KIO_RETURN_SUCCESS { - Err(result) - } else { - 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) - } -} - -unsafe fn get_temperature(con: ffi::io_connect_t, key: *mut c_char) -> f32 { - if let Ok(val) = read_key(con, key) { - if val.data_size > 0 && - libc::strcmp(val.data_type.as_ptr(), b"sp78\0".as_ptr() as *const i8) == 0 { - // convert fp78 value to temperature - let x = (i32::from(val.bytes[0]) << 6) + (i32::from(val.bytes[1]) >> 2); - return x as f32 / 64f32; - } - } - 0f32 -} - -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 -} - -macro_rules! unwrapper { - ($b:expr, $ret:expr) => {{ - match $b { - Ok(x) => x, - _ => return $ret, - } - }} -} - -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 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()) -} - -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::new(); - - 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 { - //println!("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 get_disks() -> Vec { - let disk_types = get_disk_types(); - - unwrapper!(fs::read_dir("/Volumes"), Vec::new()) - .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() - .unwrap_or_else(|| OsStr::new("")) - .to_owned(); - let type_ = disk_types.get(&name).cloned().unwrap_or(DiskType::Unknown(-2)); - Some(disk::new(name, &mount_point, type_)) - } - } else { - None + unsafe { + ffi::IOServiceClose(conn); } - }) - .collect() -} - -fn get_uptime() -> u64 { - let mut boottime: libc::timeval = unsafe { mem::zeroed() }; - let mut len = mem::size_of::(); - let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_BOOTTIME]; - unsafe { - if libc::sysctl(mib.as_mut_ptr(), - - 2, - &mut boottime as *mut libc::timeval as *mut _, - &mut len, - ::std::ptr::null_mut(), 0) < 0 { - return 0; - } - } - let bsec = boottime.tv_sec; - let csec = unsafe { libc::time(::std::ptr::null_mut()) }; - - unsafe { libc::difftime(csec, bsec) as u64 } -} - -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 } -struct Wrap<'a>(UnsafeCell<&'a mut HashMap>); +pub(crate) struct Wrap<'a>(pub UnsafeCell<&'a mut HashMap>); unsafe impl<'a> Send for Wrap<'a> {} unsafe impl<'a> Sync for Wrap<'a> {} -fn update_process(wrap: &Wrap, pid: Pid, - taskallinfo_size: i32, taskinfo_size: i32, threadinfo_size: i32, - mib: &mut [c_int], mut size: size_t) -> Result, ()> { - let mut proc_args = Vec::with_capacity(size as usize); - unsafe { - 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, - threadinfo_size) != 0 { - (thread_info.pth_user_time, - thread_info.pth_system_time, - Some(ThreadStatus::from(thread_info.pth_run_state))) - } else { - (0, 0, None) - }; - 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); - } - p.status = thread_status; - let mut task_info = mem::zeroed::(); - if ffi::proc_pidinfo(pid, - libc::PROC_PIDTASKINFO, - 0, - &mut task_info as *mut libc::proc_taskinfo as *mut c_void, - taskinfo_size) != taskinfo_size { - return Err(()); - } - 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 - return Ok(None); - } - - let mut task_info = mem::zeroed::(); - if ffi::proc_pidinfo(pid, - libc::PROC_PIDTASKALLINFO, - 0, - &mut task_info as *mut libc::proc_taskallinfo as *mut c_void, - taskallinfo_size as i32) != taskallinfo_size as i32 { - match Command::new("/bin/ps") // not very nice, might be worth running a which first. - .arg("wwwe") - .arg("-o") - .arg("ppid=,command=") - .arg(pid.to_string().as_str()) - .output() { - Ok(o) => { - let o = String::from_utf8(o.stdout).unwrap_or_else(|_| String::new()); - let o = o.split(' ').filter(|c| !c.is_empty()).collect::>(); - if o.len() < 2 { - return Err(()); - } - let mut command = parse_command_line(&o[1..]); - if let Some(ref mut x) = command.last_mut() { - **x = x.replace("\n", ""); - } - let p = match i32::from_str_radix(&o[0].replace("\n", ""), 10) { - Ok(x) => x, - _ => return Err(()), - }; - let mut p = Process::new(pid, if p == 0 { None } else { Some(p) }, 0); - p.exe = PathBuf::from(&command[0]); - p.name = match p.exe.file_name() { - Some(x) => x.to_str().unwrap_or_else(|| "").to_owned(), - None => String::new(), - }; - p.cmd = command; - return Ok(Some(p)); - } - _ => { - return Err(()); - } - } - } - - let parent = match task_info.pbsd.pbi_ppid as Pid { - 0 => None, - p => Some(p) - }; - - let mut p = Process::new(pid, - parent, - task_info.pbsd.pbi_start_tvsec); - p.memory = task_info.ptinfo.pti_resident_size >> 10; // divide by 1024 - - p.uid = task_info.pbsd.pbi_uid; - p.gid = task_info.pbsd.pbi_gid; - p.process_status = ProcessStatus::from(task_info.pbsd.pbi_status); - - 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 { - 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; - if cp < ptr.add(size) { - while cp < ptr.add(size) && *cp != 0 { - cp = cp.offset(1); - } - p.exe = Path::new(get_unchecked_str(cp, start).as_str()).to_path_buf(); - p.name = p.exe.file_name() - .unwrap_or_else(|| OsStr::new("")) - .to_str() - .unwrap_or_else(|| "") - .to_owned(); - let mut need_root = true; - if p.exe.is_absolute() { - if let Some(parent) = p.exe.parent() { - p.root = parent.to_path_buf(); - need_root = false; - } - } - while cp < ptr.add(size) && *cp == 0 { - cp = cp.offset(1); - } - start = cp; - let mut c = 0; - let mut cmd = Vec::new(); - 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); - } - p.cmd = parse_command_line(&cmd); - start = cp; - while cp < ptr.add(size) { - if *cp == 0 { - if cp == start { - break; - } - p.environ.push(get_unchecked_str(cp, start)); - start = cp.offset(1); - } - cp = cp.offset(1); - } - if need_root { - for env in p.environ.iter() { - if env.starts_with("PATH=") { - p.root = Path::new(&env[6..]).to_path_buf(); - break - } - } - } - } - } else { - return Err(()); // not enough rights I assume? - } - Ok(Some(p)) - } -} - -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) - } -} - -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 - } - } -} - impl System { fn clear_procs(&mut self) { let mut to_delete = Vec::new(); @@ -563,59 +71,93 @@ } } +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::new(), + process_list: HashMap::with_capacity(200), mem_total: 0, mem_free: 0, swap_total: 0, swap_free: 0, - processors: Vec::new(), + global_processor, + processors, page_size_kb: unsafe { sysconf(_SC_PAGESIZE) as u64 >> 10 }, // divide by 1024 - temperatures: Vec::new(), + components: Vec::with_capacity(2), connection: get_io_service_connection(), - disks: Vec::new(), - network: network::new(), - uptime: get_uptime(), - port: unsafe { ffi::mach_host_self() }, + disks: Vec::with_capacity(1), + networks: Networks::new(), + port, + users: Vec::new(), + boot_time: boot_time(), }; s.refresh_specifics(refreshes); s } - fn refresh_system(&mut self) { - self.uptime = get_uptime(); - unsafe fn get_sys_value(high: u32, low: u32, mut len: usize, value: *mut 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 - } - + 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) { + 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); + 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 { + 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: // // /* @@ -625,164 +167,96 @@ // * 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; + 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], + ); - if let Some(con) = self.connection { - if self.temperatures.is_empty() { - // getting CPU critical temperature - let mut v = vec!('T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0); - let tmp = get_temperature(con, v.as_mut_ptr()); - let critical_temp = if tmp > 0f32 { - Some(tmp) - } else { - None - }; - // getting CPU temperature - // "TC0P" - v[3] = 'P' as i8; - let temp = get_temperature(con, v.as_mut_ptr() as *mut i8); - if temp > 0f32 { - self.temperatures.push(Component::new("CPU".to_owned(), - None, critical_temp)); - } - // getting GPU temperature - // "TG0P" - v[1] = 'G' as i8; - let temp = get_temperature(con, v.as_mut_ptr() as *mut i8); - if temp > 0f32 { - self.temperatures.push(Component::new("GPU".to_owned(), - None, critical_temp)); - } - // getting battery temperature - // "TB0T" - v[1] = 'B' as i8; - v[3] = 'T' as i8; - let temp = get_temperature(con, v.as_mut_ptr() as *mut i8); - if temp > 0f32 { - self.temperatures.push(Component::new("Battery".to_owned(), - None, critical_temp)); - } - } else { - let mut v = vec!('T' as i8, 'C' as i8, '0' as i8, 'P' as i8, 0); - for comp in &mut self.temperatures { - match &*comp.get_label() { - "CPU" => { - v[1] = 'C' as i8; - v[3] = 'P' as i8; - } - "GPU" => { - v[1] = 'G' as i8; - v[3] = 'P' as i8; - } - _ => { - v[1] = 'B' as i8; - v[3] = 'T' as i8; - } - }; - let temp = get_temperature(con, v.as_mut_ptr() as *mut i8); - ::sys::component::update_component(comp, temp); - } + 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); } } + } + } - // 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; - - if self.processors.is_empty() { - let mut num_cpu = 0; - - 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; - } + 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; - self.processors.push( - processor::create_proc("0".to_owned(), - Arc::new(ProcessorData::new(::std::ptr::null_mut(), 0)))); - 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 in 0..num_cpu { - let mut p = processor::create_proc(format!("{}", i + 1), Arc::clone(&proc_data)); - 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); - processor::set_cpu_proc(&mut p, in_use as f32 / total as f32); - self.processors.push(p); - } - } - } else 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 mut pourcent = 0f32; + 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().skip(1).enumerate() { - let old_proc_data = &*processor::get_processor_data(proc_); - 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)); - processor::update_proc(proc_, in_use as f32 / total as f32, Arc::clone(&proc_data)); + 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(); } - if self.processors.len() > 1 { - let len = self.processors.len() - 1; - if let Some(p) = self.processors.get_mut(0) { - processor::set_cpu_usage(p, pourcent / len as f32); - } - } } } - } - - fn refresh_network(&mut self) { - network::update_network(&mut self.network); + 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 + return; } if let Some(pids) = get_proc_list() { - let taskallinfo_size = mem::size_of::() as i32; - let taskinfo_size = mem::size_of::() as i32; - let threadinfo_size = mem::size_of::() as i32; let arg_max = get_arg_max(); - let entries: Vec = { let wrap = &Wrap(UnsafeCell::new(&mut self.process_list)); pids.par_iter() - .flat_map(|pid| { - let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0]; - match update_process(wrap, *pid, taskallinfo_size, taskinfo_size, - threadinfo_size, &mut mib, arg_max as size_t) { - Ok(x) => x, - Err(_) => None, - } + .flat_map(|pid| match update_process(wrap, *pid, arg_max as size_t) { + Ok(x) => x, + Err(_) => None, }) .collect() }; @@ -794,16 +268,10 @@ } fn refresh_process(&mut self, pid: Pid) -> bool { - let taskallinfo_size = mem::size_of::() as i32; - let taskinfo_size = mem::size_of::() as i32; - let threadinfo_size = mem::size_of::() as i32; - - let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_ARGMAX, 0]; let arg_max = get_arg_max(); match { let wrap = Wrap(UnsafeCell::new(&mut self.process_list)); - update_process(&wrap, pid, taskallinfo_size, taskinfo_size, - threadinfo_size, &mut mib, arg_max as size_t) + update_process(&wrap, pid, arg_max as size_t) } { Ok(Some(p)) => { self.process_list.insert(p.pid(), p); @@ -814,21 +282,19 @@ } } - fn refresh_disks(&mut self) { - for disk in &mut self.disks { - disk.update(); - } + fn refresh_disks_list(&mut self) { + self.disks = crate::mac::disk::get_disks(); } - fn refresh_disk_list(&mut self) { - self.disks = 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_process_list(&self) -> &HashMap { + fn get_processes(&self) -> &HashMap { &self.process_list } @@ -836,12 +302,20 @@ self.process_list.get(&pid) } - fn get_processor_list(&self) -> &[Processor] { - &self.processors[..] + fn get_global_processor_info(&self) -> &Processor { + &self.global_processor + } + + fn get_processors(&self) -> &[Processor] { + &self.processors } - fn get_network(&self) -> &NetworkData { - &self.network + fn get_networks(&self) -> &Networks { + &self.networks + } + + fn get_networks_mut(&mut self) -> &mut Networks { + &mut self.networks } fn get_total_memory(&self) -> u64 { @@ -869,16 +343,46 @@ self.swap_total - self.swap_free } - fn get_components_list(&self) -> &[Component] { - &self.temperatures[..] + 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[..] + &self.disks + } + + fn get_disks_mut(&mut self) -> &mut [Disk] { + &mut self.disks } fn get_uptime(&self) -> u64 { - self.uptime + 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 } } @@ -887,3 +391,78 @@ 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 +} diff -Nru rust-sysinfo-0.9.5/src/mac/users.rs rust-sysinfo-0.13.2/src/mac/users.rs --- rust-sysinfo-0.9.5/src/mac/users.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/src/mac/users.rs 2020-03-05 14:15:22.000000000 +0000 @@ -0,0 +1,177 @@ +// +// 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() +// } diff -Nru rust-sysinfo-0.9.5/src/processor.rs rust-sysinfo-0.13.2/src/processor.rs --- rust-sysinfo-0.9.5/src/processor.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/processor.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -// -// Sysinfo -// -// Copyright (c) 2015 Guillaume Gomez -// - -use std::fmt::{self, Debug, Formatter}; -use sys::Processor; -use ::ProcessorExt; - -impl Debug for Processor { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}: {}%", self.get_name(), self.get_cpu_usage()) - } -} diff -Nru rust-sysinfo-0.9.5/src/process.rs rust-sysinfo-0.13.2/src/process.rs --- rust-sysinfo-0.9.5/src/process.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/process.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -// -// Sysinfo -// -// Copyright (c) 2015 Guillaume Gomez -// - -// Functions 'set_time' and 'has_been_updated' will need to get moved here. \ No newline at end of file diff -Nru rust-sysinfo-0.9.5/src/sysinfo.rs rust-sysinfo-0.13.2/src/sysinfo.rs --- rust-sysinfo-0.9.5/src/sysinfo.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/sysinfo.rs 2020-04-08 22:13:12.000000000 +0000 @@ -14,18 +14,18 @@ //! ``` //! use sysinfo::{ProcessExt, SystemExt}; //! -//! let mut system = sysinfo::System::new(); +//! 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_process_list() { +//! 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_list() { +//! for component in system.get_components() { //! println!("{:?}", component); //! } //! @@ -35,168 +35,190 @@ //! } //! //! // And finally the RAM and SWAP information: -//! println!("total memory: {} kB", system.get_total_memory()); -//! println!("used memory : {} kB", system.get_used_memory()); -//! println!("total swap : {} kB", system.get_total_swap()); -//! println!("used swap : {} kB", system.get_used_swap()); +//! 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(target_os = "unknown"))] +#[cfg(not(any(target_os = "unknown", target_arch = "wasm32")))] extern crate libc; extern crate rayon; #[macro_use] extern crate doc_comment; -#[cfg(test)] +#[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, - Pid, - RefreshKind, -}; -pub use sys::{ - Component, - Disk, - DiskType, - NetworkData, - Process, - ProcessStatus, - Processor, - System, + 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, - ProcessExt, - ProcessorExt, - SystemExt, - NetworkExt, + ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, ProcessorExt, SystemExt, UserExt, }; -pub use utils::get_current_pid; #[cfg(feature = "c-interface")] pub use c_interface::*; +pub use utils::get_current_pid; +#[cfg(feature = "c-interface")] +mod c_interface; mod common; -mod component; -mod process; -mod processor; +mod debug; mod system; mod traits; mod utils; -#[cfg(feature = "c-interface")] -mod c_interface; -/// 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, +/// 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 traits::{ProcessExt, SystemExt}; + use super::*; #[test] fn check_memory_usage() { let mut s = ::System::new(); s.refresh_all(); - assert_eq!(s.get_process_list().iter().all(|(_, proc_)| proc_.memory() == 0), false); + 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::(); +```" +); diff -Nru rust-sysinfo-0.9.5/src/system.rs rust-sysinfo-0.13.2/src/system.rs --- rust-sysinfo-0.9.5/src/system.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/system.rs 2020-04-08 22:13:12.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // @@ -9,8 +9,8 @@ #[cfg(test)] mod tests { - use ::{ProcessExt, System, SystemExt}; - use ::utils; + use utils; + use {ProcessExt, System, SystemExt}; #[test] fn test_refresh_system() { @@ -25,15 +25,24 @@ #[test] fn test_refresh_process() { let mut sys = System::new(); - assert!(sys.refresh_process(utils::get_current_pid().expect("failed to get current pid"))); + 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"); + let p = sys + .get_process(utils::get_current_pid().expect("failed to get current pid")) + .expect("didn't find process"); assert!(p.memory() > 0); } @@ -52,8 +61,9 @@ 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"); + 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 diff -Nru rust-sysinfo-0.9.5/src/traits.rs rust-sysinfo-0.13.2/src/traits.rs --- rust-sysinfo-0.9.5/src/traits.rs 2019-09-05 18:24:27.000000000 +0000 +++ rust-sysinfo-0.13.2/src/traits.rs 2020-04-13 13:30:04.000000000 +0000 @@ -4,209 +4,706 @@ // Copyright (c) 2017 Guillaume Gomez // -use sys::{Component, Disk, DiskType, NetworkData, Process, Processor}; +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` struct. -pub trait DiskExt { +/// 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; - /// Update the disk' information. - fn update(&mut self) -> bool; + /// 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` struct. -pub trait ProcessExt { - /// Create a new process only containing the given information. +/// 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. - // /// - // /// On Windows, this is always a one element vector. + /// + /// ```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. + /// 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 kB). + /// 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. + /// 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` struct. -pub trait ProcessorExt { +/// 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 at first if you want to have a - /// non-zero value. + /// 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`] type. -pub trait SystemExt: Sized { - /// Creates a new [`System`] instance. It only contains the disks' list and the processes list - /// at this stage. Use the [`refresh_all`] method to update its internal information (or any of - /// the `refresh_` method). +/// 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 { - let mut s = Self::new_with_specifics(RefreshKind::new()); - s.refresh_disk_list(); - s.refresh_all(); - s + 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`]. /// - /// # Example + /// [`System`]: crate::System /// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// /// // We want everything except disks. - /// let mut system = System::new_with_specifics(RefreshKind::everything().without_disk_list()); + /// let mut system = System::new_with_specifics(RefreshKind::everything().without_disks_list()); /// /// assert_eq!(system.get_disks().len(), 0); - /// assert!(system.get_process_list().len() > 0); + /// assert!(system.get_processes().len() > 0); /// /// // If you want the disks list afterwards, just call the corresponding - /// // "refresh_disk_list": - /// system.refresh_disk_list(); - /// assert!(system.get_disks().len() > 0); + /// // "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. /// - /// # Examples - /// /// ``` /// use sysinfo::{RefreshKind, System, SystemExt}; /// - /// let mut s = System::new(); + /// let mut s = System::new_all(); /// - /// // Let's just update network data and processes: - /// s.refresh_specifics(RefreshKind::new().with_network().with_processes()); + /// // 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.system() { - self.refresh_system(); + if refreshes.memory() { + self.refresh_memory(); + } + if refreshes.cpu() { + self.refresh_cpu(); } - if refreshes.network() { - self.refresh_network(); + 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.disk_list() { - self.refresh_disk_list(); - } - if refreshes.disks() { + 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(); } - /// Refresh system information (such as memory, swap, CPU usage and components' temperature). - fn refresh_system(&mut self); + /// 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); - /// Get all processes and update their information. + /// 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); - /// Refresh *only* the process corresponding to `pid`. Returns `false` if the process doesn't - /// exist. + /// 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. - fn refresh_disks(&mut self); + /// + /// ```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. - fn refresh_disk_list(&mut self); + /// + /// ```no_run + /// use sysinfo::{System, SystemExt}; + /// + /// let mut s = System::new_all(); + /// s.refresh_disks_list(); + /// ``` + fn refresh_disks_list(&mut self); - /// Refresh data network. - fn refresh_network(&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 and disks information. + /// 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_network(); + self.refresh_networks(); } /// Returns the process list. - fn get_process_list(&self) -> &HashMap; + /// + /// ```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_process_list().values() { + let mut ret = vec![]; + for val in self.get_processes().values() { if val.name().contains(name) { ret.push(val); } @@ -214,57 +711,503 @@ ret } - /// The first processor in the array is the "main" one (aka the addition of all the others). - fn get_processor_list(&self) -> &[Processor]; + /// 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 total RAM size in KiB. + /// 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 free RAM size in KiB. + /// 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 used RAM size in KiB. + /// 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 SWAP size in KiB. + /// 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 free SWAP size in KiB. + /// 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 used SWAP size in KiB. + /// 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 components list. - fn get_components_list(&self) -> &[Component]; + /// 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 disks' list. + /// 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 network data. - fn get_network(&self) -> &NetworkData; + /// 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 system uptime. + /// 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 incoming and outgoing data. -pub trait NetworkExt { - /// Returns the number of incoming bytes. - fn get_income(&self) -> u64; +/// 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 outgoing bytes. - fn get_outcome(&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 { - /// Returns the component's temperature (in celsius degree). +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 this component. + + /// 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 computer halts. + + /// 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 component's label. + + /// 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]; } diff -Nru rust-sysinfo-0.9.5/src/unknown/component.rs rust-sysinfo-0.13.2/src/unknown/component.rs --- rust-sysinfo-0.9.5/src/unknown/component.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/component.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,14 +1,13 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // use ComponentExt; /// Dummy struct representing a component. -pub struct Component { -} +pub struct Component {} impl ComponentExt for Component { fn get_temperature(&self) -> f32 { @@ -26,4 +25,6 @@ fn get_label(&self) -> &str { "" } + + fn refresh(&mut self) {} } diff -Nru rust-sysinfo-0.9.5/src/unknown/disk.rs rust-sysinfo-0.13.2/src/unknown/disk.rs --- rust-sysinfo-0.9.5/src/unknown/disk.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/disk.rs 2020-02-06 22:36:19.000000000 +0000 @@ -1,22 +1,17 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // -use ::DiskExt; +use DiskExt; +use DiskType; -use std::path::Path; use std::ffi::OsStr; - -/// Enum containing the different handled disks types. -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum DiskType { -} +use std::path::Path; /// Struct containing a disk information. -pub struct Disk { -} +pub struct Disk {} impl DiskExt for Disk { fn get_type(&self) -> DiskType { @@ -43,7 +38,7 @@ 0 } - fn update(&mut self) -> bool { + fn refresh(&mut self) -> bool { true } } diff -Nru rust-sysinfo-0.9.5/src/unknown/mod.rs rust-sysinfo-0.13.2/src/unknown/mod.rs --- rust-sysinfo-0.9.5/src/unknown/mod.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/mod.rs 2020-02-06 22:36:19.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // @@ -12,8 +12,8 @@ pub mod system; pub use self::component::Component; -pub use self::disk::{Disk, DiskType}; -pub use self::network::NetworkData; +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; diff -Nru rust-sysinfo-0.9.5/src/unknown/network.rs rust-sysinfo-0.13.2/src/unknown/network.rs --- rust-sysinfo-0.9.5/src/unknown/network.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/network.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,21 +1,94 @@ -// +// // 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. -#[derive(Debug)] pub struct NetworkData; impl NetworkExt for NetworkData { - fn get_income(&self) -> u64 { + 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_outcome(&self) -> u64 { + fn get_total_errors_on_transmitted(&self) -> u64 { 0 } } diff -Nru rust-sysinfo-0.9.5/src/unknown/processor.rs rust-sysinfo-0.13.2/src/unknown/processor.rs --- rust-sysinfo-0.9.5/src/unknown/processor.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/processor.rs 2020-03-11 16:52:16.000000000 +0000 @@ -1,13 +1,18 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // -use ::ProcessorExt; +use ProcessorExt; /// Dummy struct that represents a processor. -pub struct Processor { +pub struct Processor {} + +impl Processor { + pub(crate) fn new() -> Processor { + Processor {} + } } impl ProcessorExt for Processor { @@ -18,4 +23,16 @@ fn get_name(&self) -> &str { "" } + + fn get_frequency(&self) -> u64 { + 0 + } + + fn get_vendor_id(&self) -> &str { + "" + } + + fn get_brand(&self) -> &str { + "" + } } diff -Nru rust-sysinfo-0.9.5/src/unknown/process.rs rust-sysinfo-0.13.2/src/unknown/process.rs --- rust-sysinfo-0.9.5/src/unknown/process.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/process.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,19 +1,20 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // use std::path::Path; +use DiskUsage; use Pid; -use ::ProcessExt; +use ProcessExt; /// Enum describing the different status of a process. #[derive(Clone, Copy, Debug)] pub struct ProcessStatus; /// Struct containing a process' information. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct Process { pid: Pid, parent: Option, @@ -21,10 +22,7 @@ impl ProcessExt for Process { fn new(pid: Pid, parent: Option, _start_time: u64) -> Process { - Process { - pid, - parent, - } + Process { pid, parent } } fn kill(&self, _signal: ::Signal) -> bool { @@ -63,6 +61,10 @@ 0 } + fn virtual_memory(&self) -> u64 { + 0 + } + fn parent(&self) -> Option { self.parent } @@ -78,4 +80,8 @@ fn cpu_usage(&self) -> f32 { 0.0 } + + fn disk_usage(&self) -> DiskUsage { + DiskUsage::default() + } } diff -Nru rust-sysinfo-0.9.5/src/unknown/system.rs rust-sysinfo-0.13.2/src/unknown/system.rs --- rust-sysinfo-0.9.5/src/unknown/system.rs 2019-06-24 17:21:20.000000000 +0000 +++ rust-sysinfo-0.13.2/src/unknown/system.rs 2020-04-08 22:13:12.000000000 +0000 @@ -1,58 +1,58 @@ -// +// // Sysinfo -// +// // Copyright (c) 2015 Guillaume Gomez // use sys::component::Component; -use sys::processor::*; use sys::process::*; +use sys::processor::*; use sys::Disk; -use sys::NetworkData; -use ::{RefreshKind, SystemExt}; +use sys::Networks; +use LoadAvg; use Pid; +use User; +use {RefreshKind, SystemExt}; use std::collections::HashMap; /// Structs containing system's information. -#[derive(Debug)] pub struct System { processes_list: HashMap, - network: NetworkData, + networks: Networks, + global_processor: Processor, } impl SystemExt for System { fn new_with_specifics(_: RefreshKind) -> System { System { processes_list: Default::default(), - network: NetworkData, + networks: Networks::new(), + global_processor: Processor::new(), } } - fn refresh_system(&mut self) { - } + fn refresh_memory(&mut self) {} - fn refresh_processes(&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(&mut self) { - } - - fn refresh_disk_list(&mut self) { - } + fn refresh_disks_list(&mut self) {} - fn refresh_network(&mut self) { - } + fn refresh_users_list(&mut self) {} // COMMON PART // // Need to be moved into a "common" file to avoid duplication. - fn get_process_list(&self) -> &HashMap { + fn get_processes(&self) -> &HashMap { &self.processes_list } @@ -60,11 +60,19 @@ None } - fn get_network(&self) -> &NetworkData { - &self.network + fn get_networks(&self) -> &Networks { + &self.networks } - fn get_processor_list(&self) -> &[Processor] { + 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] { &[] } @@ -92,17 +100,41 @@ 0 } - fn get_components_list(&self) -> &[Component] { + 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 { diff -Nru rust-sysinfo-0.9.5/src/utils.rs rust-sysinfo-0.13.2/src/utils.rs --- rust-sysinfo-0.9.5/src/utils.rs 2019-09-05 18:24:27.000000000 +0000 +++ rust-sysinfo-0.13.2/src/utils.rs 2020-03-18 15:11:16.000000000 +0000 @@ -1,22 +1,22 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // -#[cfg(all(not(target_os = "windows"), not(target_os = "unknown")))] -use std::fs; -#[cfg(all(not(target_os = "windows"), not(target_os = "unknown")))] -use std::path::{Path, PathBuf}; -#[cfg(all(not(target_os = "windows"), not(target_os = "unknown")))] +#[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(all(not(target_os = "windows"), not(target_os = "unknown")))] +#[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(all(not(target_os = "windows"), not(target_os = "unknown")))] -use libc::{c_char, lstat, stat, S_IFLNK, S_IFMT}; +#[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] +use std::path::{Path, PathBuf}; use Pid; -#[cfg(all(not(target_os = "windows"), not(target_os = "unknown")))] +#[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] pub fn realpath(original: &Path) -> PathBuf { use std::mem::MaybeUninit; @@ -51,7 +51,7 @@ } /* convert a path to a NUL-terminated Vec suitable for use with C functions */ -#[cfg(all(not(target_os = "windows"), not(target_os = "unknown")))] +#[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(); @@ -62,9 +62,22 @@ /// 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(all(not(target_os = "windows"), not(target_os = "unknown")))] { + if #[cfg(not(any(target_os = "windows", target_os = "unknown", target_arch = "wasm32")))] { fn inner() -> Result { unsafe { Ok(::libc::getpid()) } } diff -Nru rust-sysinfo-0.9.5/src/windows/component.rs rust-sysinfo-0.13.2/src/windows/component.rs --- rust-sysinfo-0.9.5/src/windows/component.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/component.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,27 +1,63 @@ -// +// // 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. - pub fn new(label: String, max: Option, critical: Option) -> Component { - Component { - temperature: 0f32, - label: label, - max: max.unwrap_or(0.0), - critical: critical, + 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, } } } @@ -42,8 +78,302 @@ 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 { - Vec::new() + 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(); + } + } } diff -Nru rust-sysinfo-0.9.5/src/windows/disk.rs rust-sysinfo-0.13.2/src/windows/disk.rs --- rust-sysinfo-0.9.5/src/windows/disk.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/disk.rs 2020-03-05 10:42:29.000000000 +0000 @@ -1,42 +1,25 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // use std::ffi::{OsStr, OsString}; -use std::fmt::{Debug, Error, Formatter}; -use std::str; use std::path::Path; use DiskExt; +use DiskType; use winapi::um::fileapi::GetDiskFreeSpaceExW; use winapi::um::winnt::ULARGE_INTEGER; -/// Enum containing the different handled disks types. -#[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), - } - } -} - -pub fn new_disk(name: &OsStr, mount_point: &[u16], file_system: &[u8], type_: DiskType, - total_space: u64) -> Disk { +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(), @@ -46,7 +29,7 @@ total_space: total_space, available_space: 0, }; - d.update(); + d.refresh(); d } @@ -61,15 +44,6 @@ available_space: u64, } -impl Debug for Disk { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { - write!(fmt, - "Disk({:?})[FS: {:?}][Type: {:?}] mounted on {:?}: {}/{} B", - self.get_name(), str::from_utf8(self.get_file_system()).unwrap(), self.get_type(), - self.get_mount_point(), self.get_available_space(), self.get_total_space()) - } -} - impl DiskExt for Disk { fn get_type(&self) -> DiskType { self.type_ @@ -95,14 +69,17 @@ self.available_space } - fn update(&mut self) -> bool { + 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 { + 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; } diff -Nru rust-sysinfo-0.9.5/src/windows/ffi.rs rust-sysinfo-0.13.2/src/windows/ffi.rs --- rust-sysinfo-0.9.5/src/windows/ffi.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/ffi.rs 2020-03-05 10:42:29.000000000 +0000 @@ -1,333 +1,236 @@ -// +// // Sysinfo -// -// Copyright (c) 2015 Guillaume Gomez +// +// Copyright (c) 2020 Guillaume Gomez // -use libc::{c_int, c_char, c_void, c_uchar, c_uint, size_t, uint32_t, uint64_t, c_ushort}; - -extern "C" { - pub static kCFAllocatorDefault: CFAllocatorRef; - - 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_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 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: *mut 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 static kCFAllocatorNull: CFAllocatorRef; - - 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) -> u32; - 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) -> u32; - //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) -> u32; -} - -// 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)* - } -} +// TO BE REMOVED ONCE https://github.com/retep998/winapi-rs/pull/802 IS MERGED!!! -// 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; +#![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; + } + )+} } } -// TODO: waiting for https://github.com/rust-lang/libc/pull/678 -#[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: uint32_t, - pub ifi_metric: uint32_t, - pub ifi_baudrate: uint64_t, - pub ifi_ipackets: uint64_t, - pub ifi_ierrors: uint64_t, - pub ifi_opackets: uint64_t, - pub ifi_oerrors: uint64_t, - pub ifi_collisions: uint64_t, - pub ifi_ibytes: uint64_t, - pub ifi_obytes: uint64_t, - pub ifi_imcasts: uint64_t, - pub ifi_omcasts: uint64_t, - pub ifi_iqdrops: uint64_t, - pub ifi_noproto: uint64_t, - pub ifi_recvtiming: uint32_t, - pub ifi_xmittiming: uint32_t, - pub ifi_lastchange: timeval32, +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; } - -// TODO: waiting for https://github.com/rust-lang/libc/pull/678 -#[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, -} - -#[repr(C)] -pub struct __CFAllocator { - __private: c_void, -} - -#[repr(C)] -pub struct __CFDictionary { - __private: c_void, -} - -#[repr(C)] -pub struct __CFString { - __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; - -//#[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; - -/*#[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, -}*/ - -#[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, -} - -#[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 -} - -#[repr(C)] -pub struct KeyData_vers_t { - pub major: u8, - pub minor: u8, - pub build: u8, - pub reserved: [u8; 1], - pub release: u16, -} - -#[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, -} - -#[repr(C)] -pub struct KeyData_keyInfo_t { - pub data_size: u32, - pub data_type: u32, - pub data_attributes: u8, -} - -#[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 -} - -#[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, -} - -//pub const HOST_CPU_LOAD_INFO_COUNT: usize = 4; -//pub const HOST_CPU_LOAD_INFO: u32 = 3; -pub const KERN_SUCCESS: u32 = 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 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 KIO_RETURN_SUCCESS: i32 = 0; -#[allow(non_upper_case_globals)] -pub const kCFStringEncodingMacRoman: CFStringEncoding = 0; diff -Nru rust-sysinfo-0.9.5/src/windows/macros.rs rust-sysinfo-0.13.2/src/windows/macros.rs --- rust-sysinfo-0.9.5/src/windows/macros.rs 2019-03-03 18:01:39.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/macros.rs 2019-12-05 17:11:28.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // @@ -16,5 +16,5 @@ { $t } - }} + }}; } diff -Nru rust-sysinfo-0.9.5/src/windows/mod.rs rust-sysinfo-0.13.2/src/windows/mod.rs --- rust-sysinfo-0.9.5/src/windows/mod.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/mod.rs 2020-03-05 10:42:29.000000000 +0000 @@ -1,37 +1,25 @@ -// +// // 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 use self::component::Component; -pub use self::disk::{Disk, DiskType}; -pub use self::network::NetworkData; -pub use self::process::{Process,ProcessStatus}; -pub use self::processor::Processor; -pub use self::system::System;*/ - mod component; mod disk; -//mod ffi; -#[macro_use] mod macros; +#[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, DiskType}; -pub use self::network::NetworkData; +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; diff -Nru rust-sysinfo-0.9.5/src/windows/network.rs rust-sysinfo-0.13.2/src/windows/network.rs --- rust-sysinfo-0.9.5/src/windows/network.rs 2019-01-17 20:31:16.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/network.rs 2020-03-20 16:16:00.000000000 +0000 @@ -1,57 +1,246 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // +use std::collections::{HashMap, HashSet}; + +use windows::ffi::{self, MIB_IF_ROW2, PMIB_IF_TABLE2}; use NetworkExt; -use windows::tools::KeyHandler; -use windows::processor::Query; +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, - keys_in: Vec, - keys_out: Vec, + 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_income(&self) -> u64 { + fn get_received(&self) -> u64 { + self.current_in - self.old_in + } + + fn get_total_received(&self) -> u64 { self.current_in } - fn get_outcome(&self) -> u64 { + fn get_transmitted(&self) -> u64 { + self.current_out - self.old_out + } + + fn get_total_transmitted(&self) -> u64 { self.current_out } -} -pub fn new() -> NetworkData { - NetworkData { - current_in: 0, - current_out: 0, - keys_in: Vec::new(), - keys_out: Vec::new(), + fn get_packets_received(&self) -> u64 { + self.packets_in - self.old_packets_in } -} -pub fn refresh(network: &mut NetworkData, query: &Option) { - if let &Some(ref query) = query { - network.current_in = 0; - for key in &network.keys_in { - network.current_in += query.get_u64(&key.unique_id).expect("key disappeared"); - } - network.current_out = 0; - for key in &network.keys_out { - network.current_out += query.get_u64(&key.unique_id).expect("key disappeared"); - } + fn get_total_packets_received(&self) -> u64 { + self.packets_in } -} -pub fn get_keys_in(network: &mut NetworkData) -> &mut Vec { - &mut network.keys_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 + } -pub fn get_keys_out(network: &mut NetworkData) -> &mut Vec { - &mut network.keys_out + 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 + } } diff -Nru rust-sysinfo-0.9.5/src/windows/processor.rs rust-sysinfo-0.13.2/src/windows/processor.rs --- rust-sysinfo-0.9.5/src/windows/processor.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/processor.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,154 +1,167 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // use std::collections::HashMap; -use std::sync::{Arc, Mutex}; -use std::thread::{self/*, sleep*/, JoinHandle}; -//use std::time::Duration; +use std::mem; +use std::ops::DerefMut; +use std::ptr::null_mut; +use std::sync::Mutex; -use ProcessorExt; use windows::tools::KeyHandler; +use LoadAvg; +use ProcessorExt; -use winapi::shared::minwindef::{FALSE, ULONG}; +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::{ - PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_FMT_LARGE, PDH_HCOUNTER, PDH_HQUERY, PdhAddCounterW, - PdhCollectQueryData, PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, + PdhAddCounterW, PdhAddEnglishCounterA, PdhCloseQuery, PdhCollectQueryData, + PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter, + PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY, }; -use winapi::um::synchapi::{CreateEventA, WaitForSingleObject}; -use winapi::um::winbase::{INFINITE, WAIT_OBJECT_0}; -use winapi::um::winnt::HANDLE; - -#[derive(Debug)] -pub enum CounterValue { - Float(f32), - Integer(u64), -} - -impl CounterValue { - pub fn get_f32(&self) -> f32 { - match *self { - CounterValue::Float(v) => v, - _ => panic!("not a float"), - } - } - - pub fn get_u64(&self) -> u64 { - match *self { - CounterValue::Integer(v) => v, - _ => panic!("not an integer"), - } - } -} - -#[allow(dead_code)] -#[derive(Debug)] -struct Counter { - counter: PDH_HCOUNTER, - value: CounterValue, - getter: Vec, -} - -impl Counter { - fn new_f32(counter: PDH_HCOUNTER, value: f32, getter: Vec) -> Counter { - Counter { - counter: counter, - value: CounterValue::Float(value), - getter: getter, - } - } - - fn new_u64(counter: PDH_HCOUNTER, value: u64, getter: Vec) -> Counter { - Counter { - counter: counter, - value: CounterValue::Integer(value), - getter: getter, - } +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: Mutex>, + data: HashMap, } unsafe impl Send for InternalQuery {} unsafe impl Sync for InternalQuery {} -impl InternalQuery { - pub fn record(&self) -> bool { +impl Drop for InternalQuery { + fn drop(&mut self) { unsafe { - let status = PdhCollectQueryData(self.query); - if status != ERROR_SUCCESS as i32 { - println!("error: {:?} {} {:?}", status, ERROR_SUCCESS, self.query); - return false; + for (_, counter) in self.data.iter() { + PdhRemoveCounter(*counter); } - if PdhCollectQueryDataEx(self.query, 1, self.event) != ERROR_SUCCESS as i32 { - return false; + + if !self.event.is_null() { + CloseHandle(self.event); } - if WaitForSingleObject(self.event, INFINITE) == WAIT_OBJECT_0 { - if let Ok(ref mut data) = self.data.lock() { - let mut counter_type: ULONG = 0; - let mut display_value: PDH_FMT_COUNTERVALUE = ::std::mem::zeroed(); - for (_, x) in data.iter_mut() { - match x.value { - CounterValue::Float(ref mut value) => { - if PdhGetFormattedCounterValue(x.counter, - PDH_FMT_DOUBLE, - &mut counter_type, - &mut display_value) == ERROR_SUCCESS as i32 { - *value = *display_value.u.doubleValue() as f32 / 100f32; - } - } - CounterValue::Integer(ref mut value) => { - if PdhGetFormattedCounterValue(x.counter, - PDH_FMT_LARGE, - &mut counter_type, - &mut display_value) == ERROR_SUCCESS as i32 { - *value = *display_value.u.largeValue() as u64; - } - } - } - } - } - true - } else { - false + + if !self.query.is_null() { + PdhCloseQuery(self.query); } } } } pub struct Query { - internal: Arc, - thread: Option>, + internal: InternalQuery, } impl Query { pub fn new() -> Option { - let mut query = ::std::ptr::null_mut(); + let mut query = null_mut(); unsafe { - if PdhOpenQueryA(::std::ptr::null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 { - let event = CreateEventA(::std::ptr::null_mut(), FALSE, FALSE, - b"some_ev\0".as_ptr() as *const i8); - if event.is_null() { - None - } else { - let q = Arc::new(InternalQuery { - query: query, - event: event, - data: Mutex::new(HashMap::new()), - }); - Some( - Query { - internal: q, - thread: None, - }) - } + 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 } @@ -156,59 +169,52 @@ } pub fn get(&self, name: &String) -> Option { - if let Ok(data) = self.internal.data.lock() { - if let Some(ref counter) = data.get(name) { - return Some(counter.value.get_f32()); - } - } - None - } - - pub fn get_u64(&self, name: &String) -> Option { - if let Ok(data) = self.internal.data.lock() { - if let Some(ref counter) = data.get(name) { - return Some(counter.value.get_u64()); + 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, value: CounterValue) -> bool { - if let Ok(data) = self.internal.data.lock() { - if data.contains_key(name) { - return false; - } + 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 i32 { - self.internal.data.lock() - .expect("couldn't add counter...") - .insert(name.clone(), - match value { - CounterValue::Float(v) => Counter::new_f32(counter, v, getter), - CounterValue::Integer(v) => Counter::new_u64(counter, v, getter), - }); + 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 { - eprintln!("failed to add counter '{}': {:x}...", name, ret); + sysinfo_debug!("failed to add counter '{}': {:x}...", name, ret); return false; } } true } - pub fn start(&mut self) { - let internal = Arc::clone(&self.internal); - self.thread = Some( - thread::spawn(move || { - loop { - internal.record(); - } - })); + pub fn refresh(&self) { + unsafe { + if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ { + sysinfo_debug!("failed to refresh CPU data"); + } + } } } @@ -216,8 +222,10 @@ pub struct Processor { name: String, cpu_usage: f32, - key_idle: Option, key_used: Option, + vendor_id: String, + brand: String, + frequency: u64, } impl ProcessorExt for Processor { @@ -228,31 +236,172 @@ 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 { - fn new_with_values(name: &str) -> 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_idle: None, key_used: None, + vendor_id, + brand, + frequency, } } -} -pub fn create_processor(name: &str) -> Processor { - Processor::new_with_values(name) + pub(crate) fn set_cpu_usage(&mut self, value: f32) { + self.cpu_usage = value; + } } -pub fn set_cpu_usage(p: &mut Processor, value: f32) { - p.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; -pub fn get_key_idle(p: &mut Processor) -> &mut Option { - &mut p.key_idle + 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] + } +} diff -Nru rust-sysinfo-0.9.5/src/windows/process.rs rust-sysinfo-0.13.2/src/windows/process.rs --- rust-sysinfo-0.9.5/src/windows/process.rs 2019-01-17 20:31:16.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/process.rs 2020-04-13 13:30:04.000000000 +0000 @@ -4,32 +4,33 @@ // Copyright (c) 2018 Guillaume Gomez // -use std::fmt::{self, Formatter, Debug}; -use std::mem::{size_of, zeroed}; +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_uint, c_void, memcpy}; +use libc::{c_void, memcpy}; +use DiskUsage; use Pid; use ProcessExt; -use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, MAX_PATH/*, TRUE, USHORT*/}; -use winapi::um::handleapi::CloseHandle; -use winapi::um::winnt::{ - HANDLE, ULARGE_INTEGER, /*THREAD_GET_CONTEXT, THREAD_QUERY_INFORMATION, THREAD_SUSPEND_RESUME,*/ - /*, PWSTR*/ PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ, +use ntapi::ntpsapi::{ + NtQueryInformationProcess, ProcessBasicInformation, PROCESS_BASIC_INFORMATION, }; -use winapi::um::processthreadsapi::{GetProcessTimes, OpenProcess, TerminateProcess}; +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::{ - GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX, - EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, LIST_MODULES_ALL, + EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo, + LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX, }; use winapi::um::sysinfoapi::GetSystemTimeAsFileTime; -use winapi::um::tlhelp32::{ - CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, -}; +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)] @@ -57,37 +58,30 @@ if pid == 0 { return None; } - let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE; + let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; let process_handler = unsafe { OpenProcess(options, FALSE, pid as DWORD) }; if process_handler.is_null() { - 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) - } + None } else { Some(process_handler) } } #[derive(Clone)] -struct HandleWrapper(HANDLE); +struct PtrWrapper(T); -impl Deref for HandleWrapper { - type Target = HANDLE; +impl Deref for PtrWrapper { + type Target = T; - fn deref(&self) -> &HANDLE { + fn deref(&self) -> &Self::Target { &self.0 } } -unsafe impl Send for HandleWrapper {} -unsafe impl Sync for HandleWrapper {} +unsafe impl Send for PtrWrapper {} +unsafe impl Sync for PtrWrapper {} /// Struct containing a process' information. -#[derive(Clone)] pub struct Process { name: String, cmd: Vec, @@ -96,15 +90,212 @@ environ: Vec, cwd: PathBuf, root: PathBuf, - memory: u64, + pub(crate) memory: u64, + pub(crate) virtual_memory: u64, parent: Option, status: ProcessStatus, - handle: HandleWrapper, + 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 @@ -115,94 +306,41 @@ impl ProcessExt for Process { fn new(pid: Pid, parent: Option, _: u64) -> Process { if let Some(process_handler) = get_process_handler(pid) { - let mut h_mod = ::std::ptr::null_mut(); - let mut process_name = [0u16; MAX_PATH + 1]; - let mut cb_needed = 0; - - unsafe { - if EnumProcessModulesEx(process_handler, - &mut h_mod, - ::std::mem::size_of::() as DWORD, - &mut cb_needed, - LIST_MODULES_ALL) != 0 { - GetModuleBaseNameW(process_handler, - h_mod, - 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; - } - let name = String::from_utf16_lossy(&process_name[..pos]); - let environ = get_proc_env(process_handler, pid as u32, &name); - - let mut exe_buf = [0u16; MAX_PATH + 1]; - GetModuleFileNameExW(process_handler, - h_mod, - exe_buf.as_mut_ptr(), - MAX_PATH as DWORD + 1); - - pos = 0; - for x in exe_buf.iter() { - if *x == 0 { - break - } - pos += 1; - } - - let exe = PathBuf::from(String::from_utf16_lossy(&exe_buf[..pos])); - let mut root = exe.clone(); - root.pop(); - Process { - handle: HandleWrapper(process_handler), - name: name, - pid: pid, - parent: parent, - cmd: get_cmd_line(pid), - environ: environ, - exe: exe, - cwd: PathBuf::new(), - root: root, - status: ProcessStatus::Run, - memory: 0, - cpu_usage: 0., - old_cpu: 0, - old_sys_cpu: 0, - old_user_cpu: 0, - start_time: get_start_time(process_handler), - } - } + Process::new_with_handle(pid, parent, process_handler) } else { Process { - handle: HandleWrapper(::std::ptr::null_mut()), + handle: PtrWrapper(null_mut()), name: String::new(), - pid: pid, - parent: parent, - cmd: get_cmd_line(pid), + 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 { - if self.handle.is_null() { - false - } else { - unsafe { TerminateProcess(*self.handle, signal as c_uint) != 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, } } @@ -238,6 +376,10 @@ self.memory } + fn virtual_memory(&self) -> u64 { + self.virtual_memory + } + fn parent(&self) -> Option { self.parent } @@ -253,98 +395,130 @@ 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 + return; } CloseHandle(*self.handle); } } } -#[allow(unused_must_use)] -impl Debug for Process { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - writeln!(f, "pid: {}", self.pid); - writeln!(f, "name: {}", self.name); - writeln!(f, "environment:"); - for var in self.environ.iter() { - if var.len() > 0 { - writeln!(f, "\t{}", var); - } - } - writeln!(f, "command:"); - for arg in &self.cmd { - writeln!(f, "\t{}", arg); - } - writeln!(f, "executable path: {:?}", self.exe); - writeln!(f, "current working directory: {:?}", self.cwd); - writeln!(f, "memory usage: {} kB", self.memory); - writeln!(f, "cpu usage: {}", self.cpu_usage); - writeln!(f, "root path: {:?}", self.root) - } -} - 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); + 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 } -pub unsafe fn get_parent_process_id(pid: Pid) -> Option { - let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - let mut entry: PROCESSENTRY32 = zeroed(); - entry.dwSize = size_of::() as u32; - let mut not_the_end = Process32First(snapshot, &mut entry); - while not_the_end != 0 { - if pid == entry.th32ProcessID as usize { - // TODO: if some day I have the motivation to add threads: - // ListProcessThreads(entry.th32ProcessID); - CloseHandle(snapshot); - return Some(entry.th32ParentProcessID as usize); - } - not_the_end = Process32Next(snapshot, &mut entry); - } - CloseHandle(snapshot); - None -} - -/*fn run_wmi(args: &[&str]) -> Option { - use std::process::Command; - - if let Ok(out) = Command::new("wmic") - .args(args) - .output() { - if out.status.success() { - return Some(String::from_utf8_lossy(&out.stdout).into_owned()); +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)); } - } - None -}*/ -fn get_cmd_line(_pid: Pid) -> Vec { - /*let where_req = format!("ProcessId={}", pid); + winapi::um::winbase::LocalFree(argv_p as *mut _); - if let Some(ret) = run_wmi(&["process", "where", &where_req, "get", "CommandLine"]) { - for line in ret.lines() { - if line.is_empty() || line == "CommandLine" { - continue - } - return vec![line.to_owned()]; - } - }*/ - Vec::new() + res + } } unsafe fn get_proc_env(_handle: HANDLE, _pid: u32, _name: &str) -> Vec { @@ -384,7 +558,7 @@ context.MxCsr as usize as *mut winapi::c_void, x.as_mut_ptr() as *mut winapi::c_void, x.len() as u64, - ::std::ptr::null_mut()) != 0 { + null_mut()) != 0 { for y in x { print!("{}", y as char); } @@ -411,7 +585,7 @@ ret } -pub fn get_executable_path(_pid: Pid) -> PathBuf { +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"]) { @@ -425,35 +599,51 @@ PathBuf::new() } -pub fn compute_cpu_usage(p: &mut Process, nb_processors: u64) { +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(); - 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::()); - - 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) + 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.; + / nb_processors as f32 + * 100.; p.old_cpu = *now.QuadPart(); p.old_user_cpu = *user.QuadPart(); p.old_sys_cpu = *sys.QuadPart(); @@ -464,17 +654,32 @@ *p.handle } -pub fn update_proc_info(p: &mut Process) { - update_memory(p); +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.PrivateUsage as u64) >> 10u64; // / 1024; + 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; } } } diff -Nru rust-sysinfo-0.9.5/src/windows/system.rs rust-sysinfo-0.13.2/src/windows/system.rs --- rust-sysinfo-0.9.5/src/windows/system.rs 2019-06-22 12:30:58.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/system.rs 2020-04-13 13:30:04.000000000 +0000 @@ -7,225 +7,154 @@ 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 DiskExt; +use LoadAvg; +use Networks; use Pid; use ProcessExt; use RefreshKind; use SystemExt; +use User; -use windows::network::{self, NetworkData}; -use windows::processor::CounterValue; -use windows::tools::*; use windows::process::{ - Process, compute_cpu_usage, get_handle, get_parent_process_id, update_proc_info, + compute_cpu_usage, get_handle, get_system_computation_time, update_disk_usage, update_memory, + Process, }; +use windows::tools::*; -use winapi::shared::minwindef::{DWORD, FALSE}; -use winapi::shared::winerror::ERROR_SUCCESS; +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::pdh::PdhEnumObjectItemsW; use winapi::um::processthreadsapi::GetExitCodeProcess; -use winapi::um::psapi::K32EnumProcesses; -use winapi::um::sysinfoapi::{ - GlobalMemoryStatusEx, MEMORYSTATUSEX, -}; +use winapi::um::sysinfoapi::{GetTickCount64, GlobalMemoryStatusEx, MEMORYSTATUSEX}; use winapi::um::winnt::HANDLE; - use rayon::prelude::*; -const PROCESS_LEN: usize = 10192; - -/// Struct containing system's information. +/// 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, - temperatures: Vec, + components: Vec, disks: Vec, query: Option, - network: NetworkData, - uptime: u64, + networks: Networks, + boot_time: u64, + users: Vec, } -impl System { - fn clear_procs(&mut self) { - if self.processors.len() > 0 { - let mut to_delete = Vec::new(); - - for (pid, proc_) in self.process_list.iter_mut() { - if !is_proc_running(get_handle(proc_)) { - to_delete.push(*pid); - } else { - compute_cpu_usage(proc_, self.processors.len() as u64 - 1); - } - } - for pid in to_delete { - self.process_list.remove(&pid); - } - } - } -} - -struct Wrap(Process); - -unsafe impl Send for Wrap {} +// Useful for parallel iterations. +struct Wrap(T); -struct WrapSystem<'a>(UnsafeCell<&'a mut System>); +unsafe impl Send for Wrap {} +unsafe impl Sync for Wrap {} -impl<'a> WrapSystem<'a> { - fn get(&self) -> &'a mut System { - unsafe { *(self.0.get()) } +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 + } } } -unsafe impl<'a> Send for WrapSystem<'a> {} -unsafe impl<'a> Sync for WrapSystem<'a> {} - 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::new(), + process_list: HashMap::with_capacity(500), mem_total: 0, mem_free: 0, swap_total: 0, swap_free: 0, - processors: init_processors(), - temperatures: component::get_components(), - disks: Vec::new(), + global_processor: Processor::new_with_values("Total CPU", vendor_id, brand, 0), + processors, + components: Vec::new(), + disks: Vec::with_capacity(2), query: Query::new(), - network: network::new(), - uptime: get_uptime(), + 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 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.processors[0]), - "tot_0".to_owned(), - CounterValue::Float(0.)); - } - if let Some(ref idle_time_trans) = idle_time_trans { - add_counter(format!("\\{}(_Total)\\{}", processor_trans, idle_time_trans), - query, - get_key_idle(&mut s.processors[0]), - "tot_1".to_owned(), - CounterValue::Float(0.)); + 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().skip(1).enumerate() { + 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), - CounterValue::Float(0.)); - } - if let Some(ref idle_time_trans) = idle_time_trans { - add_counter(format!("\\{}({})\\{}", processor_trans, pos, idle_time_trans), - query, - get_key_idle(proc_), - format!("{}_1", pos), - CounterValue::Float(0.)); + 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 + } - if let Some(network_trans) = get_translation(&"Network Interface".to_owned(), &x) { - let network_in_trans = get_translation(&"Bytes Received/Sec".to_owned(), &x); - let network_out_trans = get_translation(&"Bytes Sent/sec".to_owned(), &x); - - const PERF_DETAIL_WIZARD: DWORD = 400; - const PDH_MORE_DATA: DWORD = 0x800007D2; - - let mut network_trans_utf16: Vec = network_trans.encode_utf16().collect(); - network_trans_utf16.push(0); - let mut dwCounterListSize: DWORD = 0; - let mut dwInstanceListSize: DWORD = 0; - let status = unsafe { - PdhEnumObjectItemsW(::std::ptr::null(), - ::std::ptr::null(), - network_trans_utf16.as_ptr(), - ::std::ptr::null_mut(), - &mut dwCounterListSize, - ::std::ptr::null_mut(), - &mut dwInstanceListSize, - PERF_DETAIL_WIZARD, - 0) - }; - if status != PDH_MORE_DATA as i32 { - panic!("got invalid status: {:x}", status); - } - let mut pwsCounterListBuffer: Vec = Vec::with_capacity(dwCounterListSize as usize); - let mut pwsInstanceListBuffer: Vec = Vec::with_capacity(dwInstanceListSize as usize); - unsafe { - pwsCounterListBuffer.set_len(dwCounterListSize as usize); - pwsInstanceListBuffer.set_len(dwInstanceListSize as usize); - } - let status = unsafe { - PdhEnumObjectItemsW(::std::ptr::null(), - ::std::ptr::null(), - network_trans_utf16.as_ptr(), - pwsCounterListBuffer.as_mut_ptr(), - &mut dwCounterListSize, - pwsInstanceListBuffer.as_mut_ptr(), - &mut dwInstanceListSize, - PERF_DETAIL_WIZARD, - 0) - }; - if status != ERROR_SUCCESS as i32 { - panic!("got invalid status: {:x}", status); + 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"), + ); } - - for (pos, x) in pwsInstanceListBuffer.split(|x| *x == 0) - .filter(|x| x.len() > 0) - .enumerate() { - let net_interface = String::from_utf16(x).expect("invalid utf16"); - if let Some(ref network_in_trans) = network_in_trans { - let mut key_in = None; - add_counter(format!("\\{}({})\\{}", - network_trans, net_interface, network_in_trans), - query, - &mut key_in, - format!("net{}_in", pos), - CounterValue::Integer(0)); - if key_in.is_some() { - network::get_keys_in(&mut s.network).push(key_in.unwrap()); - } - } - if let Some(ref network_out_trans) = network_out_trans { - let mut key_out = None; - add_counter(format!("\\{}({})\\{}", - network_trans, net_interface, network_out_trans), - query, - &mut key_out, - format!("net{}_out", pos), - CounterValue::Integer(0)); - if key_out.is_some() { - network::get_keys_out(&mut s.network).push(key_out.unwrap()); - } - } + if let Some(used_time) = used_time { + p.set_cpu_usage(used_time); } } - query.start(); } - s.refresh_specifics(refreshes); - s } - fn refresh_system(&mut self) { + fn refresh_memory(&mut self) { unsafe { let mut mem_info: MEMORYSTATUSEX = zeroed(); mem_info.dwLength = size_of::() as u32; @@ -235,84 +164,144 @@ //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64); //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64); } - self.uptime = get_uptime(); - if let Some(ref mut query) = self.query { - for p in self.processors.iter_mut() { - let mut idle_time = None; - if let &mut Some(ref key_idle) = get_key_idle(p) { - idle_time = Some(query.get(&key_idle.unique_id).expect("key disappeared")); - } - if let Some(idle_time) = idle_time { - set_cpu_usage(p, 1. - idle_time); - } - } - } } - fn refresh_network(&mut self) { - network::refresh(&mut self.network, &self.query); + fn refresh_components_list(&mut self) { + self.components = component::get_components(); } fn refresh_process(&mut self, pid: Pid) -> bool { - if refresh_existing_process(self, pid, true) == false { - self.process_list.remove(&pid); - false - } else { + 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) { - // I think that 10192 as length will be enough to get all processes at once... - let mut process_ids: Vec = Vec::with_capacity(PROCESS_LEN); - let mut cb_needed = 0; + // Windows 10 notebook requires at least 512KiB of memory to make it in one go + let mut buffer_size: usize = 512 * 1024; - unsafe { process_ids.set_len(PROCESS_LEN); } - let size = ::std::mem::size_of::() * process_ids.len(); - unsafe { - if K32EnumProcesses(process_ids.as_mut_ptr(), - size as DWORD, - &mut cb_needed) == 0 { - return - } - } - let nb_processes = cb_needed / ::std::mem::size_of::() as DWORD; - unsafe { process_ids.set_len(nb_processes as usize); } + loop { + let mut process_information: Vec = Vec::with_capacity(buffer_size); - { - let this = WrapSystem(UnsafeCell::new(self)); + 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)); - process_ids.par_iter() - .filter_map(|pid| { - let pid = *pid as usize; - if !refresh_existing_process(this.get(), pid, false) { - let ppid = unsafe { get_parent_process_id(pid) }; - let mut p = Process::new(pid, ppid, 0); - update_proc_info(&mut p); - Some(Wrap(p)) - } else { - None - } - }) - .collect::>() - }.into_iter() - .for_each(|p| { - self.process_list.insert(p.0.pid(), p.0); - }); - self.clear_procs(); - } - - fn refresh_disks(&mut self) { - for disk in &mut self.disks { - disk.update(); + 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_disk_list(&mut self) { + fn refresh_disks_list(&mut self) { self.disks = unsafe { get_disks() }; } - fn get_process_list(&self) -> &HashMap { + fn refresh_users_list(&mut self) { + self.users = unsafe { get_users() }; + } + + fn get_processes(&self) -> &HashMap { &self.process_list } @@ -320,8 +309,12 @@ self.process_list.get(&(pid as usize)) } - fn get_processor_list(&self) -> &[Processor] { - &self.processors[..] + fn get_global_processor_info(&self) -> &Processor { + &self.global_processor + } + + fn get_processors(&self) -> &[Processor] { + &self.processors } fn get_total_memory(&self) -> u64 { @@ -348,20 +341,50 @@ (self.swap_total - self.swap_free) >> 10 } - fn get_components_list(&self) -> &[Component] { - &self.temperatures[..] + 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[..] + &self.disks + } + + fn get_disks_mut(&mut self) -> &mut [Disk] { + &mut self.disks } - fn get_network(&self) -> &NetworkData { - &self.network + 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 { - self.uptime + 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() } } @@ -371,17 +394,41 @@ !(ret == FALSE || exit_code != STILL_ACTIVE) } -fn refresh_existing_process(s: &mut System, pid: Pid, compute_cpu: bool) -> bool { +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_proc_info(entry); - if compute_cpu { - compute_cpu_usage(entry, s.processors.len() as u64 - 1); - } + 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) + } +} diff -Nru rust-sysinfo-0.9.5/src/windows/tools.rs rust-sysinfo-0.13.2/src/windows/tools.rs --- rust-sysinfo-0.9.5/src/windows/tools.rs 2019-09-05 18:24:27.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/tools.rs 2020-03-06 10:31:55.000000000 +0000 @@ -1,17 +1,20 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // -use windows::processor::{create_processor, CounterValue, Processor, Query}; +use windows::processor::{self, Processor, Query}; -use sys::disk::{new_disk, Disk, DiskType}; +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}; @@ -19,18 +22,14 @@ CreateFileW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW, OPEN_EXISTING, }; use winapi::um::handleapi::CloseHandle; -use winapi::um::ioapiset::DeviceIoControl; use winapi::um::handleapi::INVALID_HANDLE_VALUE; -use winapi::um::sysinfoapi::{ - GetSystemInfo, GetTickCount64, SYSTEM_INFO, -}; +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_STORAGE_QUERY_PROPERTY, IOCTL_DISK_GET_DRIVE_GEOMETRY, -}; -use winapi::um::winnt::{ - BOOLEAN, FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, + DISK_GEOMETRY, IOCTL_DISK_GET_DRIVE_GEOMETRY, IOCTL_STORAGE_QUERY_PROPERTY, }; -use winapi::um::winbase::DRIVE_FIXED; +use winapi::um::winnt::{BOOLEAN, FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE}; pub struct KeyHandler { pub unique_id: String, @@ -46,231 +45,236 @@ } } -/*#[allow(non_snake_case)] -#[allow(unused)] -unsafe fn browser() { - use winapi::um::pdh::{PdhBrowseCountersA, PDH_BROWSE_DLG_CONFIG_A}; - use winapi::shared::winerror::ERROR_SUCCESS; - - let mut BrowseDlgData: PDH_BROWSE_DLG_CONFIG_A = ::std::mem::zeroed(); - let mut CounterPathBuffer: [i8; 255] = ::std::mem::zeroed(); - const PERF_DETAIL_WIZARD: u32 = 400; - let text = b"Select a counter to monitor.\0"; - - BrowseDlgData.set_IncludeInstanceIndex(FALSE as u32); - BrowseDlgData.set_SingleCounterPerAdd(TRUE as u32); - BrowseDlgData.set_SingleCounterPerDialog(TRUE as u32); - BrowseDlgData.set_LocalCountersOnly(FALSE as u32); - BrowseDlgData.set_WildCardInstances(TRUE as u32); - BrowseDlgData.set_HideDetailBox(TRUE as u32); - BrowseDlgData.set_InitializePath(FALSE as u32); - BrowseDlgData.set_DisableMachineSelection(FALSE as u32); - BrowseDlgData.set_IncludeCostlyObjects(FALSE as u32); - BrowseDlgData.set_ShowObjectBrowser(FALSE as u32); - BrowseDlgData.hWndOwner = ::std::ptr::null_mut(); - BrowseDlgData.szReturnPathBuffer = CounterPathBuffer.as_mut_ptr(); - BrowseDlgData.cchReturnPathLength = 255; - BrowseDlgData.pCallBack = None; - BrowseDlgData.dwCallBackArg = 0; - BrowseDlgData.CallBackStatus = ERROR_SUCCESS as i32; - BrowseDlgData.dwDefaultDetailLevel = PERF_DETAIL_WIZARD; - BrowseDlgData.szDialogBoxCaption = text as *const _ as usize as *mut i8; - let ret = PdhBrowseCountersA(&mut BrowseDlgData as *mut _); - println!("browser: {:?}", ret); - for x in CounterPathBuffer.iter() { - print!("{:?} ", *x); - } - println!(""); - for x in 0..256 { - print!("{:?} ", *BrowseDlgData.szReturnPathBuffer.offset(x)); - } - println!(""); -}*/ - -pub fn init_processors() -> Vec { +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(create_processor(&format!("CPU {}", nb + 1))); + ret.push(Processor::new_with_values( + &format!("CPU {}", nb + 1), + vendor_id.clone(), + brand.clone(), + frequencies[nb as usize], + )); } - ret.insert(0, create_processor("Total CPU")); - ret + (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()) + 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()); + 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 + *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 mut disks = Vec::new(); let drives = GetLogicalDrives(); if drives == 0 { - return disks; + return Vec::new(); } - for x in 0..size_of::() * 8 { - if (drives >> x) & 1 == 0 { - continue - } - 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 { - continue - } - 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 { - continue - } - let mut pos = 0; - for x in name.iter() { - if *x == 0 { - break + (0..size_of::() * 8) + .into_par_iter() + .filter_map(|x| { + if (drives >> x) & 1 == 0 { + return None; } - pos += 1; - } - let name = String::from_utf16_lossy(&name[..pos]); - let name = OsStr::new(&name); + 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 = 0; + for x in file_system.iter() { + if *x == 0 { + break; + } + pos += 1; } - pos += 1; - } - let file_system: Vec = file_system[..pos].iter().map(|x| *x as u8).collect(); + 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 { - disks.push(new_disk(name, &mount_point, &file_system, DiskType::Unknown(-1), 0)); - CloseHandle(handle); - continue - } - 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 { - disks.push(new_disk(name, &mount_point, &file_system, DiskType::Unknown(-1), - disk_size)); + 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); - continue - } - let is_ssd = dtd.TrimEnabled != 0; - CloseHandle(handle); - disks.push(new_disk(name, &mount_point, &file_system, - if is_ssd { DiskType::SSD } else { DiskType::HDD }, - disk_size)); - } - disks + 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::{HKEY_PERFORMANCE_DATA, RegQueryValueExA}; + 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 _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 + 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, - } - }) { + _ => None, + }, + ) + { ret.insert(s, pos as u32); } ret @@ -282,20 +286,23 @@ 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 _); + 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 { + } 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 _); + 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")); } } @@ -303,15 +310,15 @@ None } -pub fn add_counter(s: String, query: &mut Query, keys: &mut Option, counter_name: String, - value: CounterValue) { +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(), value) { + if query.add_counter(&counter_name, full.clone()) { *keys = Some(KeyHandler::new(counter_name, full)); } } - -pub fn get_uptime() -> u64 { - unsafe { GetTickCount64() / 1000 } -} diff -Nru rust-sysinfo-0.9.5/src/windows/users.rs rust-sysinfo-0.13.2/src/windows/users.rs --- rust-sysinfo-0.9.5/src/windows/users.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/src/windows/users.rs 2020-04-13 13:30:04.000000000 +0000 @@ -0,0 +1,116 @@ +// +// 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 +} diff -Nru rust-sysinfo-0.9.5/tests/disk_list.rs rust-sysinfo-0.13.2/tests/disk_list.rs --- rust-sysinfo-0.9.5/tests/disk_list.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/disk_list.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2017 Guillaume Gomez // @@ -10,7 +10,6 @@ fn test_disks() { use sysinfo::SystemExt; - let s = sysinfo::System::new(); - println!("total memory: {}", s.get_total_memory()); - println!("total cpu cores: {}", s.get_processor_list().len()); + let s = sysinfo::System::new_all(); + assert!(s.get_disks().len() > 0); } diff -Nru rust-sysinfo-0.9.5/tests/network.rs rust-sysinfo-0.13.2/tests/network.rs --- rust-sysinfo-0.9.5/tests/network.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/network.rs 2020-04-08 16:30:55.000000000 +0000 @@ -0,0 +1,20 @@ +// +// 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); +} diff -Nru rust-sysinfo-0.9.5/tests/processor.rs rust-sysinfo-0.13.2/tests/processor.rs --- rust-sysinfo-0.9.5/tests/processor.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/processor.rs 2020-02-07 16:36:36.000000000 +0000 @@ -0,0 +1,20 @@ +// +// 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); +} diff -Nru rust-sysinfo-0.9.5/tests/process.rs rust-sysinfo-0.13.2/tests/process.rs --- rust-sysinfo-0.9.5/tests/process.rs 2018-09-22 23:52:58.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/process.rs 2020-04-13 13:30:04.000000000 +0000 @@ -1,20 +1,106 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // extern crate sysinfo; +use sysinfo::ProcessExt; +use sysinfo::SystemExt; + #[test] fn test_process() { - use sysinfo::{ProcessExt, SystemExt}; - let mut s = sysinfo::System::new(); + assert_eq!(s.get_processes().len(), 0); s.refresh_processes(); - assert!(s.get_process_list().len() != 0); + assert!(s.get_processes().len() != 0); #[cfg(not(windows))] - assert!(s.get_process_list() - .values() - .any(|p| p.exe().to_str().unwrap_or_else(|| "").len() != 0)); + 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 + ); } diff -Nru rust-sysinfo-0.9.5/tests/send_sync.rs rust-sysinfo-0.13.2/tests/send_sync.rs --- rust-sysinfo-0.9.5/tests/send_sync.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/send_sync.rs 2020-03-20 16:16:00.000000000 +0000 @@ -0,0 +1,16 @@ +// +// Sysinfo +// +// Copyright (c) 2020 Guillaume Gomez +// + +extern crate sysinfo; + +#[test] +fn test_send_sync() { + fn is_send() {} + fn is_sync() {} + + is_send::(); + is_sync::(); +} diff -Nru rust-sysinfo-0.9.5/tests/uptime.rs rust-sysinfo-0.13.2/tests/uptime.rs --- rust-sysinfo-0.9.5/tests/uptime.rs 2018-08-06 18:43:09.000000000 +0000 +++ rust-sysinfo-0.13.2/tests/uptime.rs 2020-04-08 22:13:12.000000000 +0000 @@ -1,6 +1,6 @@ -// +// // Sysinfo -// +// // Copyright (c) 2018 Guillaume Gomez // diff -Nru rust-sysinfo-0.9.5/.travis.yml rust-sysinfo-0.13.2/.travis.yml --- rust-sysinfo-0.9.5/.travis.yml 2019-06-22 11:17:46.000000000 +0000 +++ rust-sysinfo-0.13.2/.travis.yml 2020-03-11 16:52:16.000000000 +0000 @@ -25,6 +25,10 @@ rust: stable dist: trusty - os: linux + env: TARGET=wasm32-unknown-unknown + rust: stable + dist: trusty + - os: linux rust: nightly dist: trusty - os: linux @@ -55,7 +59,7 @@ - rustc --version - sysctl -a | grep mem - if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then - (rustup component add clippy-preview && cargo clippy) || touch clippy_install_failed; + (rustup component add clippy && cargo clippy) || touch clippy_install_failed; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cargo build --features debug; @@ -74,6 +78,7 @@ - if [[ -z "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo test; fi + - cargo doc - cd examples - if [[ -z "$EXTRA" ]]; then RUST_BACKTRACE=1 cargo build; @@ -87,4 +92,4 @@ fi - if [[ -z "$EXTRA" ]]; then LD_LIBRARY_PATH=./target/debug ./simple; - fi + fi \ No newline at end of file