forked from extern/nushell
Add nu-system and rewrite ps command (#734)
* Add nu-system and rewrite ps command * Add more deps * Add more deps * clippy * clippy * clippy * clippy * clippy * clippy
This commit is contained in:
parent
2b6ce4dfe5
commit
ca215c1152
79
Cargo.lock
generated
79
Cargo.lock
generated
@ -870,6 +870,7 @@ dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-system",
|
||||
"nu-table",
|
||||
"nu-term-grid",
|
||||
"nu_plugin_example",
|
||||
@ -903,6 +904,27 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
@ -1225,6 +1247,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "htmlescape"
|
||||
version = "0.3.1"
|
||||
@ -1547,6 +1575,16 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||
|
||||
[[package]]
|
||||
name = "libproc"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6466fc1f834276563fbbd4be1c24236ef92bb9efdbd4691e07f1cf85a0b407f0"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.2.23"
|
||||
@ -1912,6 +1950,7 @@ dependencies = [
|
||||
"nu-path",
|
||||
"nu-pretty-hex",
|
||||
"nu-protocol",
|
||||
"nu-system",
|
||||
"nu-table",
|
||||
"nu-term-grid",
|
||||
"num 0.4.0",
|
||||
@ -2027,6 +2066,20 @@ dependencies = [
|
||||
"typetag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-system"
|
||||
version = "0.60.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"errno",
|
||||
"libc",
|
||||
"libproc",
|
||||
"procfs",
|
||||
"users",
|
||||
"which",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-table"
|
||||
version = "0.36.0"
|
||||
@ -2614,6 +2667,21 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0941606b9934e2d98a3677759a971756eb821f75764d0e0d26946d08e74d9104"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@ -3848,6 +3916,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
@ -11,6 +11,7 @@ members = [
|
||||
"crates/nu-cli",
|
||||
"crates/nu-engine",
|
||||
"crates/nu-parser",
|
||||
"crates/nu-system",
|
||||
"crates/nu-command",
|
||||
"crates/nu-protocol",
|
||||
"crates/nu-plugin",
|
||||
@ -32,6 +33,7 @@ nu-path = { path="./crates/nu-path" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex" }
|
||||
nu-protocol = { path = "./crates/nu-protocol" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true }
|
||||
nu-system = { path = "./crates/nu-system"}
|
||||
nu-table = { path = "./crates/nu-table" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid" }
|
||||
# nu-ansi-term = { path = "./crates/nu-ansi-term" }
|
||||
|
@ -16,6 +16,7 @@ nu-protocol = { path = "../nu-protocol" }
|
||||
nu-table = { path = "../nu-table" }
|
||||
nu-term-grid = { path = "../nu-term-grid" }
|
||||
nu-parser = { path = "../nu-parser" }
|
||||
nu-system = { path = "../nu-system" }
|
||||
# nu-ansi-term = { path = "../nu-ansi-term" }
|
||||
nu-ansi-term = "0.42.0"
|
||||
nu-color-config = { path = "../nu-color-config" }
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
|
||||
};
|
||||
use sysinfo::{ProcessExt, System, SystemExt};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Ps;
|
||||
@ -49,86 +50,59 @@ impl Command for Ps {
|
||||
}
|
||||
|
||||
fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, ShellError> {
|
||||
let mut output = vec![];
|
||||
let span = call.head;
|
||||
let long = call.has_flag("long");
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
|
||||
let duration = std::time::Duration::from_millis(500);
|
||||
std::thread::sleep(duration);
|
||||
for proc in nu_system::collect_proc(Duration::from_millis(100), false) {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let mut output = vec![];
|
||||
cols.push("pid".to_string());
|
||||
vals.push(Value::Int {
|
||||
val: proc.pid() as i64,
|
||||
span,
|
||||
});
|
||||
|
||||
let result: Vec<_> = sys.processes().iter().map(|x| *x.0).collect();
|
||||
cols.push("name".to_string());
|
||||
vals.push(Value::String {
|
||||
val: proc.name(),
|
||||
span,
|
||||
});
|
||||
|
||||
for pid in result {
|
||||
sys.refresh_process(pid);
|
||||
if let Some(result) = sys.process(pid) {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
cols.push("status".to_string());
|
||||
vals.push(Value::String {
|
||||
val: proc.status(),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("pid".into());
|
||||
vals.push(Value::Int {
|
||||
val: pid as i64,
|
||||
span,
|
||||
});
|
||||
cols.push("cpu".to_string());
|
||||
vals.push(Value::Float {
|
||||
val: proc.cpu_usage(),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("name".into());
|
||||
cols.push("mem".to_string());
|
||||
vals.push(Value::Filesize {
|
||||
val: proc.mem_size() as i64,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("virtual".to_string());
|
||||
vals.push(Value::Filesize {
|
||||
val: proc.virtual_size() as i64,
|
||||
span,
|
||||
});
|
||||
|
||||
if long {
|
||||
cols.push("command".to_string());
|
||||
vals.push(Value::String {
|
||||
val: result.name().into(),
|
||||
val: proc.command(),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("status".into());
|
||||
vals.push(Value::String {
|
||||
val: format!("{:?}", result.status()),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("cpu".into());
|
||||
vals.push(Value::Float {
|
||||
val: result.cpu_usage() as f64,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("mem".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: result.memory() as i64 * 1000,
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("virtual".into());
|
||||
vals.push(Value::Filesize {
|
||||
val: result.virtual_memory() as i64 * 1000,
|
||||
span,
|
||||
});
|
||||
|
||||
if long {
|
||||
cols.push("parent".into());
|
||||
if let Some(parent) = result.parent() {
|
||||
vals.push(Value::Int {
|
||||
val: parent as i64,
|
||||
span,
|
||||
});
|
||||
} else {
|
||||
vals.push(Value::Nothing { span });
|
||||
}
|
||||
|
||||
cols.push("exe".into());
|
||||
vals.push(Value::String {
|
||||
val: result.exe().to_string_lossy().to_string(),
|
||||
span,
|
||||
});
|
||||
|
||||
cols.push("command".into());
|
||||
vals.push(Value::String {
|
||||
val: result.cmd().join(" "),
|
||||
span,
|
||||
});
|
||||
}
|
||||
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
|
||||
output.push(Value::Record { cols, vals, span });
|
||||
}
|
||||
|
||||
Ok(output
|
||||
|
1
crates/nu-system/.gitignore
vendored
Normal file
1
crates/nu-system/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
253
crates/nu-system/Cargo.lock
generated
Normal file
253
crates/nu-system/Cargo.lock
generated
Normal file
@ -0,0 +1,253 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libproc"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6466fc1f834276563fbbd4be1c24236ef92bb9efdbd4691e07f1cf85a0b407f0"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-system"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"errno",
|
||||
"libproc",
|
||||
"procfs",
|
||||
"users",
|
||||
"which",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0941606b9934e2d98a3677759a971756eb821f75764d0e0d26946d08e74d9104"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
32
crates/nu-system/Cargo.toml
Normal file
32
crates/nu-system/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
authors = ["The Nu Project Contributors", "procs creators"]
|
||||
description = "Nushell system querying"
|
||||
name = "nu-system"
|
||||
version = "0.60.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "ps"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
procfs = "0.12.0"
|
||||
users = "0.11"
|
||||
which = "4"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
libproc = "0.10"
|
||||
errno = "0.2"
|
||||
users = "0.11"
|
||||
which = "4"
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3", features = ["handleapi", "minwindef", "psapi", "securitybaseapi", "tlhelp32", "winbase", "winnt"] }
|
||||
chrono = "0.4"
|
||||
libc = "0.2"
|
21
crates/nu-system/LICENSE
Normal file
21
crates/nu-system/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 procs developers and Nushell developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
13
crates/nu-system/src/lib.rs
Normal file
13
crates/nu-system/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use self::linux::*;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::macos::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use self::windows::*;
|
253
crates/nu-system/src/linux.rs
Normal file
253
crates/nu-system/src/linux.rs
Normal file
@ -0,0 +1,253 @@
|
||||
use procfs::process::{FDInfo, Io, Process, Stat, Status, TasksIter};
|
||||
use procfs::{ProcError, ProcessCgroup};
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub enum ProcessTask {
|
||||
Process(Process),
|
||||
Task { stat: Stat, owner: u32 },
|
||||
}
|
||||
|
||||
impl ProcessTask {
|
||||
pub fn stat(&self) -> &Stat {
|
||||
match self {
|
||||
ProcessTask::Process(x) => &x.stat,
|
||||
ProcessTask::Task { stat: x, owner: _ } => x,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cmdline(&self) -> Result<Vec<String>, ProcError> {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.cmdline(),
|
||||
_ => Err(ProcError::Other("not supported".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cgroups(&self) -> Result<Vec<ProcessCgroup>, ProcError> {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.cgroups(),
|
||||
_ => Err(ProcError::Other("not supported".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd(&self) -> Result<Vec<FDInfo>, ProcError> {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.fd(),
|
||||
_ => Err(ProcError::Other("not supported".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loginuid(&self) -> Result<u32, ProcError> {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.loginuid(),
|
||||
_ => Err(ProcError::Other("not supported".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> u32 {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.owner,
|
||||
ProcessTask::Task { stat: _, owner: x } => *x,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wchan(&self) -> Result<String, ProcError> {
|
||||
match self {
|
||||
ProcessTask::Process(x) => x.wchan(),
|
||||
_ => Err(ProcError::Other("not supported".to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcessInfo {
|
||||
pub pid: i32,
|
||||
pub ppid: i32,
|
||||
pub curr_proc: ProcessTask,
|
||||
pub prev_proc: ProcessTask,
|
||||
pub curr_io: Option<Io>,
|
||||
pub prev_io: Option<Io>,
|
||||
pub curr_status: Option<Status>,
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
pub fn collect_proc(interval: Duration, with_thread: bool) -> Vec<ProcessInfo> {
|
||||
let mut base_procs = Vec::new();
|
||||
let mut base_tasks = HashMap::new();
|
||||
let mut ret = Vec::new();
|
||||
|
||||
if let Ok(all_proc) = procfs::process::all_processes() {
|
||||
for proc in all_proc {
|
||||
let io = proc.io().ok();
|
||||
let time = Instant::now();
|
||||
if with_thread {
|
||||
if let Ok(iter) = proc.tasks() {
|
||||
collect_task(iter, &mut base_tasks);
|
||||
}
|
||||
}
|
||||
base_procs.push((proc.pid(), proc, io, time));
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(interval);
|
||||
|
||||
for (pid, prev_proc, prev_io, prev_time) in base_procs {
|
||||
let curr_proc = if let Ok(proc) = Process::new(pid) {
|
||||
proc
|
||||
} else {
|
||||
prev_proc.clone()
|
||||
};
|
||||
let curr_io = curr_proc.io().ok();
|
||||
let curr_status = curr_proc.status().ok();
|
||||
let curr_time = Instant::now();
|
||||
let interval = curr_time - prev_time;
|
||||
let ppid = curr_proc.stat.ppid;
|
||||
let owner = curr_proc.owner;
|
||||
|
||||
let mut curr_tasks = HashMap::new();
|
||||
if with_thread {
|
||||
if let Ok(iter) = curr_proc.tasks() {
|
||||
collect_task(iter, &mut curr_tasks);
|
||||
}
|
||||
}
|
||||
|
||||
let curr_proc = ProcessTask::Process(curr_proc);
|
||||
let prev_proc = ProcessTask::Process(prev_proc);
|
||||
|
||||
let proc = ProcessInfo {
|
||||
pid,
|
||||
ppid,
|
||||
curr_proc,
|
||||
prev_proc,
|
||||
curr_io,
|
||||
prev_io,
|
||||
curr_status,
|
||||
interval,
|
||||
};
|
||||
|
||||
ret.push(proc);
|
||||
|
||||
for (tid, (pid, curr_stat, curr_status, curr_io)) in curr_tasks {
|
||||
if let Some((_, prev_stat, _, prev_io)) = base_tasks.remove(&tid) {
|
||||
let proc = ProcessInfo {
|
||||
pid: tid,
|
||||
ppid: pid,
|
||||
curr_proc: ProcessTask::Task {
|
||||
stat: curr_stat,
|
||||
owner,
|
||||
},
|
||||
prev_proc: ProcessTask::Task {
|
||||
stat: prev_stat,
|
||||
owner,
|
||||
},
|
||||
curr_io,
|
||||
prev_io,
|
||||
curr_status,
|
||||
interval,
|
||||
};
|
||||
ret.push(proc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn collect_task(iter: TasksIter, map: &mut HashMap<i32, (i32, Stat, Option<Status>, Option<Io>)>) {
|
||||
for task in iter {
|
||||
let task = if let Ok(x) = task {
|
||||
x
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if task.tid != task.pid {
|
||||
let stat = if let Ok(x) = task.stat() {
|
||||
x
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let status = task.status().ok();
|
||||
let io = task.io().ok();
|
||||
map.insert(task.tid, (task.pid, stat, status, io));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessInfo {
|
||||
/// PID of process
|
||||
pub fn pid(&self) -> i32 {
|
||||
self.pid
|
||||
}
|
||||
|
||||
/// Name of command
|
||||
pub fn name(&self) -> String {
|
||||
self.command()
|
||||
.split(' ')
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Full name of command, with arguments
|
||||
pub fn command(&self) -> String {
|
||||
if let Ok(cmd) = &self.curr_proc.cmdline() {
|
||||
if !cmd.is_empty() {
|
||||
let mut cmd = cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut x| {
|
||||
x.push(' ');
|
||||
x
|
||||
})
|
||||
.collect::<String>();
|
||||
cmd.pop();
|
||||
cmd = cmd.replace("\n", " ").replace("\t", " ");
|
||||
cmd
|
||||
} else {
|
||||
self.curr_proc.stat().comm.clone()
|
||||
}
|
||||
} else {
|
||||
self.curr_proc.stat().comm.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the status of the process
|
||||
pub fn status(&self) -> String {
|
||||
match self.curr_proc.stat().state {
|
||||
'S' => "Sleeping".into(),
|
||||
'R' => "Running".into(),
|
||||
'D' => "Disk sleep".into(),
|
||||
'Z' => "Zombie".into(),
|
||||
'T' => "Stopped".into(),
|
||||
't' => "Tracing".into(),
|
||||
'X' => "Dead".into(),
|
||||
'x' => "Dead".into(),
|
||||
'K' => "Wakekill".into(),
|
||||
'W' => "Waking".into(),
|
||||
'P' => "Parked".into(),
|
||||
_ => "Unknown".into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU usage as a percent of total
|
||||
pub fn cpu_usage(&self) -> f64 {
|
||||
let curr_time = self.curr_proc.stat().utime + self.curr_proc.stat().stime;
|
||||
let prev_time = self.prev_proc.stat().utime + self.prev_proc.stat().stime;
|
||||
let usage_ms =
|
||||
(curr_time - prev_time) * 1000 / procfs::ticks_per_second().unwrap_or(100) as u64;
|
||||
let interval_ms = self.interval.as_secs() * 1000 + u64::from(self.interval.subsec_millis());
|
||||
usage_ms as f64 * 100.0 / interval_ms as f64
|
||||
}
|
||||
|
||||
/// Memory size in number of bytes
|
||||
pub fn mem_size(&self) -> u64 {
|
||||
self.curr_proc.stat().rss_bytes().unwrap_or(0) as u64
|
||||
}
|
||||
|
||||
/// Virtual memory size in bytes
|
||||
pub fn virtual_size(&self) -> u64 {
|
||||
self.curr_proc.stat().vsize
|
||||
}
|
||||
}
|
391
crates/nu-system/src/macos.rs
Normal file
391
crates/nu-system/src/macos.rs
Normal file
@ -0,0 +1,391 @@
|
||||
use libc::{c_int, c_void, size_t};
|
||||
use libproc::libproc::bsd_info::BSDInfo;
|
||||
use libproc::libproc::file_info::{pidfdinfo, ListFDs, ProcFDType};
|
||||
use libproc::libproc::net_info::{InSockInfo, SocketFDInfo, SocketInfoKind, TcpSockInfo};
|
||||
use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
|
||||
use libproc::libproc::proc_pid::{listpidinfo, listpids, pidinfo, ListThreads, ProcType};
|
||||
use libproc::libproc::task_info::{TaskAllInfo, TaskInfo};
|
||||
use libproc::libproc::thread_info::ThreadInfo;
|
||||
use std::cmp;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
pub struct ProcessInfo {
|
||||
pub pid: i32,
|
||||
pub ppid: i32,
|
||||
pub curr_task: TaskAllInfo,
|
||||
pub prev_task: TaskAllInfo,
|
||||
pub curr_path: Option<PathInfo>,
|
||||
pub curr_threads: Vec<ThreadInfo>,
|
||||
pub curr_udps: Vec<InSockInfo>,
|
||||
pub curr_tcps: Vec<TcpSockInfo>,
|
||||
pub curr_res: Option<RUsageInfoV2>,
|
||||
pub prev_res: Option<RUsageInfoV2>,
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
pub fn collect_proc(interval: Duration, _with_thread: bool) -> Vec<ProcessInfo> {
|
||||
let mut base_procs = Vec::new();
|
||||
let mut ret = Vec::new();
|
||||
let arg_max = get_arg_max();
|
||||
|
||||
if let Ok(procs) = listpids(ProcType::ProcAllPIDS) {
|
||||
for p in procs {
|
||||
if let Ok(task) = pidinfo::<TaskAllInfo>(p as i32, 0) {
|
||||
let res = pidrusage::<RUsageInfoV2>(p as i32).ok();
|
||||
let time = Instant::now();
|
||||
base_procs.push((p as i32, task, res, time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(interval);
|
||||
|
||||
for (pid, prev_task, prev_res, prev_time) in base_procs {
|
||||
let curr_task = if let Ok(task) = pidinfo::<TaskAllInfo>(pid, 0) {
|
||||
task
|
||||
} else {
|
||||
clone_task_all_info(&prev_task)
|
||||
};
|
||||
|
||||
let curr_path = get_path_info(pid, arg_max);
|
||||
|
||||
let threadids = listpidinfo::<ListThreads>(pid, curr_task.ptinfo.pti_threadnum as usize);
|
||||
let mut curr_threads = Vec::new();
|
||||
if let Ok(threadids) = threadids {
|
||||
for t in threadids {
|
||||
if let Ok(thread) = pidinfo::<ThreadInfo>(pid, t) {
|
||||
curr_threads.push(thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut curr_tcps = Vec::new();
|
||||
let mut curr_udps = Vec::new();
|
||||
|
||||
let fds = listpidinfo::<ListFDs>(pid, curr_task.pbsd.pbi_nfiles as usize);
|
||||
if let Ok(fds) = fds {
|
||||
for fd in fds {
|
||||
if let ProcFDType::Socket = fd.proc_fdtype.into() {
|
||||
if let Ok(socket) = pidfdinfo::<SocketFDInfo>(pid, fd.proc_fd) {
|
||||
match socket.psi.soi_kind.into() {
|
||||
SocketInfoKind::In => {
|
||||
if socket.psi.soi_protocol == libc::IPPROTO_UDP {
|
||||
let info = unsafe { socket.psi.soi_proto.pri_in };
|
||||
curr_udps.push(info);
|
||||
}
|
||||
}
|
||||
SocketInfoKind::Tcp => {
|
||||
let info = unsafe { socket.psi.soi_proto.pri_tcp };
|
||||
curr_tcps.push(info);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let curr_res = pidrusage::<RUsageInfoV2>(pid).ok();
|
||||
|
||||
let curr_time = Instant::now();
|
||||
let interval = curr_time - prev_time;
|
||||
let ppid = curr_task.pbsd.pbi_ppid as i32;
|
||||
|
||||
let proc = ProcessInfo {
|
||||
pid,
|
||||
ppid,
|
||||
curr_task,
|
||||
prev_task,
|
||||
curr_path,
|
||||
curr_threads,
|
||||
curr_udps,
|
||||
curr_tcps,
|
||||
curr_res,
|
||||
prev_res,
|
||||
interval,
|
||||
};
|
||||
|
||||
ret.push(proc);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_arg_max() -> size_t {
|
||||
let mut mib: [c_int; 2] = [libc::CTL_KERN, libc::KERN_ARGMAX];
|
||||
let mut arg_max = 0i32;
|
||||
let mut size = ::std::mem::size_of::<c_int>();
|
||||
unsafe {
|
||||
while 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
|
||||
{}
|
||||
}
|
||||
arg_max as size_t
|
||||
}
|
||||
|
||||
pub struct PathInfo {
|
||||
pub name: String,
|
||||
pub exe: PathBuf,
|
||||
pub root: PathBuf,
|
||||
pub cmd: Vec<String>,
|
||||
pub env: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
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());
|
||||
::std::mem::forget(part);
|
||||
tmp
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_path_info(pid: i32, mut size: size_t) -> Option<PathInfo> {
|
||||
let mut proc_args = Vec::with_capacity(size as usize);
|
||||
let ptr: *mut u8 = proc_args.as_mut_slice().as_mut_ptr();
|
||||
|
||||
let mut mib: [c_int; 3] = [libc::CTL_KERN, libc::KERN_PROCARGS2, pid as c_int];
|
||||
|
||||
unsafe {
|
||||
let ret = libc::sysctl(
|
||||
mib.as_mut_ptr(),
|
||||
3,
|
||||
ptr as *mut c_void,
|
||||
&mut size,
|
||||
::std::ptr::null_mut(),
|
||||
0,
|
||||
);
|
||||
if ret != -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,
|
||||
::std::mem::size_of::<c_int>(),
|
||||
);
|
||||
let mut cp = ptr.add(::std::mem::size_of::<c_int>());
|
||||
let mut start = cp;
|
||||
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("")
|
||||
.to_owned();
|
||||
let mut need_root = true;
|
||||
let mut root = Default::default();
|
||||
if exe.is_absolute() {
|
||||
if let Some(parent) = exe.parent() {
|
||||
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);
|
||||
}
|
||||
start = cp;
|
||||
let mut env = Vec::new();
|
||||
while cp < ptr.add(size) {
|
||||
if *cp == 0 {
|
||||
if cp == start {
|
||||
break;
|
||||
}
|
||||
env.push(get_unchecked_str(cp, start));
|
||||
start = cp.offset(1);
|
||||
}
|
||||
cp = cp.offset(1);
|
||||
}
|
||||
if need_root {
|
||||
for env in env.iter() {
|
||||
if env.starts_with("PATH=") {
|
||||
root = Path::new(&env[6..]).to_path_buf();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(PathInfo {
|
||||
exe,
|
||||
name,
|
||||
root,
|
||||
cmd,
|
||||
env,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn clone_task_all_info(src: &TaskAllInfo) -> TaskAllInfo {
|
||||
let pbsd = BSDInfo {
|
||||
pbi_flags: src.pbsd.pbi_flags,
|
||||
pbi_status: src.pbsd.pbi_status,
|
||||
pbi_xstatus: src.pbsd.pbi_xstatus,
|
||||
pbi_pid: src.pbsd.pbi_pid,
|
||||
pbi_ppid: src.pbsd.pbi_ppid,
|
||||
pbi_uid: src.pbsd.pbi_uid,
|
||||
pbi_gid: src.pbsd.pbi_gid,
|
||||
pbi_ruid: src.pbsd.pbi_ruid,
|
||||
pbi_rgid: src.pbsd.pbi_rgid,
|
||||
pbi_svuid: src.pbsd.pbi_svuid,
|
||||
pbi_svgid: src.pbsd.pbi_svgid,
|
||||
rfu_1: src.pbsd.rfu_1,
|
||||
pbi_comm: src.pbsd.pbi_comm,
|
||||
pbi_name: src.pbsd.pbi_name,
|
||||
pbi_nfiles: src.pbsd.pbi_nfiles,
|
||||
pbi_pgid: src.pbsd.pbi_pgid,
|
||||
pbi_pjobc: src.pbsd.pbi_pjobc,
|
||||
e_tdev: src.pbsd.e_tdev,
|
||||
e_tpgid: src.pbsd.e_tpgid,
|
||||
pbi_nice: src.pbsd.pbi_nice,
|
||||
pbi_start_tvsec: src.pbsd.pbi_start_tvsec,
|
||||
pbi_start_tvusec: src.pbsd.pbi_start_tvusec,
|
||||
};
|
||||
let ptinfo = TaskInfo {
|
||||
pti_virtual_size: src.ptinfo.pti_virtual_size,
|
||||
pti_resident_size: src.ptinfo.pti_resident_size,
|
||||
pti_total_user: src.ptinfo.pti_total_user,
|
||||
pti_total_system: src.ptinfo.pti_total_system,
|
||||
pti_threads_user: src.ptinfo.pti_threads_user,
|
||||
pti_threads_system: src.ptinfo.pti_threads_system,
|
||||
pti_policy: src.ptinfo.pti_policy,
|
||||
pti_faults: src.ptinfo.pti_faults,
|
||||
pti_pageins: src.ptinfo.pti_pageins,
|
||||
pti_cow_faults: src.ptinfo.pti_cow_faults,
|
||||
pti_messages_sent: src.ptinfo.pti_messages_sent,
|
||||
pti_messages_received: src.ptinfo.pti_messages_received,
|
||||
pti_syscalls_mach: src.ptinfo.pti_syscalls_mach,
|
||||
pti_syscalls_unix: src.ptinfo.pti_syscalls_unix,
|
||||
pti_csw: src.ptinfo.pti_csw,
|
||||
pti_threadnum: src.ptinfo.pti_threadnum,
|
||||
pti_numrunning: src.ptinfo.pti_numrunning,
|
||||
pti_priority: src.ptinfo.pti_priority,
|
||||
};
|
||||
TaskAllInfo { pbsd, ptinfo }
|
||||
}
|
||||
|
||||
impl ProcessInfo {
|
||||
/// PID of process
|
||||
pub fn pid(&self) -> i32 {
|
||||
self.pid
|
||||
}
|
||||
|
||||
/// Name of command
|
||||
pub fn name(&self) -> String {
|
||||
self.command()
|
||||
.split(' ')
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Full name of command, with arguments
|
||||
pub fn command(&self) -> String {
|
||||
if let Some(path) = &self.curr_path {
|
||||
if !path.cmd.is_empty() {
|
||||
let mut cmd = path
|
||||
.cmd
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|mut x| {
|
||||
x.push(' ');
|
||||
x
|
||||
})
|
||||
.collect::<String>();
|
||||
cmd.pop();
|
||||
cmd = cmd.replace("\n", " ").replace("\t", " ");
|
||||
cmd
|
||||
} else {
|
||||
String::from("")
|
||||
}
|
||||
} else {
|
||||
String::from("")
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the status of the process
|
||||
pub fn status(&self) -> String {
|
||||
let mut state = 7;
|
||||
for t in &self.curr_threads {
|
||||
let s = match t.pth_run_state {
|
||||
1 => 1, // TH_STATE_RUNNING
|
||||
2 => 5, // TH_STATE_STOPPED
|
||||
3 => {
|
||||
if t.pth_sleep_time > 20 {
|
||||
4
|
||||
} else {
|
||||
3
|
||||
}
|
||||
} // TH_STATE_WAITING
|
||||
4 => 2, // TH_STATE_UNINTERRUPTIBLE
|
||||
5 => 6, // TH_STATE_HALTED
|
||||
_ => 7,
|
||||
};
|
||||
state = cmp::min(s, state);
|
||||
}
|
||||
let state = match state {
|
||||
0 => "",
|
||||
1 => "Running",
|
||||
2 => "Uninterruptible",
|
||||
3 => "Sleep",
|
||||
4 => "Waiting",
|
||||
5 => "Stopped",
|
||||
6 => "Halted",
|
||||
_ => "?",
|
||||
};
|
||||
state.to_string()
|
||||
}
|
||||
|
||||
/// CPU usage as a percent of total
|
||||
pub fn cpu_usage(&self) -> f64 {
|
||||
let curr_time =
|
||||
self.curr_task.ptinfo.pti_total_user + self.curr_task.ptinfo.pti_total_system;
|
||||
let prev_time =
|
||||
self.prev_task.ptinfo.pti_total_user + self.prev_task.ptinfo.pti_total_system;
|
||||
let usage_ms = (curr_time - prev_time) / 1000000u64;
|
||||
let interval_ms = self.interval.as_secs() * 1000 + u64::from(self.interval.subsec_millis());
|
||||
usage_ms as f64 * 100.0 / interval_ms as f64
|
||||
}
|
||||
|
||||
/// Memory size in number of bytes
|
||||
pub fn mem_size(&self) -> u64 {
|
||||
self.curr_task.ptinfo.pti_resident_size
|
||||
}
|
||||
|
||||
/// Virtual memory size in bytes
|
||||
pub fn virtual_size(&self) -> u64 {
|
||||
self.curr_task.ptinfo.pti_virtual_size
|
||||
}
|
||||
}
|
17
crates/nu-system/src/main.rs
Normal file
17
crates/nu-system/src/main.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
for proc in nu_system::collect_proc(Duration::from_millis(100), false) {
|
||||
// if proc.cpu_usage() > 0.1 {
|
||||
println!(
|
||||
"{} - {} - {} - {:.1} - {}M - {}M",
|
||||
proc.pid(),
|
||||
proc.name(),
|
||||
proc.status(),
|
||||
proc.cpu_usage(),
|
||||
proc.mem_size() / (1024 * 1024),
|
||||
proc.virtual_size() / (1024 * 1024),
|
||||
)
|
||||
// }
|
||||
}
|
||||
}
|
689
crates/nu-system/src/windows.rs
Normal file
689
crates/nu-system/src/windows.rs
Normal file
@ -0,0 +1,689 @@
|
||||
use chrono::offset::TimeZone;
|
||||
use chrono::{Local, NaiveDate};
|
||||
use libc::c_void;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::{size_of, zeroed};
|
||||
use std::ptr;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, MAX_PATH};
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::{
|
||||
GetCurrentProcess, GetPriorityClass, GetProcessTimes, OpenProcess, OpenProcessToken,
|
||||
};
|
||||
use winapi::um::psapi::{
|
||||
EnumProcessModulesEx, GetModuleBaseNameW, GetProcessMemoryInfo, K32EnumProcesses,
|
||||
LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX,
|
||||
};
|
||||
use winapi::um::securitybaseapi::{AdjustTokenPrivileges, GetTokenInformation};
|
||||
use winapi::um::tlhelp32::{
|
||||
CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS,
|
||||
};
|
||||
use winapi::um::winbase::{GetProcessIoCounters, LookupAccountSidW, LookupPrivilegeValueW};
|
||||
use winapi::um::winnt::{
|
||||
TokenGroups, TokenUser, HANDLE, IO_COUNTERS, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, PSID,
|
||||
SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED, SID, TOKEN_ADJUST_PRIVILEGES, TOKEN_GROUPS,
|
||||
TOKEN_PRIVILEGES, TOKEN_QUERY, TOKEN_USER,
|
||||
};
|
||||
|
||||
pub struct ProcessInfo {
|
||||
pub pid: i32,
|
||||
pub command: String,
|
||||
pub ppid: i32,
|
||||
pub start_time: chrono::DateTime<chrono::Local>,
|
||||
pub cpu_info: CpuInfo,
|
||||
pub memory_info: MemoryInfo,
|
||||
pub disk_info: DiskInfo,
|
||||
pub user: SidName,
|
||||
pub groups: Vec<SidName>,
|
||||
pub priority: u32,
|
||||
pub thread: i32,
|
||||
pub interval: Duration,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MemoryInfo {
|
||||
pub page_fault_count: u64,
|
||||
pub peak_working_set_size: u64,
|
||||
pub working_set_size: u64,
|
||||
pub quota_peak_paged_pool_usage: u64,
|
||||
pub quota_paged_pool_usage: u64,
|
||||
pub quota_peak_non_paged_pool_usage: u64,
|
||||
pub quota_non_paged_pool_usage: u64,
|
||||
pub page_file_usage: u64,
|
||||
pub peak_page_file_usage: u64,
|
||||
pub private_usage: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DiskInfo {
|
||||
pub prev_read: u64,
|
||||
pub prev_write: u64,
|
||||
pub curr_read: u64,
|
||||
pub curr_write: u64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CpuInfo {
|
||||
pub prev_sys: u64,
|
||||
pub prev_user: u64,
|
||||
pub curr_sys: u64,
|
||||
pub curr_user: u64,
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
pub fn collect_proc(interval: Duration, _with_thread: bool) -> Vec<ProcessInfo> {
|
||||
let mut base_procs = Vec::new();
|
||||
let mut ret = Vec::new();
|
||||
|
||||
let _ = set_privilege();
|
||||
|
||||
for pid in get_pids() {
|
||||
let handle = get_handle(pid);
|
||||
|
||||
if let Some(handle) = handle {
|
||||
let times = get_times(handle);
|
||||
let io = get_io(handle);
|
||||
|
||||
let time = Instant::now();
|
||||
|
||||
if let (Some((_, _, sys, user)), Some((read, write))) = (times, io) {
|
||||
base_procs.push((pid, sys, user, read, write, time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(interval);
|
||||
|
||||
let (mut ppids, mut threads) = get_ppid_threads();
|
||||
|
||||
for (pid, prev_sys, prev_user, prev_read, prev_write, prev_time) in base_procs {
|
||||
let ppid = ppids.remove(&pid);
|
||||
let thread = threads.remove(&pid);
|
||||
let handle = get_handle(pid);
|
||||
|
||||
if let Some(handle) = handle {
|
||||
let command = get_command(handle);
|
||||
let memory_info = get_memory_info(handle);
|
||||
let times = get_times(handle);
|
||||
let io = get_io(handle);
|
||||
|
||||
let start_time = if let Some((start, _, _, _)) = times {
|
||||
let time = chrono::Duration::seconds(start as i64 / 10_000_000);
|
||||
let base = NaiveDate::from_ymd(1600, 1, 1).and_hms(0, 0, 0);
|
||||
let time = base + time;
|
||||
Local.from_utc_datetime(&time)
|
||||
} else {
|
||||
Local.from_utc_datetime(&NaiveDate::from_ymd(1600, 1, 1).and_hms(0, 0, 0))
|
||||
};
|
||||
|
||||
let cpu_info = if let Some((_, _, curr_sys, curr_user)) = times {
|
||||
Some(CpuInfo {
|
||||
prev_sys,
|
||||
prev_user,
|
||||
curr_sys,
|
||||
curr_user,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let disk_info = if let Some((curr_read, curr_write)) = io {
|
||||
Some(DiskInfo {
|
||||
prev_read,
|
||||
prev_write,
|
||||
curr_read,
|
||||
curr_write,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let user = get_user(handle);
|
||||
let groups = get_groups(handle);
|
||||
|
||||
let priority = get_priority(handle);
|
||||
|
||||
let curr_time = Instant::now();
|
||||
let interval = curr_time - prev_time;
|
||||
|
||||
let mut all_ok = true;
|
||||
all_ok &= command.is_some();
|
||||
all_ok &= cpu_info.is_some();
|
||||
all_ok &= memory_info.is_some();
|
||||
all_ok &= disk_info.is_some();
|
||||
all_ok &= user.is_some();
|
||||
all_ok &= groups.is_some();
|
||||
all_ok &= thread.is_some();
|
||||
|
||||
if all_ok {
|
||||
let command = command.unwrap_or_default();
|
||||
let ppid = ppid.unwrap_or(0);
|
||||
let cpu_info = cpu_info.unwrap_or_default();
|
||||
let memory_info = memory_info.unwrap_or_default();
|
||||
let disk_info = disk_info.unwrap_or_default();
|
||||
let user = user.unwrap_or_else(|| SidName {
|
||||
sid: vec![],
|
||||
name: None,
|
||||
domainname: None,
|
||||
});
|
||||
let groups = groups.unwrap_or_else(Vec::new);
|
||||
let thread = thread.unwrap_or_default();
|
||||
|
||||
let proc = ProcessInfo {
|
||||
pid,
|
||||
command,
|
||||
ppid,
|
||||
start_time,
|
||||
cpu_info,
|
||||
memory_info,
|
||||
disk_info,
|
||||
user,
|
||||
groups,
|
||||
priority,
|
||||
thread,
|
||||
interval,
|
||||
};
|
||||
|
||||
ret.push(proc);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn set_privilege() -> bool {
|
||||
unsafe {
|
||||
let handle = GetCurrentProcess();
|
||||
let mut token: HANDLE = zeroed();
|
||||
let ret = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, &mut token);
|
||||
if ret == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut tps: TOKEN_PRIVILEGES = zeroed();
|
||||
let se_debug_name: Vec<u16> = format!("{}\0", SE_DEBUG_NAME).encode_utf16().collect();
|
||||
tps.PrivilegeCount = 1;
|
||||
let ret = LookupPrivilegeValueW(
|
||||
ptr::null(),
|
||||
se_debug_name.as_ptr(),
|
||||
&mut tps.Privileges[0].Luid,
|
||||
);
|
||||
if ret == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
tps.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
let ret = AdjustTokenPrivileges(
|
||||
token,
|
||||
FALSE,
|
||||
&mut tps,
|
||||
0,
|
||||
ptr::null::<TOKEN_PRIVILEGES>() as *mut TOKEN_PRIVILEGES,
|
||||
ptr::null::<u32>() as *mut u32,
|
||||
);
|
||||
if ret == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_pids() -> Vec<i32> {
|
||||
let dword_size = size_of::<DWORD>();
|
||||
let mut pids: Vec<DWORD> = Vec::with_capacity(10192);
|
||||
let mut cb_needed = 0;
|
||||
|
||||
unsafe {
|
||||
pids.set_len(10192);
|
||||
let result = K32EnumProcesses(
|
||||
pids.as_mut_ptr(),
|
||||
(dword_size * pids.len()) as DWORD,
|
||||
&mut cb_needed,
|
||||
);
|
||||
if result == 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
let pids_len = cb_needed / dword_size as DWORD;
|
||||
pids.set_len(pids_len as usize);
|
||||
}
|
||||
|
||||
pids.iter().map(|x| *x as i32).collect()
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_ppid_threads() -> (HashMap<i32, i32>, HashMap<i32, i32>) {
|
||||
let mut ppids = HashMap::new();
|
||||
let mut threads = HashMap::new();
|
||||
|
||||
unsafe {
|
||||
let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
let mut entry: PROCESSENTRY32 = zeroed();
|
||||
entry.dwSize = size_of::<PROCESSENTRY32>() as u32;
|
||||
let mut not_the_end = Process32First(snapshot, &mut entry);
|
||||
|
||||
while not_the_end != 0 {
|
||||
ppids.insert(entry.th32ProcessID as i32, entry.th32ParentProcessID as i32);
|
||||
threads.insert(entry.th32ProcessID as i32, entry.cntThreads as i32);
|
||||
not_the_end = Process32Next(snapshot, &mut entry);
|
||||
}
|
||||
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
|
||||
(ppids, threads)
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_handle(pid: i32) -> Option<HANDLE> {
|
||||
if pid == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let handle = unsafe {
|
||||
OpenProcess(
|
||||
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
|
||||
FALSE,
|
||||
pid as DWORD,
|
||||
)
|
||||
};
|
||||
|
||||
if handle.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_times(handle: HANDLE) -> Option<(u64, u64, u64, u64)> {
|
||||
unsafe {
|
||||
let mut start: FILETIME = zeroed();
|
||||
let mut exit: FILETIME = zeroed();
|
||||
let mut sys: FILETIME = zeroed();
|
||||
let mut user: FILETIME = zeroed();
|
||||
|
||||
let ret = GetProcessTimes(
|
||||
handle,
|
||||
&mut start as *mut FILETIME,
|
||||
&mut exit as *mut FILETIME,
|
||||
&mut sys as *mut FILETIME,
|
||||
&mut user as *mut FILETIME,
|
||||
);
|
||||
|
||||
let start = u64::from(start.dwHighDateTime) << 32 | u64::from(start.dwLowDateTime);
|
||||
let exit = u64::from(exit.dwHighDateTime) << 32 | u64::from(exit.dwLowDateTime);
|
||||
let sys = u64::from(sys.dwHighDateTime) << 32 | u64::from(sys.dwLowDateTime);
|
||||
let user = u64::from(user.dwHighDateTime) << 32 | u64::from(user.dwLowDateTime);
|
||||
|
||||
if ret != 0 {
|
||||
Some((start, exit, sys, user))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_memory_info(handle: HANDLE) -> Option<MemoryInfo> {
|
||||
unsafe {
|
||||
let mut pmc: PROCESS_MEMORY_COUNTERS_EX = zeroed();
|
||||
let ret = GetProcessMemoryInfo(
|
||||
handle,
|
||||
&mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void
|
||||
as *mut PROCESS_MEMORY_COUNTERS,
|
||||
size_of::<PROCESS_MEMORY_COUNTERS_EX>() as DWORD,
|
||||
);
|
||||
|
||||
if ret != 0 {
|
||||
let info = MemoryInfo {
|
||||
page_fault_count: u64::from(pmc.PageFaultCount),
|
||||
peak_working_set_size: pmc.PeakWorkingSetSize as u64,
|
||||
working_set_size: pmc.WorkingSetSize as u64,
|
||||
quota_peak_paged_pool_usage: pmc.QuotaPeakPagedPoolUsage as u64,
|
||||
quota_paged_pool_usage: pmc.QuotaPagedPoolUsage as u64,
|
||||
quota_peak_non_paged_pool_usage: pmc.QuotaPeakNonPagedPoolUsage as u64,
|
||||
quota_non_paged_pool_usage: pmc.QuotaNonPagedPoolUsage as u64,
|
||||
page_file_usage: pmc.PagefileUsage as u64,
|
||||
peak_page_file_usage: pmc.PeakPagefileUsage as u64,
|
||||
private_usage: pmc.PrivateUsage as u64,
|
||||
};
|
||||
Some(info)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_command(handle: HANDLE) -> Option<String> {
|
||||
unsafe {
|
||||
let mut exe_buf = [0u16; MAX_PATH + 1];
|
||||
let mut h_mod = std::ptr::null_mut();
|
||||
let mut cb_needed = 0;
|
||||
|
||||
let ret = EnumProcessModulesEx(
|
||||
handle,
|
||||
&mut h_mod,
|
||||
size_of::<DWORD>() as DWORD,
|
||||
&mut cb_needed,
|
||||
LIST_MODULES_ALL,
|
||||
);
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ret = GetModuleBaseNameW(handle, h_mod, 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;
|
||||
}
|
||||
|
||||
if ret != 0 {
|
||||
Some(String::from_utf16_lossy(&exe_buf[..pos]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_io(handle: HANDLE) -> Option<(u64, u64)> {
|
||||
unsafe {
|
||||
let mut io: IO_COUNTERS = zeroed();
|
||||
let ret = GetProcessIoCounters(handle, &mut io);
|
||||
|
||||
if ret != 0 {
|
||||
Some((io.ReadTransferCount, io.WriteTransferCount))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SidName {
|
||||
pub sid: Vec<u64>,
|
||||
pub name: Option<String>,
|
||||
pub domainname: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_user(handle: HANDLE) -> Option<SidName> {
|
||||
unsafe {
|
||||
let mut token: HANDLE = zeroed();
|
||||
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
||||
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut cb_needed = 0;
|
||||
let _ = GetTokenInformation(
|
||||
token,
|
||||
TokenUser,
|
||||
ptr::null::<c_void>() as *mut c_void,
|
||||
0,
|
||||
&mut cb_needed,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(cb_needed as usize);
|
||||
|
||||
let ret = GetTokenInformation(
|
||||
token,
|
||||
TokenUser,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
cb_needed,
|
||||
&mut cb_needed,
|
||||
);
|
||||
buf.set_len(cb_needed as usize);
|
||||
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let token_user = buf.as_ptr() as *const TOKEN_USER;
|
||||
let psid = (*token_user).User.Sid;
|
||||
|
||||
let sid = get_sid(psid);
|
||||
let (name, domainname) = if let Some((x, y)) = get_name_cached(psid) {
|
||||
(Some(x), Some(y))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
Some(SidName {
|
||||
sid,
|
||||
name,
|
||||
domainname,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_groups(handle: HANDLE) -> Option<Vec<SidName>> {
|
||||
unsafe {
|
||||
let mut token: HANDLE = zeroed();
|
||||
let ret = OpenProcessToken(handle, TOKEN_QUERY, &mut token);
|
||||
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut cb_needed = 0;
|
||||
let _ = GetTokenInformation(
|
||||
token,
|
||||
TokenGroups,
|
||||
ptr::null::<c_void>() as *mut c_void,
|
||||
0,
|
||||
&mut cb_needed,
|
||||
);
|
||||
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(cb_needed as usize);
|
||||
|
||||
let ret = GetTokenInformation(
|
||||
token,
|
||||
TokenGroups,
|
||||
buf.as_mut_ptr() as *mut c_void,
|
||||
cb_needed,
|
||||
&mut cb_needed,
|
||||
);
|
||||
buf.set_len(cb_needed as usize);
|
||||
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
let token_groups = buf.as_ptr() as *const TOKEN_GROUPS;
|
||||
|
||||
let mut ret = Vec::new();
|
||||
let sa = (*token_groups).Groups.as_ptr();
|
||||
for i in 0..(*token_groups).GroupCount {
|
||||
let psid = (*sa.offset(i as isize)).Sid;
|
||||
let sid = get_sid(psid);
|
||||
let (name, domainname) = if let Some((x, y)) = get_name_cached(psid) {
|
||||
(Some(x), Some(y))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let sid_name = SidName {
|
||||
sid,
|
||||
name,
|
||||
domainname,
|
||||
};
|
||||
ret.push(sid_name);
|
||||
}
|
||||
|
||||
Some(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_sid(psid: PSID) -> Vec<u64> {
|
||||
unsafe {
|
||||
let mut ret = Vec::new();
|
||||
let psid = psid as *const SID;
|
||||
|
||||
let mut ia = 0;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[0]) << 40;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[1]) << 32;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[2]) << 24;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[3]) << 16;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[4]) << 8;
|
||||
ia |= u64::from((*psid).IdentifierAuthority.Value[5]);
|
||||
|
||||
ret.push(u64::from((*psid).Revision));
|
||||
ret.push(ia);
|
||||
let cnt = (*psid).SubAuthorityCount;
|
||||
let sa = (*psid).SubAuthority.as_ptr();
|
||||
for i in 0..cnt {
|
||||
ret.push(u64::from(*sa.offset(i as isize)));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
pub static NAME_CACHE: RefCell<HashMap<PSID, Option<(String, String)>>> =
|
||||
RefCell::new(HashMap::new());
|
||||
);
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_name_cached(psid: PSID) -> Option<(String, String)> {
|
||||
NAME_CACHE.with(|c| {
|
||||
let mut c = c.borrow_mut();
|
||||
if let Some(x) = c.get(&psid) {
|
||||
x.clone()
|
||||
} else {
|
||||
let x = get_name(psid);
|
||||
c.insert(psid, x.clone());
|
||||
x
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_name(psid: PSID) -> Option<(String, String)> {
|
||||
unsafe {
|
||||
let mut cc_name = 0;
|
||||
let mut cc_domainname = 0;
|
||||
let mut pe_use = 0;
|
||||
let _ = LookupAccountSidW(
|
||||
ptr::null::<u16>() as *mut u16,
|
||||
psid,
|
||||
ptr::null::<u16>() as *mut u16,
|
||||
&mut cc_name,
|
||||
ptr::null::<u16>() as *mut u16,
|
||||
&mut cc_domainname,
|
||||
&mut pe_use,
|
||||
);
|
||||
|
||||
if cc_name == 0 || cc_domainname == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut name: Vec<u16> = Vec::with_capacity(cc_name as usize);
|
||||
let mut domainname: Vec<u16> = Vec::with_capacity(cc_domainname as usize);
|
||||
name.set_len(cc_name as usize);
|
||||
domainname.set_len(cc_domainname as usize);
|
||||
let ret = LookupAccountSidW(
|
||||
ptr::null::<u16>() as *mut u16,
|
||||
psid,
|
||||
name.as_mut_ptr() as *mut u16,
|
||||
&mut cc_name,
|
||||
domainname.as_mut_ptr() as *mut u16,
|
||||
&mut cc_domainname,
|
||||
&mut pe_use,
|
||||
);
|
||||
|
||||
if ret == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let name = from_wide_ptr(name.as_ptr());
|
||||
let domainname = from_wide_ptr(domainname.as_ptr());
|
||||
Some((name, domainname))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn from_wide_ptr(ptr: *const u16) -> String {
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
unsafe {
|
||||
assert!(!ptr.is_null());
|
||||
let len = (0..std::isize::MAX)
|
||||
.position(|i| *ptr.offset(i) == 0)
|
||||
.unwrap_or_default();
|
||||
let slice = std::slice::from_raw_parts(ptr, len);
|
||||
OsString::from_wide(slice).to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
fn get_priority(handle: HANDLE) -> u32 {
|
||||
unsafe { GetPriorityClass(handle) }
|
||||
}
|
||||
|
||||
impl ProcessInfo {
|
||||
/// PID of process
|
||||
pub fn pid(&self) -> i32 {
|
||||
self.pid
|
||||
}
|
||||
|
||||
/// Name of command
|
||||
pub fn name(&self) -> String {
|
||||
self.command()
|
||||
.split(' ')
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Full name of command, with arguments
|
||||
pub fn command(&self) -> String {
|
||||
self.command.clone()
|
||||
}
|
||||
|
||||
/// Get the status of the process
|
||||
pub fn status(&self) -> String {
|
||||
"unknown".to_string()
|
||||
}
|
||||
|
||||
/// CPU usage as a percent of total
|
||||
pub fn cpu_usage(&self) -> f64 {
|
||||
let curr_time = self.cpu_info.curr_sys + self.cpu_info.curr_user;
|
||||
let prev_time = self.cpu_info.prev_sys + self.cpu_info.prev_user;
|
||||
|
||||
let usage_ms = (curr_time - prev_time) / 10000u64;
|
||||
let interval_ms = self.interval.as_secs() * 1000 + u64::from(self.interval.subsec_millis());
|
||||
usage_ms as f64 * 100.0 / interval_ms as f64
|
||||
}
|
||||
|
||||
/// Memory size in number of bytes
|
||||
pub fn mem_size(&self) -> u64 {
|
||||
self.memory_info.working_set_size
|
||||
}
|
||||
|
||||
/// Virtual memory size in bytes
|
||||
pub fn virtual_size(&self) -> u64 {
|
||||
self.memory_info.private_usage
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user