diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-08-31 22:46:02 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2019-08-31 22:46:02 +0300 |
commit | a635232820dc4544686010b2cc1a49a36a4f664b (patch) | |
tree | 5fb448eee498ed1f28529e1c5554d1c160776611 | |
parent | 31bf144ecdfa3c1da9e38e1f6338329aab996680 (diff) |
add username, etc
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/ui.rs | 27 | ||||
-rw-r--r-- | src/ui/components/kernel.rs | 204 | ||||
-rw-r--r-- | src/ui/components/processes.rs | 419 | ||||
-rw-r--r-- | src/ui/components/utilities.rs | 57 | ||||
-rw-r--r-- | src/ui/terminal/cells.rs | 15 |
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); }, } }, @@ -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, +} + |