snice&skill: implement interactive

This commit is contained in:
Bluemangoo
2025-08-10 12:17:43 +08:00
parent 1c5ff43507
commit f1972d1555
5 changed files with 67 additions and 6 deletions
+11 -3
View File
@@ -44,7 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
#[cfg(unix)]
let results = perform_action(&pids, &signal, take_action);
let results = perform_action(&pids, &signal, take_action, settings.interactive);
#[cfg(not(unix))]
let results: Vec<Option<ActionResult>> = Vec::new();
@@ -67,15 +67,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}
#[cfg(unix)]
fn perform_action(pids: &[u32], signal: &Signal, take_action: bool) -> Vec<Option<ActionResult>> {
fn perform_action(
pids: &[u32],
signal: &Signal,
take_action: bool,
ask: bool,
) -> Vec<Option<ActionResult>> {
let sig = if take_action { Some(*signal) } else { None };
pids.iter()
.map(|pid| {
{
if !ask || uu_snice::ask_user(*pid) {
Some(match signal::kill(Pid::from_raw(*pid as i32), sig) {
Ok(_) => ActionResult::Success,
Err(_) => ActionResult::PermissionDenied,
})
} else {
// won't be used, but we need to return (not None)
Some(ActionResult::Success)
}
})
.collect()
+10 -1
View File
@@ -3,6 +3,7 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::ask_user;
use crate::priority::Priority;
use std::{
fmt::{self, Display, Formatter},
@@ -171,7 +172,15 @@ pub(crate) fn perform_action(
pids: &[u32],
prio: &Priority,
take_action: bool,
ask: bool,
) -> Vec<Option<ActionResult>> {
let f = |pid: &u32| set_priority(*pid, prio, take_action);
let f = |pid: &u32| {
if !ask || ask_user(*pid) {
set_priority(*pid, prio, take_action)
} else {
// won't be used, but we need to return (not None)
Some(ActionResult::Success)
}
};
pids.iter().map(f).collect()
}
+3 -1
View File
@@ -15,6 +15,7 @@ pub struct Settings {
pub expressions: Option<Vec<SelectedTarget>>,
pub verbose: bool,
pub warnings: bool,
pub interactive: bool,
pub no_action: bool,
}
@@ -33,6 +34,7 @@ impl Settings {
expressions: Self::targets(matches),
verbose: matches.get_flag("verbose"),
warnings: matches.get_flag("warnings"),
interactive: matches.get_flag("interactive"),
no_action: matches.get_flag("no-action"),
})
}
@@ -85,7 +87,7 @@ impl Settings {
pub fn clap_args() -> Vec<Arg> {
vec![
// arg!(-f --fast "fast mode (not implemented)"),
// arg!(-i --interactive "interactive"),
arg!(-i --interactive "interactive").conflicts_with_all(["verbose", "no-action"]),
arg!(-l --list "list all signal names"),
arg!(-L --table "list all signal names in a nice table"),
arg!(-n --"no-action" "do not actually kill processes; just print what would happen"),
+37 -1
View File
@@ -10,6 +10,7 @@ use clap::{crate_version, Arg, Command};
use prettytable::{format::consts::FORMAT_CLEAN, row, Table};
pub use process_matcher::clap_args;
use process_matcher::*;
use std::io::Write;
use std::{collections::HashSet, path::PathBuf, str::FromStr};
use sysinfo::Pid;
use uu_pgrep::process::ProcessInformation;
@@ -104,7 +105,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
};
let pids = collect_pids(&targets);
let results = perform_action(&pids, &priority, take_action);
let results = perform_action(&pids, &priority, take_action, settings.interactive);
if results.iter().all(|it| it.is_none()) || results.is_empty() {
return Err(USimpleError::new(1, "no process selection criteria"));
@@ -124,6 +125,41 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
Ok(())
}
pub fn ask_user(pid: u32) -> bool {
let process = process_snapshot().process(Pid::from_u32(pid)).unwrap();
let tty = ProcessInformation::try_new(PathBuf::from_str(&format!("/proc/{pid}")).unwrap())
.map(|v| v.tty().to_string())
.unwrap_or(String::from("?"));
let user = process
.user_id()
.and_then(|uid| users().iter().find(|it| it.id() == uid))
.map(|it| it.name())
.unwrap_or("?")
.to_owned();
let cmd = process
.exe()
.and_then(|it| it.iter().next_back())
.unwrap_or("?".as_ref());
let cmd = cmd.to_str().unwrap();
// no newline at the end
print!("{tty:<8} {user:<8} {pid:<5} {cmd:<18} ? ");
std::io::stdout().flush().unwrap();
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return false;
}
let input = input.trim();
if input.eq_ignore_ascii_case("y") || input.eq_ignore_ascii_case("yes") {
return true;
}
false
}
#[allow(unused)]
pub fn construct_verbose_result(
pids: &[u32],
+6
View File
@@ -16,3 +16,9 @@ fn test_no_args() {
fn test_no_process_selected() {
new_ucmd!().arg("-u=invalid_user").fails().code_is(1);
}
#[test]
fn test_interactive_conflict_args() {
new_ucmd!().args(&["-i", "-v"]).fails().code_is(1);
new_ucmd!().args(&["-i", "-n"]).fails().code_is(1);
}