summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-08-31 22:46:02 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-08-31 22:46:02 +0300
commita635232820dc4544686010b2cc1a49a36a4f664b (patch)
tree5fb448eee498ed1f28529e1c5554d1c160776611
parent31bf144ecdfa3c1da9e38e1f6338329aab996680 (diff)
add username, etc
-rw-r--r--src/main.rs4
-rw-r--r--src/ui.rs27
-rw-r--r--src/ui/components/kernel.rs204
-rw-r--r--src/ui/components/processes.rs419
-rw-r--r--src/ui/components/utilities.rs57
-rw-r--r--src/ui/terminal/cells.rs15
6 files changed, 551 insertions, 175 deletions
diff --git a/src/main.rs b/src/main.rs
index 68d51d6..5c0be61 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -60,7 +60,7 @@ fn main() -> Result<(), Error> {
signal_hook::SIGWINCH,
];
- let ticker = tick(Duration::from_millis(800));
+ let ticker = tick(Duration::from_millis(1600));
let signal_recvr = notify(signals)?;
@@ -119,7 +119,7 @@ fn main() -> Result<(), Error> {
},
key => {
state.rcv_event(UIEvent::Input(key));
- state.redraw(false);
+ state.redraw(false);
},
}
},
diff --git a/src/ui.rs b/src/ui.rs
index 091d679..a01e7e6 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -43,7 +43,7 @@ pub mod username {
use libc;
use std::ptr::null_mut;
/* taken from whoami-0.1.1 */
- fn getpwuid() -> libc::passwd {
+ fn getpwuid(pw_uid: u32) -> libc::passwd {
let mut pwentp = null_mut();
let mut buffer = [0i8; 16384]; // from the man page
#[cfg(any(
@@ -58,7 +58,7 @@ pub mod username {
let mut pwent = libc::passwd {
pw_name: null_mut(),
pw_passwd: null_mut(),
- pw_uid: 0,
+ pw_uid,
pw_gid: 0,
pw_change: 0,
pw_class: null_mut(),
@@ -68,13 +68,7 @@ pub mod username {
pw_expire: 0,
};
unsafe {
- libc::getpwuid_r(
- libc::geteuid(),
- &mut pwent,
- &mut buffer[0],
- 16384,
- &mut pwentp,
- );
+ libc::getpwuid_r(pw_uid, &mut pwent, &mut buffer[0], 16384, &mut pwentp);
}
pwent
@@ -84,7 +78,7 @@ pub mod username {
let mut pwent = libc::passwd {
pw_name: null_mut(),
pw_passwd: null_mut(),
- pw_uid: 0,
+ pw_uid,
pw_gid: 0,
pw_gecos: null_mut(),
pw_dir: null_mut(),
@@ -92,13 +86,7 @@ pub mod username {
};
unsafe {
- libc::getpwuid_r(
- libc::geteuid(),
- &mut pwent,
- &mut buffer[0],
- 16384,
- &mut pwentp,
- );
+ libc::getpwuid_r(pw_uid, &mut pwent, &mut buffer[0], 16384, &mut pwentp);
}
pwent
@@ -117,8 +105,9 @@ pub mod username {
string
}
- pub fn username() -> String {
- let pwent = getpwuid();
+
+ pub fn username(uid: u32) -> String {
+ let pwent = getpwuid(uid);
ptr_to_string(pwent.pw_name)
}
diff --git a/src/ui/components/kernel.rs b/src/ui/components/kernel.rs
index 15d923b..9f1c4e0 100644
--- a/src/ui/components/kernel.rs
+++ b/src/ui/components/kernel.rs
@@ -133,50 +133,51 @@ impl Component for KernelMetrics {
}
/* Draw CPU usage bars */
- let bar_max = std::dbg!((0.6 * total_cols as f32) as usize);
+ let bar_max = (0.6 * total_cols as f32) as usize;
+ let old_cpu_stat = self.cpu_stat[0];
let mut boot_time: usize = 0;
for (i, cpu_stat) in get_stat(&mut boot_time).into_iter().enumerate() {
- let (mut x, y) = write_string_to_grid(
- "CPU",
- grid,
- Color::Byte(250),
- Color::Default,
- Attr::Default,
- (pos_inc(upper_left, (2, 2 + i)), bottom_right),
- false,
- );
- if i > 0 {
+ let (mut x, y) = if i > 0 {
write_string_to_grid(
- &i.to_string(),
+ &format!("CPU{}", i),
grid,
Color::Default,
Color::Default,
- Attr::Default,
- ((x, y), bottom_right),
+ Attr::Bold,
+ (pos_inc(upper_left, (2, 2 + i)), bottom_right),
false,
- );
+ )
} else {
/* add padding */
- write_string_to_grid(
- " ",
+ let (x, y) = write_string_to_grid(
+ "Σ",
grid,
Color::Default,
Color::Default,
Attr::Default,
- ((x, y), bottom_right),
+ (pos_inc(upper_left, (2, 2 + i)), bottom_right),
false,
);
- }
+ write_string_to_grid(
+ "CPU",
+ grid,
+ Color::Default,
+ Color::Default,
+ Attr::Bold,
+ ((x, y), bottom_right),
+ false,
+ )
+ };
x += 2;
/* Calculate percentages for the cpu usage bar */
let busy_length = (cpu_stat.user_time + cpu_stat.system_time)
- (self.cpu_stat[i].user_time + self.cpu_stat[i].system_time);
let iowait_length = cpu_stat.iowait_time - self.cpu_stat[i].iowait_time;
- let bar_length: usize = std::dbg!(
- (((busy_length + iowait_length) as f64 / 100.0) * bar_max as f64) as usize
- );
+ let bar_length: usize = (((busy_length + iowait_length) as f64
+ / (cpu_stat.total_time() - self.cpu_stat[i].total_time()) as f64)
+ * bar_max as f64) as usize;
let mut x_offset = 0;
while x_offset < bar_length {
@@ -213,7 +214,7 @@ impl Component for KernelMetrics {
y_offset += 1;
- let bar_max = bar_max + 5;
+ let bar_max = bar_max + 6;
let (available, total) = get_mem_info();
let available_length = ((available as f64 / total as f64) * bar_max as f64) as usize;
let mem_bar_length = bar_max - available_length;
@@ -283,6 +284,74 @@ impl Component for KernelMetrics {
false,
);
}
+
+ /* Various values table */
+
+ /* CPU Times */
+ let mut cpu_column_width = "CPU".len();
+ let upper_left = pos_inc(upper_left, (bar_max + 5, 2));
+ write_string_to_grid(
+ "CPU%",
+ grid,
+ Color::Default,
+ Color::Default,
+ Attr::Bold,
+ (upper_left, bottom_right),
+ false,
+ );
+ for (i, (tag, s, fg_color, bg_color)) in get_cpu_times(&old_cpu_stat, &self.cpu_stat[0])
+ .into_iter()
+ .enumerate()
+ {
+ let (x, y) = write_string_to_grid(
+ tag,
+ grid,
+ Color::Default,
+ Color::Default,
+ Attr::Default,
+ (pos_inc(upper_left, (0, i + 1)), bottom_right),
+ false,
+ );
+
+ let padding = 6 - s.len();
+ clear_area(grid, ((x, y), (x + padding + 1, y)));
+
+ write_string_to_grid(
+ &s,
+ grid,
+ fg_color,
+ bg_color,
+ Attr::Default,
+ ((x + 2 + padding, y), bottom_right),
+ false,
+ );
+ cpu_column_width = std::cmp::max(tag.len() + s.len() + 4, cpu_column_width);
+ }
+
+ /* Load average */
+ let mut load_column_width = "LOAD_AVG".len();
+ let upper_left = pos_inc(upper_left, (cpu_column_width + 3, 0));
+ write_string_to_grid(
+ "LOAD_AVG",
+ grid,
+ Color::Default,
+ Color::Default,
+ Attr::Bold,
+ (upper_left, bottom_right),
+ false,
+ );
+ let loadavgs = get_loadavg();
+ for (i, avg) in loadavgs.into_iter().enumerate() {
+ write_string_to_grid(
+ avg,
+ grid,
+ Color::Default,
+ Color::Default,
+ Attr::Default,
+ (pos_inc(upper_left, (0, i + 1)), bottom_right),
+ false,
+ );
+ }
}
fn process_event(&mut self, event: &mut UIEvent) {
@@ -331,46 +400,61 @@ fn get_mem_info() -> (usize, usize) {
(mem_available, mem_total)
}
-#[derive(Debug)]
-struct Stat {
- user_time: usize,
- system_time: usize,
- idle_time: usize,
- iowait_time: usize,
-}
-
-fn get_stat(boot_time: &mut usize) -> Vec<Stat> {
- let mut file = File::open("/proc/stat").unwrap();
+fn get_loadavg() -> [String; 3] {
+ let mut file = File::open("/proc/loadavg").unwrap();
let mut res = String::with_capacity(2048);
file.read_to_string(&mut res).unwrap();
- let mut lines_iter = res.lines();
- let mut ret = Vec::with_capacity(8);
- let mut line;
- loop {
- line = lines_iter.next().unwrap();
- if !line.starts_with("cpu") {
- break;
- }
+ let mut mut_value_iter = res.split_whitespace();
+ let avg_1 = mut_value_iter.next().unwrap().to_string();
+ let avg_5 = mut_value_iter.next().unwrap().to_string();
+ let avg_15 = mut_value_iter.next().unwrap().to_string();
+ [avg_1, avg_5, avg_15]
+}
- let mut mut_value_iter = line.split_whitespace().skip(1);
-
- let user_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
- /* skip nice time */
- mut_value_iter.next();
- let system_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
- let idle_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
- let iowait_time = usize::from_str(&mut_value_iter.next().unwrap()).unwrap();
- ret.push(Stat {
- user_time,
- system_time,
- idle_time,
- iowait_time,
- });
- }
- while !line.starts_with("btime") {
- line = lines_iter.next().unwrap();
- }
- *boot_time = usize::from_str(&line.split_whitespace().skip(1).next().unwrap()).unwrap();
+fn get_cpu_times(
+ old_cpu_stat: &Stat,
+ cpu_stat: &Stat,
+) -> Vec<(&'static str, String, Color, Color)> {
+ let mut ret = Vec::new();
+
+ macro_rules! val {
+ ($tag:literal, $field:tt) => {
+ let percent = (cpu_stat.$field - old_cpu_stat.$field) as f64
+ / (cpu_stat.total_time() - old_cpu_stat.total_time()) as f64;
+ let s = format!("{:.1}%", percent * 100.0);
+ ret.push((
+ $tag,
+ s,
+ if percent < 0.15 {
+ Color::Default
+ } else if percent < 0.50 {
+ Color::Default
+ } else {
+ Color::White
+ },
+ if percent < 0.15 {
+ Color::Default
+ } else if percent < 0.50 {
+ Color::Byte(70)
+ } else if $tag != "idle% " {
+ Color::Red
+ } else {
+ Color::Default
+ },
+ ));
+ };
+ };
+
+ /* user % */
+ val!("user% ", user_time);
+ /* system % */
+ val!("system%", system_time);
+ /* nice % */
+ val!("nice% ", nice_time);
+ /* idle % */
+ val!("idle% ", idle_time);
+ /* iowait % */
+ val!("iowait%", iowait_time);
ret
}
diff --git a/src/ui/components/processes.rs b/src/ui/components/processes.rs
index 88d1fa3..dba2236 100644
--- a/src/ui/components/processes.rs
+++ b/src/ui/components/processes.rs
@@ -4,13 +4,19 @@ use std::io::prelude::*;
use std::path::PathBuf;
use std::str::FromStr;
+type Pid = usize;
+
/* process list components */
#[derive(Debug)]
pub struct ProcessList {
page_movement: Option<PageMovement>,
+ cpu_stat: Stat,
pid_max: usize,
cursor: usize,
dirty: bool,
+ processes: HashMap<Pid, usize>,
+ /* refresh process list every 4 cycles */
+ cycle: u8,
}
enum State {
@@ -86,10 +92,11 @@ impl std::fmt::Display for State {
struct Process {
pid: usize,
ppid: usize,
- vm_size: usize,
+ vm_rss: usize,
state: State,
- uid: usize,
+ uid: u32,
cmd_line: String,
+ utime: usize,
}
impl fmt::Display for ProcessList {
@@ -106,62 +113,49 @@ impl ProcessList {
ProcessList {
cursor: 0,
page_movement: None,
+ cpu_stat: get_stat(&mut 0).remove(0),
pid_max: usize::from_str(pid_max.trim()).unwrap(),
+ processes: Default::default(),
+ cycle: 0,
dirty: true,
}
}
}
+/* Prints the process list */
impl Component for ProcessList {
fn draw(
&mut self,
grid: &mut CellBuffer,
area: Area,
dirty_areas: &mut VecDeque<Area>,
- tick: bool,
+ mut tick: bool,
) {
if !is_valid_area!(area) {
return;
}
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
+
+ if !self.dirty && !tick {
+ return;
+ }
+
+ let upper_left = pos_inc(upper_left!(area), (1, 0));
+ let bottom_right = pos_dec(bottom_right!(area), (1, 1));
/* Reserve first row for column headers */
let height = height!(area) - 1;
+ let old_pages = self.cursor / height;
let width = width!(area);
- clear_area(grid, area);
- dirty_areas.push_back(area);
-
- let mut processes: Vec<(String, String, String, String, String)> = Vec::with_capacity(1024);
- let mut pid = 0;
- let mut maxima = ("PID".len(), "PPID".len(), "VM_SIZE".len(), "STATE".len());
- for entry in std::fs::read_dir("/proc/").unwrap() {
- let dir = entry.unwrap();
- if let Some(fname) = dir.file_name().to_str() {
- if !fname.chars().all(|c| c.is_numeric()) {
- continue;
- }
- } else {
- continue;
- }
- let process = get_pid_info(dir.path());
- let strings = (
- process.pid.to_string(),
- process.ppid.to_string(),
- Bytes(process.vm_size * 1024).as_convenient_string(),
- process.state.to_string(),
- process.cmd_line,
- );
- maxima.0 = std::cmp::max(maxima.0, strings.0.len());
- maxima.1 = std::cmp::max(maxima.1, strings.1.len());
- maxima.2 = std::cmp::max(maxima.2, strings.2.len());
- maxima.3 = std::cmp::max(maxima.3, strings.3.len());
- processes.push(strings);
- }
-
- processes.sort_unstable_by(|a, b| a.4.cmp(&b.4));
+ let old_cursor = self.cursor;
if let Some(mvm) = self.page_movement.take() {
match mvm {
+ PageMovement::Up => {
+ self.cursor = self.cursor.saturating_sub(1);
+ }
+ PageMovement::Down => {
+ self.cursor =
+ std::cmp::min(self.processes.len().saturating_sub(1), self.cursor + 1);
+ }
PageMovement::Home => {
self.cursor = 0;
}
@@ -170,27 +164,106 @@ impl Component for ProcessList {
}
PageMovement::PageDown => {
self.cursor =
- std::cmp::min(processes.len().saturating_sub(1), self.cursor + height);
+ std::cmp::min(self.processes.len().saturating_sub(1), self.cursor + height);
}
PageMovement::End => {
- self.cursor = processes.len().saturating_sub(1);
+ self.cursor = self.processes.len().saturating_sub(1);
}
}
}
- if self.dirty {
+ let pages = self.cursor / height;
+ if pages != old_pages {
+ tick = true;
+ }
+
+ if tick {
+ clear_area(grid, area);
+ dirty_areas.push_back(area);
+
+ let mut processes: Vec<(String, String, String, usize, String, String, String)> =
+ Vec::with_capacity(1024);
+
+ let cpu_stat = get_stat(&mut 0).remove(0);
+
+ /* Keep tabs on biggest element in each column */
+ let mut maxima = (
+ "PID".len(),
+ "PPID".len(),
+ "VM_RSS".len(),
+ "CPU%".len(),
+ " ".len(),
+ "USER".len(),
+ );
+
+ for entry in std::fs::read_dir("/proc/").unwrap() {
+ let dir = entry.unwrap();
+ if let Some(fname) = dir.file_name().to_str() {
+ if !fname.chars().all(|c| c.is_numeric()) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+
+ let process = if let Ok(p) = get_pid_info(dir.path()) {
+ p
+ } else {
+ continue;
+ };
+
+ if process.cmd_line.is_empty() {
+ /* This is a kernel thread, skip for now */
+ continue;
+ }
+
+ let strings = (
+ process.pid.to_string(),
+ process.ppid.to_string(),
+ Bytes(process.vm_rss * 1024).as_convenient_string(),
+ (100.0
+ * ((process.utime
+ - self
+ .processes
+ .get(&process.pid)
+ .map(|v| *v)
+ .unwrap_or(process.utime)) as f64
+ / ((cpu_stat.total_time() - self.cpu_stat.total_time()) as f64)))
+ as usize,
+ process.state.to_string(),
+ process.cmd_line,
+ crate::ui::username(process.uid),
+ );
+
+ self.processes.insert(process.pid, process.utime);
+
+ maxima.0 = std::cmp::max(maxima.0, strings.0.len());
+ maxima.1 = std::cmp::max(maxima.1, strings.1.len());
+ maxima.2 = std::cmp::max(maxima.2, strings.2.len());
+ maxima.4 = std::cmp::max(maxima.4, strings.4.len());
+ maxima.5 = std::cmp::max(maxima.5, strings.6.len());
+ processes.push(strings);
+ }
+ self.cpu_stat = cpu_stat;
+
+ processes.sort_unstable_by(|a, b| b.3.cmp(&a.3));
+
/* Write column headers */
let (x, y) = write_string_to_grid(
&format!(
- "{:>maxima0$} {:>maxima1$} {:>maxima2$} {:>maxima3$} {}",
+ "{:>maxima0$} {:>maxima1$} {:>maxima5$} {:>maxima2$} {:>maxima3$} {:>maxima4$} {}",
"PID",
"PPID",
- "VM_SIZE",
- "STATE",
+ "USER",
+ "VM_RSS",
+ "CPU%",
+ " ",
"CMD_LINE",
maxima0 = maxima.0,
maxima1 = maxima.1,
maxima2 = maxima.2,
maxima3 = maxima.3,
+ maxima4 = maxima.4,
+ maxima5 = maxima.5,
),
grid,
Color::Black,
@@ -202,69 +275,185 @@ impl Component for ProcessList {
change_colors(
grid,
((x, y), set_y(bottom_right, y)),
- Color::Black,
- Color::White,
+ Some(Color::Black),
+ Some(Color::White),
);
- }
- let mut y_offset = 0;
- let pages = self.cursor / height;
- let p_len = processes.len();
- for (pid, ppid, vm_size, state, cmd_line) in processes.into_iter().skip(pages * height) {
- let (x, y) = write_string_to_grid(
- &format!(
- "{:>maxima0$} {:>maxima1$} {:>maxima2$} {:>maxima3$} {}",
- pid,
- ppid,
- vm_size,
- state,
- cmd_line,
- maxima0 = maxima.0,
- maxima1 = maxima.1,
- maxima2 = maxima.2,
- maxima3 = maxima.3,
- ),
- grid,
- if pages * height + y_offset == self.cursor {
- Color::Black
+ let mut y_offset = 0;
+ let p_len = processes.len();
+ for (pid, ppid, vm_rss, cpu, state, cmd_line, username) in
+ processes.into_iter().skip(pages * height)
+ {
+ let fg_color = Color::Default;
+ let bg_color = if pages * height + y_offset == self.cursor {
+ Color::Byte(235)
} else {
Color::Default
- },
- if pages * height + y_offset == self.cursor {
- Color::Byte(243)
- } else {
- Color::Default
- },
- Attr::Default,
- (pos_inc(upper_left, (0, y_offset + 3)), bottom_right),
- false,
- );
- y_offset += 1;
- if y_offset >= height {
- break;
+ };
+ match executable_path_color(&cmd_line) {
+ Ok((path, bin, rest)) => {
+ let (x, y) = write_string_to_grid(
+ &format!(
+ "{:>maxima0$} {:>maxima1$} {:>maxima5$} {:>maxima2$} {:>maxima3$}% {:>maxima4$}",
+ pid,
+ ppid,
+ username,
+ vm_rss,
+ cpu,
+ state,
+ maxima0 = maxima.0,
+ maxima1 = maxima.1,
+ maxima2 = maxima.2,
+ maxima3 = maxima.3,
+ maxima4 = maxima.4,
+ maxima5 = maxima.5,
+ ),
+ grid,
+ fg_color,
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (0, y_offset + 3)), bottom_right),
+ false,
+ );
+ let (x, _) = write_string_to_grid(
+ path,
+ grid,
+ Color::Byte(243),
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (x, y_offset + 3)), bottom_right),
+ false,
+ );
+ let (x, y) = write_string_to_grid(
+ bin,
+ grid,
+ Color::Byte(34),
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (x - 1, y_offset + 3)), bottom_right),
+ false,
+ );
+ let (x, y) = write_string_to_grid(
+ rest,
+ grid,
+ fg_color,
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (x - 1, y_offset + 3)), bottom_right),
+ false,
+ );
+ change_colors(
+ grid,
+ ((x, y), set_y(bottom_right, y)),
+ Some(fg_color),
+ Some(bg_color),
+ );
+ }
+ Err((bin, rest)) => {
+ let(x,y)=write_string_to_grid(
+ &format!(
+ "{:>maxima0$} {:>maxima1$} {:>maxima5$} {:>maxima2$} {:>maxima3$}% {:>maxima4$} ",
+ pid,
+ ppid,
+ username,
+ vm_rss,
+ cpu,
+ state,
+ maxima0 = maxima.0,
+ maxima1 = maxima.1,
+ maxima2 = maxima.2,
+ maxima3 = maxima.3,
+ maxima4 = maxima.4,
+ maxima5 = maxima.5,
+ ),
+ grid,
+ fg_color,
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (0, y_offset + 3)), bottom_right),
+ false,
+ );
+ let (x, y) = write_string_to_grid(
+ bin,
+ grid,
+ Color::Byte(34),
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (x - 1, y_offset + 3)), bottom_right),
+ false,
+ );
+ let (x, y) = write_string_to_grid(
+ rest,
+ grid,
+ fg_color,
+ bg_color,
+ Attr::Default,
+ (pos_inc(upper_left, (x - 1, y_offset + 3)), bottom_right),
+ false,
+ );
+ change_colors(
+ grid,
+ ((x, y), set_y(bottom_right, y)),
+ Some(fg_color),
+ Some(bg_color),
+ );
+ }
+ }
+ y_offset += 1;
+ if y_offset >= height {
+ break;
+ }
}
+ self.cycle += 1;
+ self.cycle %= 4;
+ } else if old_cursor != self.cursor {
+ let new_area = (
+ pos_inc(upper_left, (0, self.cursor - pages * height + 3)),
+ set_y(
+ bottom_right,
+ get_y(upper_left) + self.cursor - pages * height + 3,
+ ),
+ );
+ let old_area = (
+ pos_inc(upper_left, (0, old_cursor - old_pages * height + 3)),
+ set_y(
+ bottom_right,
+ get_y(upper_left) + old_cursor - old_pages * height + 3,
+ ),
+ );
+ change_colors(grid, new_area, None, Some(Color::Byte(235)));
+ change_colors(grid, old_area, None, Some(Color::Default));
+ dirty_areas.push_back(old_area);
+ dirty_areas.push_back(new_area);
}
+ self.dirty = false;
}
fn process_event(&mut self, event: &mut UIEvent) {
match event {
UIEvent::Input(Key::Up) => {
- self.cursor = self.cursor.saturating_sub(1);
+ self.page_movement = Some(PageMovement::Up);
+ self.dirty = true;
}
UIEvent::Input(Key::Down) => {
- self.cursor += 1;
+ self.page_movement = Some(PageMovement::Down);
+ self.dirty = true;
}
UIEvent::Input(Key::Home) => {
self.page_movement = Some(PageMovement::Home);
+ self.dirty = true;
}
UIEvent::Input(Key::PageUp) => {
self.page_movement = Some(PageMovement::PageUp);
+ self.dirty = true;
}
UIEvent::Input(Key::PageDown) => {
self.page_movement = Some(PageMovement::PageDown);
+ self.dirty = true;
}
UIEvent::Input(Key::End) => {
self.page_movement = Some(PageMovement::End);
+ self.dirty = true;
}
_ => {}
}
@@ -277,17 +466,19 @@ impl Component for ProcessList {
fn set_dirty(&mut self) {}
}
-fn get_pid_info(mut path: PathBuf) -> Process {
+/* proc file structure can be found in man 5 proc */
+fn get_pid_info(mut path: PathBuf) -> Result<Process, std::io::Error> {
path.push("status");
- let mut file: File = File::open(&path).unwrap();
+ let mut file: File = File::open(&path)?;
let mut res = String::with_capacity(2048);
- file.read_to_string(&mut res).unwrap();
+ file.read_to_string(&mut res)?;
let mut lines_iter = res.lines();
let mut ret = Process {
pid: 0,
ppid: 0,
- vm_size: 0,
+ vm_rss: 0,
uid: 0,
+ utime: 0,
state: State::Waiting,
cmd_line: String::new(),
};
@@ -301,11 +492,8 @@ fn get_pid_info(mut path: PathBuf) -> Process {
line = line_opt.unwrap();
let mut mut_value_iter = line.split_whitespace();
match mut_value_iter.next().unwrap() {
- "Name:" => {
- ret.cmd_line = mut_value_iter.next().unwrap().to_string();
- }
- "VmSize:" => {
- ret.vm_size = usize::from_str(mut_value_iter.next().unwrap()).unwrap();
+ "VmRSS:" => {
+ ret.vm_rss = usize::from_str(mut_value_iter.next().unwrap()).unwrap();
}
"State:" => {
ret.state = State::from(mut_value_iter.next().unwrap().chars().next().unwrap());
@@ -317,7 +505,7 @@ fn get_pid_info(mut path: PathBuf) -> Process {
ret.ppid = usize::from_str(mut_value_iter.next().unwrap()).unwrap();
}
"Uid:" => {
- ret.uid = usize::from_str(mut_value_iter.next().unwrap()).unwrap();
+ ret.uid = u32::from_str(mut_value_iter.next().unwrap()).unwrap();
}
_ => {}
}
@@ -328,7 +516,56 @@ fn get_pid_info(mut path: PathBuf) -> Process {
res.clear();
file.read_to_string(&mut res).unwrap();
if !res.is_empty() {
+ /* values are separated by null bytes */
ret.cmd_line = format!("{}", res.split('\0').collect::<Vec<&str>>().join(" "));
}
- ret
+ path.pop();
+ path.push("stat");
+ let mut file: File = File::open(&path).unwrap();
+ res.clear();
+ file.read_to_string(&mut res).unwrap();
+ /* values are separated by whitespace and are in a specific order */
+ if !res.is_empty() {
+ let mut vals = res.split_whitespace().skip(13);
+ ret.utime = usize::from_str(vals.next().unwrap()).unwrap();
+ ret.utime += usize::from_str(vals.next().unwrap()).unwrap();
+ ret.utime += usize::from_str(vals.next().unwrap()).unwrap();
+ ret.utime += usize::from_str(vals.next().unwrap()).unwrap();
+ }
+ Ok(ret)
+}
+
+fn executable_path_color(p: &str) -> Result<(&str, &str, &str), (&str, &str)> {
+ if !p.starts_with("/") {
+ return if let Some(first_whitespace) = p.as_bytes().iter().position(|c| *c == b' ') {
+ Err(p.split_at(first_whitespace))
+ } else {
+ Err((p, ""))
+ };
+ }
+
+ if let Some(first_whitespace) = p.as_bytes().iter().position(|c| *c == b' ') {
+ if let Some(path_end) = p[0..first_whitespace]
+ .as_bytes()
+ .iter()
+ .rposition(|c| *c == b'/')
+ {
+ let (path, rest) = p.split_at(path_end + 1);
+ if let Some(first_whitespace) = rest.as_bytes().iter().position(|c| *c == b' ') {
+ let (bin, rest) = rest.split_at(first_whitespace);
+ Ok((path, bin, rest))
+ } else {
+ Ok((path, rest, ""))
+ }
+ } else {
+ return Err(p.split_at(first_whitespace));
+ }
+ } else {
+ if let Some(path_end) = p.as_bytes().iter().rposition(|c| *c == b'/') {
+ let (path, bin) = p.split_at(path_end + 1);
+ Ok((path, bin, ""))
+ } else {
+ return Err((p, ""));
+ }
+ }
}
diff --git a/src/ui/components/utilities.rs b/src/ui/components/utilities.rs
index 274cc0b..1b173f0 100644
--- a/src/ui/components/utilities.rs
+++ b/src/ui/components/utilities.rs
@@ -22,6 +22,9 @@
/*! Various useful components that can be used in a generic fashion.
*/
use super::*;
+use std::fs::File;
+use std::io::prelude::*;
+use std::str::FromStr;
mod widgets;
@@ -249,6 +252,8 @@ impl Component for VSplit {
#[derive(Debug)]
pub enum PageMovement {
+ Up,
+ Down,
Home,
PageUp,
PageDown,
@@ -280,3 +285,55 @@ impl Bytes {
}
}
}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Stat {
+ pub user_time: usize,
+ pub system_time: usize,
+ pub nice_time: usize,
+ pub idle_time: usize,
+ pub iowait_time: usize,
+}
+