Skip to content

Commit f1972d1

Browse files
committed
snice&skill: implement interactive
1 parent 1c5ff43 commit f1972d1

File tree

5 files changed

+67
-6
lines changed

5 files changed

+67
-6
lines changed

src/uu/skill/src/skill.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
4444
};
4545

4646
#[cfg(unix)]
47-
let results = perform_action(&pids, &signal, take_action);
47+
let results = perform_action(&pids, &signal, take_action, settings.interactive);
4848
#[cfg(not(unix))]
4949
let results: Vec<Option<ActionResult>> = Vec::new();
5050

@@ -67,15 +67,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
6767
}
6868

6969
#[cfg(unix)]
70-
fn perform_action(pids: &[u32], signal: &Signal, take_action: bool) -> Vec<Option<ActionResult>> {
70+
fn perform_action(
71+
pids: &[u32],
72+
signal: &Signal,
73+
take_action: bool,
74+
ask: bool,
75+
) -> Vec<Option<ActionResult>> {
7176
let sig = if take_action { Some(*signal) } else { None };
7277
pids.iter()
7378
.map(|pid| {
74-
{
79+
if !ask || uu_snice::ask_user(*pid) {
7580
Some(match signal::kill(Pid::from_raw(*pid as i32), sig) {
7681
Ok(_) => ActionResult::Success,
7782
Err(_) => ActionResult::PermissionDenied,
7883
})
84+
} else {
85+
// won't be used, but we need to return (not None)
86+
Some(ActionResult::Success)
7987
}
8088
})
8189
.collect()

src/uu/snice/src/action.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6+
use crate::ask_user;
67
use crate::priority::Priority;
78
use std::{
89
fmt::{self, Display, Formatter},
@@ -171,7 +172,15 @@ pub(crate) fn perform_action(
171172
pids: &[u32],
172173
prio: &Priority,
173174
take_action: bool,
175+
ask: bool,
174176
) -> Vec<Option<ActionResult>> {
175-
let f = |pid: &u32| set_priority(*pid, prio, take_action);
177+
let f = |pid: &u32| {
178+
if !ask || ask_user(*pid) {
179+
set_priority(*pid, prio, take_action)
180+
} else {
181+
// won't be used, but we need to return (not None)
182+
Some(ActionResult::Success)
183+
}
184+
};
176185
pids.iter().map(f).collect()
177186
}

src/uu/snice/src/process_matcher.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Settings {
1515
pub expressions: Option<Vec<SelectedTarget>>,
1616
pub verbose: bool,
1717
pub warnings: bool,
18+
pub interactive: bool,
1819
pub no_action: bool,
1920
}
2021

@@ -33,6 +34,7 @@ impl Settings {
3334
expressions: Self::targets(matches),
3435
verbose: matches.get_flag("verbose"),
3536
warnings: matches.get_flag("warnings"),
37+
interactive: matches.get_flag("interactive"),
3638
no_action: matches.get_flag("no-action"),
3739
})
3840
}
@@ -85,7 +87,7 @@ impl Settings {
8587
pub fn clap_args() -> Vec<Arg> {
8688
vec![
8789
// arg!(-f --fast "fast mode (not implemented)"),
88-
// arg!(-i --interactive "interactive"),
90+
arg!(-i --interactive "interactive").conflicts_with_all(["verbose", "no-action"]),
8991
arg!(-l --list "list all signal names"),
9092
arg!(-L --table "list all signal names in a nice table"),
9193
arg!(-n --"no-action" "do not actually kill processes; just print what would happen"),

src/uu/snice/src/snice.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use clap::{crate_version, Arg, Command};
1010
use prettytable::{format::consts::FORMAT_CLEAN, row, Table};
1111
pub use process_matcher::clap_args;
1212
use process_matcher::*;
13+
use std::io::Write;
1314
use std::{collections::HashSet, path::PathBuf, str::FromStr};
1415
use sysinfo::Pid;
1516
use uu_pgrep::process::ProcessInformation;
@@ -104,7 +105,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
104105
};
105106

106107
let pids = collect_pids(&targets);
107-
let results = perform_action(&pids, &priority, take_action);
108+
let results = perform_action(&pids, &priority, take_action, settings.interactive);
108109

109110
if results.iter().all(|it| it.is_none()) || results.is_empty() {
110111
return Err(USimpleError::new(1, "no process selection criteria"));
@@ -124,6 +125,41 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
124125
Ok(())
125126
}
126127

128+
pub fn ask_user(pid: u32) -> bool {
129+
let process = process_snapshot().process(Pid::from_u32(pid)).unwrap();
130+
131+
let tty = ProcessInformation::try_new(PathBuf::from_str(&format!("/proc/{pid}")).unwrap())
132+
.map(|v| v.tty().to_string())
133+
.unwrap_or(String::from("?"));
134+
135+
let user = process
136+
.user_id()
137+
.and_then(|uid| users().iter().find(|it| it.id() == uid))
138+
.map(|it| it.name())
139+
.unwrap_or("?")
140+
.to_owned();
141+
142+
let cmd = process
143+
.exe()
144+
.and_then(|it| it.iter().next_back())
145+
.unwrap_or("?".as_ref());
146+
let cmd = cmd.to_str().unwrap();
147+
148+
// no newline at the end
149+
print!("{tty:<8} {user:<8} {pid:<5} {cmd:<18} ? ");
150+
std::io::stdout().flush().unwrap();
151+
let mut input = String::new();
152+
if std::io::stdin().read_line(&mut input).is_err() {
153+
return false;
154+
}
155+
let input = input.trim();
156+
if input.eq_ignore_ascii_case("y") || input.eq_ignore_ascii_case("yes") {
157+
return true;
158+
}
159+
160+
false
161+
}
162+
127163
#[allow(unused)]
128164
pub fn construct_verbose_result(
129165
pids: &[u32],

tests/by-util/test_snice.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ fn test_no_args() {
1616
fn test_no_process_selected() {
1717
new_ucmd!().arg("-u=invalid_user").fails().code_is(1);
1818
}
19+
20+
#[test]
21+
fn test_interactive_conflict_args() {
22+
new_ucmd!().args(&["-i", "-v"]).fails().code_is(1);
23+
new_ucmd!().args(&["-i", "-n"]).fails().code_is(1);
24+
}

0 commit comments

Comments
 (0)