proc_cli/commands/
stop.rs1#[cfg(unix)]
11use crate::core::parse_signal_name;
12use crate::core::{apply_filters, parse_targets, resolve_targets_excluding_self, Process};
13use crate::error::{ProcError, Result};
14use crate::ui::Printer;
15use clap::Args;
16
17#[derive(Args, Debug)]
19pub struct StopCommand {
20 #[arg(required = true)]
22 target: String,
23
24 #[arg(long, short = 'y')]
26 yes: bool,
27
28 #[arg(long)]
30 dry_run: bool,
31
32 #[arg(long, short = 'j')]
34 json: bool,
35
36 #[arg(long, short = 'v')]
38 verbose: bool,
39
40 #[arg(long, short, default_value = "10")]
42 timeout: u64,
43
44 #[arg(long = "in", short = 'i', num_args = 0..=1, default_missing_value = ".")]
46 pub in_dir: Option<String>,
47
48 #[arg(long = "by", short = 'b')]
50 pub by_name: Option<String>,
51
52 #[arg(long, short = 'S')]
54 pub signal: Option<String>,
55}
56
57impl StopCommand {
58 pub fn execute(&self) -> Result<()> {
60 let printer = Printer::from_flags(self.json, self.verbose);
61
62 let targets = parse_targets(&self.target);
65 let (mut processes, not_found) = resolve_targets_excluding_self(&targets);
66
67 if !not_found.is_empty() {
69 printer.warning(&format!("Not found: {}", not_found.join(", ")));
70 }
71
72 apply_filters(&mut processes, &self.in_dir, &self.by_name);
74
75 if processes.is_empty() {
76 return Err(ProcError::ProcessNotFound(self.target.clone()));
77 }
78
79 if self.dry_run {
81 printer.print_dry_run("stop", &processes);
82 return Ok(());
83 }
84
85 if !printer.ask_confirm("stop", &processes, self.yes)? {
87 return Ok(());
88 }
89
90 #[cfg(unix)]
92 let custom_signal = if let Some(ref sig_name) = self.signal {
93 Some(parse_signal_name(sig_name)?)
94 } else {
95 None
96 };
97
98 let mut stopped = Vec::new();
100 let mut failed = Vec::new();
101
102 for proc in &processes {
103 #[cfg(unix)]
104 let send_result = if let Some(signal) = custom_signal {
105 proc.send_signal(signal)
106 } else {
107 proc.terminate()
108 };
109 #[cfg(not(unix))]
110 let send_result = proc.terminate();
111
112 match send_result {
113 Ok(()) => {
114 let stopped_gracefully = self.wait_for_exit(proc);
116 if stopped_gracefully {
117 stopped.push(proc.clone());
118 } else {
119 match proc.kill_and_wait() {
121 Ok(_) => stopped.push(proc.clone()),
122 Err(e) => failed.push((proc.clone(), e.to_string())),
123 }
124 }
125 }
126 Err(e) => failed.push((proc.clone(), e.to_string())),
127 }
128 }
129
130 printer.print_action_result("stop", &stopped, &failed);
132
133 Ok(())
134 }
135
136 fn wait_for_exit(&self, proc: &Process) -> bool {
137 let start = std::time::Instant::now();
138 let timeout = std::time::Duration::from_secs(self.timeout);
139
140 while start.elapsed() < timeout {
141 if !proc.is_running() {
142 return true;
143 }
144 std::thread::sleep(std::time::Duration::from_millis(100));
145 }
146
147 false
148 }
149}