diff --git a/Cargo.lock b/Cargo.lock index 4c2e401..ba99a63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,130 +2,12 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "anstream" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "clap" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - [[package]] name = "signal-hook" version = "0.3.17" @@ -149,18 +31,11 @@ dependencies = [ name = "sonar" version = "0.10.0" dependencies = [ - "clap", "libc", "signal-hook", "subprocess", ] -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - [[package]] name = "subprocess" version = "0.2.9" @@ -171,29 +46,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "syn" -version = "2.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "winapi" version = "0.3.9" @@ -215,69 +67,3 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml index 63ff867..b7a73f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" [dependencies] subprocess = "0.2" -clap = { version = "4.5", features = ["derive"] } libc = "0.2" signal-hook = { version = "0.3", default-features = false, features = [] } diff --git a/src/main.rs b/src/main.rs index 288d656..5172b92 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use clap::{Parser, Subcommand}; - mod amd; mod batchless; mod command; @@ -19,60 +17,41 @@ mod util; const TIMEOUT_SECONDS: u64 = 5; // For subprocesses -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] enum Commands { /// Take a snapshot of the currently running processes PS { /// Synthesize a job ID from the process tree in which a process finds itself - #[arg(long, default_value_t = false)] batchless: bool, /// Merge process records that have the same job ID and command name - #[arg(long, default_value_t = false)] rollup: bool, /// Include records for jobs that have on average used at least this percentage of CPU, /// note this is nonmonotonic [default: none] - #[arg(long)] min_cpu_percent: Option, /// Include records for jobs that presently use at least this percentage of real memory, /// note this is nonmonotonic [default: none] - #[arg(long)] min_mem_percent: Option, /// Include records for jobs that have used at least this much CPU time (in seconds) /// [default: none] - #[arg(long)] min_cpu_time: Option, /// Exclude records for system jobs (uid < 1000) - #[arg(long, default_value_t = false)] exclude_system_jobs: bool, /// Exclude records whose users match these comma-separated names [default: none] - #[arg(long)] exclude_users: Option, /// Exclude records whose commands start with these comma-separated names [default: none] - #[arg(long)] exclude_commands: Option, /// Create a per-host lockfile in this directory and exit early if the file exists on startup [default: none] - #[arg(long)] lockdir: Option, }, /// Extract system information Sysinfo {}, - /// Not yet implemented - Analyze {}, } fn main() { @@ -84,9 +63,7 @@ fn main() { log::init(); - let cli = Cli::parse(); - - match &cli.command { + match &command_line() { Commands::PS { rollup, batchless, @@ -128,8 +105,149 @@ fn main() { Commands::Sysinfo {} => { sysinfo::show_system(×tamp); } - Commands::Analyze {} => { - println!("sonar analyze not yet completed"); + } +} + +// For the sake of simplicity: +// - allow repeated options to overwrite earlier values +// - all error reporting is via a generic "usage" message, without specificity as to what was wrong + +fn command_line() -> Commands { + let mut args = std::env::args(); + let _executable = args.next(); + if let Some(command) = args.next() { + match command.as_str() { + "ps" => { + let mut batchless = false; + let mut rollup = false; + let mut min_cpu_percent = None; + let mut min_mem_percent = None; + let mut min_cpu_time = None; + let mut exclude_system_jobs = false; + let mut exclude_users = None; + let mut exclude_commands = None; + let mut lockdir = None; + loop { + if let Some(arg) = args.next() { + match arg.as_str() { + "--batchless" => { + batchless = true; + } + "--rollup" => { + rollup = true; + } + "--exclude-system-jobs" => { + exclude_system_jobs = true; + } + "--exclude-users" => { + (args, exclude_users) = string_value(args); + } + "--exclude-commands" => { + (args, exclude_commands) = string_value(args); + } + "--lockdir" => { + (args, lockdir) = string_value(args); + } + "--min-cpu-percent" => { + (args, min_cpu_percent) = parsed_value::(args); + } + "--min-mem-percent" => { + (args, min_mem_percent) = parsed_value::(args); + } + "--min-cpu-time" => { + (args, min_cpu_time) = parsed_value::(args); + } + _ => { + usage(true); + } + } + } else { + break; + } + } + return Commands::PS { + batchless, + rollup, + min_cpu_percent, + min_mem_percent, + min_cpu_time, + exclude_system_jobs, + exclude_users, + exclude_commands, + lockdir, + }; + } + "sysinfo" => return Commands::Sysinfo {}, + "help" => { + usage(false); + } + _ => { + usage(true); + } + } + } else { + usage(true); + } +} + +fn string_value(mut args: std::env::Args) -> (std::env::Args, Option) { + if let Some(val) = args.next() { + (args, Some(val)) + } else { + usage(true); + } +} + +fn parsed_value(mut args: std::env::Args) -> (std::env::Args, Option) { + if let Some(val) = args.next() { + match val.parse::() { + Ok(value) => (args, Some(value)), + _ => { + usage(true); + } } + } else { + usage(true); } } + +fn usage(is_error: bool) -> ! { + let mut stdout = std::io::stdout(); + let mut stderr = std::io::stderr(); + let out: &mut dyn std::io::Write = if is_error { &mut stderr } else { &mut stdout }; + let _ = out.write( + b"Usage: sonar + +Commands: + ps Take a snapshot of the currently running processes + sysinfo Extract system information + help Print this message + +Options for `ps`: + --batchless + Synthesize a job ID from the process tree in which a process finds itself + --rollup + Merge process records that have the same job ID and command name + --min-cpu-percent percentage + Include records for jobs that have on average used at least this + percentage of CPU, note this is nonmonotonic [default: none] + --min-mem-percent percentage + Include records for jobs that presently use at least this percentage of + real memory, note this is nonmonotonic [default: none] + --min-cpu-time seconds + Include records for jobs that have used at least this much CPU time + [default: none] + --exclude-system-jobs + Exclude records for system jobs (uid < 1000) + --exclude-users user,user,... + Exclude records whose users match these names [default: none] + --exclude-commands command,command,... + Exclude records whose commands start with these names [default: none] + --lockdir directory + Create a per-host lockfile in this directory and exit early if the file + exists on startup [default: none] +", + ); + let _ = out.flush(); + std::process::exit(if is_error { 2 } else { 0 }); +}