From d308aefcf2c707e39632b5c4d01774075fd3d80c Mon Sep 17 00:00:00 2001 From: Krysztal Huang Date: Wed, 7 Aug 2024 23:33:37 +0800 Subject: [PATCH] pidwait: Add command `pidwait` And also introduce dependency `polling` for polling `waitpid` syscall --- Cargo.lock | 68 ++++++++++++++++- Cargo.toml | 3 + src/uu/pgrep/src/process.rs | 2 +- src/uu/pidwait/Cargo.toml | 27 +++++++ src/uu/pidwait/pidwait.md | 7 ++ src/uu/pidwait/src/main.rs | 1 + src/uu/pidwait/src/pidwait.rs | 133 ++++++++++++++++++++++++++++++++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 src/uu/pidwait/Cargo.toml create mode 100644 src/uu/pidwait/pidwait.md create mode 100644 src/uu/pidwait/src/main.rs create mode 100644 src/uu/pidwait/src/pidwait.rs diff --git a/Cargo.lock b/Cargo.lock index e2ecf28..b463c54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,6 +193,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -344,6 +353,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -373,7 +388,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -384,7 +399,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -566,6 +581,27 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "polling" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix 0.38.32", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -632,6 +668,7 @@ dependencies = [ "uu_free", "uu_pgrep", "uu_pidof", + "uu_pidwait", "uu_pmap", "uu_ps", "uu_pwdx", @@ -986,6 +1023,22 @@ dependencies = [ "time-core", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -1039,6 +1092,17 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_pidwait" +version = "0.0.1" +dependencies = [ + "clap", + "polling", + "regex", + "uu_pgrep", + "uucore", +] + [[package]] name = "uu_pmap" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 06135b2..8a4f5bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ feat_common_core = [ "pgrep", "pidof", "ps", + "pidwait", ] [workspace.dependencies] @@ -57,6 +58,7 @@ chrono = { version = "0.4.38", default-features = false, features = ["clock"] } walkdir = "2.5.0" prettytable-rs = "0.10.0" nix = { version = "0.29", default-features = false } +polling = "3.7.2" [dependencies] clap = { workspace = true } @@ -78,6 +80,7 @@ slabtop = { optional = true, version = "0.0.1", package = "uu_slabtop", path = " pgrep = { optional = true, version = "0.0.1", package = "uu_pgrep", path = "src/uu/pgrep" } pidof = { optional = true, version = "0.0.1", package = "uu_pidof", path = "src/uu/pidof" } ps = { optional = true, version = "0.0.1", package = "uu_ps", path = "src/uu/ps" } +pidwait = { optional = true, version = "0.0.1", package = "uu_pidwait", path = "src/uu/pidwait" } [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/src/uu/pgrep/src/process.rs b/src/uu/pgrep/src/process.rs index 67229f4..d0c63c7 100644 --- a/src/uu/pgrep/src/process.rs +++ b/src/uu/pgrep/src/process.rs @@ -97,7 +97,7 @@ impl TryFrom for Teletype { } /// State or process -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum RunState { ///`R`, running Running, diff --git a/src/uu/pidwait/Cargo.toml b/src/uu/pidwait/Cargo.toml new file mode 100644 index 0000000..14ad159 --- /dev/null +++ b/src/uu/pidwait/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "uu_pidwait" +version = "0.0.1" +edition = "2021" +authors = ["uutils developers"] +license = "MIT" +description = "pidwait ~ (uutils) Wait for processes based on name" + +homepage = "https://github.com/uutils/procps" +repository = "https://github.com/uutils/procps/tree/main/src/uu/pidwait" +keywords = ["acl", "uutils", "cross-platform", "cli", "utility"] +categories = ["command-line-utilities"] + + +[dependencies] +uucore = { workspace = true } +clap = { workspace = true } +polling = { workspace = true } +regex = { workspace = true } +uu_pgrep = { path = "../pgrep" } + +[lib] +path = "src/pidwait.rs" + +[[bin]] +name = "pidwait" +path = "src/main.rs" diff --git a/src/uu/pidwait/pidwait.md b/src/uu/pidwait/pidwait.md new file mode 100644 index 0000000..fb60ef5 --- /dev/null +++ b/src/uu/pidwait/pidwait.md @@ -0,0 +1,7 @@ +# pidwait + +``` +pidwait [options] pattern +``` + +Wait for processes based on name. diff --git a/src/uu/pidwait/src/main.rs b/src/uu/pidwait/src/main.rs new file mode 100644 index 0000000..5a989ee --- /dev/null +++ b/src/uu/pidwait/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_pidwait); diff --git a/src/uu/pidwait/src/pidwait.rs b/src/uu/pidwait/src/pidwait.rs new file mode 100644 index 0000000..21eeea7 --- /dev/null +++ b/src/uu/pidwait/src/pidwait.rs @@ -0,0 +1,133 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use clap::{arg, crate_version, ArgMatches, Command}; +use regex::Regex; +use std::{collections::HashSet, env, sync::OnceLock}; +use uu_pgrep::process::{walk_process, ProcessInformation, RunState, Teletype}; +use uucore::{ + error::{UResult, USimpleError}, + format_usage, help_about, help_usage, +}; + +const ABOUT: &str = help_about!("pidwait.md"); +const USAGE: &str = help_usage!("pidwait.md"); + +static REGEX: OnceLock = OnceLock::new(); + +#[derive(Debug)] +struct Settings { + echo: bool, + count: bool, + full: bool, + ignore_case: bool, + newest: bool, + oldest: bool, + older: usize, + terminal: HashSet, + exact: bool, + runstates: HashSet, +} + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let matches = uu_app().try_get_matches_from(args)?; + + let settings = Settings { + echo: matches.get_flag("echo"), + count: matches.get_flag("count"), + full: matches.get_flag("full"), + ignore_case: matches.get_flag("ignore-case"), + newest: matches.get_flag("newest"), + oldest: matches.get_flag("oldest"), + older: matches + .get_one::("older") + .copied() + .unwrap_or_default(), + terminal: matches + .get_many("terminal") + .unwrap_or_default() + .cloned() + .collect(), + exact: matches.get_flag("exact"), + runstates: matches + .get_many("terminal") + .unwrap_or_default() + .cloned() + .collect(), + }; + + let pattern = try_get_pattern_from(&matches, &settings)?; + REGEX + .set(Regex::new(&pattern).map_err(|e| USimpleError::new(2, e.to_string()))?) + .unwrap(); + + collect_proc_infos(settings); + + Ok(()) +} + +fn try_get_pattern_from(matches: &ArgMatches, settings: &Settings) -> UResult { + let pattern = match matches.get_many::("pattern") { + Some(patterns) if patterns.len() > 1 => { + return Err(USimpleError::new( + 2, + "only one pattern can be provided\nTry `pidwait --help' for more information.", + )) + } + Some(mut patterns) => patterns.next().unwrap(), + None => return Ok(String::new()), + }; + + let pattern = if settings.ignore_case { + &pattern.to_lowercase() + } else { + pattern + }; + + let pattern = if settings.exact { + &format!("^{}$", pattern) + } else { + pattern + }; + + Ok(pattern.to_string()) +} + +fn collect_proc_infos(settings: Settings) -> Vec { + let proc_infos: Vec<_> = walk_process().collect(); + + proc_infos +} + +#[allow(clippy::cognitive_complexity)] +pub fn uu_app() -> Command { + Command::new(env!("CARGO_PKG_NAME")) + .version(crate_version!()) + .about(ABOUT) + .override_usage(format_usage(USAGE)) + .infer_long_args(true) + .args([ + arg!(-e --echo "display PIDs before waiting"), + arg!(-c --count "count of matching processes"), + arg!(-f --full "use full process name to match"), + // arg!(-g --pgroup "match listed process group IDs"), + // arg!(-G --group "match real group IDs"), + arg!(-i --"ignore-case" "match case insensitively"), + arg!(-n --newest "select most recently started"), + arg!(-o --oldest "select least recently started"), + arg!(-O --older "select where older than seconds"), + // arg!(-P --parent "match only child processes of the given parent"), + // arg!(-s --session "match session IDs"), + arg!(-t --terminal "match by controlling terminal"), + // arg!(-u --euid "match by effective IDs"), + // arg!(-U --uid "match by real IDs"), + arg!(-x --exact "match exactly with the command name"), + // arg!(-F --pidfile "read PIDs from file"), + // arg!(-L --logpidfile "fail if PID file is not locked"), + arg!(-r --runstates "match runstates [D,S,Z,...]"), + // arg!(-A --"ignore-ancestors" "exclude our ancestors from results"), + ]) +}