proc_cli/commands/
stuck.rs1use crate::core::{apply_filters, Process};
9use crate::error::Result;
10use crate::ui::{plural, Printer};
11use clap::Args;
12use dialoguer::Confirm;
13use std::time::Duration;
14
15#[derive(Args, Debug)]
17pub struct StuckCommand {
18 #[arg(long, short = 't', default_value = "300")]
20 pub timeout: u64,
21
22 #[arg(long, short = 'k')]
24 pub kill: bool,
25
26 #[arg(long)]
28 pub dry_run: bool,
29
30 #[arg(long, short = 'y')]
32 pub yes: bool,
33
34 #[arg(long, short = 'j')]
36 pub json: bool,
37
38 #[arg(long, short = 'v')]
40 pub verbose: bool,
41
42 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
44 pub in_dir: Option<String>,
45
46 #[arg(long = "by", short = 'b')]
48 pub by_name: Option<String>,
49}
50
51impl StuckCommand {
52 pub fn execute(&self) -> Result<()> {
54 let printer = Printer::from_flags(self.json, self.verbose);
55
56 let timeout = Duration::from_secs(self.timeout);
57 let mut processes = Process::find_stuck(timeout)?;
58
59 apply_filters(&mut processes, &self.in_dir, &self.by_name);
61
62 if processes.is_empty() {
63 printer.print_empty_result(
64 "stuck",
65 &format!("No stuck processes found (threshold: {}s)", self.timeout),
66 );
67 return Ok(());
68 }
69
70 printer.warning(&format!(
71 "Found {} potentially stuck process{}",
72 processes.len(),
73 plural(processes.len())
74 ));
75
76 if !self.kill {
77 printer.print_processes_as("stuck", &processes, None);
78 return Ok(());
79 }
80
81 if self.dry_run {
83 printer.print_dry_run_with(
84 "stuck",
85 &processes,
86 &format!(
87 "Dry run: would kill {} stuck process{}",
88 processes.len(),
89 plural(processes.len())
90 ),
91 );
92 return Ok(());
93 }
94
95 if !self.json {
97 printer.print_processes_as("stuck", &processes, None);
98 }
99
100 if !self.yes && !self.json {
101 let confirmed = Confirm::new()
102 .with_prompt(format!(
103 "Kill {} stuck process{}?",
104 processes.len(),
105 plural(processes.len())
106 ))
107 .default(false)
108 .interact()?;
109
110 if !confirmed {
111 printer.warning("Cancelled");
112 return Ok(());
113 }
114 }
115
116 let mut killed = Vec::new();
117 let mut failed = Vec::new();
118
119 for proc in processes {
120 match proc.kill_and_wait() {
122 Ok(_) => killed.push(proc),
123 Err(e) => failed.push((proc, e.to_string())),
124 }
125 }
126
127 printer.print_action_result_as("stuck", "kill", &killed, &failed)
128 }
129}