From 78d267d332e3546582b21076889263cfb092c889 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 11:11:59 +0530 Subject: [PATCH 1/8] Basic scaffold Signed-off-by: Malhar Vora --- Cargo.lock | 9 +++++++++ Cargo.toml | 2 ++ src/uu/kill/Cargo.toml | 15 +++++++++++++++ src/uu/kill/kill.md | 7 +++++++ src/uu/kill/src/kill.rs | 29 +++++++++++++++++++++++++++++ src/uu/kill/src/main.rs | 1 + 6 files changed, 63 insertions(+) create mode 100644 src/uu/kill/Cargo.toml create mode 100644 src/uu/kill/kill.md create mode 100644 src/uu/kill/src/kill.rs create mode 100644 src/uu/kill/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 0f364dfe..80b25a5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1540,6 +1540,7 @@ dependencies = [ "uu_dmesg", "uu_fsfreeze", "uu_hexdump", + "uu_kill", "uu_last", "uu_lscpu", "uu_lsipc", @@ -1635,6 +1636,14 @@ dependencies = [ "uucore 0.2.2", ] +[[package]] +name = "uu_kill" +version = "0.0.1" +dependencies = [ + "clap", + "uucore 0.2.2", +] + [[package]] name = "uu_last" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index a35f33c5..11b45436 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ feat_common_core = [ "dmesg", "fsfreeze", "hexdump", + "kill", "last", "lscpu", "lsipc", @@ -105,6 +106,7 @@ chcpu = { optional = true, version = "0.0.1", package = "uu_chcpu", path = "src/ ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" } dmesg = { optional = true, version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg" } fsfreeze = { optional = true, version = "0.0.1", package = "uu_fsfreeze", path = "src/uu/fsfreeze" } +kill = { optional = true, version = "0.0.1", package = "uu_kill", path = "src/uu/kill" } hexdump = { optional = true, version = "0.0.1", package = "uu_hexdump", path = "src/uu/hexdump" } last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu/last" } lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } diff --git a/src/uu/kill/Cargo.toml b/src/uu/kill/Cargo.toml new file mode 100644 index 00000000..2a927068 --- /dev/null +++ b/src/uu/kill/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "uu_kill" +version = "0.0.1" +edition = "2024" + +[lib] +path = "src/kill.rs" + +[[bin]] +name = "kill" +path = "src/main.rs" + +[dependencies] +uucore = { workspace = true, features = ["entries"] } +clap = { workspace = true } diff --git a/src/uu/kill/kill.md b/src/uu/kill/kill.md new file mode 100644 index 00000000..6ab558bd --- /dev/null +++ b/src/uu/kill/kill.md @@ -0,0 +1,7 @@ +# lsns + +``` +kill [OPTION]... +``` + +Send a signal to a job. diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs new file mode 100644 index 00000000..f8b181bc --- /dev/null +++ b/src/uu/kill/src/kill.rs @@ -0,0 +1,29 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +// Remove this if the tool is ported to Non-UNIX platforms. + +use clap::{Command, crate_version}; +#[cfg(target_os = "linux")] +#[cfg(target_os = "linux")] +use uucore::{error::UResult, format_usage, help_about, help_usage}; + +const ABOUT: &str = help_about!("kill.md"); +const USAGE: &str = help_usage!("kill.md"); + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let _matches = uu_app().try_get_matches_from(args)?; + + Ok(()) +} + +pub fn uu_app() -> Command { + Command::new(uucore::util_name()) + .version(crate_version!()) + .about(ABOUT) + .override_usage(format_usage(USAGE)) + .infer_long_args(true) +} diff --git a/src/uu/kill/src/main.rs b/src/uu/kill/src/main.rs new file mode 100644 index 00000000..6f3fbac4 --- /dev/null +++ b/src/uu/kill/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_kill); From 5c7a6c574dd21f4b317a554e79dad8e359db132d Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 12:32:24 +0530 Subject: [PATCH 2/8] Implement basic functional kill command with some tests Signed-off-by: Malhar Vora --- src/uu/kill/src/kill.rs | 20 ++++++++++++++++++-- tests/by-util/test_kill.rs | 23 +++++++++++++++++++++++ tests/tests.rs | 5 +++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/by-util/test_kill.rs diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index f8b181bc..153c9e6c 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -5,7 +5,8 @@ // Remove this if the tool is ported to Non-UNIX platforms. -use clap::{Command, crate_version}; +use clap::{Arg, ArgAction, Command, crate_version, value_parser}; +use uucore::libc; #[cfg(target_os = "linux")] #[cfg(target_os = "linux")] use uucore::{error::UResult, format_usage, help_about, help_usage}; @@ -13,9 +14,16 @@ use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("kill.md"); const USAGE: &str = help_usage!("kill.md"); + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let _matches = uu_app().try_get_matches_from(args)?; + let matches = uu_app().try_get_matches_from(args)?; + + if let Some(pids) = matches.get_many::("pid") { + for pid in pids { + unsafe { libc::kill(*pid, libc::SIGTERM) }; + } + } Ok(()) } @@ -26,4 +34,12 @@ pub fn uu_app() -> Command { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) + .arg( + Arg::new("pid") + .help("PID of the process to kill") + .required(false) + .action(ArgAction::Append) + .value_name("PID") + .value_parser(value_parser!(i32)), + ) } diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs new file mode 100644 index 00000000..8f14ab26 --- /dev/null +++ b/tests/by-util/test_kill.rs @@ -0,0 +1,23 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use uutests::new_ucmd; + +#[test] +fn test_invalid_arg() { + new_ucmd!().arg("--definitely-invalid").fails().code_is(1); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_non_number_pid() { + let res = new_ucmd!().arg("xyz").run(); + + let stdout = res.stdout_str(); + let stderr = res.stderr_str(); + + assert!(stdout.trim().len() == 0); + assert!(stderr.contains("invalid value 'xyz'")); +} diff --git a/tests/tests.rs b/tests/tests.rs index 09ffc245..8a53def2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -15,6 +15,11 @@ fn init() { std::env::set_var("UUTESTS_BINARY_PATH", TESTS_BINARY); } } + +#[cfg(feature = "kill")] +#[path = "by-util/test_kill.rs"] +mod test_kill; + #[cfg(feature = "lscpu")] #[path = "by-util/test_lscpu.rs"] mod test_lscpu; From 89d00aa5bf9733480e205b24e51eff53d01aab03 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 12:53:54 +0530 Subject: [PATCH 3/8] Add return value handling and some tests Signed-off-by: Malhar Vora --- src/uu/kill/src/kill.rs | 14 +++++++++++++- tests/by-util/test_kill.rs | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 153c9e6c..94cc854f 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -14,7 +14,6 @@ use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("kill.md"); const USAGE: &str = help_usage!("kill.md"); - #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; @@ -22,6 +21,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { if let Some(pids) = matches.get_many::("pid") { for pid in pids { unsafe { libc::kill(*pid, libc::SIGTERM) }; + + let err = std::io::Error::last_os_error().raw_os_error(); + if let Some(err_no) = err { + match err_no { + libc::EPERM => { + eprintln!("bash: kill: ({pid}) - Operation not permitted"); + } + libc::ESRCH => { + eprintln!("bash: kill: ({pid}) - No such process"); + } + _ => {} + } + } } } diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index 8f14ab26..32bef0b6 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -12,7 +12,7 @@ fn test_invalid_arg() { #[test] #[cfg(target_os = "linux")] -fn test_non_number_pid() { +fn test_non_numerical_pid() { let res = new_ucmd!().arg("xyz").run(); let stdout = res.stdout_str(); @@ -21,3 +21,17 @@ fn test_non_number_pid() { assert!(stdout.trim().len() == 0); assert!(stderr.contains("invalid value 'xyz'")); } + +#[test] +#[cfg(target_os = "linux")] +fn test_pid_doesnt_exist() { + let non_existent_pid = "1234567890"; + let res = new_ucmd!().arg(non_existent_pid).run(); + + let stdout = res.stdout_str(); + let stderr = res.stderr_str(); + let error_msg = format!("bash: kill: ({non_existent_pid}) - No such process"); + + assert!(stdout.trim().len() == 0); + assert!(stderr.contains(error_msg.as_str())); +} From dcf5f969edb10bfd3c25830a6813740319dc6f6b Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 13:43:59 +0530 Subject: [PATCH 4/8] Some improvements in tests Signed-off-by: Malhar Vora --- src/uu/kill/src/kill.rs | 1 - tests/by-util/test_kill.rs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 94cc854f..daf8950b 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -8,7 +8,6 @@ use clap::{Arg, ArgAction, Command, crate_version, value_parser}; use uucore::libc; #[cfg(target_os = "linux")] -#[cfg(target_os = "linux")] use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("kill.md"); diff --git a/tests/by-util/test_kill.rs b/tests/by-util/test_kill.rs index 32bef0b6..a843aefc 100644 --- a/tests/by-util/test_kill.rs +++ b/tests/by-util/test_kill.rs @@ -18,7 +18,7 @@ fn test_non_numerical_pid() { let stdout = res.stdout_str(); let stderr = res.stderr_str(); - assert!(stdout.trim().len() == 0); + assert!(stdout.trim().is_empty()); assert!(stderr.contains("invalid value 'xyz'")); } @@ -32,6 +32,6 @@ fn test_pid_doesnt_exist() { let stderr = res.stderr_str(); let error_msg = format!("bash: kill: ({non_existent_pid}) - No such process"); - assert!(stdout.trim().len() == 0); + assert!(stdout.trim().is_empty()); assert!(stderr.contains(error_msg.as_str())); } From 41c2ffac7f4d7cefab881c8ad41a9ce589364798 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 13:50:54 +0530 Subject: [PATCH 5/8] Address cargo check issue for Macos-latest Signed-off-by: Malhar Vora --- src/uu/kill/src/kill.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index daf8950b..836a16e2 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -7,7 +7,6 @@ use clap::{Arg, ArgAction, Command, crate_version, value_parser}; use uucore::libc; -#[cfg(target_os = "linux")] use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("kill.md"); From 3be01aa2d94be65fe23ef55eead847d8500da2f4 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 16:51:24 +0530 Subject: [PATCH 6/8] Add error handling and some other improvements Signed-off-by: Malhar Vora --- src/uu/kill/src/errors.rs | 42 +++++++++++++++++++++++++++++++++++++++ src/uu/kill/src/kill.rs | 42 +++++++++++++++++++++++++-------------- 2 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 src/uu/kill/src/errors.rs diff --git a/src/uu/kill/src/errors.rs b/src/uu/kill/src/errors.rs new file mode 100644 index 00000000..d8128fdf --- /dev/null +++ b/src/uu/kill/src/errors.rs @@ -0,0 +1,42 @@ +// This file is part of the uutils util-linux package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::error::Error; + +use uucore::error::UError; + +#[derive(Debug)] +pub enum KillError { + /// Unsupported platform + #[cfg(not(target_os = "linux"))] + UnsupportedPlatform, + OperationNotPermitted(i32), + NoSuchProcess(i32), +} + +impl std::fmt::Display for KillError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + #[cfg(not(target_os = "linux"))] + Self::UnsupportedPlatform => write!(f, "kill is only supported on Linux for now"), + Self::OperationNotPermitted(pid) => { + write!(f, "bash: kill: ({pid}) - Operation not permitted") + } + Self::NoSuchProcess(pid) => write!(f, "bash: kill: ({pid}) - No such process"), + } + } +} + +impl UError for KillError { + fn code(&self) -> i32 { + 1 + } + + fn usage(&self) -> bool { + false + } +} + +impl Error for KillError {} diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 836a16e2..770d0311 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -4,7 +4,11 @@ // file that was distributed with this source code. // Remove this if the tool is ported to Non-UNIX platforms. +#![cfg_attr(not(target_os = "linux"), allow(dead_code))] +mod errors; + +use crate::errors::KillError::{self, NoSuchProcess, OperationNotPermitted}; use clap::{Arg, ArgAction, Command, crate_version, value_parser}; use uucore::libc; use uucore::{error::UResult, format_usage, help_about, help_usage}; @@ -12,26 +16,34 @@ use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("kill.md"); const USAGE: &str = help_usage!("kill.md"); +#[cfg(not(target_os = "linux"))] +fn kill(pid: i32, signal: i32) -> Result<(), KillError> { + Err(UnsupportedPlatform) +} + +#[cfg(target_os = "linux")] +fn kill(pid: i32, signal: i32) -> Result<(), KillError> { + unsafe { libc::kill(pid, signal) }; + + let err = std::io::Error::last_os_error().raw_os_error(); + if let Some(err_no) = err { + match err_no { + libc::EPERM => return Err(OperationNotPermitted(pid)), + libc::ESRCH => return Err(NoSuchProcess(pid)), + _ => {} + } + } + + Ok(()) +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args)?; if let Some(pids) = matches.get_many::("pid") { for pid in pids { - unsafe { libc::kill(*pid, libc::SIGTERM) }; - - let err = std::io::Error::last_os_error().raw_os_error(); - if let Some(err_no) = err { - match err_no { - libc::EPERM => { - eprintln!("bash: kill: ({pid}) - Operation not permitted"); - } - libc::ESRCH => { - eprintln!("bash: kill: ({pid}) - No such process"); - } - _ => {} - } - } + kill(*pid, libc::SIGTERM)?; } } @@ -47,7 +59,7 @@ pub fn uu_app() -> Command { .arg( Arg::new("pid") .help("PID of the process to kill") - .required(false) + .required(true) .action(ArgAction::Append) .value_name("PID") .value_parser(value_parser!(i32)), From 09f0d4c818d328a19582d50d582628b786c97708 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 17:06:54 +0530 Subject: [PATCH 7/8] Some correction for non-linux platforms Signed-off-by: Malhar Vora --- src/uu/kill/src/errors.rs | 1 - src/uu/kill/src/kill.rs | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/uu/kill/src/errors.rs b/src/uu/kill/src/errors.rs index d8128fdf..faf4ae45 100644 --- a/src/uu/kill/src/errors.rs +++ b/src/uu/kill/src/errors.rs @@ -9,7 +9,6 @@ use uucore::error::UError; #[derive(Debug)] pub enum KillError { - /// Unsupported platform #[cfg(not(target_os = "linux"))] UnsupportedPlatform, OperationNotPermitted(i32), diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 770d0311..28e5beb1 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -8,7 +8,9 @@ mod errors; -use crate::errors::KillError::{self, NoSuchProcess, OperationNotPermitted}; +use crate::errors::KillError; +#[cfg(target_os = "linux")] +use crate::errors::KillError::{NoSuchProcess, OperationNotPermitted}; use clap::{Arg, ArgAction, Command, crate_version, value_parser}; use uucore::libc; use uucore::{error::UResult, format_usage, help_about, help_usage}; @@ -18,7 +20,7 @@ const USAGE: &str = help_usage!("kill.md"); #[cfg(not(target_os = "linux"))] fn kill(pid: i32, signal: i32) -> Result<(), KillError> { - Err(UnsupportedPlatform) + Err(KillError::UnsupportedPlatform) } #[cfg(target_os = "linux")] From b68f13383008d69afb213cd2c8c30254329d7ae5 Mon Sep 17 00:00:00 2001 From: Malhar Vora Date: Thu, 11 Jun 2026 17:38:47 +0530 Subject: [PATCH 8/8] Some refactoring Signed-off-by: Malhar Vora --- src/uu/kill/src/kill.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/kill/src/kill.rs b/src/uu/kill/src/kill.rs index 28e5beb1..4f469d32 100644 --- a/src/uu/kill/src/kill.rs +++ b/src/uu/kill/src/kill.rs @@ -19,7 +19,7 @@ const ABOUT: &str = help_about!("kill.md"); const USAGE: &str = help_usage!("kill.md"); #[cfg(not(target_os = "linux"))] -fn kill(pid: i32, signal: i32) -> Result<(), KillError> { +fn kill(_pid: i32, _signal: i32) -> Result<(), KillError> { Err(KillError::UnsupportedPlatform) }