1use clap::{Arg, ArgAction, Command};
9use nix::sys::signal::{self, Signal};
10use nix::unistd::Pid;
11use std::io::Error;
12use uucore::display::Quotable;
13use uucore::error::{FromIo, UResult, USimpleError};
14use uucore::translate;
15
16use uucore::signals::{ALL_SIGNALS, signal_by_name_or_value, signal_name_by_value};
17use uucore::{format_usage, show};
18
19const OFFSET: usize = 128;
23
24pub mod options {
25 pub static PIDS_OR_SIGNALS: &str = "pids_or_signals";
26 pub static LIST: &str = "list";
27 pub static TABLE: &str = "table";
28 pub static SIGNAL: &str = "signal";
29}
30
31#[derive(Clone, Copy)]
32pub enum Mode {
33 Kill,
34 Table,
35 List,
36}
37
38#[uucore::main]
39pub fn uumain(args: impl uucore::Args) -> UResult<()> {
40 let mut args = args.collect_ignore();
41 let obs_signal = handle_obsolete(&mut args);
42
43 let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
44
45 let mode = if matches.get_flag(options::TABLE) {
46 Mode::Table
47 } else if matches.get_flag(options::LIST) {
48 Mode::List
49 } else {
50 Mode::Kill
51 };
52
53 let pids_or_signals: Vec<String> = matches
54 .get_many::<String>(options::PIDS_OR_SIGNALS)
55 .map(|v| v.map(ToString::to_string).collect())
56 .unwrap_or_default();
57
58 match mode {
59 Mode::Kill => {
60 let sig = if let Some(signal) = obs_signal {
61 signal
62 } else if let Some(signal) = matches.get_one::<String>(options::SIGNAL) {
63 parse_signal_value(signal)?
64 } else {
65 15_usize };
67
68 let sig_name = signal_name_by_value(sig);
69 let sig: Option<Signal> = if sig_name.is_some_and(|name| name == "EXIT") {
72 None
73 } else {
74 let sig = (sig as i32)
75 .try_into()
76 .map_err(|e| Error::from_raw_os_error(e as i32))?;
77 Some(sig)
78 };
79
80 let pids = parse_pids(&pids_or_signals)?;
81 if pids.is_empty() {
82 Err(USimpleError::new(1, translate!("kill-error-no-process-id")))
83 } else {
84 kill(sig, &pids);
85 Ok(())
86 }
87 }
88 Mode::Table => {
89 table();
90 Ok(())
91 }
92 Mode::List => {
93 list(&pids_or_signals);
94 Ok(())
95 }
96 }
97}
98
99pub fn uu_app() -> Command {
100 Command::new(uucore::util_name())
101 .version(uucore::crate_version!())
102 .help_template(uucore::localized_help_template(uucore::util_name()))
103 .about(translate!("kill-about"))
104 .override_usage(format_usage(&translate!("kill-usage")))
105 .infer_long_args(true)
106 .allow_negative_numbers(true)
107 .arg(
108 Arg::new(options::LIST)
109 .short('l')
110 .long(options::LIST)
111 .help(translate!("kill-help-list"))
112 .conflicts_with(options::TABLE)
113 .action(ArgAction::SetTrue),
114 )
115 .arg(
116 Arg::new(options::TABLE)
117 .short('t')
118 .short_alias('L')
119 .long(options::TABLE)
120 .help(translate!("kill-help-table"))
121 .action(ArgAction::SetTrue),
122 )
123 .arg(
124 Arg::new(options::SIGNAL)
125 .short('s')
126 .short_alias('n') .long(options::SIGNAL)
128 .value_name("signal")
129 .help(translate!("kill-help-signal"))
130 .conflicts_with_all([options::LIST, options::TABLE]),
131 )
132 .arg(
133 Arg::new(options::PIDS_OR_SIGNALS)
134 .hide(true)
135 .action(ArgAction::Append),
136 )
137}
138
139fn handle_obsolete(args: &mut Vec<String>) -> Option<usize> {
140 if args.len() > 2 {
142 let slice = args[1].as_str();
144 if let Some(signal) = slice.strip_prefix('-') {
145 if signal.chars().next().is_some_and(|c| c.is_lowercase()) {
147 return None;
148 }
149 let opt_signal = signal_by_name_or_value(signal);
151 if opt_signal.is_some() {
152 args.remove(1);
154 return opt_signal;
155 }
156 }
157 }
158 None
159}
160
161fn table() {
162 for (idx, signal) in ALL_SIGNALS.iter().enumerate() {
163 println!("{idx: >#2} {signal}");
164 }
165}
166
167fn print_signal(signal_name_or_value: &str) -> UResult<()> {
168 let lower_8_bits = |x: usize| x & 0xff;
174 let option_num_parse = signal_name_or_value.parse::<usize>().ok();
175
176 for (value, &signal) in ALL_SIGNALS.iter().enumerate() {
177 if signal.eq_ignore_ascii_case(signal_name_or_value)
178 || format!("SIG{signal}").eq_ignore_ascii_case(signal_name_or_value)
179 {
180 println!("{value}");
181 return Ok(());
182 } else if signal_name_or_value == value.to_string()
183 || option_num_parse.is_some_and(|signal_value| lower_8_bits(signal_value) == value)
184 || option_num_parse.is_some_and(|signal_value| signal_value == value + OFFSET)
185 {
186 println!("{signal}");
187 return Ok(());
188 }
189 }
190 Err(USimpleError::new(
191 1,
192 translate!("kill-error-invalid-signal", "signal" => signal_name_or_value.quote()),
193 ))
194}
195
196fn print_signals() {
197 for signal in ALL_SIGNALS {
198 println!("{signal}");
199 }
200}
201
202fn list(signals: &Vec<String>) {
203 if signals.is_empty() {
204 print_signals();
205 } else {
206 for signal in signals {
207 if let Err(e) = print_signal(signal) {
208 uucore::show!(e);
209 }
210 }
211 }
212}
213
214fn parse_signal_value(signal_name: &str) -> UResult<usize> {
215 let optional_signal_value = signal_by_name_or_value(signal_name);
216 match optional_signal_value {
217 Some(x) => Ok(x),
218 None => Err(USimpleError::new(
219 1,
220 translate!("kill-error-invalid-signal", "signal" => signal_name.quote()),
221 )),
222 }
223}
224
225fn parse_pids(pids: &[String]) -> UResult<Vec<i32>> {
226 pids.iter()
227 .map(|x| {
228 x.parse::<i32>().map_err(|e| {
229 USimpleError::new(
230 1,
231 translate!("kill-error-parse-argument", "argument" => x.quote(), "error" => e),
232 )
233 })
234 })
235 .collect()
236}
237
238fn kill(sig: Option<Signal>, pids: &[i32]) {
239 for &pid in pids {
240 if let Err(e) = signal::kill(Pid::from_raw(pid), sig) {
241 show!(
242 Error::from_raw_os_error(e as i32)
243 .map_err_context(|| { translate!("kill-error-sending-signal", "pid" => pid) })
244 );
245 }
246 }
247}