proc_cli/commands/
stuck.rs1use crate::core::{apply_filters, apply_to_each, NameMatch, Process};
15use crate::error::Result;
16use crate::ui::{must_confirm, plural, prompt, Printer};
17use clap::Args;
18use std::time::Duration;
19
20#[derive(Args, Debug)]
22pub struct StuckCommand {
23 #[arg(long, short = 't', default_value = "300")]
25 pub timeout: u64,
26
27 #[arg(long, short = 'k')]
29 pub kill: bool,
30
31 #[arg(long)]
33 pub dry_run: bool,
34
35 #[arg(long, short = 'y')]
37 pub yes: bool,
38
39 #[arg(long, short = 'j')]
41 pub json: bool,
42
43 #[arg(long, short = 'v')]
45 pub verbose: bool,
46
47 #[arg(long, short = 'w')]
49 pub wide: bool,
50
51 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
54 pub in_dir: Option<String>,
55
56 #[arg(long = "by", short = 'b')]
58 pub by_name: Option<String>,
59
60 #[arg(long)]
62 pub cmd: bool,
63}
64
65impl StuckCommand {
66 pub fn execute(&self) -> Result<()> {
69 let printer = Printer::from_flags(self.json, self.verbose).wide(self.wide);
70
71 let timeout = Duration::from_secs(self.timeout);
72 let mut processes = Process::find_stuck(timeout)?;
73
74 apply_filters(
76 &mut processes,
77 &self.in_dir,
78 &self.by_name,
79 NameMatch::from_flag(self.cmd),
80 )?;
81
82 if processes.is_empty() {
83 printer.print_empty_result(
84 "stuck",
85 &format!("No stuck processes found (threshold: {}s)", self.timeout),
86 );
87 return Ok(());
88 }
89
90 printer.warning(&format!(
91 "Found {} potentially stuck process{}",
92 processes.len(),
93 plural(processes.len())
94 ));
95
96 if !self.kill {
97 printer.print_processes_under_headline("stuck", &processes);
98 return Ok(());
99 }
100
101 if self.dry_run {
103 printer.print_dry_run_with(
104 "stuck",
105 &processes,
106 &format!(
107 "Dry run: would kill {} stuck process{}",
108 processes.len(),
109 plural(processes.len())
110 ),
111 );
112 return Ok(());
113 }
114
115 let asking = must_confirm("stuck --kill", self.yes, self.json, &processes, None);
117
118 if !self.json && !matches!(asking, Ok(true)) {
121 printer.print_processes_under_headline("stuck", &processes);
122 }
123
124 if asking? {
125 let question = format!(
126 "Kill {} stuck process{}?",
127 processes.len(),
128 plural(processes.len())
129 );
130 let list = || printer.print_processes_under_headline("stuck", &processes);
131 let confirmed = prompt::ask("stuck --kill", &processes, None, &question, list)?;
132
133 if !confirmed {
134 printer.warning("Cancelled");
135 return Ok(());
136 }
137 }
138
139 let (killed, failed) = apply_to_each(&processes, Process::kill_and_wait);
141
142 printer.print_action_result_as("stuck", "kill", &killed, &failed)
143 }
144}