Skip to content

Commit 0b170d0

Browse files
committed
top: tui impl k (kill)
1 parent 6ff7d1b commit 0b170d0

File tree

5 files changed

+86
-4
lines changed

5 files changed

+86
-4
lines changed

src/uu/top/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ license.workspace = true
1111
version.workspace = true
1212

1313
[dependencies]
14-
uucore = { workspace = true, features = ["utmpx", "uptime"] }
14+
uucore = { workspace = true, features = ["utmpx", "uptime", "signals"] }
1515
clap = { workspace = true }
1616
crossterm = { workspace = true }
1717
libc = { workspace = true }

src/uu/top/src/action.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,22 @@
77
pub(crate) fn renice(pid: u32, nice_value: i32) -> uucore::error::UResult<()> {
88
use libc::{setpriority, PRIO_PROCESS};
99
use uucore::error::USimpleError;
10+
1011
let result = unsafe { setpriority(PRIO_PROCESS, pid, nice_value) };
1112
if result == -1 {
1213
Err(USimpleError::new(0, "Permission Denied"))
1314
} else {
1415
Ok(())
1516
}
1617
}
18+
19+
#[cfg(unix)]
20+
pub(crate) fn kill_process(pid: u32, sig: usize) -> uucore::error::UResult<()> {
21+
use nix::sys::signal;
22+
use nix::sys::signal::Signal;
23+
use nix::unistd::Pid;
24+
use uucore::error::USimpleError;
25+
26+
signal::kill(Pid::from_raw(pid as i32), Signal::try_from(sig as i32)?)
27+
.map_err(|_| USimpleError::new(0, "Permission Denied"))
28+
}

src/uu/top/src/picker.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ fn mem(pid: u32, _stat: Stat) -> Box<dyn Column> {
435435
#[cfg(target_os = "linux")]
436436
pub(crate) fn get_supplementary_groups(pid: u32) -> String {
437437
use sysinfo::{Gid, Groups};
438+
438439
let groups = Groups::new_with_refreshed_list();
439440
let path = PathBuf::from_str(&format!("/proc/{pid}/status")).unwrap();
440441
if let Ok(file) = File::open(path) {
@@ -452,10 +453,8 @@ pub(crate) fn get_supplementary_groups(pid: u32) -> String {
452453
return groups;
453454
}
454455
}
455-
String::new()
456-
} else {
457-
String::new()
458456
}
457+
String::new()
459458
}
460459

461460
#[cfg(target_os = "linux")]

src/uu/top/src/tui/input.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ pub(crate) enum InputEvent {
3030
ReniceProc,
3131
#[cfg(target_os = "linux")]
3232
ReniceValue,
33+
#[cfg(unix)]
34+
KillProc,
35+
#[cfg(unix)]
36+
KillSignal,
3337
}
3438

3539
macro_rules! char {
@@ -169,6 +173,25 @@ pub fn handle_input(
169173
}
170174
should_update.store(true, Ordering::Relaxed);
171175
}
176+
#[cfg(unix)]
177+
char!('k') => {
178+
let data = data.read().unwrap();
179+
let mut tui_stat = tui_stat.write().unwrap();
180+
let mut nth = tui_stat.list_offset;
181+
if data.1.collected.is_empty() {
182+
return false;
183+
}
184+
if data.1.collected.len() <= nth {
185+
nth = data.1.collected.len() - 1;
186+
}
187+
let pid = data.1.collected[nth].0;
188+
tui_stat.input_value.clear();
189+
tui_stat.input_label = format!("PID to signal/kill [default pid = {}]", pid);
190+
tui_stat.selected_process = Some(pid);
191+
tui_stat.input_mode = InputMode::Input(InputEvent::KillProc);
192+
193+
should_update.store(true, Ordering::Relaxed);
194+
}
172195
char!('l') => {
173196
let mut stat = tui_stat.write().unwrap();
174197
stat.show_load_avg = !stat.show_load_avg;
@@ -607,5 +630,52 @@ fn handle_input_value(
607630
}
608631
should_update.store(true, Ordering::Relaxed);
609632
}
633+
#[cfg(unix)]
634+
InputEvent::KillProc => {
635+
let input_value = { tui_stat.read().unwrap().input_value.parse::<u32>() };
636+
let mut stat = tui_stat.write().unwrap();
637+
if let Ok(pid) = input_value {
638+
stat.selected_process = Some(pid);
639+
} else {
640+
let is_empty = stat.input_value.trim().is_empty();
641+
stat.reset_input();
642+
if !is_empty {
643+
stat.input_message = Some(" Unacceptable integer ".into());
644+
should_update.store(true, Ordering::Relaxed);
645+
return;
646+
}
647+
};
648+
stat.input_value.clear();
649+
stat.input_label = format!(
650+
"Send pid {} signal [15/sigterm]",
651+
stat.selected_process.unwrap()
652+
);
653+
stat.input_mode = InputMode::Input(InputEvent::KillSignal);
654+
should_update.store(true, Ordering::Relaxed);
655+
}
656+
#[cfg(unix)]
657+
InputEvent::KillSignal => {
658+
use uucore::signals::signal_by_name_or_value;
659+
let mut stat = tui_stat.write().unwrap();
660+
stat.input_mode = InputMode::Command;
661+
stat.reset_input();
662+
let signal = if stat.input_value.is_empty() {
663+
15
664+
} else if let Some(sig) = signal_by_name_or_value(&stat.input_value) {
665+
sig
666+
} else {
667+
stat.input_message = Some(" Unacceptable signal value".into());
668+
should_update.store(true, Ordering::Relaxed);
669+
return;
670+
};
671+
let pid = stat.selected_process.unwrap();
672+
if let Err(e) = crate::action::kill_process(pid, signal) {
673+
stat.input_message = Some(format!(
674+
" Failed signal pid {} with {}: {} ",
675+
pid, signal, e
676+
));
677+
}
678+
should_update.store(true, Ordering::Relaxed);
679+
}
610680
}
611681
}

src/uu/top/src/tui/stat.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub(crate) struct TuiStat {
1212
pub input_label: String,
1313
pub input_value: String,
1414
pub input_message: Option<String>, // Info or error
15+
#[allow(dead_code)]
1516
pub selected_process: Option<u32>,
1617

1718
pub show_load_avg: bool,

0 commit comments

Comments
 (0)