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 #[cfg(unix)]
65 let custom_signal = if let Some(ref sig_name) = self.signal {
66 Some(parse_signal_name(sig_name)?)
67 } else {
68 None
69 };
70 #[cfg(not(unix))]
72 if self.signal.is_some() {
73 return Err(ProcError::InvalidInput(
74 "--signal is not supported on Windows (proc stop asks the process to exit)"
75 .to_string(),
76 ));
77 }
78
79 let targets = parse_targets(&self.target);
82 let (mut processes, not_found) = resolve_targets_excluding_self(&targets);
83
84 if !not_found.is_empty() {
86 printer.warning(&format!("Not found: {}", not_found.join(", ")));
87 }
88
89 apply_filters(&mut processes, &self.in_dir, &self.by_name);
91
92 if processes.is_empty() {
93 return Err(ProcError::ProcessNotFound(self.target.clone()));
94 }
95
96 if self.dry_run {
98 printer.print_dry_run("stop", &processes);
99 return Ok(());
100 }
101
102 if !printer.ask_confirm("stop", &processes, self.yes)? {
104 return Ok(());
105 }
106
107 let mut stopped = Vec::new();
109 let mut failed = Vec::new();
110
111 for proc in &processes {
112 #[cfg(unix)]
113 let send_result = if let Some(signal) = custom_signal {
114 proc.send_signal(signal)
115 } else {
116 proc.terminate()
117 };
118 #[cfg(not(unix))]
119 let send_result = proc.terminate();
120
121 match send_result {
122 Ok(()) => {
123 let stopped_gracefully = self.wait_for_exit(proc);
125 if stopped_gracefully {
126 stopped.push(proc.clone());
127 } else {
128 match proc.kill_and_wait() {
130 Ok(_) => stopped.push(proc.clone()),
131 Err(e) => failed.push((proc.clone(), e.to_string())),
132 }
133 }
134 }
135 Err(e) => failed.push((proc.clone(), e.to_string())),
136 }
137 }
138
139 printer.print_action_result("stop", &stopped, &failed)
141 }
142
143 fn wait_for_exit(&self, proc: &Process) -> bool {
144 let start = std::time::Instant::now();
145 let timeout = std::time::Duration::from_secs(self.timeout);
146
147 while start.elapsed() < timeout {
148 if !proc.is_running() {
149 return true;
150 }
151 std::thread::sleep(std::time::Duration::from_millis(100));
152 }
153
154 false
155 }
156}