add sys command

This commit is contained in:
JT 2021-10-01 19:53:47 +13:00
parent 000db46618
commit 503939dcbe
6 changed files with 483 additions and 4 deletions

114
Cargo.lock generated
View file

@ -123,6 +123,56 @@ dependencies = [
"winapi",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
dependencies = [
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.21.0"
@ -299,9 +349,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.102"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "linked-hash-map"
@ -337,6 +387,15 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "miette"
version = "3.0.0"
@ -441,6 +500,7 @@ dependencies = [
"nu-json",
"nu-protocol",
"nu-table",
"sysinfo",
"thiserror",
]
@ -519,6 +579,16 @@ dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.26.2"
@ -683,6 +753,31 @@ dependencies = [
"rand_core",
]
[[package]]
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -886,6 +981,21 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "sysinfo"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffff4a02fa61eee51f95210fc9c98ea6eeb46bb071adeafd61e1a0b9b22c6a6d"
dependencies = [
"cfg-if",
"core-foundation-sys",
"libc",
"ntapi",
"once_cell",
"rayon",
"winapi",
]
[[package]]
name = "tempfile"
version = "3.2.0"

View file

@ -14,3 +14,4 @@ nu-table = { path = "../nu-table" }
# Potential dependencies for extras
glob = "0.3.0"
thiserror = "1.0.29"
sysinfo = "0.20.4"

View file

@ -7,7 +7,7 @@ use nu_protocol::{
use crate::{
Alias, Benchmark, BuildString, Def, Do, Each, External, For, From, FromJson, Git, GitCheckout,
If, Length, Let, LetEnv, Lines, ListGitBranches, Ls, Module, Table, Use, Where,
If, Length, Let, LetEnv, Lines, ListGitBranches, Ls, Module, Sys, Table, Use, Where,
};
pub fn create_default_context() -> Rc<RefCell<EngineState>> {
@ -37,6 +37,7 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
working_set.add_decl(Box::new(Lines));
working_set.add_decl(Box::new(Ls));
working_set.add_decl(Box::new(Module));
working_set.add_decl(Box::new(Sys));
working_set.add_decl(Box::new(Table));
working_set.add_decl(Box::new(Use));
working_set.add_decl(Box::new(Where));

View file

@ -1,5 +1,7 @@
mod benchmark;
mod run_external;
mod sys;
pub use benchmark::Benchmark;
pub use run_external::{External, ExternalCommand};
pub use sys::Sys;

View file

@ -0,0 +1,356 @@
use nu_protocol::{
ast::Call,
engine::{Command, EvaluationContext},
ShellError, Signature, Span, Value,
};
use sysinfo::{ComponentExt, DiskExt, NetworkExt, ProcessorExt, System, SystemExt, UserExt};
pub struct Sys;
impl Command for Sys {
fn name(&self) -> &str {
"sys"
}
fn signature(&self) -> Signature {
Signature::build("sys")
.desc("View information about the current system.")
.filter()
}
fn usage(&self) -> &str {
"View information about the system."
}
fn run(
&self,
_context: &EvaluationContext,
call: &Call,
_input: Value,
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
run_sys(call)
}
// fn examples(&self) -> Vec<Example> {
// vec![Example {
// description: "Show info about the system",
// example: "sys",
// result: None,
// }]
// }
}
fn run_sys(call: &Call) -> Result<Value, ShellError> {
let span = call.head;
let mut sys = System::new();
let mut headers = vec![];
let mut values = vec![];
if let Some(value) = host(&mut sys, span) {
headers.push("host".into());
values.push(value);
}
if let Some(value) = cpu(&mut sys, span) {
headers.push("cpu".into());
values.push(value);
}
if let Some(value) = disks(&mut sys, span) {
headers.push("disks".into());
values.push(value);
}
if let Some(value) = mem(&mut sys, span) {
headers.push("mem".into());
values.push(value);
}
if let Some(value) = temp(&mut sys, span) {
headers.push("temp".into());
values.push(value);
}
if let Some(value) = net(&mut sys, span) {
headers.push("net".into());
values.push(value);
}
Ok(Value::Record {
cols: headers,
vals: values,
span,
})
}
pub fn trim_cstyle_null(s: String) -> String {
s.trim_matches(char::from(0)).to_string()
}
pub fn disks(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_disks();
sys.refresh_disks_list();
let mut output = vec![];
for disk in sys.disks() {
let mut cols = vec![];
let mut vals = vec![];
cols.push("device".into());
vals.push(Value::String {
val: trim_cstyle_null(disk.name().to_string_lossy().to_string()),
span,
});
cols.push("type".into());
vals.push(Value::String {
val: trim_cstyle_null(String::from_utf8_lossy(disk.file_system()).to_string()),
span,
});
cols.push("mount".into());
vals.push(Value::String {
val: disk.mount_point().to_string_lossy().to_string(),
span,
});
cols.push("total".into());
vals.push(Value::Filesize {
val: disk.total_space(),
span,
});
cols.push("free".into());
vals.push(Value::Filesize {
val: disk.available_space(),
span,
});
output.push(Value::Record { cols, vals, span });
}
if !output.is_empty() {
Some(Value::List { vals: output, span })
} else {
None
}
}
pub fn net(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_networks();
sys.refresh_networks_list();
let mut output = vec![];
for (iface, data) in sys.networks() {
let mut cols = vec![];
let mut vals = vec![];
cols.push("name".into());
vals.push(Value::String {
val: trim_cstyle_null(iface.to_string()),
span,
});
cols.push("sent".into());
vals.push(Value::Filesize {
val: data.total_transmitted(),
span,
});
cols.push("recv".into());
vals.push(Value::Filesize {
val: data.total_received(),
span,
});
output.push(Value::Record { cols, vals, span });
}
if !output.is_empty() {
Some(Value::List { vals: output, span })
} else {
None
}
}
pub fn cpu(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_cpu();
let mut output = vec![];
for cpu in sys.processors() {
let mut cols = vec![];
let mut vals = vec![];
cols.push("name".into());
vals.push(Value::String {
val: trim_cstyle_null(cpu.name().to_string()),
span,
});
cols.push("brand".into());
vals.push(Value::String {
val: trim_cstyle_null(cpu.brand().to_string()),
span,
});
cols.push("freq".into());
vals.push(Value::Int {
val: cpu.frequency() as i64,
span,
});
output.push(Value::Record { cols, vals, span });
}
if !output.is_empty() {
Some(Value::List { vals: output, span })
} else {
None
}
}
pub fn mem(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_memory();
let mut cols = vec![];
let mut vals = vec![];
let total_mem = sys.total_memory();
let free_mem = sys.free_memory();
let total_swap = sys.total_swap();
let free_swap = sys.free_swap();
cols.push("total".into());
vals.push(Value::Filesize {
val: total_mem * 1000,
span,
});
cols.push("free".into());
vals.push(Value::Filesize {
val: free_mem * 1000,
span,
});
cols.push("swap total".into());
vals.push(Value::Filesize {
val: total_swap * 1000,
span,
});
cols.push("swap free".into());
vals.push(Value::Filesize {
val: free_swap * 1000,
span,
});
Some(Value::Record { cols, vals, span })
}
pub fn host(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_users_list();
let mut cols = vec![];
let mut vals = vec![];
if let Some(name) = sys.name() {
cols.push("name".into());
vals.push(Value::String {
val: trim_cstyle_null(name),
span,
});
}
if let Some(version) = sys.os_version() {
cols.push("os version".into());
vals.push(Value::String {
val: trim_cstyle_null(version),
span,
});
}
if let Some(version) = sys.kernel_version() {
cols.push("kernel version".into());
vals.push(Value::String {
val: trim_cstyle_null(version),
span,
});
}
if let Some(hostname) = sys.host_name() {
cols.push("hostname".into());
vals.push(Value::String {
val: trim_cstyle_null(hostname),
span,
});
}
// dict.insert_untagged(
// "uptime",
// UntaggedValue::duration(1000000000 * sys.uptime() as i64),
// );
let mut users = vec![];
for user in sys.users() {
let mut cols = vec![];
let mut vals = vec![];
cols.push("name".into());
vals.push(Value::String {
val: trim_cstyle_null(user.name().to_string()),
span,
});
let mut groups = vec![];
for group in user.groups() {
groups.push(Value::String {
val: trim_cstyle_null(group.to_string()),
span,
});
}
cols.push("groups".into());
vals.push(Value::List { vals: groups, span });
users.push(Value::Record { cols, vals, span });
}
if !users.is_empty() {
cols.push("sessions".into());
vals.push(Value::List { vals: users, span });
}
Some(Value::Record { cols, vals, span })
}
pub fn temp(sys: &mut System, span: Span) -> Option<Value> {
sys.refresh_components();
sys.refresh_components_list();
let mut output = vec![];
for component in sys.components() {
let mut cols = vec![];
let mut vals = vec![];
cols.push("unit".into());
vals.push(Value::String {
val: component.label().to_string(),
span,
});
cols.push("temp".into());
vals.push(Value::Float {
val: component.temperature() as f64,
span,
});
cols.push("high".into());
vals.push(Value::Float {
val: component.max() as f64,
span,
});
if let Some(critical) = component.critical() {
cols.push("critical".into());
vals.push(Value::Float {
val: critical as f64,
span,
});
}
output.push(Value::Record { cols, vals, span });
}
if !output.is_empty() {
Some(Value::List { vals: output, span })
} else {
None
}
}

View file

@ -25,6 +25,10 @@ pub enum Value {
val: i64,
span: Span,
},
Filesize {
val: u64,
span: Span,
},
Range {
val: Box<Range>,
span: Span,
@ -81,6 +85,7 @@ impl Value {
Value::Bool { span, .. } => *span,
Value::Int { span, .. } => *span,
Value::Float { span, .. } => *span,
Value::Filesize { span, .. } => *span,
Value::Range { span, .. } => *span,
Value::String { span, .. } => *span,
Value::Record { span, .. } => *span,
@ -98,6 +103,7 @@ impl Value {
Value::Bool { span, .. } => *span = new_span,
Value::Int { span, .. } => *span = new_span,
Value::Float { span, .. } => *span = new_span,
Value::Filesize { span, .. } => *span = new_span,
Value::Range { span, .. } => *span = new_span,
Value::String { span, .. } => *span = new_span,
Value::Record { span, .. } => *span = new_span,
@ -118,6 +124,7 @@ impl Value {
Value::Bool { .. } => Type::Bool,
Value::Int { .. } => Type::Int,
Value::Float { .. } => Type::Float,
Value::Filesize { .. } => Type::Filesize,
Value::Range { .. } => Type::Range,
Value::String { .. } => Type::String,
Value::Record { cols, vals, .. } => {
@ -138,6 +145,7 @@ impl Value {
Value::Bool { val, .. } => val.to_string(),
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::Filesize { val, .. } => format!("{} bytes", val),
Value::Range { val, .. } => {
format!(
"range: [{}]",
@ -176,6 +184,7 @@ impl Value {
Value::Bool { val, .. } => val.to_string(),
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::Filesize { val, .. } => format!("{} bytes", val),
Value::Range { val, .. } => val
.into_iter()
.map(|x| x.into_string())